1 /* $NetBSD $ */ 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 * PLL Controller for the TI AM18XX SOC. 34 */ 35 36 #include <sys/param.h> 37 #include <sys/bus.h> 38 #include <sys/cdefs.h> 39 #include <sys/device.h> 40 41 #include <dev/clk/clk_backend.h> 42 #include <dev/fdt/fdtvar.h> 43 44 #include <arm/fdt/arm_fdtvar.h> 45 46 struct am18xx_pllc_softc; 47 48 struct am18xx_pllc_clk { 49 struct clk clk_base; /* must be first */ 50 u_int (*get_rate)(struct am18xx_pllc_softc *, struct am18xx_pllc_clk *); 51 u_int clk_index; 52 }; 53 54 struct am18xx_pllc_config { 55 struct am18xx_pllc_clk *sysclks; 56 struct am18xx_pllc_clk *auxclk; 57 int num_sysclk; 58 }; 59 60 struct am18xx_pllc_softc { 61 bus_space_tag_t sc_bst; 62 bus_space_handle_t sc_bsh; 63 int sc_sysclk_phandle; 64 int sc_auxclk_phandle; 65 const struct am18xx_pllc_config *sc_config; 66 struct clk_domain sc_clkdom; 67 struct clk *sc_ref_clk; 68 }; 69 70 static int am18xx_pllc_match(device_t, cfdata_t, void *); 71 static void am18xx_pllc_attach(device_t, device_t, void *); 72 static u_int am18xx_pllc_get_sysclk_rate(struct am18xx_pllc_softc *, 73 struct am18xx_pllc_clk *); 74 static u_int am18xx_pllc_get_auxclk_rate(struct am18xx_pllc_softc *, 75 struct am18xx_pllc_clk *); 76 static struct clk * am18xx_pllc_decode(device_t, int, const void *, size_t); 77 static struct clk * am18xx_pllc_clk_get(void *, const char *); 78 static u_int am18xx_pllc_clk_get_rate(void *, struct clk *); 79 static struct clk * am18xx_pllc_clk_get_parent(void *, struct clk *); 80 81 CFATTACH_DECL_NEW(am18xxpllc, sizeof(struct am18xx_pllc_softc), 82 am18xx_pllc_match, am18xx_pllc_attach, NULL, NULL); 83 84 #define AM18XX_PLLC_PLLCTL 0x100 85 #define AM18XX_PLLC_PLLM 0x110 86 #define AM18XX_PLLC_PREDIV 0x114 87 #define AM18XX_PLLC_PLLDIV1 0x118 88 #define AM18XX_PLLC_POSTDIV 0x128 89 #define AM18XX_PLLC_PLLDIV4 0x160 90 91 #define AM18XX_PLLC_PLLCTL_PLLEN __BIT(0) 92 #define AM18XX_PLLC_PLLCTL_EXTCLKSRC __BIT(9) 93 #define AM18XX_PLLC_PLLM_MULTIPLIER __BITS(4,0) 94 #define AM18XX_PLLC_PREDIV_RATIO __BITS(4,0) 95 #define AM18XX_PLLC_POSTDIV_RATIO __BITS(4,0) 96 #define AM18XX_PLLC_PLLDIV_RATIO __BITS(4,0) 97 98 #define PLLC_READ(sc, reg) \ 99 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, reg) 100 #define PLLC_WRITE(sc, reg, val) \ 101 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, reg, val) 102 103 #define PLLC_CLK(_i, _name, _rate) \ 104 { \ 105 .clk_base.name = (_name), \ 106 .clk_base.flags = 0, \ 107 .get_rate = (_rate), \ 108 .clk_index = (_i), \ 109 } 110 111 static struct am18xx_pllc_clk am18xx_pllc_pll0_auxclk = 112 PLLC_CLK(0, "pll0_auxclk", &am18xx_pllc_get_auxclk_rate); 113 114 static struct am18xx_pllc_clk am18xx_pllc_pll0_sysclks[] = { 115 PLLC_CLK(0, "pll0_sysclk1", &am18xx_pllc_get_sysclk_rate), 116 PLLC_CLK(1, "pll0_sysclk2", &am18xx_pllc_get_sysclk_rate), 117 PLLC_CLK(2, "pll0_sysclk3", &am18xx_pllc_get_sysclk_rate), 118 PLLC_CLK(3, "pll0_sysclk4", &am18xx_pllc_get_sysclk_rate), 119 PLLC_CLK(4, "pll0_sysclk5", &am18xx_pllc_get_sysclk_rate), 120 PLLC_CLK(5, "pll0_sysclk6", &am18xx_pllc_get_sysclk_rate), 121 PLLC_CLK(6, "pll0_sysclk7", &am18xx_pllc_get_sysclk_rate), 122 }; 123 124 static const struct am18xx_pllc_config am18xx_pllc_pll0_config = { 125 .auxclk = &am18xx_pllc_pll0_auxclk, 126 .sysclks = am18xx_pllc_pll0_sysclks, 127 .num_sysclk = __arraycount(am18xx_pllc_pll0_sysclks), 128 }; 129 130 static const struct fdtbus_clock_controller_func am18xx_pllc_clk_fdt_funcs = { 131 .decode = am18xx_pllc_decode, 132 }; 133 134 static const struct clk_funcs am18xx_pllc_clk_funcs = { 135 .get = am18xx_pllc_clk_get, 136 .get_rate = am18xx_pllc_clk_get_rate, 137 .get_parent = am18xx_pllc_clk_get_parent, 138 }; 139 140 static const struct device_compatible_entry compat_data[] = { 141 { .compat = "ti,da850-pll0", .data = &am18xx_pllc_pll0_config }, 142 DEVICE_COMPAT_EOL 143 }; 144 145 static struct clk * 146 am18xx_pllc_clk_get(void *priv, const char *name) 147 { 148 struct am18xx_pllc_softc * const sc = priv; 149 150 /* check if it is the auxclk */ 151 if (strcmp(sc->sc_config->auxclk->clk_base.name, name) == 0) { 152 return &sc->sc_config->auxclk->clk_base; 153 } 154 155 /* check if it is a sysclk */ 156 for (int i = 0; i < sc->sc_config->num_sysclk; i++) { 157 if (strcmp(sc->sc_config->sysclks[i].clk_base.name, name) == 0) 158 return &sc->sc_config->sysclks[i].clk_base; 159 } 160 161 return NULL; 162 } 163 164 static u_int 165 am18xx_pllc_clk_get_rate(void *priv, struct clk *clkp) 166 { 167 struct am18xx_pllc_softc * const sc = priv; 168 struct am18xx_pllc_clk *clk = (struct am18xx_pllc_clk *)clkp; 169 170 return clk->get_rate(sc, clk); 171 } 172 173 static struct clk * 174 am18xx_pllc_clk_get_parent(void *priv, struct clk *clkp) 175 { 176 struct am18xx_pllc_softc * const sc = priv; 177 178 return sc->sc_ref_clk; 179 } 180 181 static u_int 182 am18xx_pllc_get_sysclk_rate(struct am18xx_pllc_softc *sc, 183 struct am18xx_pllc_clk *clk) 184 { 185 uint32_t pllctl_reg = PLLC_READ(sc, AM18XX_PLLC_PLLCTL); 186 187 uint32_t prediv_reg = PLLC_READ(sc, AM18XX_PLLC_PREDIV); 188 uint32_t prediv_ratio = (prediv_reg & AM18XX_PLLC_PREDIV_RATIO) + 1; 189 190 uint32_t pllm_reg = PLLC_READ(sc, AM18XX_PLLC_PLLM); 191 uint32_t pllm_multiplier = (pllm_reg & AM18XX_PLLC_PLLM_MULTIPLIER) + 1; 192 193 uint32_t postdiv_reg = PLLC_READ(sc, AM18XX_PLLC_POSTDIV); 194 uint32_t postdiv_ratio = (postdiv_reg & AM18XX_PLLC_POSTDIV_RATIO) + 1; 195 196 uint32_t plldiv_regaddr; 197 if (clk->clk_index <= 2) { 198 plldiv_regaddr = AM18XX_PLLC_PLLDIV1 + 4 * clk->clk_index; 199 } else { 200 plldiv_regaddr = AM18XX_PLLC_PLLDIV4 + 4 * (clk->clk_index - 3); 201 } 202 uint32_t plldiv_reg = PLLC_READ(sc, plldiv_regaddr); 203 uint32_t plldiv_ratio = (plldiv_reg & AM18XX_PLLC_PLLDIV_RATIO) + 1; 204 205 u_int ref_clk_rate = clk_get_rate(sc->sc_ref_clk); 206 207 if (pllctl_reg & AM18XX_PLLC_PLLCTL_PLLEN) { 208 /* PLL enabled */ 209 ref_clk_rate /= prediv_ratio; 210 ref_clk_rate *= pllm_multiplier; 211 ref_clk_rate /= postdiv_ratio; 212 } else { 213 /* bypass mode (ensure we aren't using the other PLL)*/ 214 KASSERT((pllctl_reg & AM18XX_PLLC_PLLCTL_EXTCLKSRC) == 0); 215 } 216 217 ref_clk_rate /= plldiv_ratio; 218 219 return ref_clk_rate; 220 } 221 222 static u_int 223 am18xx_pllc_get_auxclk_rate(struct am18xx_pllc_softc *sc, 224 struct am18xx_pllc_clk *clk) 225 { 226 return clk_get_rate(sc->sc_ref_clk); 227 } 228 229 static struct clk * 230 am18xx_pllc_decode(device_t dev, int cc_phandle, const void *data, size_t len) 231 { 232 struct am18xx_pllc_softc * const sc = device_private(dev); 233 const u_int *cells = data; 234 235 if (cc_phandle == sc->sc_sysclk_phandle) { 236 if (len != 4) 237 return NULL; 238 const u_int clock_index = be32toh(cells[0]) - 1; 239 if (clock_index >= sc->sc_config->num_sysclk) { 240 return NULL; 241 } 242 243 return &sc->sc_config->sysclks[clock_index].clk_base; 244 } else if (cc_phandle == sc->sc_auxclk_phandle) { 245 return &sc->sc_config->auxclk->clk_base; 246 } 247 248 return NULL; 249 } 250 251 int 252 am18xx_pllc_match(device_t parent, cfdata_t cf, void *aux) 253 { 254 struct fdt_attach_args * const faa = aux; 255 256 return of_compatible_match(faa->faa_phandle, compat_data); 257 } 258 259 void 260 am18xx_pllc_attach(device_t parent, device_t self, void *aux) 261 { 262 struct am18xx_pllc_softc * const sc = device_private(self); 263 struct fdt_attach_args * const faa = aux; 264 const int phandle = faa->faa_phandle; 265 bus_addr_t addr; 266 bus_size_t size; 267 268 sc->sc_bst = faa->faa_bst; 269 sc->sc_config = of_compatible_lookup(phandle, compat_data)->data; 270 271 /* map PSC control registers */ 272 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 273 aprint_error(": couldn't get registers\n"); 274 return; 275 } 276 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 277 aprint_error(": couldn't map registers\n"); 278 return; 279 } 280 281 /* get parent clock */ 282 sc->sc_ref_clk = fdtbus_clock_get_index(phandle, 0); 283 if (sc->sc_ref_clk == NULL) { 284 aprint_error(": couldn't get reference clock\n"); 285 return; 286 } 287 288 /* initialize clock domain */ 289 sc->sc_clkdom.name = device_xname(self); 290 sc->sc_clkdom.funcs = &am18xx_pllc_clk_funcs; 291 sc->sc_clkdom.priv = sc; 292 293 /* initialize the clocks */ 294 sc->sc_config->auxclk->clk_base.domain = &sc->sc_clkdom; 295 clk_attach(&sc->sc_config->auxclk->clk_base); 296 for (int i = 0; i < sc->sc_config->num_sysclk; i++) { 297 sc->sc_config->sysclks[i].clk_base.domain = &sc->sc_clkdom; 298 clk_attach(&sc->sc_config->sysclks[i].clk_base); 299 } 300 301 /* register auxclk fdt controller*/ 302 sc->sc_auxclk_phandle = of_find_firstchild_byname(phandle, "auxclk"); 303 if (sc->sc_auxclk_phandle < 0) { 304 aprint_error(": couldn't get pll0_auxclk child\n"); 305 return; 306 } 307 fdtbus_register_clock_controller(self, sc->sc_auxclk_phandle, 308 &am18xx_pllc_clk_fdt_funcs); 309 310 /* register sysclk fdt controller*/ 311 sc->sc_sysclk_phandle = of_find_firstchild_byname(phandle, "sysclk"); 312 if (sc->sc_sysclk_phandle < 0) { 313 aprint_error(": couldn't get pll0_sysclk child\n"); 314 return; 315 } 316 fdtbus_register_clock_controller(self, sc->sc_sysclk_phandle, 317 &am18xx_pllc_clk_fdt_funcs); 318 319 aprint_normal("\n"); 320 } 321 322