[PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic

Junhui Liu posted 7 patches 2 weeks, 5 days ago
[PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
Posted by Junhui Liu 2 weeks, 5 days ago
Extract the IOSC and 32k clock logic from ccu-sun6i-rtc into a shared
module to simplify adding RTC CCU support for new SoCs. This is needed
because newer Allwinner SoCs introduce additional DCXO/HOSC logic that
prevents direct reuse of the existing driver.

Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 drivers/clk/sunxi-ng/Makefile        |   3 +
 drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 152 +----------------------------------
 drivers/clk/sunxi-ng/ccu_rtc.c       | 136 +++++++++++++++++++++++++++++++
 drivers/clk/sunxi-ng/ccu_rtc.h       |  37 +++++++++
 4 files changed, 177 insertions(+), 151 deletions(-)

diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index a1c4087d7241..c3f810a025a8 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -23,6 +23,9 @@ sunxi-ccu-y			+= ccu_nkmp.o
 sunxi-ccu-y			+= ccu_nm.o
 sunxi-ccu-y			+= ccu_mp.o
 
+# RTC clocks
+sunxi-ccu-y			+= ccu_rtc.o
+
 # SoC support
 obj-$(CONFIG_SUNIV_F1C100S_CCU)	+= suniv-f1c100s-ccu.o
 obj-$(CONFIG_SUN20I_D1_CCU)	+= sun20i-d1-ccu.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
index 6f888169412c..562ba752bcec 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
@@ -14,37 +14,12 @@
 
 #include "ccu_common.h"
 
-#include "ccu_div.h"
 #include "ccu_gate.h"
 #include "ccu_mux.h"
+#include "ccu_rtc.h"
 
 #include "ccu-sun6i-rtc.h"
 
-#define IOSC_ACCURACY			300000000 /* 30% */
-#define IOSC_RATE			16000000
-
-#define LOSC_RATE			32768
-#define LOSC_RATE_SHIFT			15
-
-#define LOSC_CTRL_REG			0x0
-#define LOSC_CTRL_KEY			0x16aa0000
-
-#define IOSC_32K_CLK_DIV_REG		0x8
-#define IOSC_32K_CLK_DIV		GENMASK(4, 0)
-#define IOSC_32K_PRE_DIV		32
-
-#define IOSC_CLK_CALI_REG		0xc
-#define IOSC_CLK_CALI_DIV_ONES		22
-#define IOSC_CLK_CALI_EN		BIT(1)
-#define IOSC_CLK_CALI_SRC_SEL		BIT(0)
-
-#define LOSC_OUT_GATING_REG		0x60
-
-#define DCXO_CTRL_REG			0x160
-#define DCXO_CTRL_CLK16M_RC_EN		BIT(0)
-
-#define SUN6I_RTC_AUX_ID(_name)		"rtc_sun6i." #_name
-
 struct sun6i_rtc_match_data {
 	bool				have_ext_osc32k		: 1;
 	bool				have_iosc_calibration	: 1;
@@ -53,137 +28,12 @@ struct sun6i_rtc_match_data {
 	u8				osc32k_fanout_nparents;
 };
 
-static int ccu_iosc_enable(struct clk_hw *hw)
-{
-	struct ccu_common *cm = hw_to_ccu_common(hw);
-
-	return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
-}
-
-static void ccu_iosc_disable(struct clk_hw *hw)
-{
-	struct ccu_common *cm = hw_to_ccu_common(hw);
-
-	return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
-}
-
-static int ccu_iosc_is_enabled(struct clk_hw *hw)
-{
-	struct ccu_common *cm = hw_to_ccu_common(hw);
-
-	return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
-}
-
-static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
-					  unsigned long parent_rate)
-{
-	struct ccu_common *cm = hw_to_ccu_common(hw);
-
-	if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
-		u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
-
-		/*
-		 * Recover the IOSC frequency by shifting the ones place of
-		 * (fixed-point divider * 32768) into bit zero.
-		 */
-		if (reg & IOSC_CLK_CALI_EN)
-			return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
-	}
-
-	return IOSC_RATE;
-}
-
-static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
-					      unsigned long parent_accuracy)
-{
-	return IOSC_ACCURACY;
-}
-
-static const struct clk_ops ccu_iosc_ops = {
-	.enable			= ccu_iosc_enable,
-	.disable		= ccu_iosc_disable,
-	.is_enabled		= ccu_iosc_is_enabled,
-	.recalc_rate		= ccu_iosc_recalc_rate,
-	.recalc_accuracy	= ccu_iosc_recalc_accuracy,
-};
-
 static struct ccu_common iosc_clk = {
 	.reg		= DCXO_CTRL_REG,
 	.hw.init	= CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
 						CLK_GET_RATE_NOCACHE),
 };
 
