[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20260121-a733-rtc-v1-4-d359437f23a7@pigmoral.tech>
Date: Wed, 21 Jan 2026 18:59:10 +0800
From: Junhui Liu <junhui.liu@...moral.tech>
To: Michael Turquette <mturquette@...libre.com>,
Stephen Boyd <sboyd@...nel.org>, Chen-Yu Tsai <wens@...nel.org>,
Jernej Skrabec <jernej.skrabec@...il.com>,
Samuel Holland <samuel@...lland.org>,
Alexandre Belloni <alexandre.belloni@...tlin.com>,
Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>, Maxime Ripard <mripard@...nel.org>
Cc: linux-clk@...r.kernel.org, linux-arm-kernel@...ts.infradead.org,
linux-sunxi@...ts.linux.dev, linux-kernel@...r.kernel.org,
linux-rtc@...r.kernel.org, devicetree@...r.kernel.org,
Junhui Liu <junhui.liu@...moral.tech>
Subject: [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
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@...moral.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@...lland.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@...lland.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
Powered by blists - more mailing lists