1 /* $NetBSD: am18xx_usbphyclk.c,v 1.3 2026/07/30 15:08:08 yurix Exp $ */ 2 3 /*- 4 * Copyright (c) 2026 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Yuri Honegger. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Drivers for the PLL of the USB PHYs on the TI AM18XX family of SoCs. 34 * 35 * Locking Strategy: Any access to the hardware and sc->sc_state should be made 36 * with sc->sc_lock held. 37 */ 38 39 #include <sys/param.h> 40 #include <sys/bus.h> 41 #include <sys/cdefs.h> 42 #include <sys/device.h> 43 #include <sys/mutex.h> 44 45 #include <dev/clk/clk_backend.h> 46 #include <dev/fdt/fdtvar.h> 47 #include <dev/fdt/syscon.h> 48 49 enum usbphyclk_state { 50 SUSPENDED, 51 USB11_ONLY, /* USB1.1 only */ 52 USB20_ONLY, /* USB2.0 only */ 53 BOTH /* USB2.0 and USB1.1 */ 54 }; 55 56 struct am18xx_usbphyclk_clk { 57 struct clk clk_base; /* must be first */ 58 }; 59 60 struct am18xx_usbphyclk_softc { 61 struct clk_domain sc_clkdom; 62 struct am18xx_usbphyclk_clk sc_usb11_clk; 63 struct am18xx_usbphyclk_clk sc_usb20_clk; 64 kmutex_t sc_lock; 65 struct clk *sc_ref_clk; 66 struct clk *sc_phy_logic_clk; 67 struct syscon *sc_syscon; 68 enum usbphyclk_state sc_state; 69 }; 70 71 static int am18xx_usbphyclk_match(device_t, cfdata_t, void *); 72 static void am18xx_usbphyclk_attach(device_t, device_t, void *); 73 static struct clk *am18xx_usbphyclk_decode(device_t, int, const void *, size_t); 74 static struct clk *am18xx_usbphyclk_clk_get(void *, const char *); 75 static u_int am18xx_usbphyclk_clk_get_rate(void *, struct clk *); 76 static struct clk *am18xx_usbphyclk_clk_get_parent(void *, struct clk *); 77 static int am18xx_usbphyclk_clk_enable(void *, struct clk *); 78 static int am18xx_usbphyclk_clk_disable(void *, struct clk *); 79 static int am18xx_usbphyclk_state_transition(struct am18xx_usbphyclk_softc *, 80 enum usbphyclk_state); 81 static int am18xx_usbphyclk_set_refreq(struct am18xx_usbphyclk_softc *); 82 83 CFATTACH_DECL_NEW(am18xxuphyclk, sizeof(struct am18xx_usbphyclk_softc), 84 am18xx_usbphyclk_match, am18xx_usbphyclk_attach, NULL, NULL); 85 86 static const struct fdtbus_clock_controller_func am18xx_usbphyclk_fdt_funcs = { 87 .decode = am18xx_usbphyclk_decode, 88 }; 89 90 static const struct clk_funcs am18xx_usbphyclk_clk_funcs = { 91 .get = am18xx_usbphyclk_clk_get, 92 .get_rate = am18xx_usbphyclk_clk_get_rate, 93 .get_parent = am18xx_usbphyclk_clk_get_parent, 94 .enable = am18xx_usbphyclk_clk_enable, 95 .disable = am18xx_usbphyclk_clk_disable, 96 }; 97 98 static const struct device_compatible_entry compat_data[] = { 99 { .compat = "ti,da830-usb-phy-clocks" }, 100 DEVICE_COMPAT_EOL 101 }; 102 103 #define AM18XX_CFGCHIP2 0x8 104 #define AM18XX_CFGCHIP2_PHYCLKGD __BIT(17) 105 #define AM18XX_CFGCHIP2_PHYRESET __BIT(15) 106 #define AM18XX_CFGCHIP2_USB11CLKMUX __BIT(12) 107 #define AM18XX_CFGCHIP2_USB20CLKMUX __BIT(11) 108 #define AM18XX_CFGCHIP2_PHYPWRDN __BIT(10) 109 #define AM18XX_CFGCHIP2_PHYPLLON __BIT(6) 110 #define AM18XX_CFGCHIP2_PHYREFFREQ __BITS(3, 0) 111 112 /* This function should be called with sc->sc_lock held. */ 113 static int 114 am18xx_usbphyclk_state_transition(struct am18xx_usbphyclk_softc *sc, 115 enum usbphyclk_state state) 116 { 117 int error; 118 119 /* you need the usb 2.0 phy clocked to change the pll configuration */ 120 error = clk_enable(sc->sc_phy_logic_clk); 121 if (error) { 122 return error; 123 } 124 125 syscon_lock(sc->sc_syscon); 126 127 /* first mux takes 0 as enabled, second takes 1 \_()_/ */ 128 uint32_t mask = 129 AM18XX_CFGCHIP2_USB11CLKMUX | AM18XX_CFGCHIP2_USB20CLKMUX; 130 uint32_t update = AM18XX_CFGCHIP2_USB20CLKMUX; 131 132 switch (state) { 133 case SUSPENDED: 134 mask |= AM18XX_CFGCHIP2_PHYRESET | AM18XX_CFGCHIP2_PHYPWRDN | 135 AM18XX_CFGCHIP2_PHYPLLON; 136 update |= AM18XX_CFGCHIP2_PHYRESET | AM18XX_CFGCHIP2_PHYPWRDN; 137 break; 138 case USB11_ONLY: 139 mask |= AM18XX_CFGCHIP2_PHYRESET | AM18XX_CFGCHIP2_PHYPWRDN | 140 AM18XX_CFGCHIP2_PHYPLLON; 141 update |= AM18XX_CFGCHIP2_PHYPLLON; 142 break; 143 case USB20_ONLY: 144 mask = AM18XX_CFGCHIP2_PHYRESET | AM18XX_CFGCHIP2_PHYPWRDN | 145 AM18XX_CFGCHIP2_PHYPLLON; 146 update |= 0; 147 break; 148 case BOTH: 149 mask = AM18XX_CFGCHIP2_PHYRESET | AM18XX_CFGCHIP2_PHYPWRDN | 150 AM18XX_CFGCHIP2_PHYPLLON; 151 update |= AM18XX_CFGCHIP2_PHYPLLON; 152 break; 153 } 154 155 /* update bits */ 156 uint32_t val = syscon_read_4(sc->sc_syscon, AM18XX_CFGCHIP2); 157 val &= ~mask; 158 val |= mask & update; 159 syscon_write_4(sc->sc_syscon, AM18XX_CFGCHIP2, val); 160 161 if (state != SUSPENDED) { 162 /* wait for the PLL to lock */ 163 while ((syscon_read_4(sc->sc_syscon, AM18XX_CFGCHIP2) & 164 AM18XX_CFGCHIP2_PHYCLKGD) == 0) { 165 delay(1); /* wait for PLL to lock */ 166 } 167 } 168 169 sc->sc_state = state; 170 syscon_unlock(sc->sc_syscon); 171 172 /* safe since psc clocks are refcounted */ 173 error = clk_disable(sc->sc_phy_logic_clk); 174 if (error) { 175 return error; 176 } 177 178 return 0; 179 } 180 181 static int 182 am18xx_usbphyclk_clk_enable(void *priv, struct clk *clkp) 183 { 184 struct am18xx_usbphyclk_softc *const sc = priv; 185 int retval; 186 187 mutex_enter(&sc->sc_lock); 188 189 if (clkp == &sc->sc_usb11_clk.clk_base) { 190 /* this is the usb 1.1 clk */ 191 switch (sc->sc_state) { 192 case SUSPENDED: 193 retval = am18xx_usbphyclk_state_transition(sc, 194 USB11_ONLY); 195 break; 196 case USB20_ONLY: 197 retval = am18xx_usbphyclk_state_transition(sc, 198 BOTH); 199 break; 200 default: 201 retval = 0; 202 } 203 } else if (clkp == &sc->sc_usb20_clk.clk_base) { 204 /* this is the usb 2.0 clk */ 205 switch (sc->sc_state) { 206 case SUSPENDED: 207 retval = am18xx_usbphyclk_state_transition(sc, 208 USB20_ONLY); 209 break; 210 case USB11_ONLY: 211 retval = am18xx_usbphyclk_state_transition(sc, 212 BOTH); 213 break; 214 default: 215 retval = 0; 216 } 217 } else { 218 retval = EINVAL; 219 } 220 221 mutex_exit(&sc->sc_lock); 222 223 return retval; 224 } 225 226 static int 227 am18xx_usbphyclk_clk_disable(void *priv, struct clk *clkp) 228 { 229 struct am18xx_usbphyclk_softc *const sc = priv; 230 int retval; 231 232 mutex_enter(&sc->sc_lock); 233 234 if (clkp == &sc->sc_usb11_clk.clk_base) { 235 /* this is the usb 1.1 clk */ 236 switch (sc->sc_state) { 237 case BOTH: 238 retval = am18xx_usbphyclk_state_transition(sc, 239 USB20_ONLY); 240 break; 241 case USB11_ONLY: 242 retval = am18xx_usbphyclk_state_transition(sc, 243 SUSPENDED); 244 break; 245 default: 246 retval = 0; 247 } 248 } else if (clkp == &sc->sc_usb20_clk.clk_base) { 249 /* this is the usb 2.0 clk */ 250 switch (sc->sc_state) { 251 case BOTH: 252 retval = am18xx_usbphyclk_state_transition(sc, 253 USB11_ONLY); 254 break; 255 case USB20_ONLY: 256 retval = am18xx_usbphyclk_state_transition(sc, 257 SUSPENDED); 258 break; 259 default: 260 retval = 0; 261 } 262 } else { 263 retval = EINVAL; 264 } 265 266 mutex_exit(&sc->sc_lock); 267 268 return retval; 269 } 270 271 static struct clk * 272 am18xx_usbphyclk_clk_get(void *priv, const char *name) 273 { 274 struct am18xx_usbphyclk_softc *const sc = priv; 275 276 if (strcmp(sc->sc_usb11_clk.clk_base.name, name) == 0) { 277 return &sc->sc_usb11_clk.clk_base; 278 } else if (strcmp(sc->sc_usb20_clk.clk_base.name, name) == 0) { 279 return &sc->sc_usb20_clk.clk_base; 280 } 281 282 return NULL; 283 } 284 285 static u_int 286 am18xx_usbphyclk_clk_get_rate(void *priv, struct clk *clkp) 287 { 288 struct am18xx_usbphyclk_softc *const sc = priv; 289 u_int retval = 0; 290 291 mutex_enter(&sc->sc_lock); 292 293 bool is_usb11 = clkp == &sc->sc_usb11_clk.clk_base; 294 bool is_usb20 = clkp == &sc->sc_usb20_clk.clk_base; 295 296 if (is_usb11 && (sc->sc_state == USB11_ONLY || sc->sc_state == BOTH)) { 297 retval = 48000000; 298 } 299 if (is_usb20 && (sc->sc_state == USB20_ONLY || sc->sc_state == BOTH)) { 300 retval = 48000000; 301 } 302 303 mutex_exit(&sc->sc_lock); 304 305 return retval; 306 } 307 308 static struct clk * 309 am18xx_usbphyclk_clk_get_parent(void *priv, struct clk *clkp) 310 { 311 struct am18xx_usbphyclk_softc *const sc = priv; 312 313 return sc->sc_ref_clk; 314 } 315 316 static struct clk * 317 am18xx_usbphyclk_decode(device_t dev, int cc_phandle, const void *data, 318 size_t len) 319 { 320 struct am18xx_usbphyclk_softc *const sc = device_private(dev); 321 const u_int *cells = data; 322 323 if (len != 4) { 324 return NULL; 325 } 326 327 const u_int index = be32toh(cells[0]); 328 329 if (index == 0) { 330 return &sc->sc_usb20_clk.clk_base; 331 } else if (index == 1) { 332 return &sc->sc_usb11_clk.clk_base; 333 } 334 335 return NULL; 336 } 337 338 int 339 am18xx_usbphyclk_match(device_t parent, cfdata_t cf, void *aux) 340 { 341 struct fdt_attach_args *const faa = aux; 342 343 return of_compatible_match(faa->faa_phandle, compat_data); 344 } 345 346 static int 347 am18xx_usbphyclk_set_refreq(struct am18xx_usbphyclk_softc *sc) 348 { 349 u_int ref_clk_rate = clk_get_rate(sc->sc_ref_clk); 350 351 uint32_t rate_val; 352 353 /* The USB PLL must know the frequency of its input clock. It only 354 * supports a few predefined clock rates, see the table in the technical 355 * reference manual. */ 356 switch (ref_clk_rate) { 357 case 12000000: /* 12 MHz */ 358 rate_val = 1; 359 break; 360 case 13000000: /* 13 MHz */ 361 rate_val = 6; 362 break; 363 case 19200000: /* 19.2 MHz */ 364 rate_val = 4; 365 break; 366 case 20000000: /* 20 MHz */ 367 rate_val = 8; 368 break; 369 case 24000000: /* 24 MHz */ 370 rate_val = 2; 371 break; 372 case 26000000: /* 26 MHz */ 373 rate_val = 7; 374 break; 375 case 38400000: /* 38.4 MHz */ 376 rate_val = 5; 377 break; 378 case 40000000: /* 40 MHz */ 379 rate_val = 9; 380 break; 381 case 48000000: /* 48 MHz */ 382 rate_val = 3; 383 break; 384 default: 385 return EINVAL; /* reference clock rate not supported */ 386 } 387 388 mutex_enter(&sc->sc_lock); 389 syscon_lock(sc->sc_syscon); 390 uint32_t val = syscon_read_4(sc->sc_syscon, AM18XX_CFGCHIP2); 391 val &= ~AM18XX_CFGCHIP2_PHYREFFREQ; 392 val |= rate_val; 393 syscon_write_4(sc->sc_syscon, AM18XX_CFGCHIP2, val); 394 syscon_unlock(sc->sc_syscon); 395 mutex_exit(&sc->sc_lock); 396 397 return 0; 398 } 399 400 void 401 am18xx_usbphyclk_attach(device_t parent, device_t self, void *aux) 402 { 403 struct am18xx_usbphyclk_softc *const sc = device_private(self); 404 struct fdt_attach_args *const faa = aux; 405 const int phandle = faa->faa_phandle; 406 407 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 408 409 /* cfgchip syscon */ 410 sc->sc_syscon = fdtbus_syscon_lookup(OF_parent(phandle)); 411 if (sc->sc_syscon == NULL) { 412 aprint_error(": couldn't get syscon registers\n"); 413 return; 414 } 415 416 /* get reference clock */ 417 sc->sc_ref_clk = fdtbus_clock_get(phandle, "auxclk"); 418 if (sc->sc_ref_clk == NULL) { 419 aprint_error(": failed to get auxclk\n"); 420 return; 421 } 422 if (clk_enable(sc->sc_ref_clk)) { 423 aprint_error(": failed to enable reference clk\n"); 424 return; 425 } 426 427 /* get clock controlling the pll transitioning logic */ 428 sc->sc_phy_logic_clk = fdtbus_clock_get(phandle, "fck"); 429 if (sc->sc_phy_logic_clk == NULL) { 430 aprint_error(": failed to get fck\n"); 431 return; 432 } 433 434 if (am18xx_usbphyclk_set_refreq(sc)) { 435 aprint_error(": failed to set reference clock freq " 436 "(unsupported freq?)\n"); 437 return; 438 } 439 440 /* prepare a clock domain */ 441 sc->sc_clkdom.name = device_xname(self); 442 sc->sc_clkdom.funcs = &am18xx_usbphyclk_clk_funcs; 443 sc->sc_clkdom.priv = sc; 444 445 /* fill the clock structures */ 446 sc->sc_usb20_clk.clk_base.name = "usb0_clk48"; 447 sc->sc_usb20_clk.clk_base.flags = 0; 448 sc->sc_usb20_clk.clk_base.domain = &sc->sc_clkdom; 449 sc->sc_usb11_clk.clk_base.name = "usb1_clk48"; 450 sc->sc_usb11_clk.clk_base.flags = 0; 451 sc->sc_usb11_clk.clk_base.domain = &sc->sc_clkdom; 452 453 /* bring the phy into a known state. We don't need to get the lock yet 454 * because the clock controller API hasn't been exposed yet. */ 455 am18xx_usbphyclk_state_transition(sc, SUSPENDED); 456 457 /* create sysctl nodes for the clocks */ 458 clk_attach(&sc->sc_usb11_clk.clk_base); 459 clk_attach(&sc->sc_usb20_clk.clk_base); 460 461 /* register the clock */ 462 fdtbus_register_clock_controller(self, phandle, 463 &am18xx_usbphyclk_fdt_funcs); 464 465 aprint_normal("\n"); 466 } 467