xref: /netbsd-src/sys/arch/arm/rockchip/rk_cru.c (revision 06e937db54c3f1ab54b11f5089af6f303bcc12eb)
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