1/* $NetBSD: omap4_prcm.c,v 1.1 2025/12/16 12:20:22 skrll Exp $ */ 2 3/*- 4 * Copyright (c) 2025 Rui-Xiang Guo 5 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31 32__KERNEL_RCSID(1, "$NetBSD: omap4_prcm.c,v 1.1 2025/12/16 12:20:22 skrll Exp $"); 33 34#include <sys/param.h> 35#include <sys/bus.h> 36#include <sys/device.h> 37#include <sys/systm.h> 38 39#include <dev/fdt/fdtvar.h> 40 41#define TI_PRCM_PRIVATE 42#include <arm/ti/ti_prcm.h> 43 44#define CM1_ABE 0x0500 45 46#define CM2_CORE 0x0700 47#define CM2_L3INIT 0x1300 48#define CM2_L4PER 0x1400 49 50#define CM_WKUP 0x1800 51 52#define CLKCTRL_MODULEMODE __BITS(1,0) 53#define CLKCTRL_MODULEMODE_DISABLE 0x0 54#define CLKCTRL_MODULEMODE_AUTO 0x1 55#define CLKCTRL_MODULEMODE_ENABLE 0x2 56 57#define CLKCTRL_IDLEST __BITS(17,16) 58#define CLKCTRL_IDLEST_ENABLE 0x0 59#define CLKCTRL_IDLEST_BUSY 0x1 60#define CLKCTRL_IDLEST_IDLE 0x2 61#define CLKCTRL_IDLEST_DISABLE 0x3 62 63enum omap4_prcm_inst { 64 PRCM_CM1 = 1, 65 PRCM_CM2, 66 PRCM_PRM, 67}; 68 69struct omap4_prcm_softc { 70 struct ti_prcm_softc sc_prcm; /* must be first */ 71 bus_addr_t sc_regbase; 72 enum omap4_prcm_inst sc_inst; 73}; 74 75static int omap4_prcm_match(device_t, cfdata_t, void *); 76static void omap4_prcm_attach(device_t, device_t, void *); 77 78static int 79omap4_prcm_hwmod_enable(struct ti_prcm_softc *sc, struct ti_prcm_clk *tc, int enable) 80{ 81 uint32_t val; 82 int retry; 83 84 val = PRCM_READ(sc, tc->u.hwmod.reg); 85 val &= ~CLKCTRL_MODULEMODE; 86 if (enable) { 87 val |= __SHIFTIN(CLKCTRL_MODULEMODE_ENABLE, 88 CLKCTRL_MODULEMODE); 89 } else 90 val |= __SHIFTIN(CLKCTRL_MODULEMODE_DISABLE, 91 CLKCTRL_MODULEMODE); 92 PRCM_WRITE(sc, tc->u.hwmod.reg, val); 93 94 for (retry = 100; retry > 0; retry--) { 95 val = PRCM_READ(sc, tc->u.hwmod.reg); 96 if ((val & CLKCTRL_IDLEST) == CLKCTRL_IDLEST_ENABLE) 97 break; 98 delay(10); 99 } 100 101 return (retry >= 0) ? 0 : ETIMEDOUT; 102} 103 104static int 105omap4_prcm_hwmod_enable_auto(struct ti_prcm_softc *sc, struct ti_prcm_clk *tc, int enable) 106{ 107 uint32_t val; 108 int retry; 109 110 val = PRCM_READ(sc, tc->u.hwmod.reg); 111 val &= ~CLKCTRL_MODULEMODE; 112 if (enable) { 113 val |= __SHIFTIN(CLKCTRL_MODULEMODE_AUTO, 114 CLKCTRL_MODULEMODE); 115 } else 116 val |= __SHIFTIN(CLKCTRL_MODULEMODE_DISABLE, 117 CLKCTRL_MODULEMODE); 118 PRCM_WRITE(sc, tc->u.hwmod.reg, val); 119 120 for (retry = 100; retry > 0; retry--) { 121 val = PRCM_READ(sc, tc->u.hwmod.reg); 122 if ((val & CLKCTRL_IDLEST) == CLKCTRL_IDLEST_ENABLE) 123 break; 124 delay(10); 125 } 126 127 return (retry >= 0) ? 0 : ETIMEDOUT; 128} 129 130#define OMAP4_PRCM_HWMOD_CM1_ABE(_name, _reg, _parent) \ 131 TI_PRCM_HWMOD((_name), CM1_ABE + (_reg), (_parent), omap4_prcm_hwmod_enable) 132#define OMAP4_PRCM_HWMOD_CM2_L3INIT(_name, _reg, _parent) \ 133 TI_PRCM_HWMOD((_name), CM2_L3INIT + (_reg), (_parent), omap4_prcm_hwmod_enable) 134#define OMAP4_PRCM_HWMOD_CM2_L3INIT_AUTO(_name, _reg, _parent) \ 135 TI_PRCM_HWMOD((_name), CM2_L3INIT + (_reg), (_parent), omap4_prcm_hwmod_enable_auto) 136#define OMAP4_PRCM_HWMOD_CM2_L4PER(_name, _reg, _parent) \ 137 TI_PRCM_HWMOD((_name), CM2_L4PER + (_reg), (_parent), omap4_prcm_hwmod_enable) 138#define OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO(_name, _reg, _parent) \ 139 TI_PRCM_HWMOD((_name), CM2_L4PER + (_reg), (_parent), omap4_prcm_hwmod_enable_auto) 140#define OMAP4_PRCM_HWMOD_CM_WKUP(_name, _reg, _parent) \ 141 TI_PRCM_HWMOD((_name), CM_WKUP + (_reg), (_parent), omap4_prcm_hwmod_enable) 142#define OMAP4_PRCM_HWMOD_CM_WKUP_AUTO(_name, _reg, _parent) \ 143 TI_PRCM_HWMOD((_name), CM_WKUP + (_reg), (_parent), omap4_prcm_hwmod_enable_auto) 144 145static const struct device_compatible_entry compat_data[] = { 146 { .compat = "ti,omap4-cm1", .value = PRCM_CM1 }, 147 { .compat = "ti,omap4-cm2", .value = PRCM_CM2 }, 148 { .compat = "ti,omap4-prm", .value = PRCM_PRM }, 149 { .compat = "ti,omap4-scrm", .value = PRCM_PRM }, 150 DEVICE_COMPAT_EOL 151}; 152 153static const struct device_compatible_entry cm_compat_data[] = { 154 { .compat = "ti,omap4-cm" }, 155 DEVICE_COMPAT_EOL 156}; 157 158static const struct device_compatible_entry clkctrl_compat_data[] = { 159 { .compat = "ti,clkctrl" }, 160 DEVICE_COMPAT_EOL 161}; 162 163CFATTACH_DECL_NEW(omap4_prcm, sizeof(struct omap4_prcm_softc), 164 omap4_prcm_match, omap4_prcm_attach, NULL, NULL); 165 166static struct ti_prcm_clk omap4_cm1_clks[] = { 167 /* XXX until we get a proper clock tree */ 168 TI_PRCM_FIXED("FIXED_32K", 32768), 169 TI_PRCM_FIXED("FIXED_24MHZ", 24000000), 170 171 OMAP4_PRCM_HWMOD_CM1_ABE("timer5", 0x68, "FIXED_24MHZ"), 172 OMAP4_PRCM_HWMOD_CM1_ABE("timer6", 0x70, "FIXED_24MHZ"), 173 OMAP4_PRCM_HWMOD_CM1_ABE("timer7", 0x78, "FIXED_24MHZ"), 174 OMAP4_PRCM_HWMOD_CM1_ABE("timer8", 0x80, "FIXED_24MHZ"), 175// OMAP4_PRCM_HWMOD_CM1_ABE("wd_timer3", 0x88, "FIXED_32K"), 176}; 177 178static struct ti_prcm_clk omap4_cm2_clks[] = { 179 /* XXX until we get a proper clock tree */ 180 TI_PRCM_FIXED("FIXED_32K", 32768), 181 TI_PRCM_FIXED("FIXED_24MHZ", 24000000), 182 TI_PRCM_FIXED("FIXED_48MHZ", 48000000), 183 TI_PRCM_FIXED("FIXED_96MHZ", 96000000), 184 TI_PRCM_FIXED_FACTOR("PERIPH_CLK", 1, 1, "FIXED_48MHZ"), 185 TI_PRCM_FIXED_FACTOR("MMC_CLK", 1, 1, "FIXED_96MHZ"), 186 187 OMAP4_PRCM_HWMOD_CM2_L4PER("uart1", 0x140, "PERIPH_CLK"), 188 OMAP4_PRCM_HWMOD_CM2_L4PER("uart2", 0x148, "PERIPH_CLK"), 189 OMAP4_PRCM_HWMOD_CM2_L4PER("uart3", 0x150, "PERIPH_CLK"), 190 OMAP4_PRCM_HWMOD_CM2_L4PER("uart4", 0x158, "PERIPH_CLK"), 191 192 OMAP4_PRCM_HWMOD_CM2_L4PER("i2c1", 0xa0, "PERIPH_CLK"), 193 OMAP4_PRCM_HWMOD_CM2_L4PER("i2c2", 0xa8, "PERIPH_CLK"), 194 OMAP4_PRCM_HWMOD_CM2_L4PER("i2c3", 0xb0, "PERIPH_CLK"), 195 OMAP4_PRCM_HWMOD_CM2_L4PER("i2c4", 0xb8, "PERIPH_CLK"), 196 197 OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio2", 0x60, "PERIPH_CLK"), 198 OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio3", 0x68, "PERIPH_CLK"), 199 OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio4", 0x70, "PERIPH_CLK"), 200 OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio5", 0x78, "PERIPH_CLK"), 201 OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("gpio6", 0x80, "PERIPH_CLK"), 202 203 OMAP4_PRCM_HWMOD_CM2_L4PER("timer2", 0x38, "FIXED_24MHZ"), 204 OMAP4_PRCM_HWMOD_CM2_L4PER("timer3", 0x40, "FIXED_24MHZ"), 205 OMAP4_PRCM_HWMOD_CM2_L4PER("timer4", 0x48, "FIXED_24MHZ"), 206 OMAP4_PRCM_HWMOD_CM2_L4PER("timer9", 0x50, "FIXED_24MHZ"), 207 OMAP4_PRCM_HWMOD_CM2_L4PER("timer10", 0x28, "FIXED_24MHZ"), 208 OMAP4_PRCM_HWMOD_CM2_L4PER("timer11", 0x30, "FIXED_24MHZ"), 209 210 OMAP4_PRCM_HWMOD_CM2_L3INIT("mmc1", 0x28, "MMC_CLK"), 211 OMAP4_PRCM_HWMOD_CM2_L3INIT("mmc2", 0x30, "MMC_CLK"), 212 OMAP4_PRCM_HWMOD_CM2_L4PER("mmc3", 0x120, "PERIPH_CLK"), 213 OMAP4_PRCM_HWMOD_CM2_L4PER("mmc4", 0x128, "PERIPH_CLK"), 214 OMAP4_PRCM_HWMOD_CM2_L4PER("mmc5", 0x160, "PERIPH_CLK"), 215 216 OMAP4_PRCM_HWMOD_CM2_L3INIT("usb_host_hs", 0x58, "PERIPH_CLK"), 217 OMAP4_PRCM_HWMOD_CM2_L3INIT_AUTO("usb_otg_hs", 0x60, "PERIPH_CLK"), 218 OMAP4_PRCM_HWMOD_CM2_L3INIT_AUTO("usb_tll_hs", 0x68, "PERIPH_CLK"), 219 220// OMAP4_PRCM_HWMOD_CM2_L4PER_AUTO("rng", 0x1c0, "PERIPH_CLK"), 221}; 222 223static struct ti_prcm_clk omap4_prm_clks[] = { 224 /* XXX until we get a proper clock tree */ 225 TI_PRCM_FIXED("FIXED_32K", 32768), 226 TI_PRCM_FIXED("FIXED_48MHZ", 48000000), 227 TI_PRCM_FIXED_FACTOR("PERIPH_CLK", 1, 1, "FIXED_48MHZ"), 228 229 OMAP4_PRCM_HWMOD_CM_WKUP_AUTO("gpio1", 0x38, "PERIPH_CLK"), 230 OMAP4_PRCM_HWMOD_CM_WKUP("timer1", 0x40, "FIXED_32K"), 231 OMAP4_PRCM_HWMOD_CM_WKUP("wd_timer2", 0x30, "FIXED_32K"), 232}; 233 234static struct clk * 235omap4_prcm_clock_decode(device_t dev, int cc_phandle, const void *data, size_t len) 236{ 237 struct omap4_prcm_softc * const sc = device_private(dev); 238 const u_int *cells = data; 239 bus_addr_t regbase; 240 u_int n; 241 242 if (len != 8) 243 return NULL; 244 245 bus_size_t regoff = be32toh(cells[0]); 246 const u_int clock_index = be32toh(cells[1]); 247 248 /* XXX not sure how to handle this yet */ 249 if (clock_index != 0) 250 return NULL; 251 252 /* 253 * Register offset in specifier is relative to base address of the 254 * clock node. Translate this to an address relative to the start 255 * of PRCM space. 256 */ 257 if (fdtbus_get_reg(cc_phandle, 0, ®base, NULL) != 0) 258 return NULL; 259 regoff += (regbase - sc->sc_regbase); 260 261 /* 262 * Look for a matching hwmod. 263 */ 264 for (n = 0; n < sc->sc_prcm.sc_nclks; n++) { 265 struct ti_prcm_clk *tclk = &sc->sc_prcm.sc_clks[n]; 266 if (tclk->type != TI_PRCM_HWMOD) 267 continue; 268 269 if (tclk->u.hwmod.reg == regoff) 270 return &tclk->base; 271 } 272 273 /* Not found */ 274 return NULL; 275} 276 277static const struct fdtbus_clock_controller_func omap4_prcm_clock_fdt_funcs = { 278 .decode = omap4_prcm_clock_decode 279}; 280 281static int 282omap4_prcm_match(device_t parent, cfdata_t cf, void *aux) 283{ 284 struct fdt_attach_args * const faa = aux; 285 286 return of_compatible_match(faa->faa_phandle, compat_data); 287} 288 289static void 290omap4_prcm_attach(device_t parent, device_t self, void *aux) 291{ 292 struct omap4_prcm_softc * const sc = device_private(self); 293 struct fdt_attach_args * const faa = aux; 294 const int phandle = faa->faa_phandle; 295 int clocks, child, cm_child; 296 char iname[5]; 297 298 if (fdtbus_get_reg(phandle, 0, &sc->sc_regbase, NULL) != 0) { 299 aprint_error(": couldn't get registers\n"); 300 return; 301 } 302 303 sc->sc_prcm.sc_dev = self; 304 sc->sc_prcm.sc_phandle = phandle; 305 sc->sc_prcm.sc_bst = faa->faa_bst; 306 sc->sc_inst = of_compatible_lookup(phandle, compat_data)->value; 307 308 switch (sc->sc_inst) { 309 case PRCM_CM1: 310 sc->sc_prcm.sc_clks = omap4_cm1_clks; 311 sc->sc_prcm.sc_nclks = __arraycount(omap4_cm1_clks); 312 strcpy(iname, "CM1"); 313 break; 314 case PRCM_CM2: 315 sc->sc_prcm.sc_clks = omap4_cm2_clks; 316 sc->sc_prcm.sc_nclks = __arraycount(omap4_cm2_clks); 317 strcpy(iname, "CM2"); 318 break; 319 case PRCM_PRM: 320 sc->sc_prcm.sc_clks = omap4_prm_clks; 321 sc->sc_prcm.sc_nclks = __arraycount(omap4_prm_clks); 322 strcpy(iname, "PRM"); 323 break; 324 default: 325 aprint_error(": unsupported instance\n"); 326 return; 327 } 328 329 if (ti_prcm_attach(&sc->sc_prcm) != 0) 330 return; 331 332 aprint_naive("\n"); 333 aprint_normal(": OMAP44xx PRCM (%s)\n", iname); 334 335 for (child = OF_child(phandle); child; child = OF_peer(child)) { 336 if (of_compatible_match(child, cm_compat_data) == 0) 337 continue; 338 339 for (cm_child = OF_child(child); cm_child; 340 cm_child = OF_peer(cm_child)) { 341 if (of_compatible_match(cm_child, 342 clkctrl_compat_data) == 0) 343 continue; 344 345 aprint_debug_dev(self, "clkctrl: %s\n", fdtbus_get_string(cm_child, "name")); 346 fdtbus_register_clock_controller(self, cm_child, 347 &omap4_prcm_clock_fdt_funcs); 348 } 349 } 350 351 clocks = of_find_firstchild_byname(phandle, "clocks"); 352 if (clocks > 0) 353 fdt_add_bus(self, clocks, faa); 354} 355