1 /* $NetBSD: meson_clk.c,v 1.3 2019/04/19 19:07:56 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2017-2019 Jared McNeill <jmcneill@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: meson_clk.c,v 1.3 2019/04/19 19:07:56 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/cpu.h> 35 #include <sys/device.h> 36 37 #include <dev/fdt/fdtvar.h> 38 #include <dev/fdt/syscon.h> 39 40 #include <dev/clk/clk_backend.h> 41 42 #include <arm/amlogic/meson_clk.h> 43 44 static void * 45 meson_clk_reset_acquire(device_t dev, const void *data, size_t len) 46 { 47 struct meson_clk_softc * const sc = device_private(dev); 48 struct meson_clk_reset *reset; 49 50 if (len != 4) 51 return NULL; 52 53 const u_int reset_id = be32dec(data); 54 55 if (reset_id >= sc->sc_nresets) 56 return NULL; 57 58 reset = &sc->sc_resets[reset_id]; 59 if (reset->mask == 0) 60 return NULL; 61 62 return reset; 63 } 64 65 static void 66 meson_clk_reset_release(device_t dev, void *priv) 67 { 68 } 69 70 static int 71 meson_clk_reset_assert(device_t dev, void *priv) 72 { 73 struct meson_clk_softc * const sc = device_private(dev); 74 struct meson_clk_reset * const reset = priv; 75 76 CLK_LOCK(sc); 77 const uint32_t val = CLK_READ(sc, reset->reg); 78 CLK_WRITE(sc, reset->reg, val | reset->mask); 79 CLK_UNLOCK(sc); 80 81 return 0; 82 } 83 84 static int 85 meson_clk_reset_deassert(device_t dev, void *priv) 86 { 87 struct meson_clk_softc * const sc = device_private(dev); 88 struct meson_clk_reset * const reset = priv; 89 90 CLK_LOCK(sc); 91 const uint32_t val = CLK_READ(sc, reset->reg); 92 CLK_WRITE(sc, reset->reg, val & ~reset->mask); 93 CLK_UNLOCK(sc); 94 95 return 0; 96 } 97 98 static const struct fdtbus_reset_controller_func meson_clk_fdtreset_funcs = { 99 .acquire = meson_clk_reset_acquire, 100 .release = meson_clk_reset_release, 101 .reset_assert = meson_clk_reset_assert, 102 .reset_deassert = meson_clk_reset_deassert, 103 }; 104 105 static struct clk * 106 meson_clk_clock_decode(device_t dev, int cc_phandle, const void *data, 107 size_t len) 108 { 109 struct meson_clk_softc * const sc = device_private(dev); 110 struct meson_clk_clk *clk; 111 112 if (len != 4) 113 return NULL; 114 115 const u_int clock_id = be32dec(data); 116 if (clock_id >= sc->sc_nclks) 117 return NULL; 118 119 clk = &sc->sc_clks[clock_id]; 120 if (clk->type == MESON_CLK_UNKNOWN) 121 return NULL; 122 123 return &clk->base; 124 } 125 126 static const struct fdtbus_clock_controller_func meson_clk_fdtclock_funcs = { 127 .decode = meson_clk_clock_decode, 128 }; 129 130 static struct clk * 131 meson_clk_clock_get(void *priv, const char *name) 132 { 133 struct meson_clk_softc * const sc = priv; 134 struct meson_clk_clk *clk; 135 136 clk = meson_clk_clock_find(sc, name); 137 if (clk == NULL) 138 return NULL; 139 140 return &clk->base; 141 } 142 143 static void 144 meson_clk_clock_put(void *priv, struct clk *clk) 145 { 146 } 147 148 static u_int 149 meson_clk_clock_get_rate(void *priv, struct clk *clkp) 150 { 151 struct meson_clk_softc * const sc = priv; 152 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp; 153 struct clk *clkp_parent; 154 155 if (clk->get_rate) 156 return clk->get_rate(sc, clk); 157 158 clkp_parent = clk_get_parent(clkp); 159 if (clkp_parent == NULL) { 160 aprint_debug("%s: no parent for %s\n", __func__, clk->base.name); 161 return 0; 162 } 163 164 return clk_get_rate(clkp_parent); 165 } 166 167 static int 168 meson_clk_clock_set_rate(void *priv, struct clk *clkp, u_int rate) 169 { 170 struct meson_clk_softc * const sc = priv; 171 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp; 172 struct clk *clkp_parent; 173 174 if (clkp->flags & CLK_SET_RATE_PARENT) { 175 clkp_parent = clk_get_parent(clkp); 176 if (clkp_parent == NULL) { 177 aprint_debug("%s: no parent for %s\n", __func__, clk->base.name); 178 return ENXIO; 179 } 180 return clk_set_rate(clkp_parent, rate); 181 } 182 183 if (clk->set_rate) 184 return clk->set_rate(sc, clk, rate); 185 186 return ENXIO; 187 } 188 189 static u_int 190 meson_clk_clock_round_rate(void *priv, struct clk *clkp, u_int rate) 191 { 192 struct meson_clk_softc * const sc = priv; 193 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp; 194 struct clk *clkp_parent; 195 196 if (clkp->flags & CLK_SET_RATE_PARENT) { 197 clkp_parent = clk_get_parent(clkp); 198 if (clkp_parent == NULL) { 199 aprint_debug("%s: no parent for %s\n", __func__, clk->base.name); 200 return 0; 201 } 202 return clk_round_rate(clkp_parent, rate); 203 } 204 205 if (clk->round_rate) 206 return clk->round_rate(sc, clk, rate); 207 208 return 0; 209 } 210 211 static int 212 meson_clk_clock_enable(void *priv, struct clk *clkp) 213 { 214 struct meson_clk_softc * const sc = priv; 215 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp; 216 struct clk *clkp_parent; 217 int error = 0; 218 219 clkp_parent = clk_get_parent(clkp); 220 if (clkp_parent != NULL) { 221 error = clk_enable(clkp_parent); 222 if (error != 0) 223 return error; 224 } 225 226 if (clk->enable) 227 error = clk->enable(sc, clk, 1); 228 229 return error; 230 } 231 232 static int 233 meson_clk_clock_disable(void *priv, struct clk *clkp) 234 { 235 struct meson_clk_softc * const sc = priv; 236 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp; 237 int error = EINVAL; 238 239 if (clk->enable) 240 error = clk->enable(sc, clk, 0); 241 242 return error; 243 } 244 245 static int 246 meson_clk_clock_set_parent(void *priv, struct clk *clkp, 247 struct clk *clkp_parent) 248 { 249 struct meson_clk_softc * const sc = priv; 250 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp; 251 252 if (clk->set_parent == NULL) 253 return EINVAL; 254 255 return clk->set_parent(sc, clk, clkp_parent->name); 256 } 257 258 static struct clk * 259 meson_clk_clock_get_parent(void *priv, struct clk *clkp) 260 { 261 struct meson_clk_softc * const sc = priv; 262 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp; 263 struct meson_clk_clk *clk_parent; 264 const char *parent; 265 266 if (clk->get_parent == NULL) 267 return NULL; 268 269 parent = clk->get_parent(sc, clk); 270 if (parent == NULL) 271 return NULL; 272 273 clk_parent = meson_clk_clock_find(sc, parent); 274 if (clk_parent != NULL) 275 return &clk_parent->base; 276 277 /* No parent in this domain, try FDT */ 278 return fdtbus_clock_get(sc->sc_phandle, parent); 279 } 280 281 static const struct clk_funcs meson_clk_clock_funcs = { 282 .get = meson_clk_clock_get, 283 .put = meson_clk_clock_put, 284 .get_rate = meson_clk_clock_get_rate, 285 .set_rate = meson_clk_clock_set_rate, 286 .round_rate = meson_clk_clock_round_rate, 287 .enable = meson_clk_clock_enable, 288 .disable = meson_clk_clock_disable, 289 .set_parent = meson_clk_clock_set_parent, 290 .get_parent = meson_clk_clock_get_parent, 291 }; 292 293 struct meson_clk_clk * 294 meson_clk_clock_find(struct meson_clk_softc *sc, const char *name) 295 { 296 for (int i = 0; i < sc->sc_nclks; i++) { 297 if (sc->sc_clks[i].base.name == NULL) 298 continue; 299 if (strcmp(sc->sc_clks[i].base.name, name) == 0) 300 return &sc->sc_clks[i]; 301 } 302 303 return NULL; 304 } 305 306 void 307 meson_clk_attach(struct meson_clk_softc *sc) 308 { 309 int i; 310 311 sc->sc_clkdom.name = device_xname(sc->sc_dev); 312 sc->sc_clkdom.funcs = &meson_clk_clock_funcs; 313 sc->sc_clkdom.priv = sc; 314 for (i = 0; i < sc->sc_nclks; i++) { 315 sc->sc_clks[i].base.domain = &sc->sc_clkdom; 316 clk_attach(&sc->sc_clks[i].base); 317 } 318 319 if (sc->sc_nclks > 0) 320 fdtbus_register_clock_controller(sc->sc_dev, sc->sc_phandle, 321 &meson_clk_fdtclock_funcs); 322 323 if (sc->sc_nresets > 0) 324 fdtbus_register_reset_controller(sc->sc_dev, sc->sc_phandle, 325 &meson_clk_fdtreset_funcs); 326 } 327 328 void 329 meson_clk_print(struct meson_clk_softc *sc) 330 { 331 struct meson_clk_clk *clk; 332 struct clk *clkp_parent; 333 const char *type; 334 int i; 335 336 for (i = 0; i < sc->sc_nclks; i++) { 337 clk = &sc->sc_clks[i]; 338 if (clk->type == MESON_CLK_UNKNOWN) 339 continue; 340 341 clkp_parent = clk_get_parent(&clk->base); 342 343 switch (clk->type) { 344 case MESON_CLK_FIXED: type = "fixed"; break; 345 case MESON_CLK_GATE: type = "gate"; break; 346 case MESON_CLK_MPLL: type = "mpll"; break; 347 case MESON_CLK_PLL: type = "pll"; break; 348 case MESON_CLK_DIV: type = "div"; break; 349 case MESON_CLK_FIXED_FACTOR: type = "fixed-factor"; break; 350 case MESON_CLK_MUX: type = "mux"; break; 351 default: type = "???"; break; 352 } 353 354 aprint_debug_dev(sc->sc_dev, 355 "%3d %-12s %2s %-12s %-7s ", 356 i, 357 clk->base.name, 358 clkp_parent ? "<-" : "", 359 clkp_parent ? clkp_parent->name : "", 360 type); 361 aprint_debug("%10u Hz\n", clk_get_rate(&clk->base)); 362 } 363 } 364 365 void 366 meson_clk_lock(struct meson_clk_softc *sc) 367 { 368 if (sc->sc_syscon != NULL) 369 syscon_lock(sc->sc_syscon); 370 } 371 372 void 373 meson_clk_unlock(struct meson_clk_softc *sc) 374 { 375 if (sc->sc_syscon != NULL) 376 syscon_unlock(sc->sc_syscon); 377 } 378 379 uint32_t 380 meson_clk_read(struct meson_clk_softc *sc, bus_size_t reg) 381 { 382 if (sc->sc_syscon != NULL) 383 return syscon_read_4(sc->sc_syscon, reg); 384 else 385 return bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg); 386 } 387 388 void 389 meson_clk_write(struct meson_clk_softc *sc, bus_size_t reg, uint32_t val) 390 { 391 if (sc->sc_syscon != NULL) 392 syscon_write_4(sc->sc_syscon, reg, val); 393 else 394 bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, val); 395 } 396