Home | History | Annotate | Line # | Download | only in ti
      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