-static int ccu_iosc_32k_prepare(struct clk_hw *hw)
-{
-	struct ccu_common *cm = hw_to_ccu_common(hw);
-	u32 val;
-
-	if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
-		return 0;
-
-	val = readl(cm->base + IOSC_CLK_CALI_REG);
-	writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
-	       cm->base + IOSC_CLK_CALI_REG);
-
-	return 0;
-}
-
-static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
-{
-	struct ccu_common *cm = hw_to_ccu_common(hw);
-	u32 val;
-
-	if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
-		return;
-
-	val = readl(cm->base + IOSC_CLK_CALI_REG);
-	writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
-	       cm->base + IOSC_CLK_CALI_REG);
-}
-
-static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
-					      unsigned long parent_rate)
-{
-	struct ccu_common *cm = hw_to_ccu_common(hw);
-	u32 val;
-
-	if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
-		val = readl(cm->base + IOSC_CLK_CALI_REG);
-
-		/* Assume the calibrated 32k clock is accurate. */
-		if (val & IOSC_CLK_CALI_SRC_SEL)
-			return LOSC_RATE;
-	}
-
-	val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
-
-	return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
-}
-
-static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
-						  unsigned long parent_accuracy)
-{
-	struct ccu_common *cm = hw_to_ccu_common(hw);
-	u32 val;
-
-	if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
-		val = readl(cm->base + IOSC_CLK_CALI_REG);
-
-		/* Assume the calibrated 32k clock is accurate. */
-		if (val & IOSC_CLK_CALI_SRC_SEL)
-			return 0;
-	}
-
-	return parent_accuracy;
-}
-
-static const struct clk_ops ccu_iosc_32k_ops = {
-	.prepare		= ccu_iosc_32k_prepare,
-	.unprepare		= ccu_iosc_32k_unprepare,
-	.recalc_rate		= ccu_iosc_32k_recalc_rate,
-	.recalc_accuracy	= ccu_iosc_32k_recalc_accuracy,
-};
-
 static struct ccu_common iosc_32k_clk = {
 	.hw.init	= CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
 					 &ccu_iosc_32k_ops,
diff --git a/drivers/clk/sunxi-ng/ccu_rtc.c b/drivers/clk/sunxi-ng/ccu_rtc.c
new file mode 100644
index 000000000000..cfc10218517c
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu_rtc.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+
+#include "ccu_common.h"
+
+#include "ccu_gate.h"
+#include "ccu_rtc.h"
+
+static int ccu_iosc_enable(struct clk_hw *hw)
+{
+	struct ccu_common *cm = hw_to_ccu_common(hw);
+
+	return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
+}
+
+static void ccu_iosc_disable(struct clk_hw *hw)
+{
+	struct ccu_common *cm = hw_to_ccu_common(hw);
+
+	return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
+}
+
+static int ccu_iosc_is_enabled(struct clk_hw *hw)
+{
+	struct ccu_common *cm = hw_to_ccu_common(hw);
+
+	return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
+}
+
+static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	struct ccu_common *cm = hw_to_ccu_common(hw);
+
+	if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
+		u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
+		/*
+		 * Recover the IOSC frequency by shifting the ones place of
+		 * (fixed-point divider * 32768) into bit zero.
+		 */
+		if (reg & IOSC_CLK_CALI_EN)
+			return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
+	}
+
+	return IOSC_RATE;
+}
+
+static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
+					      unsigned long parent_accuracy)
+{
+	return IOSC_ACCURACY;
+}
+
+const struct clk_ops ccu_iosc_ops = {
+	.enable			= ccu_iosc_enable,
+	.disable		= ccu_iosc_disable,
+	.is_enabled		= ccu_iosc_is_enabled,
+	.recalc_rate		= ccu_iosc_recalc_rate,
+	.recalc_accuracy	= ccu_iosc_recalc_accuracy,
+};
+
+static int ccu_iosc_32k_prepare(struct clk_hw *hw)
+{
+	struct ccu_common *cm = hw_to_ccu_common(hw);
+	u32 val;
+
+	if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
+		return 0;
+
+	val = readl(cm->base + IOSC_CLK_CALI_REG);
+	writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
+	       cm->base + IOSC_CLK_CALI_REG);
+
+	return 0;
+}
+
+static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
+{
+	struct ccu_common *cm = hw_to_ccu_common(hw);
+	u32 val;
+
+	if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
+		return;
+
+	val = readl(cm->base + IOSC_CLK_CALI_REG);
+	writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
+	       cm->base + IOSC_CLK_CALI_REG);
+}
+
+static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
+					      unsigned long parent_rate)
+{
+	struct ccu_common *cm = hw_to_ccu_common(hw);
+	u32 val;
+
+	if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
+		val = readl(cm->base + IOSC_CLK_CALI_REG);
+
+		/* Assume the calibrated 32k clock is accurate. */
+		if (val & IOSC_CLK_CALI_SRC_SEL)
+			return LOSC_RATE;
+	}
+
+	val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
+
+	return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
+}
+
+static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
+						  unsigned long parent_accuracy)
+{
+	struct ccu_common *cm = hw_to_ccu_common(hw);
+	u32 val;
+
+	if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
+		val = readl(cm->base + IOSC_CLK_CALI_REG);
+
+		/* Assume the calibrated 32k clock is accurate. */
+		if (val & IOSC_CLK_CALI_SRC_SEL)
+			return 0;
+	}
+
+	return parent_accuracy;
+}
+
+const struct clk_ops ccu_iosc_32k_ops = {
+	.prepare		= ccu_iosc_32k_prepare,
+	.unprepare		= ccu_iosc_32k_unprepare,
+	.recalc_rate		= ccu_iosc_32k_recalc_rate,
+	.recalc_accuracy	= ccu_iosc_32k_recalc_accuracy,
+};
diff --git a/drivers/clk/sunxi-ng/ccu_rtc.h b/drivers/clk/sunxi-ng/ccu_rtc.h
new file mode 100644
index 000000000000..1c44c2206a25
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu_rtc.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
+ */
+
+#ifndef _CCU_RTC_H_
+#define _CCU_RTC_H_
+
+#define IOSC_ACCURACY			300000000 /* 30% */
+#define IOSC_RATE			16000000
+
+#define LOSC_RATE			32768
+#define LOSC_RATE_SHIFT			15
+
+#define LOSC_CTRL_REG			0x0
+#define LOSC_CTRL_KEY			0x16aa0000
+
+#define IOSC_32K_CLK_DIV_REG		0x8
+#define IOSC_32K_CLK_DIV		GENMASK(4, 0)
+#define IOSC_32K_PRE_DIV		32
+
+#define IOSC_CLK_CALI_REG		0xc
+#define IOSC_CLK_CALI_DIV_ONES		22
+#define IOSC_CLK_CALI_EN		BIT(1)
+#define IOSC_CLK_CALI_SRC_SEL		BIT(0)
+
+#define LOSC_OUT_GATING_REG		0x60
+
+#define DCXO_CTRL_REG			0x160
+#define DCXO_CTRL_CLK16M_RC_EN		BIT(0)
+
+#define SUN6I_RTC_AUX_ID(_name)		"rtc_sun6i." #_name
+
+extern const struct clk_ops ccu_iosc_ops;
+extern const struct clk_ops ccu_iosc_32k_ops;
+
+#endif /* _CCU_RTC_H_ */

