From d3e35e71ecd7f655d02e1a611f51b92a07d93b2d Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Mon, 8 Apr 2024 18:14:04 +0000 Subject: clk: rockchip: rk3308: Add support for SCLK_RTC32K clock Add support to get and set the SCLK_RTC32K clock rate. Signed-off-by: Finley Xiao [jonas@kwiboo.se: Update commit message] Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/clk/rockchip/clk_rk3308.c | 95 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'drivers/clk/rockchip') diff --git a/drivers/clk/rockchip/clk_rk3308.c b/drivers/clk/rockchip/clk_rk3308.c index 7755b01..7515fc8 100644 --- a/drivers/clk/rockchip/clk_rk3308.c +++ b/drivers/clk/rockchip/clk_rk3308.c @@ -65,6 +65,57 @@ static struct rockchip_pll_clock rk3308_pll_clks[] = { RK3308_MODE_CON, 6, 10, 0, NULL), }; +/* + * + * rational_best_approximation(31415, 10000, + * (1 << 8) - 1, (1 << 5) - 1, &n, &d); + * + * you may look at given_numerator as a fixed point number, + * with the fractional part size described in given_denominator. + * + * for theoretical background, see: + * http://en.wikipedia.org/wiki/Continued_fraction + */ +static void rational_best_approximation(unsigned long given_numerator, + unsigned long given_denominator, + unsigned long max_numerator, + unsigned long max_denominator, + unsigned long *best_numerator, + unsigned long *best_denominator) +{ + unsigned long n, d, n0, d0, n1, d1; + + n = given_numerator; + d = given_denominator; + n0 = 0; + d1 = 0; + n1 = 1; + d0 = 1; + for (;;) { + unsigned long t, a; + + if (n1 > max_numerator || d1 > max_denominator) { + n1 = n0; + d1 = d0; + break; + } + if (d == 0) + break; + t = d; + a = n / d; + d = n % d; + n = t; + t = n0 + a * n1; + n0 = n1; + n1 = t; + t = d0 + a * d1; + d0 = d1; + d1 = t; + } + *best_numerator = n1; + *best_denominator = d1; +} + static ulong rk3308_armclk_set_clk(struct rk3308_clk_priv *priv, ulong hz) { struct rk3308_cru *cru = priv->cru; @@ -832,6 +883,44 @@ static ulong rk3308_crypto_set_clk(struct rk3308_clk_priv *priv, ulong clk_id, return rk3308_crypto_get_clk(priv, clk_id); } +static ulong rk3308_rtc32k_get_clk(struct rk3308_clk_priv *priv, ulong clk_id) +{ + struct rk3308_cru *cru = priv->cru; + unsigned long m, n; + u32 con, fracdiv; + + con = readl(&cru->clksel_con[2]); + if ((con & CLK_RTC32K_SEL_MASK) >> CLK_RTC32K_SEL_SHIFT != + CLK_RTC32K_FRAC_DIV) + return -EINVAL; + + fracdiv = readl(&cru->clksel_con[3]); + m = fracdiv & CLK_RTC32K_FRAC_NUMERATOR_MASK; + m >>= CLK_RTC32K_FRAC_NUMERATOR_SHIFT; + n = fracdiv & CLK_RTC32K_FRAC_DENOMINATOR_MASK; + n >>= CLK_RTC32K_FRAC_DENOMINATOR_SHIFT; + + return OSC_HZ * m / n; +} + +static ulong rk3308_rtc32k_set_clk(struct rk3308_clk_priv *priv, ulong clk_id, + ulong hz) +{ + struct rk3308_cru *cru = priv->cru; + unsigned long m, n, val; + + rational_best_approximation(hz, OSC_HZ, + GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), + &m, &n); + val = m << CLK_RTC32K_FRAC_NUMERATOR_SHIFT | n; + writel(val, &cru->clksel_con[3]); + rk_clrsetreg(&cru->clksel_con[2], CLK_RTC32K_SEL_MASK, + CLK_RTC32K_FRAC_DIV << CLK_RTC32K_SEL_SHIFT); + + return rk3308_rtc32k_get_clk(priv, clk_id); +} + static ulong rk3308_clk_get_rate(struct clk *clk) { struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); @@ -912,6 +1001,9 @@ static ulong rk3308_clk_get_rate(struct clk *clk) case SCLK_CRYPTO_APK: rate = rk3308_crypto_get_clk(priv, clk->id); break; + case SCLK_RTC32K: + rate = rk3308_rtc32k_get_clk(priv, clk->id); + break; default: return -ENOENT; } @@ -990,6 +1082,9 @@ static ulong rk3308_clk_set_rate(struct clk *clk, ulong rate) case SCLK_CRYPTO_APK: ret = rk3308_crypto_set_clk(priv, clk->id, rate); break; + case SCLK_RTC32K: + ret = rk3308_rtc32k_set_clk(priv, clk->id, rate); + break; default: return -ENOENT; } -- cgit v1.1