xref: /netbsd-src/sys/arch/arm/rockchip/rk_cru_composite.c (revision 4fb053697f9d68002b95c174d5a1bc8eedbd6977)
1*4fb05369Sryo /* $NetBSD: rk_cru_composite.c,v 1.8 2022/08/23 05:39:06 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 <sys/cdefs.h>
30*4fb05369Sryo __KERNEL_RCSID(0, "$NetBSD: rk_cru_composite.c,v 1.8 2022/08/23 05:39:06 ryo Exp $");
316726462dSjmcneill 
326726462dSjmcneill #include <sys/param.h>
336726462dSjmcneill #include <sys/bus.h>
346726462dSjmcneill 
356726462dSjmcneill #include <dev/clk/clk_backend.h>
366726462dSjmcneill 
376726462dSjmcneill #include <arm/rockchip/rk_cru.h>
386726462dSjmcneill 
39ded90cc0Sjmcneill #include <dev/fdt/fdtvar.h>
40ded90cc0Sjmcneill 
416726462dSjmcneill int
rk_cru_composite_enable(struct rk_cru_softc * sc,struct rk_cru_clk * clk,int enable)426726462dSjmcneill rk_cru_composite_enable(struct rk_cru_softc *sc, struct rk_cru_clk *clk,
436726462dSjmcneill     int enable)
446726462dSjmcneill {
456726462dSjmcneill 	struct rk_cru_composite *composite = &clk->u.composite;
466726462dSjmcneill 
476726462dSjmcneill 	KASSERT(clk->type == RK_CRU_COMPOSITE);
486726462dSjmcneill 
496726462dSjmcneill 	if (composite->gate_mask == 0)
506726462dSjmcneill 		return enable ? 0 : ENXIO;
516726462dSjmcneill 
526726462dSjmcneill 	const uint32_t write_mask = composite->gate_mask << 16;
5362d94959Sjmcneill 	const uint32_t write_val = enable ? 0 : composite->gate_mask;
546726462dSjmcneill 
556726462dSjmcneill 	CRU_WRITE(sc, composite->gate_reg, write_mask | write_val);
566726462dSjmcneill 
576726462dSjmcneill 	return 0;
586726462dSjmcneill }
596726462dSjmcneill 
606726462dSjmcneill u_int
rk_cru_composite_get_rate(struct rk_cru_softc * sc,struct rk_cru_clk * clk)616726462dSjmcneill rk_cru_composite_get_rate(struct rk_cru_softc *sc,
626726462dSjmcneill     struct rk_cru_clk *clk)
636726462dSjmcneill {
646726462dSjmcneill 	struct rk_cru_composite *composite = &clk->u.composite;
656726462dSjmcneill 	struct clk *clkp, *clkp_parent;
666726462dSjmcneill 
676726462dSjmcneill 	KASSERT(clk->type == RK_CRU_COMPOSITE);
686726462dSjmcneill 
696726462dSjmcneill 	clkp = &clk->base;
706726462dSjmcneill 	clkp_parent = clk_get_parent(clkp);
716726462dSjmcneill 	if (clkp_parent == NULL)
726726462dSjmcneill 		return 0;
736726462dSjmcneill 
746726462dSjmcneill 	const u_int prate = clk_get_rate(clkp_parent);
756726462dSjmcneill 	if (prate == 0)
766726462dSjmcneill 		return 0;
776726462dSjmcneill 
78bc3e5e3bSjmcneill 	if (composite->flags & RK_COMPOSITE_FRACDIV) {
79bc3e5e3bSjmcneill 		const uint32_t val = CRU_READ(sc, composite->frac_reg);
80bc3e5e3bSjmcneill 		const u_int num = (val >> 16) & 0xffff;
81bc3e5e3bSjmcneill 		const u_int den = val & 0xffff;
82bc3e5e3bSjmcneill 
83bc3e5e3bSjmcneill 		return (u_int)((uint64_t)prate * num / den);
84bc3e5e3bSjmcneill 	} else {
856726462dSjmcneill 		const uint32_t val = CRU_READ(sc, composite->muxdiv_reg);
8601470923Sjmcneill 		u_int div;
876726462dSjmcneill 
8801470923Sjmcneill 		if (composite->flags & RK_COMPOSITE_POW2) {
8901470923Sjmcneill 			div = 1U << __SHIFTOUT(val, composite->div_mask);
90*4fb05369Sryo 		} else if (composite->flags & RK_COMPOSITE_HALFDIV) {
91*4fb05369Sryo 			div = __SHIFTOUT(val, composite->div_mask) * 2 + 3;
92*4fb05369Sryo 			return ((uint64_t)prate * 2 + div - 1) / div;
9301470923Sjmcneill 		} else {
9401470923Sjmcneill 			div = (composite->div_mask != 0)
9501470923Sjmcneill 			    ? __SHIFTOUT(val, composite->div_mask) + 1 : 1;
9601470923Sjmcneill 		}
976726462dSjmcneill 		return prate / div;
986726462dSjmcneill 	}
99bc3e5e3bSjmcneill }
100bc3e5e3bSjmcneill 
101bc3e5e3bSjmcneill static u_int
rk_cru_composite_get_frac_div(u_int n,u_int d)102bc3e5e3bSjmcneill rk_cru_composite_get_frac_div(u_int n, u_int d)
103bc3e5e3bSjmcneill {
104bc3e5e3bSjmcneill 	u_int tmp;
105bc3e5e3bSjmcneill 
106bc3e5e3bSjmcneill 	while (d > 0) {
107bc3e5e3bSjmcneill 		tmp = d;
108bc3e5e3bSjmcneill 		d = n % d;
109bc3e5e3bSjmcneill 		n = tmp;
110bc3e5e3bSjmcneill 	}
111bc3e5e3bSjmcneill 
112bc3e5e3bSjmcneill 	return n;
113bc3e5e3bSjmcneill }
114bc3e5e3bSjmcneill 
115bc3e5e3bSjmcneill static int
rk_cru_composite_set_rate_frac(struct rk_cru_softc * sc,struct rk_cru_clk * clk,u_int rate)116bc3e5e3bSjmcneill rk_cru_composite_set_rate_frac(struct rk_cru_softc *sc,
117bc3e5e3bSjmcneill     struct rk_cru_clk *clk, u_int rate)
118bc3e5e3bSjmcneill {
119bc3e5e3bSjmcneill 	struct rk_cru_composite *composite = &clk->u.composite;
120bc3e5e3bSjmcneill 	struct clk *clk_parent;
121bc3e5e3bSjmcneill 
122bc3e5e3bSjmcneill 	clk_parent = clk_get_parent(&clk->base);
123bc3e5e3bSjmcneill 	if (clk_parent == NULL)
124bc3e5e3bSjmcneill 		return ENXIO;
125bc3e5e3bSjmcneill 
126bc3e5e3bSjmcneill 	const u_int prate = clk_get_rate(clk_parent);
127bc3e5e3bSjmcneill 	const u_int v = rk_cru_composite_get_frac_div(prate, rate);
128bc3e5e3bSjmcneill 	const u_int num = (prate / v) & 0xffff;
129bc3e5e3bSjmcneill 	const u_int den = (rate / v) & 0xffff;
130bc3e5e3bSjmcneill 	if (prate / num * den != rate)
131bc3e5e3bSjmcneill 		return EINVAL;
132bc3e5e3bSjmcneill 
133bc3e5e3bSjmcneill 	CRU_WRITE(sc, composite->frac_reg, (den << 16) | num);
134bc3e5e3bSjmcneill 
135bc3e5e3bSjmcneill 	return 0;
136bc3e5e3bSjmcneill }
1376726462dSjmcneill 
1386726462dSjmcneill int
rk_cru_composite_set_rate(struct rk_cru_softc * sc,struct rk_cru_clk * clk,u_int rate)1396726462dSjmcneill rk_cru_composite_set_rate(struct rk_cru_softc *sc,
1406726462dSjmcneill     struct rk_cru_clk *clk, u_int rate)
1416726462dSjmcneill {
1426726462dSjmcneill 	struct rk_cru_composite *composite = &clk->u.composite;
1436726462dSjmcneill 	u_int best_div, best_mux, best_diff;
144ded90cc0Sjmcneill 	struct rk_cru_clk *rclk_parent;
145ded90cc0Sjmcneill 	struct clk *clk_parent;
1466726462dSjmcneill 
1476726462dSjmcneill 	KASSERT(clk->type == RK_CRU_COMPOSITE);
1486726462dSjmcneill 
149ebd1df72Sjmcneill 	if (composite->flags & RK_COMPOSITE_SET_RATE_PARENT) {
150ebd1df72Sjmcneill 		clk_parent = clk_get_parent(&clk->base);
151ebd1df72Sjmcneill 		if (clk_parent == NULL)
152ebd1df72Sjmcneill 			return ENXIO;
153ebd1df72Sjmcneill 		return clk_set_rate(clk_parent, rate);
154ebd1df72Sjmcneill 	}
155ebd1df72Sjmcneill 
156bc3e5e3bSjmcneill 	if (composite->flags & RK_COMPOSITE_FRACDIV) {
157bc3e5e3bSjmcneill 		return rk_cru_composite_set_rate_frac(sc, clk, rate);
158bc3e5e3bSjmcneill 	}
159bc3e5e3bSjmcneill 
16001470923Sjmcneill 	if (composite->flags & RK_COMPOSITE_POW2) {
16101470923Sjmcneill 		return ENXIO;	/* TODO */
16201470923Sjmcneill 	}
163*4fb05369Sryo 	if (composite->flags & RK_COMPOSITE_HALFDIV) {
164*4fb05369Sryo 		return ENXIO;	/* TODO */
165*4fb05369Sryo 	}
16601470923Sjmcneill 
1676726462dSjmcneill 	best_div = 0;
1686726462dSjmcneill 	best_mux = 0;
1696726462dSjmcneill 	best_diff = INT_MAX;
1706726462dSjmcneill 	for (u_int mux = 0; mux < composite->nparents; mux++) {
171ded90cc0Sjmcneill 		rclk_parent = rk_cru_clock_find(sc, composite->parents[mux]);
172ded90cc0Sjmcneill 		if (rclk_parent != NULL)
173ded90cc0Sjmcneill 			clk_parent = &rclk_parent->base;
174ded90cc0Sjmcneill 		else
175ded90cc0Sjmcneill 			clk_parent = fdtbus_clock_byname(composite->parents[mux]);
1766726462dSjmcneill 		if (clk_parent == NULL)
1776726462dSjmcneill 			continue;
178ded90cc0Sjmcneill 
179ded90cc0Sjmcneill 		const u_int prate = clk_get_rate(clk_parent);
1806726462dSjmcneill 		if (prate == 0)
1816726462dSjmcneill 			continue;
1826726462dSjmcneill 
1836726462dSjmcneill 		for (u_int div = 1; div <= __SHIFTOUT_MASK(composite->div_mask) + 1; div++) {
1846726462dSjmcneill 			const u_int cur_rate = prate / div;
1856726462dSjmcneill 			const int diff = (int)rate - (int)cur_rate;
1866726462dSjmcneill 			if (composite->flags & RK_COMPOSITE_ROUND_DOWN) {
1876726462dSjmcneill 				if (diff >= 0 && diff < best_diff) {
1886726462dSjmcneill 					best_diff = diff;
1896726462dSjmcneill 					best_mux = mux;
1906726462dSjmcneill 					best_div = div;
1916726462dSjmcneill 				}
1926726462dSjmcneill 			} else {
1936726462dSjmcneill 				if (abs(diff) < best_diff) {
1946726462dSjmcneill 					best_diff = abs(diff);
1956726462dSjmcneill 					best_mux = mux;
1966726462dSjmcneill 					best_div = div;
1976726462dSjmcneill 				}
1986726462dSjmcneill 			}
1996726462dSjmcneill 		}
2006726462dSjmcneill 	}
2016726462dSjmcneill 	if (best_diff == INT_MAX)
2026726462dSjmcneill 		return ERANGE;
2036726462dSjmcneill 
2046726462dSjmcneill 	uint32_t write_mask = composite->div_mask << 16;
2056726462dSjmcneill 	uint32_t write_val = __SHIFTIN(best_div - 1, composite->div_mask);
2066726462dSjmcneill 	if (composite->mux_mask) {
2076726462dSjmcneill 		write_mask |= composite->mux_mask << 16;
2086726462dSjmcneill 		write_val |= __SHIFTIN(best_mux, composite->mux_mask);
2096726462dSjmcneill 	}
2106726462dSjmcneill 
2116726462dSjmcneill 	CRU_WRITE(sc, composite->muxdiv_reg, write_mask | write_val);
2126726462dSjmcneill 
2136726462dSjmcneill 	return 0;
2146726462dSjmcneill }
2156726462dSjmcneill 
2166726462dSjmcneill const char *
rk_cru_composite_get_parent(struct rk_cru_softc * sc,struct rk_cru_clk * clk)2176726462dSjmcneill rk_cru_composite_get_parent(struct rk_cru_softc *sc,
2186726462dSjmcneill     struct rk_cru_clk *clk)
2196726462dSjmcneill {
2206726462dSjmcneill 	struct rk_cru_composite *composite = &clk->u.composite;
2216726462dSjmcneill 	uint32_t val;
2226726462dSjmcneill 	u_int mux;
2236726462dSjmcneill 
2246726462dSjmcneill 	KASSERT(clk->type == RK_CRU_COMPOSITE);
2256726462dSjmcneill 
2266726462dSjmcneill 	if (composite->mux_mask) {
2276726462dSjmcneill 		val = CRU_READ(sc, composite->muxdiv_reg);
2286726462dSjmcneill 		mux = __SHIFTOUT(val, composite->mux_mask);
2296726462dSjmcneill 	} else {
2306726462dSjmcneill 		mux = 0;
2316726462dSjmcneill 	}
2326726462dSjmcneill 
2336726462dSjmcneill 	return composite->parents[mux];
2346726462dSjmcneill }
2356726462dSjmcneill 
2366726462dSjmcneill int
rk_cru_composite_set_parent(struct rk_cru_softc * sc,struct rk_cru_clk * clk,const char * parent)2376726462dSjmcneill rk_cru_composite_set_parent(struct rk_cru_softc *sc,
2386726462dSjmcneill     struct rk_cru_clk *clk, const char *parent)
2396726462dSjmcneill {
2406726462dSjmcneill 	struct rk_cru_composite *composite = &clk->u.composite;
2416726462dSjmcneill 
2426726462dSjmcneill 	KASSERT(clk->type == RK_CRU_COMPOSITE);
2436726462dSjmcneill 
2446726462dSjmcneill 	if (!composite->mux_mask)
2456726462dSjmcneill 		return EINVAL;
2466726462dSjmcneill 
2476726462dSjmcneill 	for (u_int mux = 0; mux < composite->nparents; mux++) {
2486726462dSjmcneill 		if (strcmp(composite->parents[mux], parent) == 0) {
2496726462dSjmcneill 			const uint32_t write_mask = composite->mux_mask << 16;
2506726462dSjmcneill 			const uint32_t write_val = __SHIFTIN(mux, composite->mux_mask);
2516726462dSjmcneill 
2526726462dSjmcneill 			CRU_WRITE(sc, composite->muxdiv_reg, write_mask | write_val);
2536726462dSjmcneill 			return 0;
2546726462dSjmcneill 		}
2556726462dSjmcneill 	}
2566726462dSjmcneill 
2576726462dSjmcneill 	return EINVAL;
2586726462dSjmcneill }
259