-- 
2.52.0
Re: [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
Posted by Chen-Yu Tsai 2 weeks, 1 day ago
On Wed, Jan 21, 2026 at 7:04 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
> Extract the IOSC and 32k clock logic from ccu-sun6i-rtc into a shared
> module to simplify adding RTC CCU support for new SoCs. This is needed
> because newer Allwinner SoCs introduce additional DCXO/HOSC logic that
> prevents direct reuse of the existing driver.
>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
>  drivers/clk/sunxi-ng/Makefile        |   3 +
>  drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 152 +----------------------------------
>  drivers/clk/sunxi-ng/ccu_rtc.c       | 136 +++++++++++++++++++++++++++++++
>  drivers/clk/sunxi-ng/ccu_rtc.h       |  37 +++++++++
>  4 files changed, 177 insertions(+), 151 deletions(-)
>
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index a1c4087d7241..c3f810a025a8 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -23,6 +23,9 @@ sunxi-ccu-y                   += ccu_nkmp.o
>  sunxi-ccu-y                    += ccu_nm.o
>  sunxi-ccu-y                    += ccu_mp.o
>
> +# RTC clocks
> +sunxi-ccu-y                    += ccu_rtc.o
> +
>  # SoC support
>  obj-$(CONFIG_SUNIV_F1C100S_CCU)        += suniv-f1c100s-ccu.o
>  obj-$(CONFIG_SUN20I_D1_CCU)    += sun20i-d1-ccu.o
> diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> index 6f888169412c..562ba752bcec 100644
> --- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> +++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> @@ -14,37 +14,12 @@
>
>  #include "ccu_common.h"
>
> -#include "ccu_div.h"
>  #include "ccu_gate.h"
>  #include "ccu_mux.h"
> +#include "ccu_rtc.h"
>
>  #include "ccu-sun6i-rtc.h"
>
> -#define IOSC_ACCURACY                  300000000 /* 30% */
> -#define IOSC_RATE                      16000000
> -
> -#define LOSC_RATE                      32768
> -#define LOSC_RATE_SHIFT                        15
> -
> -#define LOSC_CTRL_REG                  0x0
> -#define LOSC_CTRL_KEY                  0x16aa0000
> -
> -#define IOSC_32K_CLK_DIV_REG           0x8
> -#define IOSC_32K_CLK_DIV               GENMASK(4, 0)
> -#define IOSC_32K_PRE_DIV               32
> -
> -#define IOSC_CLK_CALI_REG              0xc
> -#define IOSC_CLK_CALI_DIV_ONES         22
> -#define IOSC_CLK_CALI_EN               BIT(1)
> -#define IOSC_CLK_CALI_SRC_SEL          BIT(0)
> -
> -#define LOSC_OUT_GATING_REG            0x60
> -
> -#define DCXO_CTRL_REG                  0x160
> -#define DCXO_CTRL_CLK16M_RC_EN         BIT(0)
> -
> -#define SUN6I_RTC_AUX_ID(_name)                "rtc_sun6i." #_name
> -
>  struct sun6i_rtc_match_data {
>         bool                            have_ext_osc32k         : 1;
>         bool                            have_iosc_calibration   : 1;
> @@ -53,137 +28,12 @@ struct sun6i_rtc_match_data {
>         u8                              osc32k_fanout_nparents;
>  };
>
> -static int ccu_iosc_enable(struct clk_hw *hw)
> -{
> -       struct ccu_common *cm = hw_to_ccu_common(hw);
> -
> -       return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
> -}
> -
> -static void ccu_iosc_disable(struct clk_hw *hw)
> -{
> -       struct ccu_common *cm = hw_to_ccu_common(hw);
> -
> -       return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
> -}
> -
> -static int ccu_iosc_is_enabled(struct clk_hw *hw)
> -{
> -       struct ccu_common *cm = hw_to_ccu_common(hw);
> -
> -       return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
> -}
> -
> -static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
> -                                         unsigned long parent_rate)
> -{
> -       struct ccu_common *cm = hw_to_ccu_common(hw);
> -
> -       if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> -               u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
> -
> -               /*
> -                * Recover the IOSC frequency by shifting the ones place of
> -                * (fixed-point divider * 32768) into bit zero.
> -                */
> -               if (reg & IOSC_CLK_CALI_EN)
> -                       return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
> -       }
> -
> -       return IOSC_RATE;
> -}
> -
> -static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
> -                                             unsigned long parent_accuracy)
> -{
> -       return IOSC_ACCURACY;
> -}
> -
> -static const struct clk_ops ccu_iosc_ops = {
> -       .enable                 = ccu_iosc_enable,
> -       .disable                = ccu_iosc_disable,
> -       .is_enabled             = ccu_iosc_is_enabled,
> -       .recalc_rate            = ccu_iosc_recalc_rate,
> -       .recalc_accuracy        = ccu_iosc_recalc_accuracy,
> -};
> -
>  static struct ccu_common iosc_clk = {
>         .reg            = DCXO_CTRL_REG,
>         .hw.init        = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
>                                                 CLK_GET_RATE_NOCACHE),
>  };
>
> -static int ccu_iosc_32k_prepare(struct clk_hw *hw)
> -{
> -       struct ccu_common *cm = hw_to_ccu_common(hw);
> -       u32 val;
> -
> -       if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
> -               return 0;
> -
> -       val = readl(cm->base + IOSC_CLK_CALI_REG);
> -       writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
> -              cm->base + IOSC_CLK_CALI_REG);
> -
> -       return 0;
> -}
> -
> -static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
> -{
> -       struct ccu_common *cm = hw_to_ccu_common(hw);
> -       u32 val;
> -
> -       if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
> -               return;
> -
> -       val = readl(cm->base + IOSC_CLK_CALI_REG);
> -       writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
> -              cm->base + IOSC_CLK_CALI_REG);
> -}
> -
> -static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
> -                                             unsigned long parent_rate)
> -{
> -       struct ccu_common *cm = hw_to_ccu_common(hw);
> -       u32 val;
> -
> -       if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> -               val = readl(cm->base + IOSC_CLK_CALI_REG);
> -
> -               /* Assume the calibrated 32k clock is accurate. */
> -               if (val & IOSC_CLK_CALI_SRC_SEL)
> -                       return LOSC_RATE;
> -       }
> -
> -       val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
> -
> -       return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
> -}
> -
> -static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
> -                                                 unsigned long parent_accuracy)
> -{
> -       struct ccu_common *cm = hw_to_ccu_common(hw);
> -       u32 val;
> -
> -       if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> -               val = readl(cm->base + IOSC_CLK_CALI_REG);
> -
> -               /* Assume the calibrated 32k clock is accurate. */
> -               if (val & IOSC_CLK_CALI_SRC_SEL)
> -                       return 0;
> -       }
> -
> -       return parent_accuracy;
> -}
> -
> -static const struct clk_ops ccu_iosc_32k_ops = {
> -       .prepare                = ccu_iosc_32k_prepare,
> -       .unprepare              = ccu_iosc_32k_unprepare,
> -       .recalc_rate            = ccu_iosc_32k_recalc_rate,
> -       .recalc_accuracy        = ccu_iosc_32k_recalc_accuracy,
> -};
> -
>  static struct ccu_common iosc_32k_clk = {
>         .hw.init        = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
>                                          &ccu_iosc_32k_ops,
> diff --git a/drivers/clk/sunxi-ng/ccu_rtc.c b/drivers/clk/sunxi-ng/ccu_rtc.c
> new file mode 100644
> index 000000000000..cfc10218517c
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu_rtc.c
> @@ -0,0 +1,136 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +
> +#include "ccu_common.h"
> +
> +#include "ccu_gate.h"
> +#include "ccu_rtc.h"
> +
> +static int ccu_iosc_enable(struct clk_hw *hw)
> +{
> +       struct ccu_common *cm = hw_to_ccu_common(hw);
> +
> +       return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
> +}
> +
> +static void ccu_iosc_disable(struct clk_hw *hw)
> +{
> +       struct ccu_common *cm = hw_to_ccu_common(hw);
> +
> +       return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
> +}
> +
> +static int ccu_iosc_is_enabled(struct clk_hw *hw)
> +{
> +       struct ccu_common *cm = hw_to_ccu_common(hw);
> +
> +       return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
> +}
> +
> +static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
> +                                         unsigned long parent_rate)
> +{
> +       struct ccu_common *cm = hw_to_ccu_common(hw);
> +
> +       if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> +               u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
> +               /*
> +                * Recover the IOSC frequency by shifting the ones place of
> +                * (fixed-point divider * 32768) into bit zero.
> +                */
> +               if (reg & IOSC_CLK_CALI_EN)
> +                       return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
> +       }
> +
> +       return IOSC_RATE;
> +}
> +
> +static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
> +                                             unsigned long parent_accuracy)
> +{
> +       return IOSC_ACCURACY;
> +}
> +
> +const struct clk_ops ccu_iosc_ops = {
> +       .enable                 = ccu_iosc_enable,
> +       .disable                = ccu_iosc_disable,
> +       .is_enabled             = ccu_iosc_is_enabled,
> +       .recalc_rate            = ccu_iosc_recalc_rate,
> +       .recalc_accuracy        = ccu_iosc_recalc_accuracy,
> +};

