* [PATCH 1/7] dt-bindings: rtc: sun6i: Add Allwinner A733 support
2026-01-21 10:59 [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Junhui Liu
@ 2026-01-21 10:59 ` Junhui Liu
2026-01-21 16:56 ` Rob Herring (Arm)
2026-03-28 12:37 ` Chen-Yu Tsai
2026-01-21 10:59 ` [PATCH 2/7] rtc: sun6i: Bind internal CCU via auxiliary bus Junhui Liu
` (6 subsequent siblings)
7 siblings, 2 replies; 17+ messages in thread
From: Junhui Liu @ 2026-01-21 10:59 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Alexandre Belloni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Ripard
Cc: linux-clk, linux-arm-kernel, linux-sunxi, linux-kernel, linux-rtc,
devicetree, Junhui Liu
The RTC module in the Allwinner A733 SoC is functionally compatible with
the sun6i RTC, but its internal Clock Control Unit (CCU) has significant
changes.
The A733 supports selecting the oscillator between three frequencies:
19.2MHz, 24MHz, and 26MHz. The RTC CCU relies on hardware to detect
which frequency is actually used on the board. By defining all three
frequencies as fixed-clocks in the device tree, the driver can identify
the hardware-detected frequency and expose it to the rest of the system.
Additionally, the A733 RTC CCU provides several new DCXO gate clocks for
specific modules, including SerDes, HDMI, and UFS.
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
.../bindings/rtc/allwinner,sun6i-a31-rtc.yaml | 38 ++++++++++++++++++++--
include/dt-bindings/clock/sun60i-a733-rtc.h | 16 +++++++++
2 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
index 9df5cdb6f63f..b18431955783 100644
--- a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
+++ b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
@@ -26,6 +26,7 @@ properties:
- allwinner,sun50i-h6-rtc
- allwinner,sun50i-h616-rtc
- allwinner,sun50i-r329-rtc
+ - allwinner,sun60i-a733-rtc
- items:
- const: allwinner,sun50i-a64-rtc
- const: allwinner,sun8i-h3-rtc
@@ -46,11 +47,11 @@ properties:
clocks:
minItems: 1
- maxItems: 4
+ maxItems: 6
clock-names:
minItems: 1
- maxItems: 4
+ maxItems: 6
clock-output-names:
minItems: 1
@@ -156,6 +157,38 @@ allOf:
- clocks
- clock-names
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: allwinner,sun60i-a733-rtc
+
+ then:
+ properties:
+ clocks:
+ minItems: 5
+ items:
+ - description: Bus clock for register access
+ - description: 19.2 MHz oscillator
+ - description: 24 MHz oscillator
+ - description: 26 MHz oscillator
+ - description: AHB parent for internal SPI clock
+ - description: External 32768 Hz oscillator
+
+ clock-names:
+ minItems: 5
+ items:
+ - const: bus
+ - const: osc19M
+ - const: osc24M
+ - const: osc26M
+ - const: ahb
+ - const: ext-osc32k
+
+ required:
+ - clocks
+ - clock-names
+
- if:
properties:
compatible:
@@ -164,6 +197,7 @@ allOf:
- allwinner,sun8i-r40-rtc
- allwinner,sun50i-h616-rtc
- allwinner,sun50i-r329-rtc
+ - allwinner,sun60i-a733-rtc
then:
properties:
diff --git a/include/dt-bindings/clock/sun60i-a733-rtc.h b/include/dt-bindings/clock/sun60i-a733-rtc.h
new file mode 100644
index 000000000000..8a2b5facad73
--- /dev/null
+++ b/include/dt-bindings/clock/sun60i-a733-rtc.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+
+#ifndef _DT_BINDINGS_CLK_SUN60I_A733_RTC_H_
+#define _DT_BINDINGS_CLK_SUN60I_A733_RTC_H_
+
+#define CLK_IOSC 0
+#define CLK_OSC32K 1
+#define CLK_HOSC 2
+#define CLK_RTC_32K 3
+#define CLK_OSC32K_FANOUT 4
+#define CLK_HOSC_SERDES1 5
+#define CLK_HOSC_SERDES0 6
+#define CLK_HOSC_HDMI 7
+#define CLK_HOSC_UFS 8
+
+#endif /* _DT_BINDINGS_CLK_SUN60I_A733_RTC_H_ */
--
2.52.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH 1/7] dt-bindings: rtc: sun6i: Add Allwinner A733 support
2026-01-21 10:59 ` [PATCH 1/7] dt-bindings: rtc: sun6i: Add Allwinner A733 support Junhui Liu
@ 2026-01-21 16:56 ` Rob Herring (Arm)
2026-03-28 12:37 ` Chen-Yu Tsai
1 sibling, 0 replies; 17+ messages in thread
From: Rob Herring (Arm) @ 2026-01-21 16:56 UTC (permalink / raw)
To: Junhui Liu
Cc: Jernej Skrabec, Alexandre Belloni, linux-rtc, Maxime Ripard,
Chen-Yu Tsai, devicetree, Krzysztof Kozlowski, Conor Dooley,
linux-kernel, Samuel Holland, linux-arm-kernel, linux-clk,
Michael Turquette, Stephen Boyd, linux-sunxi
On Wed, 21 Jan 2026 18:59:07 +0800, Junhui Liu wrote:
> The RTC module in the Allwinner A733 SoC is functionally compatible with
> the sun6i RTC, but its internal Clock Control Unit (CCU) has significant
> changes.
>
> The A733 supports selecting the oscillator between three frequencies:
> 19.2MHz, 24MHz, and 26MHz. The RTC CCU relies on hardware to detect
> which frequency is actually used on the board. By defining all three
> frequencies as fixed-clocks in the device tree, the driver can identify
> the hardware-detected frequency and expose it to the rest of the system.
>
> Additionally, the A733 RTC CCU provides several new DCXO gate clocks for
> specific modules, including SerDes, HDMI, and UFS.
>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
> .../bindings/rtc/allwinner,sun6i-a31-rtc.yaml | 38 ++++++++++++++++++++--
> include/dt-bindings/clock/sun60i-a733-rtc.h | 16 +++++++++
> 2 files changed, 52 insertions(+), 2 deletions(-)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 1/7] dt-bindings: rtc: sun6i: Add Allwinner A733 support
2026-01-21 10:59 ` [PATCH 1/7] dt-bindings: rtc: sun6i: Add Allwinner A733 support Junhui Liu
2026-01-21 16:56 ` Rob Herring (Arm)
@ 2026-03-28 12:37 ` Chen-Yu Tsai
1 sibling, 0 replies; 17+ messages in thread
From: Chen-Yu Tsai @ 2026-03-28 12:37 UTC (permalink / raw)
To: Junhui Liu
Cc: Michael Turquette, Stephen Boyd, Jernej Skrabec, Samuel Holland,
Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, linux-clk, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-rtc, devicetree
On Wed, Jan 21, 2026 at 7:03 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
> The RTC module in the Allwinner A733 SoC is functionally compatible with
> the sun6i RTC, but its internal Clock Control Unit (CCU) has significant
> changes.
>
> The A733 supports selecting the oscillator between three frequencies:
> 19.2MHz, 24MHz, and 26MHz. The RTC CCU relies on hardware to detect
> which frequency is actually used on the board. By defining all three
> frequencies as fixed-clocks in the device tree, the driver can identify
> the hardware-detected frequency and expose it to the rest of the system.
No. The board device tree shall have the exact and correct frequency
defined in the external crystal device node. The operating system can
use the hardware-detected frequency to "fix" the in-system representation
if it is off.
> Additionally, the A733 RTC CCU provides several new DCXO gate clocks for
> specific modules, including SerDes, HDMI, and UFS.
>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
> .../bindings/rtc/allwinner,sun6i-a31-rtc.yaml | 38 ++++++++++++++++++++--
> include/dt-bindings/clock/sun60i-a733-rtc.h | 16 +++++++++
> 2 files changed, 52 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
> index 9df5cdb6f63f..b18431955783 100644
> --- a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
> +++ b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
> @@ -26,6 +26,7 @@ properties:
> - allwinner,sun50i-h6-rtc
> - allwinner,sun50i-h616-rtc
> - allwinner,sun50i-r329-rtc
> + - allwinner,sun60i-a733-rtc
> - items:
> - const: allwinner,sun50i-a64-rtc
> - const: allwinner,sun8i-h3-rtc
> @@ -46,11 +47,11 @@ properties:
>
> clocks:
> minItems: 1
> - maxItems: 4
> + maxItems: 6
>
> clock-names:
> minItems: 1
> - maxItems: 4
> + maxItems: 6
>
> clock-output-names:
> minItems: 1
> @@ -156,6 +157,38 @@ allOf:
> - clocks
> - clock-names
>
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: allwinner,sun60i-a733-rtc
> +
> + then:
> + properties:
> + clocks:
> + minItems: 5
> + items:
> + - description: Bus clock for register access
> + - description: 19.2 MHz oscillator
> + - description: 24 MHz oscillator
> + - description: 26 MHz oscillator
No. There is only one input. As in there is only one set of pins for the
DCXO. The inputs are the same as on R329 / A523. Just use that list.
> + - description: AHB parent for internal SPI clock
> + - description: External 32768 Hz oscillator
> +
> + clock-names:
> + minItems: 5
> + items:
> + - const: bus
> + - const: osc19M
> + - const: osc24M
> + - const: osc26M
> + - const: ahb
> + - const: ext-osc32k
> +
> + required:
> + - clocks
> + - clock-names
> +
> - if:
> properties:
> compatible:
> @@ -164,6 +197,7 @@ allOf:
> - allwinner,sun8i-r40-rtc
> - allwinner,sun50i-h616-rtc
> - allwinner,sun50i-r329-rtc
> + - allwinner,sun60i-a733-rtc
>
> then:
> properties:
> diff --git a/include/dt-bindings/clock/sun60i-a733-rtc.h b/include/dt-bindings/clock/sun60i-a733-rtc.h
> new file mode 100644
> index 000000000000..8a2b5facad73
> --- /dev/null
> +++ b/include/dt-bindings/clock/sun60i-a733-rtc.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
> +
> +#ifndef _DT_BINDINGS_CLK_SUN60I_A733_RTC_H_
> +#define _DT_BINDINGS_CLK_SUN60I_A733_RTC_H_
> +
> +#define CLK_IOSC 0
> +#define CLK_OSC32K 1
> +#define CLK_HOSC 2
The DCXO enable control has been present since at least the H6. We just
never added it, as we would never disable it anyway.
If you compare the RTC clock trees of the A733 and A523, the only addition
besides the new gates seems to be the LOSC auto selection. But even that
is just an illusion, as the A523 has the same registers for that.
One could say the A733 RTC is almost backward compatible to the A523, if
not for the two fastboot registers the A523 has at 0x120 and 0x124.
So I ask that you try to integrate the differences into the existing
driver and bindings. You can tweak and export internal clks if you
need.
> +#define CLK_RTC_32K 3
AFAICT besides being an internal clock, this is also fed to GPIO for
debounce? We probably need to expose this on the A523 as well.
Thanks
ChenYu
> +#define CLK_OSC32K_FANOUT 4
> +#define CLK_HOSC_SERDES1 5
> +#define CLK_HOSC_SERDES0 6
> +#define CLK_HOSC_HDMI 7
> +#define CLK_HOSC_UFS 8
> +
> +#endif /* _DT_BINDINGS_CLK_SUN60I_A733_RTC_H_ */
>
> --
> 2.52.0
>
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 2/7] rtc: sun6i: Bind internal CCU via auxiliary bus
2026-01-21 10:59 [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Junhui Liu
2026-01-21 10:59 ` [PATCH 1/7] dt-bindings: rtc: sun6i: Add Allwinner A733 support Junhui Liu
@ 2026-01-21 10:59 ` Junhui Liu
2026-01-21 10:59 ` [PATCH 3/7] clk: sunxi-ng: sun6i-rtc: Add feature bit for IOSC calibration Junhui Liu
` (5 subsequent siblings)
7 siblings, 0 replies; 17+ messages in thread
From: Junhui Liu @ 2026-01-21 10:59 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Alexandre Belloni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Ripard
Cc: linux-clk, linux-arm-kernel, linux-sunxi, linux-kernel, linux-rtc,
devicetree, Junhui Liu
The sun6i RTC block contains an internal clock control unit (CCU).
Currently, the RTC driver binds this CCU part by directly calling a
probe function exported by the clock framework. This creates a tight
coupling between the RTC and clock drivers and makes it difficult to
add internal CCU support for new SoCs.
Switch to use the auxiliary bus for binding the internal CCU to
decouple the drivers.
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 29 +++++++++++++++++++++++------
drivers/rtc/rtc-sun6i.c | 31 +++++++++++++++++++++++--------
include/linux/clk/sunxi-ng.h | 2 --
3 files changed, 46 insertions(+), 16 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
index f6bfeba009e8..3088f247d927 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
@@ -3,6 +3,7 @@
// Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
//
+#include <linux/auxiliary_bus.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
@@ -11,8 +12,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/clk/sunxi-ng.h>
-
#include "ccu_common.h"
#include "ccu_div.h"
@@ -44,6 +43,8 @@
#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;
@@ -349,14 +350,18 @@ static const struct of_device_id sun6i_rtc_ccu_match[] = {
};
MODULE_DEVICE_TABLE(of, sun6i_rtc_ccu_match);
-int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
+static int sun6i_rtc_ccu_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
{
const struct sun6i_rtc_match_data *data;
struct clk *ext_osc32k_clk = NULL;
const struct of_device_id *match;
+ struct device *dev = &adev->dev;
+ void __iomem *reg = dev->platform_data;
+ struct device *parent = dev->parent;
/* This driver is only used for newer variants of the hardware. */
- match = of_match_device(sun6i_rtc_ccu_match, dev);
+ match = of_match_device(sun6i_rtc_ccu_match, parent);
if (!match)
return 0;
@@ -367,9 +372,9 @@ int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
const char *fw_name;
/* ext-osc32k was the only input clock in the old binding. */
- fw_name = of_property_present(dev->of_node, "clock-names")
+ fw_name = of_property_present(parent->of_node, "clock-names")
? "ext-osc32k" : NULL;
- ext_osc32k_clk = devm_clk_get_optional(dev, fw_name);
+ ext_osc32k_clk = devm_clk_get_optional(parent, fw_name);
if (IS_ERR(ext_osc32k_clk))
return PTR_ERR(ext_osc32k_clk);
}
@@ -392,6 +397,18 @@ int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
return devm_sunxi_ccu_probe(dev, reg, &sun6i_rtc_ccu_desc);
}
+static const struct auxiliary_device_id sun6i_ccu_rtc_ids[] = {
+ { .name = SUN6I_RTC_AUX_ID(sun6i) },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, sun6i_ccu_rtc_ids);
+
+static struct auxiliary_driver sun6i_ccu_rtc_driver = {
+ .probe = sun6i_rtc_ccu_probe,
+ .id_table = sun6i_ccu_rtc_ids,
+};
+module_auxiliary_driver(sun6i_ccu_rtc_driver);
+
MODULE_IMPORT_NS("SUNXI_CCU");
MODULE_DESCRIPTION("Support for the Allwinner H616/R329 RTC CCU");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index e5e6013d080e..b4489e0a09ce 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -11,9 +11,9 @@
* Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com>
*/
+#include <linux/auxiliary_bus.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
-#include <linux/clk/sunxi-ng.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/fs.h>
@@ -141,6 +141,11 @@ struct sun6i_rtc_clk_data {
#define RTC_LINEAR_DAY BIT(0)
+struct sun6i_rtc_match_data {
+ const char *adev_name;
+ unsigned long flags;
+};
+
struct sun6i_rtc_dev {
struct rtc_device *rtc;
const struct sun6i_rtc_clk_data *data;
@@ -745,8 +750,10 @@ static void sun6i_rtc_bus_clk_cleanup(void *data)
static int sun6i_rtc_probe(struct platform_device *pdev)
{
+ const struct sun6i_rtc_match_data *data;
struct sun6i_rtc_dev *chip = sun6i_rtc;
struct device *dev = &pdev->dev;
+ struct auxiliary_device *adev;
struct clk *bus_clk;
int ret;
@@ -765,6 +772,8 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
return ret;
}
+ data = of_device_get_match_data(&pdev->dev);
+
if (!chip) {
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -776,16 +785,17 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
- if (IS_REACHABLE(CONFIG_SUN6I_RTC_CCU)) {
- ret = sun6i_rtc_ccu_probe(dev, chip->base);
- if (ret)
- return ret;
+ if (data && data->adev_name) {
+ adev = devm_auxiliary_device_create(dev, data->adev_name, chip->base);
+ if (!adev)
+ return -ENODEV;
}
}
platform_set_drvdata(pdev, chip);
- chip->flags = (unsigned long)of_device_get_match_data(&pdev->dev);
+ if (data)
+ chip->flags = data->flags;
chip->irq = platform_get_irq(pdev, 0);
if (chip->irq < 0)
@@ -850,6 +860,11 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
return 0;
}
+static const struct sun6i_rtc_match_data sun6i_rtc_match_data = {
+ .adev_name = "sun6i",
+ .flags = RTC_LINEAR_DAY,
+};
+
/*
* As far as RTC functionality goes, all models are the same. The
* datasheets claim that different models have different number of
@@ -865,9 +880,9 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
{ .compatible = "allwinner,sun50i-h5-rtc" },
{ .compatible = "allwinner,sun50i-h6-rtc" },
{ .compatible = "allwinner,sun50i-h616-rtc",
- .data = (void *)RTC_LINEAR_DAY },
+ .data = &sun6i_rtc_match_data },
{ .compatible = "allwinner,sun50i-r329-rtc",
- .data = (void *)RTC_LINEAR_DAY },
+ .data = &sun6i_rtc_match_data },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
diff --git a/include/linux/clk/sunxi-ng.h b/include/linux/clk/sunxi-ng.h
index 57c8ec44ab4e..cf32123b39f5 100644
--- a/include/linux/clk/sunxi-ng.h
+++ b/include/linux/clk/sunxi-ng.h
@@ -9,6 +9,4 @@
int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode);
int sunxi_ccu_get_mmc_timing_mode(struct clk *clk);
-int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg);
-
#endif
--
2.52.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH 3/7] clk: sunxi-ng: sun6i-rtc: Add feature bit for IOSC calibration
2026-01-21 10:59 [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Junhui Liu
2026-01-21 10:59 ` [PATCH 1/7] dt-bindings: rtc: sun6i: Add Allwinner A733 support Junhui Liu
2026-01-21 10:59 ` [PATCH 2/7] rtc: sun6i: Bind internal CCU via auxiliary bus Junhui Liu
@ 2026-01-21 10:59 ` Junhui Liu
2026-01-21 10:59 ` [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic Junhui Liu
` (4 subsequent siblings)
7 siblings, 0 replies; 17+ messages in thread
From: Junhui Liu @ 2026-01-21 10:59 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Alexandre Belloni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Ripard
Cc: linux-clk, linux-arm-kernel, linux-sunxi, linux-kernel, linux-rtc,
devicetree, Junhui Liu
The sun6i-rtc CCU driver currently uses a global static variable to
denote whether calibration is supported, which makes IOSC operations
tightly coupled to this file.
Convert this into a feature bit to decouple the logic. This allows the
IOSC clock code to be moved into a shared module for reuse by other SoCs.
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 17 +++++++++--------
drivers/clk/sunxi-ng/ccu_common.h | 1 +
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
index 3088f247d927..6f888169412c 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
@@ -53,8 +53,6 @@ struct sun6i_rtc_match_data {
u8 osc32k_fanout_nparents;
};
-static bool have_iosc_calibration;
-
static int ccu_iosc_enable(struct clk_hw *hw)
{
struct ccu_common *cm = hw_to_ccu_common(hw);
@@ -81,7 +79,7 @@ static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
{
struct ccu_common *cm = hw_to_ccu_common(hw);
- if (have_iosc_calibration) {
+ if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
/*
@@ -120,7 +118,7 @@ static int ccu_iosc_32k_prepare(struct clk_hw *hw)
struct ccu_common *cm = hw_to_ccu_common(hw);
u32 val;
- if (!have_iosc_calibration)
+ if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
return 0;
val = readl(cm->base + IOSC_CLK_CALI_REG);
@@ -135,7 +133,7 @@ static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
struct ccu_common *cm = hw_to_ccu_common(hw);
u32 val;
- if (!have_iosc_calibration)
+ if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
return;
val = readl(cm->base + IOSC_CLK_CALI_REG);
@@ -149,7 +147,7 @@ static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
struct ccu_common *cm = hw_to_ccu_common(hw);
u32 val;
- if (have_iosc_calibration) {
+ if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
val = readl(cm->base + IOSC_CLK_CALI_REG);
/* Assume the calibrated 32k clock is accurate. */
@@ -168,7 +166,7 @@ static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
struct ccu_common *cm = hw_to_ccu_common(hw);
u32 val;
- if (have_iosc_calibration) {
+ if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
val = readl(cm->base + IOSC_CLK_CALI_REG);
/* Assume the calibrated 32k clock is accurate. */
@@ -366,7 +364,10 @@ static int sun6i_rtc_ccu_probe(struct auxiliary_device *adev,
return 0;
data = match->data;
- have_iosc_calibration = data->have_iosc_calibration;
+ if (data->have_iosc_calibration) {
+ iosc_clk.features |= CCU_FEATURE_IOSC_CALIBRATION;
+ iosc_32k_clk.features |= CCU_FEATURE_IOSC_CALIBRATION;
+ }
if (data->have_ext_osc32k) {
const char *fw_name;
diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h
index bbec283b9d99..d9dc24ad5503 100644
--- a/drivers/clk/sunxi-ng/ccu_common.h
+++ b/drivers/clk/sunxi-ng/ccu_common.h
@@ -21,6 +21,7 @@
#define CCU_FEATURE_CLOSEST_RATE BIT(9)
#define CCU_FEATURE_DUAL_DIV BIT(10)
#define CCU_FEATURE_UPDATE_BIT BIT(11)
+#define CCU_FEATURE_IOSC_CALIBRATION BIT(12)
/* MMC timing mode switch bit */
#define CCU_MMC_NEW_TIMING_MODE BIT(30)
--
2.52.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
2026-01-21 10:59 [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Junhui Liu
` (2 preceding siblings ...)
2026-01-21 10:59 ` [PATCH 3/7] clk: sunxi-ng: sun6i-rtc: Add feature bit for IOSC calibration Junhui Liu
@ 2026-01-21 10:59 ` Junhui Liu
2026-01-25 4:07 ` kernel test robot
2026-01-25 4:32 ` Chen-Yu Tsai
2026-01-21 10:59 ` [PATCH 5/7] clk: sunxi-ng: mux: Add mux read-only clock operations Junhui Liu
` (3 subsequent siblings)
7 siblings, 2 replies; 17+ messages in thread
From: Junhui Liu @ 2026-01-21 10:59 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Alexandre Belloni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Ripard
Cc: linux-clk, linux-arm-kernel, linux-sunxi, linux-kernel, linux-rtc,
devicetree, Junhui Liu
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
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
2026-01-21 10:59 ` [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic Junhui Liu
@ 2026-01-25 4:07 ` kernel test robot
2026-01-25 4:32 ` Chen-Yu Tsai
1 sibling, 0 replies; 17+ messages in thread
From: kernel test robot @ 2026-01-25 4:07 UTC (permalink / raw)
To: Junhui Liu, Michael Turquette, Stephen Boyd, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Alexandre Belloni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Ripard
Cc: llvm, oe-kbuild-all, linux-clk, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-rtc, devicetree, 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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
2026-01-21 10:59 ` [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic Junhui Liu
2026-01-25 4:07 ` kernel test robot
@ 2026-01-25 4:32 ` Chen-Yu Tsai
2026-01-25 5:50 ` Junhui Liu
1 sibling, 1 reply; 17+ messages in thread
From: Chen-Yu Tsai @ 2026-01-25 4:32 UTC (permalink / raw)
To: Junhui Liu
Cc: Michael Turquette, Stephen Boyd, Jernej Skrabec, Samuel Holland,
Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, linux-clk, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-rtc, devicetree
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
>
>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
2026-01-25 4:32 ` Chen-Yu Tsai
@ 2026-01-25 5:50 ` Junhui Liu
0 siblings, 0 replies; 17+ messages in thread
From: Junhui Liu @ 2026-01-25 5:50 UTC (permalink / raw)
To: wens, Junhui Liu
Cc: Michael Turquette, Stephen Boyd, Jernej Skrabec, Samuel Holland,
Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, linux-clk, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-rtc, devicetree
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
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 5/7] clk: sunxi-ng: mux: Add mux read-only clock operations
2026-01-21 10:59 [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Junhui Liu
` (3 preceding siblings ...)
2026-01-21 10:59 ` [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic Junhui Liu
@ 2026-01-21 10:59 ` Junhui Liu
2026-01-21 10:59 ` [PATCH 6/7] rtc: sun6i: Add support for A733 RTC Junhui Liu
` (2 subsequent siblings)
7 siblings, 0 replies; 17+ messages in thread
From: Junhui Liu @ 2026-01-21 10:59 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Alexandre Belloni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Ripard
Cc: linux-clk, linux-arm-kernel, linux-sunxi, linux-kernel, linux-rtc,
devicetree, Junhui Liu
The Allwinner A733 SoC introduces some mux clocks (such as the one
indicating the DCXO frequency) that use read-only registers to report
their current hardware configuration. Writing to these registers is not
supported by hardware and should be avoided.
Add ccu_mux_ro_ops to support these clocks, which omit .set_parent()
and .determine_rate() to prevent changing the mux state.
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
drivers/clk/sunxi-ng/ccu_mux.c | 11 +++++++++++
drivers/clk/sunxi-ng/ccu_mux.h | 1 +
2 files changed, 12 insertions(+)
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index 74f9e98a5d35..d48e7c3e065d 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -277,6 +277,17 @@ const struct clk_ops ccu_mux_ops = {
};
EXPORT_SYMBOL_NS_GPL(ccu_mux_ops, "SUNXI_CCU");
+const struct clk_ops ccu_mux_ro_ops = {
+ .disable = ccu_mux_disable,
+ .enable = ccu_mux_enable,
+ .is_enabled = ccu_mux_is_enabled,
+
+ .get_parent = ccu_mux_get_parent,
+
+ .recalc_rate = ccu_mux_recalc_rate,
+};
+EXPORT_SYMBOL_NS_GPL(ccu_mux_ro_ops, "SUNXI_CCU");
+
/*
* This clock notifier is called when the frequency of the of the parent
* PLL clock is to be changed. The idea is to switch the parent to a
diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h
index eb1172ebbd94..887c164d00f4 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.h
+++ b/drivers/clk/sunxi-ng/ccu_mux.h
@@ -129,6 +129,7 @@ static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw)
}
extern const struct clk_ops ccu_mux_ops;
+extern const struct clk_ops ccu_mux_ro_ops;
unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
struct ccu_mux_internal *cm,
--
2.52.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH 6/7] rtc: sun6i: Add support for A733 RTC
2026-01-21 10:59 [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Junhui Liu
` (4 preceding siblings ...)
2026-01-21 10:59 ` [PATCH 5/7] clk: sunxi-ng: mux: Add mux read-only clock operations Junhui Liu
@ 2026-01-21 10:59 ` Junhui Liu
2026-03-28 12:40 ` Chen-Yu Tsai
2026-01-21 10:59 ` [PATCH 7/7] clk: sunxi-ng: Add Allwinner A733 RTC CCU support Junhui Liu
2026-02-22 10:41 ` [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Jernej Škrabec
7 siblings, 1 reply; 17+ messages in thread
From: Junhui Liu @ 2026-01-21 10:59 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Alexandre Belloni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Ripard
Cc: linux-clk, linux-arm-kernel, linux-sunxi, linux-kernel, linux-rtc,
devicetree, Junhui Liu
The RTC in the Allwinner A733 SoC is compatible with the H616 in terms
of its time storage and alarm functionality. However, its internal CCU
is different, with additional DCXO handling logic.
Add new match data to register a new auxiliary device for its CCU part.
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
drivers/rtc/rtc-sun6i.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index b4489e0a09ce..a58d9c6b917c 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -865,6 +865,11 @@ static const struct sun6i_rtc_match_data sun6i_rtc_match_data = {
.flags = RTC_LINEAR_DAY,
};
+static const struct sun6i_rtc_match_data sun60i_rtc_match_data = {
+ .adev_name = "sun60i",
+ .flags = RTC_LINEAR_DAY,
+};
+
/*
* As far as RTC functionality goes, all models are the same. The
* datasheets claim that different models have different number of
@@ -883,6 +888,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
.data = &sun6i_rtc_match_data },
{ .compatible = "allwinner,sun50i-r329-rtc",
.data = &sun6i_rtc_match_data },
+ { .compatible = "allwinner,sun60i-a733-rtc",
+ .data = &sun60i_rtc_match_data },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
--
2.52.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH 6/7] rtc: sun6i: Add support for A733 RTC
2026-01-21 10:59 ` [PATCH 6/7] rtc: sun6i: Add support for A733 RTC Junhui Liu
@ 2026-03-28 12:40 ` Chen-Yu Tsai
0 siblings, 0 replies; 17+ messages in thread
From: Chen-Yu Tsai @ 2026-03-28 12:40 UTC (permalink / raw)
To: Junhui Liu
Cc: Michael Turquette, Stephen Boyd, Jernej Skrabec, Samuel Holland,
Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, linux-clk, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-rtc, devicetree
On Wed, Jan 21, 2026 at 7:04 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
> The RTC in the Allwinner A733 SoC is compatible with the H616 in terms
> of its time storage and alarm functionality. However, its internal CCU
> is different, with additional DCXO handling logic.
>
> Add new match data to register a new auxiliary device for its CCU part.
This is probably incorrect, since you aren't actually adding auxiliary
devices. It should just say "add a new compatible and matching data for
the new SoC".
>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
> drivers/rtc/rtc-sun6i.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
> index b4489e0a09ce..a58d9c6b917c 100644
> --- a/drivers/rtc/rtc-sun6i.c
> +++ b/drivers/rtc/rtc-sun6i.c
> @@ -865,6 +865,11 @@ static const struct sun6i_rtc_match_data sun6i_rtc_match_data = {
> .flags = RTC_LINEAR_DAY,
> };
>
> +static const struct sun6i_rtc_match_data sun60i_rtc_match_data = {
> + .adev_name = "sun60i",
> + .flags = RTC_LINEAR_DAY,
> +};
> +
> /*
> * As far as RTC functionality goes, all models are the same. The
> * datasheets claim that different models have different number of
> @@ -883,6 +888,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
> .data = &sun6i_rtc_match_data },
> { .compatible = "allwinner,sun50i-r329-rtc",
> .data = &sun6i_rtc_match_data },
> + { .compatible = "allwinner,sun60i-a733-rtc",
> + .data = &sun60i_rtc_match_data },
> { /* sentinel */ },
> };
> MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
>
> --
> 2.52.0
>
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 7/7] clk: sunxi-ng: Add Allwinner A733 RTC CCU support
2026-01-21 10:59 [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Junhui Liu
` (5 preceding siblings ...)
2026-01-21 10:59 ` [PATCH 6/7] rtc: sun6i: Add support for A733 RTC Junhui Liu
@ 2026-01-21 10:59 ` Junhui Liu
2026-03-28 14:41 ` Chen-Yu Tsai
2026-02-22 10:41 ` [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Jernej Škrabec
7 siblings, 1 reply; 17+ messages in thread
From: Junhui Liu @ 2026-01-21 10:59 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Alexandre Belloni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Ripard
Cc: linux-clk, linux-arm-kernel, linux-sunxi, linux-kernel, linux-rtc,
devicetree, Junhui Liu
Add support for the internal CCU found in the RTC module of the Allwinner
A733 SoC. While the basic 16MHz (IOSC) and 32kHz logic remains compatible
with older SoCs like the sun6i, the A733 introduces several new features.
The A733 RTC CCU supports choosing one of three external crystal
frequencies: 19.2MHz, 24MHz, and 26MHz. It features hardware detection
logic to automatically identify the frequency used on the board and
exports this DCXO signal as the "hosc" clock.
Furthermore, the driver implements logic to derive a 32kHz reference
from the HOSC. This is achieved through a muxed clock path using fixed
pre-dividers to normalize the different crystal frequencies to ~32kHz.
This path reuses the same hardware mux registers as the HOSC clock.
Additionally, this CCU provides several gate clocks for specific
peripherals, including SerDes, HDMI, and UFS. The driver is implemented
as an auxiliary driver to be bound to the sun6i-rtc driver.
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
drivers/clk/sunxi-ng/Kconfig | 5 +
drivers/clk/sunxi-ng/Makefile | 2 +
drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c | 204 +++++++++++++++++++++++++++++
drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h | 18 +++
drivers/clk/sunxi-ng/ccu_rtc.h | 7 +
5 files changed, 236 insertions(+)
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 6af2d020e03e..16afbf249f26 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -67,6 +67,11 @@ config SUN55I_A523_R_CCU
default ARCH_SUNXI
depends on ARM64 || COMPILE_TEST
+config SUN60I_A733_RTC_CCU
+ tristate "Support for the Allwinner A733 RTC CCU"
+ default ARCH_SUNXI
+ depends on ARM64 || COMPILE_TEST
+
config SUN4I_A10_CCU
tristate "Support for the Allwinner A10/A20 CCU"
default ARCH_SUNXI
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index c3f810a025a8..b0d823440c33 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o
obj-$(CONFIG_SUN55I_A523_CCU) += sun55i-a523-ccu.o
obj-$(CONFIG_SUN55I_A523_MCU_CCU) += sun55i-a523-mcu-ccu.o
obj-$(CONFIG_SUN55I_A523_R_CCU) += sun55i-a523-r-ccu.o
+obj-$(CONFIG_SUN60I_A733_RTC_CCU) += sun60i-a733-rtc-ccu.o
obj-$(CONFIG_SUN4I_A10_CCU) += sun4i-a10-ccu.o
obj-$(CONFIG_SUN5I_CCU) += sun5i-ccu.o
obj-$(CONFIG_SUN6I_A31_CCU) += sun6i-a31-ccu.o
@@ -67,6 +68,7 @@ sun50i-h616-ccu-y += ccu-sun50i-h616.o
sun55i-a523-ccu-y += ccu-sun55i-a523.o
sun55i-a523-mcu-ccu-y += ccu-sun55i-a523-mcu.o
sun55i-a523-r-ccu-y += ccu-sun55i-a523-r.o
+sun60i-a733-rtc-ccu-y += ccu-sun60i-a733-rtc.o
sun4i-a10-ccu-y += ccu-sun4i-a10.o
sun5i-ccu-y += ccu-sun5i.o
sun6i-a31-ccu-y += ccu-sun6i-a31.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c
new file mode 100644
index 000000000000..d17aceffa16e
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#include <linux/array_size.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include "ccu_common.h"
+
+#include "ccu_gate.h"
+#include "ccu_mux.h"
+#include "ccu_rtc.h"
+
+#include "ccu-sun60i-a733-rtc.h"
+
+static struct ccu_common iosc_clk = {
+ .reg = DCXO_CTRL_REG,
+ .features = CCU_FEATURE_IOSC_CALIBRATION,
+ .hw.init = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
+ CLK_GET_RATE_NOCACHE),
+};
+
+static struct ccu_common iosc_32k_clk = {
+ .features = CCU_FEATURE_IOSC_CALIBRATION,
+ .hw.init = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
+ &ccu_iosc_32k_ops,
+ CLK_GET_RATE_NOCACHE),
+};
+
+static SUNXI_CCU_GATE_FW(ext_osc32k_gate_clk, "ext-osc32k-gate",
+ "ext-osc32k", 0x0, BIT(4), 0);
+
+static const struct clk_hw *osc32k_parents[] = {
+ &iosc_32k_clk.hw,
+ &ext_osc32k_gate_clk.common.hw,
+};
+
+static struct ccu_mux osc32k_clk = {
+ .mux = _SUNXI_CCU_MUX(0, 1),
+ .common = {
+ .reg = LOSC_CTRL_REG,
+ .features = CCU_FEATURE_KEY_FIELD,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("osc32k",
+ osc32k_parents,
+ &ccu_mux_ops,
+ 0),
+ },
+};
+
+static const struct clk_parent_data hosc_parents[] = {
+ { .fw_name = "osc24M" },
+ { .fw_name = "osc19M" },
+ { .fw_name = "osc26M" },
+ { .fw_name = "osc24M" },
+};
+
+struct ccu_mux hosc_clk = {
+ .enable = DCXO_CTRL_DCXO_EN,
+ .mux = _SUNXI_CCU_MUX(14, 2),
+ .common = {
+ .reg = DCXO_CTRL_REG,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("hosc",
+ hosc_parents,
+ &ccu_mux_ro_ops,
+ 0),
+ },
+};
+
+static const struct ccu_mux_fixed_prediv hosc_32k_predivs[] = {
+ { .index = 0, .div = 732 },
+ { .index = 1, .div = 586 },
+ { .index = 2, .div = 793 },
+ { .index = 3, .div = 732 },
+};
+
+static struct ccu_mux hosc_32k_mux_clk = {
+ .enable = DCXO_CTRL_DCXO_EN,
+ .mux = {
+ .shift = 14,
+ .width = 2,
+ .fixed_predivs = hosc_32k_predivs,
+ .n_predivs = ARRAY_SIZE(hosc_32k_predivs),
+ },
+ .common = {
+ .reg = DCXO_CTRL_REG,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("hosc-32k-mux",
+ hosc_parents,
+ &ccu_mux_ro_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_GATE_HW(hosc_32k_clk, "hosc-32k", &hosc_32k_mux_clk.common.hw,
+ LOSC_OUT_GATING_REG, BIT(16), 0);
+
+static const struct clk_hw *rtc_32k_parents[] = {
+ &osc32k_clk.common.hw,
+ &hosc_32k_clk.common.hw,
+};
+
+static struct ccu_mux rtc_32k_clk = {
+ .mux = _SUNXI_CCU_MUX(1, 1),
+ .common = {
+ .reg = LOSC_CTRL_REG,
+ .features = CCU_FEATURE_KEY_FIELD,
+ .hw.init = CLK_HW_INIT_PARENTS_HW("rtc-32k",
+ rtc_32k_parents,
+ &ccu_mux_ops,
+ 0),
+ },
+};
+
+static const struct clk_parent_data osc32k_fanout_parents[] = {
+ { .hw = &osc32k_clk.common.hw },
+ { .hw = &ext_osc32k_gate_clk.common.hw },
+ { .hw = &hosc_32k_clk.common.hw },
+};
+
+static SUNXI_CCU_MUX_DATA_WITH_GATE(osc32k_fanout_clk, "osc32k-fanout", osc32k_fanout_parents,
+ LOSC_OUT_GATING_REG,
+ 1, 2, /* mux */
+ BIT(0), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE_HW(hosc_serdes1_clk, "hosc-serdes1", &hosc_clk.common.hw,
+ DCXO_GATING_REG, DCXO_SERDES1_GATING, 0);
+static SUNXI_CCU_GATE_HW(hosc_serdes0_clk, "hosc-serdes0", &hosc_clk.common.hw,
+ DCXO_GATING_REG, DCXO_SERDES0_GATING, 0);
+static SUNXI_CCU_GATE_HW(hosc_hdmi_clk, "hosc-hdmi", &hosc_clk.common.hw,
+ DCXO_GATING_REG, DCXO_HDMI_GATING, 0);
+static SUNXI_CCU_GATE_HW(hosc_ufs_clk, "hosc-ufs", &hosc_clk.common.hw,
+ DCXO_GATING_REG, DCXO_UFS_GATING, 0);
+
+static struct ccu_common *sun60i_rtc_ccu_clks[] = {
+ &iosc_clk,
+ &iosc_32k_clk,
+ &ext_osc32k_gate_clk.common,
+ &osc32k_clk.common,
+ &hosc_clk.common,
+ &hosc_32k_mux_clk.common,
+ &hosc_32k_clk.common,
+ &rtc_32k_clk.common,
+ &osc32k_fanout_clk.common,
+ &hosc_serdes1_clk.common,
+ &hosc_serdes0_clk.common,
+ &hosc_hdmi_clk.common,
+ &hosc_ufs_clk.common,
+};
+
+static struct clk_hw_onecell_data sun60i_rtc_ccu_hw_clks = {
+ .num = CLK_NUMBER,
+ .hws = {
+ [CLK_IOSC] = &iosc_clk.hw,
+ [CLK_OSC32K] = &osc32k_clk.common.hw,
+ [CLK_HOSC] = &hosc_clk.common.hw,
+ [CLK_RTC_32K] = &rtc_32k_clk.common.hw,
+ [CLK_OSC32K_FANOUT] = &osc32k_fanout_clk.common.hw,
+ [CLK_HOSC_SERDES1] = &hosc_serdes1_clk.common.hw,
+ [CLK_HOSC_SERDES0] = &hosc_serdes0_clk.common.hw,
+ [CLK_HOSC_HDMI] = &hosc_hdmi_clk.common.hw,
+ [CLK_HOSC_UFS] = &hosc_ufs_clk.common.hw,
+ [CLK_IOSC_32K] = &iosc_32k_clk.hw,
+ [CLK_EXT_OSC32K_GATE] = &ext_osc32k_gate_clk.common.hw,
+ [CLK_HOSC_32K_MUX] = &hosc_32k_mux_clk.common.hw,
+ [CLK_HOSC_32K] = &hosc_32k_clk.common.hw,
+ },
+};
+
+static const struct sunxi_ccu_desc sun60i_rtc_ccu_desc = {
+ .ccu_clks = sun60i_rtc_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun60i_rtc_ccu_clks),
+
+ .hw_clks = &sun60i_rtc_ccu_hw_clks,
+};
+
+static int sun60i_rtc_ccu_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct device *dev = &adev->dev;
+ void __iomem *reg = dev->platform_data;
+
+ return devm_sunxi_ccu_probe(dev, reg, &sun60i_rtc_ccu_desc);
+}
+
+static const struct auxiliary_device_id sun60i_ccu_rtc_ids[] = {
+ { .name = SUN6I_RTC_AUX_ID(sun60i) },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, sun60i_ccu_rtc_ids);
+
+static struct auxiliary_driver sun60i_ccu_rtc_driver = {
+ .probe = sun60i_rtc_ccu_probe,
+ .id_table = sun60i_ccu_rtc_ids,
+};
+module_auxiliary_driver(sun60i_ccu_rtc_driver);
+
+MODULE_IMPORT_NS("SUNXI_CCU");
+MODULE_DESCRIPTION("Support for the Allwinner A733 RTC CCU");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h
new file mode 100644
index 000000000000..41ec6195b5e7
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#ifndef _CCU_SUN60I_A733_RTC_H_
+#define _CCU_SUN60I_A733_RTC_H_
+
+#include <dt-bindings/clock/sun60i-a733-rtc.h>
+
+#define CLK_IOSC_32K 9
+#define CLK_EXT_OSC32K_GATE 10
+#define CLK_HOSC_32K_MUX 11
+#define CLK_HOSC_32K 12
+
+#define CLK_NUMBER (CLK_HOSC_32K + 1)
+
+#endif /* _CCU_SUN60I_A733_RTC_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu_rtc.h b/drivers/clk/sunxi-ng/ccu_rtc.h
index 1c44c2206a25..665162723796 100644
--- a/drivers/clk/sunxi-ng/ccu_rtc.h
+++ b/drivers/clk/sunxi-ng/ccu_rtc.h
@@ -27,8 +27,15 @@
#define LOSC_OUT_GATING_REG 0x60
#define DCXO_CTRL_REG 0x160
+#define DCXO_CTRL_DCXO_EN BIT(1)
#define DCXO_CTRL_CLK16M_RC_EN BIT(0)
+#define DCXO_GATING_REG 0x16c
+#define DCXO_SERDES1_GATING BIT(5)
+#define DCXO_SERDES0_GATING BIT(4)
+#define DCXO_HDMI_GATING BIT(1)
+#define DCXO_UFS_GATING BIT(0)
+
#define SUN6I_RTC_AUX_ID(_name) "rtc_sun6i." #_name
extern const struct clk_ops ccu_iosc_ops;
--
2.52.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH 7/7] clk: sunxi-ng: Add Allwinner A733 RTC CCU support
2026-01-21 10:59 ` [PATCH 7/7] clk: sunxi-ng: Add Allwinner A733 RTC CCU support Junhui Liu
@ 2026-03-28 14:41 ` Chen-Yu Tsai
0 siblings, 0 replies; 17+ messages in thread
From: Chen-Yu Tsai @ 2026-03-28 14:41 UTC (permalink / raw)
To: Junhui Liu
Cc: Michael Turquette, Stephen Boyd, Jernej Skrabec, Samuel Holland,
Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, linux-clk, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-rtc, devicetree, André Przywara
On Wed, Jan 21, 2026 at 7:04 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
> Add support for the internal CCU found in the RTC module of the Allwinner
> A733 SoC. While the basic 16MHz (IOSC) and 32kHz logic remains compatible
> with older SoCs like the sun6i, the A733 introduces several new features.
>
> The A733 RTC CCU supports choosing one of three external crystal
> frequencies: 19.2MHz, 24MHz, and 26MHz. It features hardware detection
> logic to automatically identify the frequency used on the board and
> exports this DCXO signal as the "hosc" clock.
>
> Furthermore, the driver implements logic to derive a 32kHz reference
> from the HOSC. This is achieved through a muxed clock path using fixed
> pre-dividers to normalize the different crystal frequencies to ~32kHz.
Have you tested whether the actually normalizes the frequency, i.e.
selects a different divider based on the DCXO frequency? Otherwise
we're just lying about the frequency.
> This path reuses the same hardware mux registers as the HOSC clock.
>
> Additionally, this CCU provides several gate clocks for specific
> peripherals, including SerDes, HDMI, and UFS. The driver is implemented
> as an auxiliary driver to be bound to the sun6i-rtc driver.
>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
> drivers/clk/sunxi-ng/Kconfig | 5 +
> drivers/clk/sunxi-ng/Makefile | 2 +
> drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c | 204 +++++++++++++++++++++++++++++
> drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h | 18 +++
> drivers/clk/sunxi-ng/ccu_rtc.h | 7 +
> 5 files changed, 236 insertions(+)
>
> diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
> index 6af2d020e03e..16afbf249f26 100644
> --- a/drivers/clk/sunxi-ng/Kconfig
> +++ b/drivers/clk/sunxi-ng/Kconfig
> @@ -67,6 +67,11 @@ config SUN55I_A523_R_CCU
> default ARCH_SUNXI
> depends on ARM64 || COMPILE_TEST
>
> +config SUN60I_A733_RTC_CCU
> + tristate "Support for the Allwinner A733 RTC CCU"
> + default ARCH_SUNXI
> + depends on ARM64 || COMPILE_TEST
> +
> config SUN4I_A10_CCU
> tristate "Support for the Allwinner A10/A20 CCU"
> default ARCH_SUNXI
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index c3f810a025a8..b0d823440c33 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o
> obj-$(CONFIG_SUN55I_A523_CCU) += sun55i-a523-ccu.o
> obj-$(CONFIG_SUN55I_A523_MCU_CCU) += sun55i-a523-mcu-ccu.o
> obj-$(CONFIG_SUN55I_A523_R_CCU) += sun55i-a523-r-ccu.o
> +obj-$(CONFIG_SUN60I_A733_RTC_CCU) += sun60i-a733-rtc-ccu.o
> obj-$(CONFIG_SUN4I_A10_CCU) += sun4i-a10-ccu.o
> obj-$(CONFIG_SUN5I_CCU) += sun5i-ccu.o
> obj-$(CONFIG_SUN6I_A31_CCU) += sun6i-a31-ccu.o
> @@ -67,6 +68,7 @@ sun50i-h616-ccu-y += ccu-sun50i-h616.o
> sun55i-a523-ccu-y += ccu-sun55i-a523.o
> sun55i-a523-mcu-ccu-y += ccu-sun55i-a523-mcu.o
> sun55i-a523-r-ccu-y += ccu-sun55i-a523-r.o
> +sun60i-a733-rtc-ccu-y += ccu-sun60i-a733-rtc.o
> sun4i-a10-ccu-y += ccu-sun4i-a10.o
> sun5i-ccu-y += ccu-sun5i.o
> sun6i-a31-ccu-y += ccu-sun6i-a31.o
> diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c
> new file mode 100644
> index 000000000000..d17aceffa16e
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.c
> @@ -0,0 +1,204 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/auxiliary_bus.h>
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/module.h>
> +
> +#include "ccu_common.h"
> +
> +#include "ccu_gate.h"
> +#include "ccu_mux.h"
> +#include "ccu_rtc.h"
> +
> +#include "ccu-sun60i-a733-rtc.h"
> +
> +static struct ccu_common iosc_clk = {
> + .reg = DCXO_CTRL_REG,
> + .features = CCU_FEATURE_IOSC_CALIBRATION,
> + .hw.init = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
> + CLK_GET_RATE_NOCACHE),
> +};
> +
> +static struct ccu_common iosc_32k_clk = {
> + .features = CCU_FEATURE_IOSC_CALIBRATION,
> + .hw.init = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
> + &ccu_iosc_32k_ops,
> + CLK_GET_RATE_NOCACHE),
> +};
> +
> +static SUNXI_CCU_GATE_FW(ext_osc32k_gate_clk, "ext-osc32k-gate",
> + "ext-osc32k", 0x0, BIT(4), 0);
> +
> +static const struct clk_hw *osc32k_parents[] = {
> + &iosc_32k_clk.hw,
> + &ext_osc32k_gate_clk.common.hw,
> +};
> +
> +static struct ccu_mux osc32k_clk = {
> + .mux = _SUNXI_CCU_MUX(0, 1),
> + .common = {
> + .reg = LOSC_CTRL_REG,
> + .features = CCU_FEATURE_KEY_FIELD,
> + .hw.init = CLK_HW_INIT_PARENTS_HW("osc32k",
> + osc32k_parents,
> + &ccu_mux_ops,
> + 0),
> + },
> +};
> +
> +static const struct clk_parent_data hosc_parents[] = {
> + { .fw_name = "osc24M" },
> + { .fw_name = "osc19M" },
> + { .fw_name = "osc26M" },
> + { .fw_name = "osc24M" },
> +};
As mentioned in my reply to the binding, this is wrong. There is only
one input.
The most you can do is check the rate of the parent clock against the
detected one, and _scream_ that the DT is wrong. And maybe override
the reported frequency.
If you want to do the latter, you could add a new fixed rate gated
clock type to our library. You would fill in the rate before the
clocks get registered. I probably wouldn't go that far. We want people
to have correct hardware descriptions.
Funnily enough Allwinner's BSP actually implements a fixed rate gate
for the next 24M-to-32k divider clock.
> +
> +struct ccu_mux hosc_clk = {
> + .enable = DCXO_CTRL_DCXO_EN,
> + .mux = _SUNXI_CCU_MUX(14, 2),
> + .common = {
> + .reg = DCXO_CTRL_REG,
> + .hw.init = CLK_HW_INIT_PARENTS_DATA("hosc",
> + hosc_parents,
> + &ccu_mux_ro_ops,
> + 0),
> + },
> +};
So this is wrong.
> +
> +static const struct ccu_mux_fixed_prediv hosc_32k_predivs[] = {
> + { .index = 0, .div = 732 },
Why is it 732 instead of 750?
> + { .index = 1, .div = 586 },
> + { .index = 2, .div = 793 },
> + { .index = 3, .div = 732 },
> +};
> +
> +static struct ccu_mux hosc_32k_mux_clk = {
> + .enable = DCXO_CTRL_DCXO_EN,
No. The parent "hosc" clock owns this. The enable bit for this clock
is actually bit 16 of LOSC_OUT_GATING_REG, which you model below as
a separate gate.
> + .mux = {
> + .shift = 14,
> + .width = 2,
> + .fixed_predivs = hosc_32k_predivs,
> + .n_predivs = ARRAY_SIZE(hosc_32k_predivs),
> + },
> + .common = {
> + .reg = DCXO_CTRL_REG,
> + .features = CCU_FEATURE_FIXED_PREDIV,
> + .hw.init = CLK_HW_INIT_PARENTS_DATA("hosc-32k-mux",
> + hosc_parents,
> + &ccu_mux_ro_ops,
Again, this is just not the way to do it.
> + 0),
> + },
> +};
I would test that it actually does switch dividers, Or at the very least,
it has a larger divider for 26M.
Maybe Andre can help? At least on this SoC the fanout pins are much more
accessible.
> +
> +static SUNXI_CCU_GATE_HW(hosc_32k_clk, "hosc-32k", &hosc_32k_mux_clk.common.hw,
> + LOSC_OUT_GATING_REG, BIT(16), 0);
> +
> +static const struct clk_hw *rtc_32k_parents[] = {
> + &osc32k_clk.common.hw,
> + &hosc_32k_clk.common.hw,
> +};
> +
> +static struct ccu_mux rtc_32k_clk = {
> + .mux = _SUNXI_CCU_MUX(1, 1),
> + .common = {
> + .reg = LOSC_CTRL_REG,
> + .features = CCU_FEATURE_KEY_FIELD,
> + .hw.init = CLK_HW_INIT_PARENTS_HW("rtc-32k",
> + rtc_32k_parents,
> + &ccu_mux_ops,
> + 0),
> + },
> +};
> +
> +static const struct clk_parent_data osc32k_fanout_parents[] = {
> + { .hw = &osc32k_clk.common.hw },
> + { .hw = &ext_osc32k_gate_clk.common.hw },
> + { .hw = &hosc_32k_clk.common.hw },
> +};
> +
> +static SUNXI_CCU_MUX_DATA_WITH_GATE(osc32k_fanout_clk, "osc32k-fanout", osc32k_fanout_parents,
> + LOSC_OUT_GATING_REG,
> + 1, 2, /* mux */
> + BIT(0), /* gate */
> + 0);
> +
> +static SUNXI_CCU_GATE_HW(hosc_serdes1_clk, "hosc-serdes1", &hosc_clk.common.hw,
> + DCXO_GATING_REG, DCXO_SERDES1_GATING, 0);
^
Just use the BIT() expression here. Adding these macros doesn't really help.
> +static SUNXI_CCU_GATE_HW(hosc_serdes0_clk, "hosc-serdes0", &hosc_clk.common.hw,
> + DCXO_GATING_REG, DCXO_SERDES0_GATING, 0);
> +static SUNXI_CCU_GATE_HW(hosc_hdmi_clk, "hosc-hdmi", &hosc_clk.common.hw,
> + DCXO_GATING_REG, DCXO_HDMI_GATING, 0);
> +static SUNXI_CCU_GATE_HW(hosc_ufs_clk, "hosc-ufs", &hosc_clk.common.hw,
> + DCXO_GATING_REG, DCXO_UFS_GATING, 0);
> +
> +static struct ccu_common *sun60i_rtc_ccu_clks[] = {
> + &iosc_clk,
> + &iosc_32k_clk,
> + &ext_osc32k_gate_clk.common,
> + &osc32k_clk.common,
> + &hosc_clk.common,
> + &hosc_32k_mux_clk.common,
> + &hosc_32k_clk.common,
> + &rtc_32k_clk.common,
> + &osc32k_fanout_clk.common,
> + &hosc_serdes1_clk.common,
> + &hosc_serdes0_clk.common,
> + &hosc_hdmi_clk.common,
> + &hosc_ufs_clk.common,
> +};
> +
> +static struct clk_hw_onecell_data sun60i_rtc_ccu_hw_clks = {
> + .num = CLK_NUMBER,
> + .hws = {
> + [CLK_IOSC] = &iosc_clk.hw,
> + [CLK_OSC32K] = &osc32k_clk.common.hw,
> + [CLK_HOSC] = &hosc_clk.common.hw,
> + [CLK_RTC_32K] = &rtc_32k_clk.common.hw,
> + [CLK_OSC32K_FANOUT] = &osc32k_fanout_clk.common.hw,
> + [CLK_HOSC_SERDES1] = &hosc_serdes1_clk.common.hw,
> + [CLK_HOSC_SERDES0] = &hosc_serdes0_clk.common.hw,
> + [CLK_HOSC_HDMI] = &hosc_hdmi_clk.common.hw,
> + [CLK_HOSC_UFS] = &hosc_ufs_clk.common.hw,
> + [CLK_IOSC_32K] = &iosc_32k_clk.hw,
> + [CLK_EXT_OSC32K_GATE] = &ext_osc32k_gate_clk.common.hw,
> + [CLK_HOSC_32K_MUX] = &hosc_32k_mux_clk.common.hw,
> + [CLK_HOSC_32K] = &hosc_32k_clk.common.hw,
> + },
> +};
> +
> +static const struct sunxi_ccu_desc sun60i_rtc_ccu_desc = {
> + .ccu_clks = sun60i_rtc_ccu_clks,
> + .num_ccu_clks = ARRAY_SIZE(sun60i_rtc_ccu_clks),
> +
> + .hw_clks = &sun60i_rtc_ccu_hw_clks,
> +};
> +
> +static int sun60i_rtc_ccu_probe(struct auxiliary_device *adev,
> + const struct auxiliary_device_id *id)
> +{
> + struct device *dev = &adev->dev;
> + void __iomem *reg = dev->platform_data;
> +
> + return devm_sunxi_ccu_probe(dev, reg, &sun60i_rtc_ccu_desc);
> +}
> +
> +static const struct auxiliary_device_id sun60i_ccu_rtc_ids[] = {
> + { .name = SUN6I_RTC_AUX_ID(sun60i) },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(auxiliary, sun60i_ccu_rtc_ids);
> +
> +static struct auxiliary_driver sun60i_ccu_rtc_driver = {
> + .probe = sun60i_rtc_ccu_probe,
> + .id_table = sun60i_ccu_rtc_ids,
> +};
> +module_auxiliary_driver(sun60i_ccu_rtc_driver);
> +
> +MODULE_IMPORT_NS("SUNXI_CCU");
> +MODULE_DESCRIPTION("Support for the Allwinner A733 RTC CCU");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h
> new file mode 100644
> index 000000000000..41ec6195b5e7
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733-rtc.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
> + */
> +
> +#ifndef _CCU_SUN60I_A733_RTC_H_
> +#define _CCU_SUN60I_A733_RTC_H_
> +
> +#include <dt-bindings/clock/sun60i-a733-rtc.h>
> +
> +#define CLK_IOSC_32K 9
> +#define CLK_EXT_OSC32K_GATE 10
> +#define CLK_HOSC_32K_MUX 11
> +#define CLK_HOSC_32K 12
> +
> +#define CLK_NUMBER (CLK_HOSC_32K + 1)
> +
> +#endif /* _CCU_SUN60I_A733_RTC_H_ */
> diff --git a/drivers/clk/sunxi-ng/ccu_rtc.h b/drivers/clk/sunxi-ng/ccu_rtc.h
> index 1c44c2206a25..665162723796 100644
> --- a/drivers/clk/sunxi-ng/ccu_rtc.h
> +++ b/drivers/clk/sunxi-ng/ccu_rtc.h
> @@ -27,8 +27,15 @@
> #define LOSC_OUT_GATING_REG 0x60
>
> #define DCXO_CTRL_REG 0x160
> +#define DCXO_CTRL_DCXO_EN BIT(1)
> #define DCXO_CTRL_CLK16M_RC_EN BIT(0)
>
> +#define DCXO_GATING_REG 0x16c
> +#define DCXO_SERDES1_GATING BIT(5)
> +#define DCXO_SERDES0_GATING BIT(4)
> +#define DCXO_HDMI_GATING BIT(1)
> +#define DCXO_UFS_GATING BIT(0)
Adding them to the header is probably even less useful, as the output
could change in future chips.
ChenYu
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC
2026-01-21 10:59 [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Junhui Liu
` (6 preceding siblings ...)
2026-01-21 10:59 ` [PATCH 7/7] clk: sunxi-ng: Add Allwinner A733 RTC CCU support Junhui Liu
@ 2026-02-22 10:41 ` Jernej Škrabec
2026-02-25 12:02 ` Junhui Liu
7 siblings, 1 reply; 17+ messages in thread
From: Jernej Škrabec @ 2026-02-22 10:41 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Chen-Yu Tsai, Samuel Holland,
Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, Junhui Liu
Cc: linux-clk, linux-arm-kernel, linux-sunxi, linux-kernel, linux-rtc,
devicetree, Junhui Liu
Hi!
Dne sreda, 21. januar 2026 ob 11:59:06 Srednjeevropski standardni čas je Junhui Liu napisal(a):
> Add support for the Allwinner A733 RTC and its internal Clock Control
> Unit (CCU). Reuse the rtc-sun6i rtc driver while introducing a new
> SoC-specific RTC CCU driver to handle the hardware's evolved clock
> structure.
>
> To facilitate this addition and improve driver modularity, transition
> the binding between the RTC and its internal CCU from direct
> cross-subsystem function calls to the auxiliary bus. Also extract shared
> IOSC and 32kHz clock logic into a standalone ccu_rtc module for reuse
> across newer SoC generations.
>
> The A733 implementation supports hardware detection of three external
> crystal frequencies (19.2MHz, 24MHz and 26MHz), which is represented in
> the driver via read-only mux operations. Implement logic to derive a
> normalized 32kHz reference from these DCXO sources using fixed
> pre-dividers. Additionally, provide several new DCXO gate clocks for
> peripherals, including SerDes, HDMI, and UFS.
This work looks nice, but I have some questions/comments:
- you're missing RTC SPI clock, which is needed for RTC, at least according
to vendor 5.15 DT. Could it be that this bit set by vendor U-Boot so you
missed it during testing? Manual says that it's disabled by default.
- Vendor DT has strange RTC CCU phandles for UFS and HDMI. In first case
uses RTC wakeup and in second DCXO, which doesn't make any sense. Did you
do any experimentation with these clocks? It wouldn't be the first time
that either code or manual contained some kind of error.
Btw, switch last two patches. With current order during bisection you would
get a complaint that A733 RTC CCU driver is not present.
Best regards,
Jernej
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC
2026-02-22 10:41 ` [PATCH 0/7] rtc: sun6i: Add support for Allwinner A733 SoC Jernej Škrabec
@ 2026-02-25 12:02 ` Junhui Liu
0 siblings, 0 replies; 17+ messages in thread
From: Junhui Liu @ 2026-02-25 12:02 UTC (permalink / raw)
To: Jernej Škrabec, Michael Turquette, Stephen Boyd,
Chen-Yu Tsai, Samuel Holland, Alexandre Belloni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Ripard, Junhui Liu
Cc: linux-clk, linux-arm-kernel, linux-sunxi, linux-kernel, linux-rtc,
devicetree
Hi Jernej,
Thanks for your review.
On Sun Feb 22, 2026 at 6:41 PM CST, Jernej Škrabec wrote:
> Hi!
>
> Dne sreda, 21. januar 2026 ob 11:59:06 Srednjeevropski standardni čas je Junhui Liu napisal(a):
>> Add support for the Allwinner A733 RTC and its internal Clock Control
>> Unit (CCU). Reuse the rtc-sun6i rtc driver while introducing a new
>> SoC-specific RTC CCU driver to handle the hardware's evolved clock
>> structure.
>>
>> To facilitate this addition and improve driver modularity, transition
>> the binding between the RTC and its internal CCU from direct
>> cross-subsystem function calls to the auxiliary bus. Also extract shared
>> IOSC and 32kHz clock logic into a standalone ccu_rtc module for reuse
>> across newer SoC generations.
>>
>> The A733 implementation supports hardware detection of three external
>> crystal frequencies (19.2MHz, 24MHz and 26MHz), which is represented in
>> the driver via read-only mux operations. Implement logic to derive a
>> normalized 32kHz reference from these DCXO sources using fixed
>> pre-dividers. Additionally, provide several new DCXO gate clocks for
>> peripherals, including SerDes, HDMI, and UFS.
>
> This work looks nice, but I have some questions/comments:
> - you're missing RTC SPI clock, which is needed for RTC, at least according
> to vendor 5.15 DT. Could it be that this bit set by vendor U-Boot so you
> missed it during testing? Manual says that it's disabled by default.
You're right! I tried disabling the RTC SPI clock in U-Boot and found
that the output of UART became garbled during booting kernel. I will add
it in the next version.
> - Vendor DT has strange RTC CCU phandles for UFS and HDMI. In first case
> uses RTC wakeup and in second DCXO, which doesn't make any sense. Did you
> do any experimentation with these clocks? It wouldn't be the first time
> that either code or manual contained some kind of error.
Regarding UFS, I am still working on getting UFS functional on the
mainline kernel. I will investigate the actual relationship between the
RTC wakeup clock and UFS during this process.
As for HDMI, I believe it actually requires the hosc_hdmi_clk
(DCXO_HDMI_GATING in the manual) provided by the RTC module.
>
> Btw, switch last two patches. With current order during bisection you would
> get a complaint that A733 RTC CCU driver is not present.
Okay, I will do it.
>
> Best regards,
> Jernej
--
Best regards,
Junhui Liu
^ permalink raw reply [flat|nested] 17+ messages in thread