1 /* $NetBSD: ti_dpll_clock.c,v 1.6 2021/01/27 03:10:20 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 Jared McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: ti_dpll_clock.c,v 1.6 2021/01/27 03:10:20 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/device.h> 35 #include <sys/kmem.h> 36 #include <sys/bus.h> 37 38 #include <dev/clk/clk_backend.h> 39 40 #include <dev/fdt/fdtvar.h> 41 42 #define DPLL_MULT __BITS(18,8) 43 #define DPLL_DIV __BITS(6,0) 44 45 #define AM3_ST_MN_BYPASS __BIT(8) 46 #define AM3_ST_DPLL_CLK __BIT(0) 47 48 #define AM3_DPLL_EN __BITS(2,0) 49 #define AM3_DPLL_EN_NM_BYPASS 4 50 #define AM3_DPLL_EN_LOCK 7 51 52 #define OMAP3_ST_MPU_CLK __BIT(0) 53 54 #define OMAP3_EN_MPU_DPLL __BITS(2,0) 55 #define OMAP3_EN_MPU_DPLL_BYPASS 5 56 #define OMAP3_EN_MPU_DPLL_LOCK 7 57 58 #define OMAP3_CORE_DPLL_CLKOUT_DIV __BITS(31,27) 59 #define OMAP3_CORE_DPLL_MULT __BITS(26,16) 60 #define OMAP3_CORE_DPLL_DIV __BITS(14,8) 61 62 static int ti_dpll_clock_match(device_t, cfdata_t, void *); 63 static void ti_dpll_clock_attach(device_t, device_t, void *); 64 65 static struct clk *ti_dpll_clock_decode(device_t, int, const void *, size_t); 66 67 static const struct fdtbus_clock_controller_func ti_dpll_clock_fdt_funcs = { 68 .decode = ti_dpll_clock_decode 69 }; 70 71 static struct clk *ti_dpll_clock_get(void *, const char *); 72 static void ti_dpll_clock_put(void *, struct clk *); 73 static u_int ti_dpll_clock_get_rate(void *, struct clk *); 74 static struct clk *ti_dpll_clock_get_parent(void *, struct clk *); 75 76 static int am3_dpll_clock_set_rate(void *, struct clk *, u_int); 77 78 static const struct clk_funcs am3_dpll_clock_clk_funcs = { 79 .get = ti_dpll_clock_get, 80 .put = ti_dpll_clock_put, 81 .set_rate = am3_dpll_clock_set_rate, 82 .get_rate = ti_dpll_clock_get_rate, 83 .get_parent = ti_dpll_clock_get_parent, 84 }; 85 86 static int omap3_dpll_clock_set_rate(void *, struct clk *, u_int); 87 88 static const struct clk_funcs omap3_dpll_clock_clk_funcs = { 89 .get = ti_dpll_clock_get, 90 .put = ti_dpll_clock_put, 91 .set_rate = omap3_dpll_clock_set_rate, 92 .get_rate = ti_dpll_clock_get_rate, 93 .get_parent = ti_dpll_clock_get_parent, 94 }; 95 96 static u_int omap3_dpll_core_clock_get_rate(void *, struct clk *); 97 98 static const struct clk_funcs omap3_dpll_core_clock_clk_funcs = { 99 .get = ti_dpll_clock_get, 100 .put = ti_dpll_clock_put, 101 .get_rate = omap3_dpll_core_clock_get_rate, 102 .get_parent = ti_dpll_clock_get_parent, 103 }; 104 105 static const struct device_compatible_entry compat_data[] = { 106 { .compat = "ti,am3-dpll-clock", 107 .data = &am3_dpll_clock_clk_funcs }, 108 { .compat = "ti,omap3-dpll-clock", 109 .data = &omap3_dpll_clock_clk_funcs }, 110 { .compat = "ti,omap3-dpll-core-clock", 111 .data = &omap3_dpll_core_clock_clk_funcs }, 112 113 DEVICE_COMPAT_EOL 114 }; 115 116 enum { 117 REG_CONTROL, 118 REG_IDLEST, 119 REG_MULT_DIV1, 120 NREG 121 }; 122 123 struct ti_dpll_clock_softc { 124 device_t sc_dev; 125 int sc_phandle; 126 bus_space_tag_t sc_bst; 127 bus_space_handle_t sc_bsh[NREG]; 128 129 struct clk_domain sc_clkdom; 130 struct clk sc_clk; 131 }; 132 133 #define RD4(sc, space) \ 134 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh[(space)], 0) 135 #define WR4(sc, space, val) \ 136 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh[(space)], 0, (val)) 137 138 CFATTACH_DECL_NEW(ti_dpll_clock, sizeof(struct ti_dpll_clock_softc), 139 ti_dpll_clock_match, ti_dpll_clock_attach, NULL, NULL); 140 141 static int 142 ti_dpll_clock_match(device_t parent, cfdata_t cf, void *aux) 143 { 144 const struct fdt_attach_args *faa = aux; 145 146 return of_compatible_match(faa->faa_phandle, compat_data); 147 } 148 149 static void 150 ti_dpll_clock_attach(device_t parent, device_t self, void *aux) 151 { 152 struct ti_dpll_clock_softc * const sc = device_private(self); 153 const struct fdt_attach_args *faa = aux; 154 const int phandle = faa->faa_phandle; 155 const struct clk_funcs *clkfuncs; 156 bus_addr_t addr[NREG], base_addr; 157 u_int n; 158 159 const int prcm_phandle = OF_parent(OF_parent(phandle)); 160 if (fdtbus_get_reg(prcm_phandle, 0, &base_addr, NULL) != 0) { 161 aprint_error(": couldn't get prcm registers\n"); 162 return; 163 } 164 165 for (n = 0; n < NREG; n++) { 166 if (fdtbus_get_reg(phandle, n, &addr[n], NULL) != 0) { 167 aprint_error(": couldn't get registers\n"); 168 return; 169 } 170 } 171 172 sc->sc_dev = self; 173 sc->sc_phandle = phandle; 174 sc->sc_bst = faa->faa_bst; 175 for (n = 0; n < NREG; n++) { 176 if (bus_space_map(sc->sc_bst, base_addr + addr[n], 4, 0, &sc->sc_bsh[n]) != 0) { 177 aprint_error(": couldn't map registers\n"); 178 return; 179 } 180 } 181 182 clkfuncs = of_compatible_lookup(phandle, compat_data)->data; 183 184 sc->sc_clkdom.name = device_xname(self); 185 sc->sc_clkdom.funcs = clkfuncs; 186 sc->sc_clkdom.priv = sc; 187 188 sc->sc_clk.domain = &sc->sc_clkdom; 189 sc->sc_clk.name = kmem_asprintf("%s", faa->faa_name); 190 clk_attach(&sc->sc_clk); 191 192 aprint_naive("\n"); 193 aprint_normal(": TI DPLL clock (%s)\n", sc->sc_clk.name); 194 195 fdtbus_register_clock_controller(self, phandle, &ti_dpll_clock_fdt_funcs); 196 } 197 198 static struct clk * 199 ti_dpll_clock_decode(device_t dev, int cc_phandle, const void *data, 200 size_t len) 201 { 202 struct ti_dpll_clock_softc * const sc = device_private(dev); 203 204 return &sc->sc_clk; 205 } 206 207 static struct clk * 208 ti_dpll_clock_get(void *priv, const char *name) 209 { 210 struct ti_dpll_clock_softc * const sc = priv; 211 212 return &sc->sc_clk; 213 } 214 215 static void 216 ti_dpll_clock_put(void *priv, struct clk *clk) 217 { 218 } 219 220 static u_int 221 ti_dpll_clock_get_rate(void *priv, struct clk *clk) 222 { 223 struct ti_dpll_clock_softc * const sc = priv; 224 struct clk *clk_parent = clk_get_parent(clk); 225 uint32_t val; 226 uint64_t parent_rate; 227 228 if (clk_parent == NULL) 229 return 0; 230 231 val = RD4(sc, REG_MULT_DIV1); 232 const u_int mult = __SHIFTOUT(val, DPLL_MULT); 233 const u_int div = __SHIFTOUT(val, DPLL_DIV) + 1; 234 235 parent_rate = clk_get_rate(clk_parent); 236 237 return (u_int)((mult * parent_rate) / div); 238 } 239 240 static struct clk * 241 ti_dpll_clock_get_parent(void *priv, struct clk *clk) 242 { 243 struct ti_dpll_clock_softc * const sc = priv; 244 245 /* XXX assume ref clk */ 246 return fdtbus_clock_get_index(sc->sc_phandle, 0); 247 } 248 249 static int 250 am3_dpll_clock_set_rate(void *priv, struct clk *clk, u_int rate) 251 { 252 struct ti_dpll_clock_softc * const sc = priv; 253 struct clk *clk_parent = clk_get_parent(clk); 254 uint64_t parent_rate; 255 uint32_t control, mult_div1; 256 257 if (clk_parent == NULL) 258 return ENXIO; 259 260 parent_rate = clk_get_rate(clk_parent); 261 if (parent_rate == 0) 262 return EIO; 263 264 const u_int div = (parent_rate / 1000000) - 1; 265 const u_int mult = rate / (parent_rate / (div + 1)); 266 if (mult < 2 || mult > 2047) 267 return EINVAL; 268 269 control = RD4(sc, REG_CONTROL); 270 control &= ~AM3_DPLL_EN; 271 control |= __SHIFTIN(AM3_DPLL_EN_NM_BYPASS, AM3_DPLL_EN); 272 WR4(sc, REG_CONTROL, control); 273 274 while (RD4(sc, REG_IDLEST) != AM3_ST_MN_BYPASS) 275 ; 276 277 mult_div1 = __SHIFTIN(mult, DPLL_MULT); 278 mult_div1 |= __SHIFTIN(div, DPLL_DIV); 279 WR4(sc, REG_MULT_DIV1, mult_div1); 280 281 control &= ~AM3_DPLL_EN; 282 control |= __SHIFTIN(AM3_DPLL_EN_LOCK, AM3_DPLL_EN); 283 WR4(sc, REG_CONTROL, control); 284 285 while (RD4(sc, REG_IDLEST) != AM3_ST_DPLL_CLK) 286 ; 287 288 return 0; 289 } 290 291 static int 292 omap3_dpll_clock_set_rate(void *priv, struct clk *clk, u_int rate) 293 { 294 struct ti_dpll_clock_softc * const sc = priv; 295 struct clk *clk_parent = clk_get_parent(clk); 296 uint64_t parent_rate; 297 uint32_t control, mult_div1; 298 299 if (clk_parent == NULL) 300 return ENXIO; 301 302 parent_rate = clk_get_rate(clk_parent); 303 304 const u_int div = (parent_rate / 1000000) - 1; 305 const u_int mult = rate / (parent_rate / (div + 1)); 306 if (mult < 2 || mult > 2047) 307 return EINVAL; 308 309 control = RD4(sc, REG_CONTROL); 310 control &= ~OMAP3_EN_MPU_DPLL; 311 control |= __SHIFTIN(OMAP3_EN_MPU_DPLL_BYPASS, OMAP3_EN_MPU_DPLL); 312 WR4(sc, REG_CONTROL, control); 313 314 delay(10); 315 316 mult_div1 = __SHIFTIN(mult, DPLL_MULT); 317 mult_div1 |= __SHIFTIN(div, DPLL_DIV); 318 WR4(sc, REG_MULT_DIV1, mult_div1); 319 320 control &= ~OMAP3_EN_MPU_DPLL; 321 control |= __SHIFTIN(OMAP3_EN_MPU_DPLL_LOCK, OMAP3_EN_MPU_DPLL); 322 WR4(sc, REG_CONTROL, control); 323 324 while ((RD4(sc, REG_IDLEST) & OMAP3_ST_MPU_CLK) != 0) 325 ; 326 327 return 0; 328 } 329 330 static u_int 331 omap3_dpll_core_clock_get_rate(void *priv, struct clk *clk) 332 { 333 struct ti_dpll_clock_softc * const sc = priv; 334 struct clk *clk_parent = clk_get_parent(clk); 335 uint32_t val; 336 uint64_t parent_rate; 337 338 if (clk_parent == NULL) 339 return 0; 340 341 val = RD4(sc, REG_MULT_DIV1); 342 const u_int mult = __SHIFTOUT(val, OMAP3_CORE_DPLL_MULT); 343 const u_int div = __SHIFTOUT(val, OMAP3_CORE_DPLL_DIV) + 1; 344 const u_int postdiv = __SHIFTOUT(val, OMAP3_CORE_DPLL_CLKOUT_DIV); 345 346 parent_rate = clk_get_rate(clk_parent); 347 348 return (u_int)((mult * parent_rate) / div) / postdiv; 349 } 350