You need to export the symbol.

> +
> +static int ccu_iosc_32k_prepare(struct clk_hw *hw)
> +{
> +       struct ccu_common *cm = hw_to_ccu_common(hw);
> +       u32 val;
> +
> +       if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
> +               return 0;
> +
> +       val = readl(cm->base + IOSC_CLK_CALI_REG);
> +       writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
> +              cm->base + IOSC_CLK_CALI_REG);
> +
> +       return 0;
> +}
> +
> +static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
> +{
> +       struct ccu_common *cm = hw_to_ccu_common(hw);
> +       u32 val;
> +
> +       if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
> +               return;
> +
> +       val = readl(cm->base + IOSC_CLK_CALI_REG);
> +       writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
> +              cm->base + IOSC_CLK_CALI_REG);
> +}
> +
> +static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
> +                                             unsigned long parent_rate)
> +{
> +       struct ccu_common *cm = hw_to_ccu_common(hw);
> +       u32 val;
> +
> +       if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> +               val = readl(cm->base + IOSC_CLK_CALI_REG);
> +
> +               /* Assume the calibrated 32k clock is accurate. */
> +               if (val & IOSC_CLK_CALI_SRC_SEL)
> +                       return LOSC_RATE;
> +       }
> +
> +       val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
> +
> +       return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
> +}
> +
> +static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
> +                                                 unsigned long parent_accuracy)
> +{
> +       struct ccu_common *cm = hw_to_ccu_common(hw);
> +       u32 val;
> +
> +       if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> +               val = readl(cm->base + IOSC_CLK_CALI_REG);
> +
> +               /* Assume the calibrated 32k clock is accurate. */
> +               if (val & IOSC_CLK_CALI_SRC_SEL)
> +                       return 0;
> +       }
> +
> +       return parent_accuracy;
> +}
> +
> +const struct clk_ops ccu_iosc_32k_ops = {
> +       .prepare                = ccu_iosc_32k_prepare,
> +       .unprepare              = ccu_iosc_32k_unprepare,
> +       .recalc_rate            = ccu_iosc_32k_recalc_rate,
> +       .recalc_accuracy        = ccu_iosc_32k_recalc_accuracy,
> +};

