Re: [PATCH v2 07/14] clk: rockchip: rk3308: Add support for SCLK_RTC32K clock

2024-04-22 Thread Kever Yang



On 2024/4/9 02:14, Jonas Karlman wrote:

From: Finley Xiao 

Add support to get and set the SCLK_RTC32K clock rate.

Signed-off-by: Finley Xiao 
[jo...@kwiboo.se: Update commit message]
Signed-off-by: Jonas Karlman 

Reviewed-by: Kever Yang 

Thanks,
- Kever

---
v2: No change
---
  arch/arm/include/asm/arch-rk3308/cru_rk3308.h | 14 +++
  drivers/clk/rockchip/clk_rk3308.c | 95 +++
  2 files changed, 109 insertions(+)

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  = 0x << 16,
+   CLK_RTC32K_FRAC_DENOMINATOR_SHIFT   = 0,
+   CLK_RTC32K_FRAC_DENOMINATOR_MASK= 0x,
+
/* 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, 1,
+ * (1 << 8) - 1, (1 << 5) - 1, , );
+ *
+ * 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(>clksel_con[2]);
+   if ((con & CLK_RTC32K_SEL_MASK) >> CLK_RTC32K_SEL_SHIFT !=
+   CLK_RTC32K_FRAC_DIV)
+   return -EINVAL;
+
+   fracdiv = readl(>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),
+   , );
+   val = m << CLK_RTC32K_FRAC_NUMERATOR_SHIFT | n;
+   writel(val, >clksel_con[3]);
+   rk_clrsetreg(>clksel_con[2], CLK_RTC32K_SEL_MASK,
+CLK_RTC32K_FRAC_DIV << CLK_RTC32K_SEL_SHIFT);
+
+   return rk3308_rtc32k_get_clk(priv, clk_id);
+}
+
  

[PATCH v2 07/14] clk: rockchip: rk3308: Add support for SCLK_RTC32K clock

2024-04-08 Thread Jonas Karlman
From: Finley Xiao 

Add support to get and set the SCLK_RTC32K clock rate.

Signed-off-by: Finley Xiao 
[jo...@kwiboo.se: Update commit message]
Signed-off-by: Jonas Karlman 
---
v2: No change
---
 arch/arm/include/asm/arch-rk3308/cru_rk3308.h | 14 +++
 drivers/clk/rockchip/clk_rk3308.c | 95 +++
 2 files changed, 109 insertions(+)

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  = 0x << 16,
+   CLK_RTC32K_FRAC_DENOMINATOR_SHIFT   = 0,
+   CLK_RTC32K_FRAC_DENOMINATOR_MASK= 0x,
+
/* 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, 1,
+ * (1 << 8) - 1, (1 << 5) - 1, , );
+ *
+ * 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(>clksel_con[2]);
+   if ((con & CLK_RTC32K_SEL_MASK) >> CLK_RTC32K_SEL_SHIFT !=
+   CLK_RTC32K_FRAC_DIV)
+   return -EINVAL;
+
+   fracdiv = readl(>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),
+   , );
+   val = m << CLK_RTC32K_FRAC_NUMERATOR_SHIFT | n;
+   writel(val, >clksel_con[3]);
+   rk_clrsetreg(>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 =