1*06e937dbSryo /* $NetBSD: rk_cru.c,v 1.10 2022/09/18 21:33:57 ryo Exp $ */
26726462dSjmcneill
36726462dSjmcneill /*-
46726462dSjmcneill * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
56726462dSjmcneill * All rights reserved.
66726462dSjmcneill *
76726462dSjmcneill * Redistribution and use in source and binary forms, with or without
86726462dSjmcneill * modification, are permitted provided that the following conditions
96726462dSjmcneill * are met:
106726462dSjmcneill * 1. Redistributions of source code must retain the above copyright
116726462dSjmcneill * notice, this list of conditions and the following disclaimer.
126726462dSjmcneill * 2. Redistributions in binary form must reproduce the above copyright
136726462dSjmcneill * notice, this list of conditions and the following disclaimer in the
146726462dSjmcneill * documentation and/or other materials provided with the distribution.
156726462dSjmcneill *
166726462dSjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
176726462dSjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
186726462dSjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
196726462dSjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
206726462dSjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
216726462dSjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
226726462dSjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
236726462dSjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
246726462dSjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
256726462dSjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
266726462dSjmcneill * SUCH DAMAGE.
276726462dSjmcneill */
286726462dSjmcneill
296726462dSjmcneill #include "opt_soc.h"
305a821b2cSskrll #include "opt_console.h"
316726462dSjmcneill
326726462dSjmcneill #include <sys/cdefs.h>
33*06e937dbSryo __KERNEL_RCSID(0, "$NetBSD: rk_cru.c,v 1.10 2022/09/18 21:33:57 ryo Exp $");
346726462dSjmcneill
356726462dSjmcneill #include <sys/param.h>
366726462dSjmcneill #include <sys/bus.h>
376726462dSjmcneill #include <sys/cpu.h>
386726462dSjmcneill #include <sys/device.h>
396726462dSjmcneill
406726462dSjmcneill #include <dev/fdt/fdtvar.h>
416726462dSjmcneill
426726462dSjmcneill #include <dev/clk/clk_backend.h>
436726462dSjmcneill
446726462dSjmcneill #include <arm/rockchip/rk_cru.h>
456726462dSjmcneill
466726462dSjmcneill static void *
rk_cru_reset_acquire(device_t dev,const void * data,size_t len)476726462dSjmcneill rk_cru_reset_acquire(device_t dev, const void *data, size_t len)
486726462dSjmcneill {
496726462dSjmcneill if (len != 4)
506726462dSjmcneill return NULL;
516726462dSjmcneill
526726462dSjmcneill return (void *)(uintptr_t)be32dec(data);
536726462dSjmcneill }
546726462dSjmcneill
556726462dSjmcneill static void
rk_cru_reset_release(device_t dev,void * priv)566726462dSjmcneill rk_cru_reset_release(device_t dev, void *priv)
576726462dSjmcneill {
586726462dSjmcneill }
596726462dSjmcneill
606726462dSjmcneill static int
rk_cru_reset_assert(device_t dev,void * priv)616726462dSjmcneill rk_cru_reset_assert(device_t dev, void *priv)
626726462dSjmcneill {
636726462dSjmcneill struct rk_cru_softc * const sc = device_private(dev);
646726462dSjmcneill const uintptr_t reset_id = (uintptr_t)priv;
658c7bec30Sjmcneill const bus_size_t reg = sc->sc_softrst_base + (reset_id / 16) * 4;
6605a266f0Sjmcneill const u_int shift = reset_id % 16;
676726462dSjmcneill
6805a266f0Sjmcneill CRU_WRITE(sc, reg, (1 << (shift + 16)) | (1 << shift));
696726462dSjmcneill
706726462dSjmcneill return 0;
716726462dSjmcneill }
726726462dSjmcneill
736726462dSjmcneill static int
rk_cru_reset_deassert(device_t dev,void * priv)746726462dSjmcneill rk_cru_reset_deassert(device_t dev, void *priv)
756726462dSjmcneill {
766726462dSjmcneill struct rk_cru_softc * const sc = device_private(dev);
776726462dSjmcneill const uintptr_t reset_id = (uintptr_t)priv;
788c7bec30Sjmcneill const bus_size_t reg = sc->sc_softrst_base + (reset_id / 16) * 4;
7905a266f0Sjmcneill const u_int shift = reset_id % 16;
806726462dSjmcneill
8105a266f0Sjmcneill CRU_WRITE(sc, reg, (1 << (shift + 16)) | (0 << shift));
826726462dSjmcneill
836726462dSjmcneill return 0;
846726462dSjmcneill }
856726462dSjmcneill
866726462dSjmcneill static const struct fdtbus_reset_controller_func rk_cru_fdtreset_funcs = {
876726462dSjmcneill .acquire = rk_cru_reset_acquire,
886726462dSjmcneill .release = rk_cru_reset_release,
896726462dSjmcneill .reset_assert = rk_cru_reset_assert,
906726462dSjmcneill .reset_deassert = rk_cru_reset_deassert,
916726462dSjmcneill };
926726462dSjmcneill
936726462dSjmcneill static struct clk *
rk_cru_clock_decode(device_t dev,int cc_phandle,const void * data,size_t len)9451b425efSaymeric rk_cru_clock_decode(device_t dev, int cc_phandle, const void *data, size_t len)
956726462dSjmcneill {
966726462dSjmcneill struct rk_cru_softc * const sc = device_private(dev);
976726462dSjmcneill struct rk_cru_clk *clk;
986726462dSjmcneill
996726462dSjmcneill if (len != 4)
1006726462dSjmcneill return NULL;
1016726462dSjmcneill
1026726462dSjmcneill const u_int clock_id = be32dec(data);
1036726462dSjmcneill
1046726462dSjmcneill for (int i = 0; i < sc->sc_nclks; i++) {
1056726462dSjmcneill clk = &sc->sc_clks[i];
1066726462dSjmcneill if (clk->id == clock_id)
1076726462dSjmcneill return &clk->base;
1086726462dSjmcneill }
1096726462dSjmcneill
1106726462dSjmcneill return NULL;
1116726462dSjmcneill }
1126726462dSjmcneill
1136726462dSjmcneill static const struct fdtbus_clock_controller_func rk_cru_fdtclock_funcs = {
1146726462dSjmcneill .decode = rk_cru_clock_decode,
1156726462dSjmcneill };
1166726462dSjmcneill
1176726462dSjmcneill static struct clk *
rk_cru_clock_get(void * priv,const char * name)1186726462dSjmcneill rk_cru_clock_get(void *priv, const char *name)
1196726462dSjmcneill {
1206726462dSjmcneill struct rk_cru_softc * const sc = priv;
1216726462dSjmcneill struct rk_cru_clk *clk;
1226726462dSjmcneill
1236726462dSjmcneill clk = rk_cru_clock_find(sc, name);
1246726462dSjmcneill if (clk == NULL)
1256726462dSjmcneill return NULL;
1266726462dSjmcneill
1276726462dSjmcneill return &clk->base;
1286726462dSjmcneill }
1296726462dSjmcneill
1306726462dSjmcneill static void
rk_cru_clock_put(void * priv,struct clk * clk)1316726462dSjmcneill rk_cru_clock_put(void *priv, struct clk *clk)
1326726462dSjmcneill {
1336726462dSjmcneill }
1346726462dSjmcneill
1356726462dSjmcneill static u_int
rk_cru_clock_get_rate(void * priv,struct clk * clkp)1366726462dSjmcneill rk_cru_clock_get_rate(void *priv, struct clk *clkp)
1376726462dSjmcneill {
1386726462dSjmcneill struct rk_cru_softc * const sc = priv;
1396726462dSjmcneill struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
1406726462dSjmcneill struct clk *clkp_parent;
1416726462dSjmcneill
1426726462dSjmcneill if (clk->get_rate)
1436726462dSjmcneill return clk->get_rate(sc, clk);
1446726462dSjmcneill
1456726462dSjmcneill clkp_parent = clk_get_parent(clkp);
1466726462dSjmcneill if (clkp_parent == NULL) {
147*06e937dbSryo aprint_debug("%s: no parent for %s\n", __func__,
148*06e937dbSryo clk->base.name);
1496726462dSjmcneill return 0;
1506726462dSjmcneill }
1516726462dSjmcneill
1526726462dSjmcneill return clk_get_rate(clkp_parent);
1536726462dSjmcneill }
1546726462dSjmcneill
1556726462dSjmcneill static int
rk_cru_clock_set_rate(void * priv,struct clk * clkp,u_int rate)1566726462dSjmcneill rk_cru_clock_set_rate(void *priv, struct clk *clkp, u_int rate)
1576726462dSjmcneill {
1586726462dSjmcneill struct rk_cru_softc * const sc = priv;
1596726462dSjmcneill struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
1606726462dSjmcneill struct clk *clkp_parent;
1616726462dSjmcneill
1626726462dSjmcneill if (clkp->flags & CLK_SET_RATE_PARENT) {
1636726462dSjmcneill clkp_parent = clk_get_parent(clkp);
1646726462dSjmcneill if (clkp_parent == NULL) {
165*06e937dbSryo aprint_error("%s: no parent for %s\n", __func__,
166*06e937dbSryo clk->base.name);
1676726462dSjmcneill return ENXIO;
1686726462dSjmcneill }
1696726462dSjmcneill return clk_set_rate(clkp_parent, rate);
1706726462dSjmcneill }
1716726462dSjmcneill
1726726462dSjmcneill if (clk->set_rate)
1736726462dSjmcneill return clk->set_rate(sc, clk, rate);
1746726462dSjmcneill
1756726462dSjmcneill return ENXIO;
1766726462dSjmcneill }
1776726462dSjmcneill
1786726462dSjmcneill static u_int
rk_cru_clock_round_rate(void * priv,struct clk * clkp,u_int rate)1796726462dSjmcneill rk_cru_clock_round_rate(void *priv, struct clk *clkp, u_int rate)
1806726462dSjmcneill {
1816726462dSjmcneill struct rk_cru_softc * const sc = priv;
1826726462dSjmcneill struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
1836726462dSjmcneill struct clk *clkp_parent;
1846726462dSjmcneill
1856726462dSjmcneill if (clkp->flags & CLK_SET_RATE_PARENT) {
1866726462dSjmcneill clkp_parent = clk_get_parent(clkp);
1876726462dSjmcneill if (clkp_parent == NULL) {
188*06e937dbSryo aprint_error("%s: no parent for %s\n", __func__,
189*06e937dbSryo clk->base.name);
1906726462dSjmcneill return 0;
1916726462dSjmcneill }
1926726462dSjmcneill return clk_round_rate(clkp_parent, rate);
1936726462dSjmcneill }
1946726462dSjmcneill
1956726462dSjmcneill if (clk->round_rate)
1966726462dSjmcneill return clk->round_rate(sc, clk, rate);
1976726462dSjmcneill
1986726462dSjmcneill return 0;
1996726462dSjmcneill }
2006726462dSjmcneill
2016726462dSjmcneill static int
rk_cru_clock_enable(void * priv,struct clk * clkp)2026726462dSjmcneill rk_cru_clock_enable(void *priv, struct clk *clkp)
2036726462dSjmcneill {
2046726462dSjmcneill struct rk_cru_softc * const sc = priv;
2056726462dSjmcneill struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
2066726462dSjmcneill struct clk *clkp_parent;
2076726462dSjmcneill int error = 0;
2086726462dSjmcneill
2096726462dSjmcneill clkp_parent = clk_get_parent(clkp);
2106726462dSjmcneill if (clkp_parent != NULL) {
2116726462dSjmcneill error = clk_enable(clkp_parent);
2126726462dSjmcneill if (error != 0)
2136726462dSjmcneill return error;
2146726462dSjmcneill }
2156726462dSjmcneill
2166726462dSjmcneill if (clk->enable)
2176726462dSjmcneill error = clk->enable(sc, clk, 1);
2186726462dSjmcneill
2196726462dSjmcneill return error;
2206726462dSjmcneill }
2216726462dSjmcneill
2226726462dSjmcneill static int
rk_cru_clock_disable(void * priv,struct clk * clkp)2236726462dSjmcneill rk_cru_clock_disable(void *priv, struct clk *clkp)
2246726462dSjmcneill {
2256726462dSjmcneill struct rk_cru_softc * const sc = priv;
2266726462dSjmcneill struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
2276726462dSjmcneill int error = EINVAL;
2286726462dSjmcneill
2296726462dSjmcneill if (clk->enable)
2306726462dSjmcneill error = clk->enable(sc, clk, 0);
2316726462dSjmcneill
2326726462dSjmcneill return error;
2336726462dSjmcneill }
2346726462dSjmcneill
2356726462dSjmcneill static int
rk_cru_clock_set_parent(void * priv,struct clk * clkp,struct clk * clkp_parent)236*06e937dbSryo rk_cru_clock_set_parent(void *priv, struct clk *clkp, struct clk *clkp_parent)
2376726462dSjmcneill {
2386726462dSjmcneill struct rk_cru_softc * const sc = priv;
2396726462dSjmcneill struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
2406726462dSjmcneill
2416726462dSjmcneill if (clk->set_parent == NULL)
2426726462dSjmcneill return EINVAL;
2436726462dSjmcneill
2446726462dSjmcneill return clk->set_parent(sc, clk, clkp_parent->name);
2456726462dSjmcneill }
2466726462dSjmcneill
2476726462dSjmcneill static struct clk *
rk_cru_clock_get_parent(void * priv,struct clk * clkp)2486726462dSjmcneill rk_cru_clock_get_parent(void *priv, struct clk *clkp)
2496726462dSjmcneill {
2506726462dSjmcneill struct rk_cru_softc * const sc = priv;
2516726462dSjmcneill struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
2526726462dSjmcneill struct rk_cru_clk *clk_parent;
2536726462dSjmcneill const char *parent;
2546726462dSjmcneill
2556726462dSjmcneill if (clk->get_parent == NULL)
2566726462dSjmcneill return NULL;
2576726462dSjmcneill
2586726462dSjmcneill parent = clk->get_parent(sc, clk);
2596726462dSjmcneill if (parent == NULL)
2606726462dSjmcneill return NULL;
2616726462dSjmcneill
2626726462dSjmcneill clk_parent = rk_cru_clock_find(sc, parent);
2636726462dSjmcneill if (clk_parent != NULL)
2646726462dSjmcneill return &clk_parent->base;
2656726462dSjmcneill
2666726462dSjmcneill /* No parent in this domain, try FDT */
2676726462dSjmcneill return fdtbus_clock_byname(parent);
2686726462dSjmcneill }
2696726462dSjmcneill
2706726462dSjmcneill static const struct clk_funcs rk_cru_clock_funcs = {
2716726462dSjmcneill .get = rk_cru_clock_get,
2726726462dSjmcneill .put = rk_cru_clock_put,
2736726462dSjmcneill .get_rate = rk_cru_clock_get_rate,
2746726462dSjmcneill .set_rate = rk_cru_clock_set_rate,
2756726462dSjmcneill .round_rate = rk_cru_clock_round_rate,
2766726462dSjmcneill .enable = rk_cru_clock_enable,
2776726462dSjmcneill .disable = rk_cru_clock_disable,
2786726462dSjmcneill .set_parent = rk_cru_clock_set_parent,
2796726462dSjmcneill .get_parent = rk_cru_clock_get_parent,
2806726462dSjmcneill };
2816726462dSjmcneill
2826726462dSjmcneill struct rk_cru_clk *
rk_cru_clock_find(struct rk_cru_softc * sc,const char * name)2836726462dSjmcneill rk_cru_clock_find(struct rk_cru_softc *sc, const char *name)
2846726462dSjmcneill {
2856726462dSjmcneill for (int i = 0; i < sc->sc_nclks; i++) {
2866726462dSjmcneill if (sc->sc_clks[i].base.name == NULL)
2876726462dSjmcneill continue;
2886726462dSjmcneill if (strcmp(sc->sc_clks[i].base.name, name) == 0)
2896726462dSjmcneill return &sc->sc_clks[i];
2906726462dSjmcneill }
2916726462dSjmcneill
2926726462dSjmcneill return NULL;
2936726462dSjmcneill }
2946726462dSjmcneill
2956726462dSjmcneill int
rk_cru_attach(struct rk_cru_softc * sc)2966726462dSjmcneill rk_cru_attach(struct rk_cru_softc *sc)
2976726462dSjmcneill {
2986726462dSjmcneill bus_addr_t addr;
2996726462dSjmcneill bus_size_t size;
3006726462dSjmcneill int i;
3016726462dSjmcneill
3026726462dSjmcneill if (of_hasprop(sc->sc_phandle, "rockchip,grf")) {
303*06e937dbSryo sc->sc_grf = fdtbus_syscon_acquire(sc->sc_phandle,
304*06e937dbSryo "rockchip,grf");
305a515ac42Sjmcneill if (sc->sc_grf == NULL) {
306a515ac42Sjmcneill aprint_error(": couldn't get grf syscon\n");
3076726462dSjmcneill return ENXIO;
3086726462dSjmcneill }
3096726462dSjmcneill }
3106726462dSjmcneill
3116726462dSjmcneill if (fdtbus_get_reg(sc->sc_phandle, 0, &addr, &size) != 0) {
3126726462dSjmcneill aprint_error(": couldn't get registers\n");
3136726462dSjmcneill return ENXIO;
3146726462dSjmcneill }
3156726462dSjmcneill if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
3166726462dSjmcneill aprint_error(": couldn't map registers\n");
3176726462dSjmcneill return ENXIO;
3186726462dSjmcneill }
3196726462dSjmcneill
3206726462dSjmcneill sc->sc_clkdom.name = device_xname(sc->sc_dev);
3216726462dSjmcneill sc->sc_clkdom.funcs = &rk_cru_clock_funcs;
3226726462dSjmcneill sc->sc_clkdom.priv = sc;
3236726462dSjmcneill for (i = 0; i < sc->sc_nclks; i++) {
3246726462dSjmcneill sc->sc_clks[i].base.domain = &sc->sc_clkdom;
3256726462dSjmcneill clk_attach(&sc->sc_clks[i].base);
3266726462dSjmcneill }
3276726462dSjmcneill
3286726462dSjmcneill fdtbus_register_clock_controller(sc->sc_dev, sc->sc_phandle,
3296726462dSjmcneill &rk_cru_fdtclock_funcs);
3306726462dSjmcneill
3316726462dSjmcneill fdtbus_register_reset_controller(sc->sc_dev, sc->sc_phandle,
3326726462dSjmcneill &rk_cru_fdtreset_funcs);
3336726462dSjmcneill
3346726462dSjmcneill return 0;
3356726462dSjmcneill }
3366726462dSjmcneill
3376726462dSjmcneill void
rk_cru_print(struct rk_cru_softc * sc)3386726462dSjmcneill rk_cru_print(struct rk_cru_softc *sc)
3396726462dSjmcneill {
3406726462dSjmcneill struct rk_cru_clk *clk;
3416726462dSjmcneill struct clk *clkp_parent;
3426726462dSjmcneill const char *type;
3436726462dSjmcneill int i;
3446726462dSjmcneill
3456726462dSjmcneill for (i = 0; i < sc->sc_nclks; i++) {
3466726462dSjmcneill clk = &sc->sc_clks[i];
3476726462dSjmcneill if (clk->type == RK_CRU_UNKNOWN)
3486726462dSjmcneill continue;
3496726462dSjmcneill
3506726462dSjmcneill clkp_parent = clk_get_parent(&clk->base);
3516726462dSjmcneill
3526726462dSjmcneill switch (clk->type) {
3536726462dSjmcneill case RK_CRU_PLL: type = "pll"; break;
3546726462dSjmcneill case RK_CRU_ARM: type = "arm"; break;
3556726462dSjmcneill case RK_CRU_COMPOSITE: type = "comp"; break;
3566726462dSjmcneill case RK_CRU_GATE: type = "gate"; break;
3576726462dSjmcneill case RK_CRU_MUX: type = "mux"; break;
3586726462dSjmcneill default: type = "???"; break;
3596726462dSjmcneill }
3606726462dSjmcneill
36157ab6277Sjmcneill aprint_debug_dev(sc->sc_dev,
3626726462dSjmcneill "%3d %-14s %2s %-14s %-7s ",
3636726462dSjmcneill clk->id,
3646726462dSjmcneill clk->base.name,
3656726462dSjmcneill clkp_parent ? "<-" : "",
3666726462dSjmcneill clkp_parent ? clkp_parent->name : "",
3676726462dSjmcneill type);
36857ab6277Sjmcneill aprint_debug("%10d Hz\n", clk_get_rate(&clk->base));
3696726462dSjmcneill }
3706726462dSjmcneill }
371