Same here.

> diff --git a/drivers/clk/sunxi-ng/ccu_rtc.h b/drivers/clk/sunxi-ng/ccu_rtc.h
> new file mode 100644
> index 000000000000..1c44c2206a25
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu_rtc.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
> + */
> +
> +#ifndef _CCU_RTC_H_
> +#define _CCU_RTC_H_
> +
> +#define IOSC_ACCURACY                  300000000 /* 30% */
> +#define IOSC_RATE                      16000000
> +
> +#define LOSC_RATE                      32768
> +#define LOSC_RATE_SHIFT                        15
> +
> +#define LOSC_CTRL_REG                  0x0
> +#define LOSC_CTRL_KEY                  0x16aa0000
> +
> +#define IOSC_32K_CLK_DIV_REG           0x8
> +#define IOSC_32K_CLK_DIV               GENMASK(4, 0)
> +#define IOSC_32K_PRE_DIV               32
> +
> +#define IOSC_CLK_CALI_REG              0xc
> +#define IOSC_CLK_CALI_DIV_ONES         22
> +#define IOSC_CLK_CALI_EN               BIT(1)
> +#define IOSC_CLK_CALI_SRC_SEL          BIT(0)
> +
> +#define LOSC_OUT_GATING_REG            0x60
> +
> +#define DCXO_CTRL_REG                  0x160
> +#define DCXO_CTRL_CLK16M_RC_EN         BIT(0)

