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