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
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
>
>
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
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
© 2016 - 2026 Red Hat, Inc.