Please keep all internals in the .c file.

ChenYu


> +
> +#define SUN6I_RTC_AUX_ID(_name)                "rtc_sun6i." #_name
> +
> +extern const struct clk_ops ccu_iosc_ops;
> +extern const struct clk_ops ccu_iosc_32k_ops;
> +
> +#endif /* _CCU_RTC_H_ */
>
> --
> 2.52.0
>
>
Re: [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
Posted by Junhui Liu 2 weeks, 1 day ago
On Sun Jan 25, 2026 at 12:32 PM CST, Chen-Yu Tsai wrote:
> On Wed, Jan 21, 2026 at 7:04 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>>
>> Extract the IOSC and 32k clock logic from ccu-sun6i-rtc into a shared
>> module to simplify adding RTC CCU support for new SoCs. This is needed
>> because newer Allwinner SoCs introduce additional DCXO/HOSC logic that
>> prevents direct reuse of the existing driver.
>>
>> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
>> ---
>>  drivers/clk/sunxi-ng/Makefile        |   3 +
>>  drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 152 +----------------------------------
>>  drivers/clk/sunxi-ng/ccu_rtc.c       | 136 +++++++++++++++++++++++++++++++
>>  drivers/clk/sunxi-ng/ccu_rtc.h       |  37 +++++++++
>>  4 files changed, 177 insertions(+), 151 deletions(-)
>>

[...]

>> +
>> +const struct clk_ops ccu_iosc_ops = {
>> +       .enable                 = ccu_iosc_enable,
>> +       .disable                = ccu_iosc_disable,
>> +       .is_enabled             = ccu_iosc_is_enabled,
>> +       .recalc_rate            = ccu_iosc_recalc_rate,
>> +       .recalc_accuracy        = ccu_iosc_recalc_accuracy,
>> +};
>
> You need to export the symbol.

Thanks, I will export them.

[...]

>> diff --git a/drivers/clk/sunxi-ng/ccu_rtc.h b/drivers/clk/sunxi-ng/ccu_rtc.h
>> new file mode 100644
>> index 000000000000..1c44c2206a25
>> --- /dev/null
>> +++ b/drivers/clk/sunxi-ng/ccu_rtc.h
>> @@ -0,0 +1,37 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
>> + */
>> +
>> +#ifndef _CCU_RTC_H_
>> +#define _CCU_RTC_H_
>> +
>> +#define IOSC_ACCURACY                  300000000 /* 30% */
>> +#define IOSC_RATE                      16000000
>> +
>> +#define LOSC_RATE                      32768
>> +#define LOSC_RATE_SHIFT                        15
>> +
>> +#define LOSC_CTRL_REG                  0x0
>> +#define LOSC_CTRL_KEY                  0x16aa0000
>> +
>> +#define IOSC_32K_CLK_DIV_REG           0x8
>> +#define IOSC_32K_CLK_DIV               GENMASK(4, 0)
>> +#define IOSC_32K_PRE_DIV               32
>> +
>> +#define IOSC_CLK_CALI_REG              0xc
>> +#define IOSC_CLK_CALI_DIV_ONES         22
>> +#define IOSC_CLK_CALI_EN               BIT(1)
>> +#define IOSC_CLK_CALI_SRC_SEL          BIT(0)
>> +
>> +#define LOSC_OUT_GATING_REG            0x60
>> +
>> +#define DCXO_CTRL_REG                  0x160
>> +#define DCXO_CTRL_CLK16M_RC_EN         BIT(0)
>
> Please keep all internals in the .c file.

My original thought was to reuse these for the A733. But since doing
so is not appropriate, I will put them in the .c files separately.

>
> ChenYu
>

-- 
Best regards,
Junhui Liu
Re: [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
Posted by kernel test robot 2 weeks, 1 day ago
Hi Junhui,

kernel test robot noticed the following build errors:

[auto build test ERROR on 24d479d26b25bce5faea3ddd9fa8f3a6c3129ea7]

url:    https://github.com/intel-lab-lkp/linux/commits/Junhui-Liu/dt-bindings-rtc-sun6i-Add-Allwinner-A733-support/20260121-192151
base:   24d479d26b25bce5faea3ddd9fa8f3a6c3129ea7
patch link:    https://lore.kernel.org/r/20260121-a733-rtc-v1-4-d359437f23a7%40pigmoral.tech
patch subject: [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
config: arm64-randconfig-002-20260125 (https://download.01.org/0day-ci/archive/20260125/202601251139.9CCmxdok-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260125/202601251139.9CCmxdok-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601251139.9CCmxdok-lkp@intel.com/

All errors (new ones prefixed by >>, old ones prefixed by <<):

>> ERROR: modpost: "ccu_iosc_32k_ops" [drivers/clk/sunxi-ng/sun6i-rtc-ccu.ko] undefined!
>> ERROR: modpost: "ccu_iosc_ops" [drivers/clk/sunxi-ng/sun6i-rtc-ccu.ko] undefined!

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki