diff mbox series

[07/13] clk: rockchip: rk3308: Add support for SCLK_RTC32K clock

Message ID 20240316225655.3244348-8-jonas@kwiboo.se
State Superseded
Delegated to: Kever Yang
Headers show
Series rockchip: rk3308: Sync DT with linux v6.8 and update defconfigs | expand

Commit Message

Jonas Karlman March 16, 2024, 10:56 p.m. UTC
From: Finley Xiao <finley.xiao@rock-chips.com>

Add support to get and set the SCLK_RTC32K clock rate.

Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
[jonas@kwiboo.se: Update commit message]
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 arch/arm/include/asm/arch-rk3308/cru_rk3308.h | 14 +++
 drivers/clk/rockchip/clk_rk3308.c             | 95 +++++++++++++++++++
 2 files changed, 109 insertions(+)
diff mbox series

Patch

diff --git a/arch/arm/include/asm/arch-rk3308/cru_rk3308.h b/arch/arm/include/asm/arch-rk3308/cru_rk3308.h
index 84b63e4d5682..091ae82d7cc1 100644
--- a/arch/arm/include/asm/arch-rk3308/cru_rk3308.h
+++ b/arch/arm/include/asm/arch-rk3308/cru_rk3308.h
@@ -147,6 +147,20 @@  enum {
 	CORE_DIV_CON_SHIFT	= 0,
 	CORE_DIV_CON_MASK	= 0x0f << CORE_DIV_CON_SHIFT,
 
+	/* CRU_CLK_SEL2_CON */
+	CLK_RTC32K_SEL_SHIFT	= 8,
+	CLK_RTC32K_SEL_MASK	= 3 << CLK_RTC32K_SEL_SHIFT,
+	CLK_RTC32K_IO		= 0,
+	CLK_RTC32K_PVTM,
+	CLK_RTC32K_FRAC_DIV,
+	CLK_RTC32K_DIV,
+
+	/* CRU_CLK_SEL3_CON */
+	CLK_RTC32K_FRAC_NUMERATOR_SHIFT		= 16,
+	CLK_RTC32K_FRAC_NUMERATOR_MASK		= 0xffff << 16,
+	CLK_RTC32K_FRAC_DENOMINATOR_SHIFT	= 0,
+	CLK_RTC32K_FRAC_DENOMINATOR_MASK	= 0xffff,
+
 	/* CRU_CLK_SEL5_CON */
 	BUS_PLL_SEL_SHIFT	= 6,
 	BUS_PLL_SEL_MASK	= 0x3 << BUS_PLL_SEL_SHIFT,
diff --git a/drivers/clk/rockchip/clk_rk3308.c b/drivers/clk/rockchip/clk_rk3308.c
index 7755b0161118..7515fc8bb244 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;
 	}