[v2,6/6] clk: sunxi-ng: sun6i-rtc: add a733 support

Message ID 20260629-a733-rtc-v2-6-7b72112784f8@baylibre.com (mailing list archive)
State New
Headers
Series clk: sun6i-rtc: Add support for Allwinner A733 SoC |

Commit Message

Jerome Brunet June 29, 2026, 12:42 p.m. UTC
Add support for the sun60i a733 CCU RTC.

Compared to the a523, this SoC has a different input oscillator divider
which auto-detects the oscillator rate and select a divider to provide
a fixed 32768Hz clock. It also provides several phy reference clocks
with dedicated clock gates.

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 74 ++++++++++++++++++++++++++++++++++--
 drivers/clk/sunxi-ng/ccu-sun6i-rtc.h |  2 +-
 2 files changed, 72 insertions(+), 4 deletions(-)
  

Comments

sashiko-bot@kernel.org June 29, 2026, 1:15 p.m. UTC | #1
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] The master DCXO enable bit is incorrectly mapped as the exclusive enable bit for the 32kHz divider. This will cause the clock framework to inadvertently turn off the master oscillator, freezing the system.
--

commit 83ebb861eeea7934e5df335b0d1620cad0b733bf
Author: Jerome Brunet <jbrunet@baylibre.com>

clk: sunxi-ng: sun6i-rtc: add a733 support

This commit adds support for the sun60i a733 CCU RTC. It introduces a
new auto-detecting oscillator divider and several phy reference clocks
with dedicated clock gates.

> diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> index 25dd87e78eb7a..2ec09e3464724 100644
> --- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> +++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c

[ ... ]

> @@ -227,8 +237,28 @@ static struct clk_fixed_factor osc24M_32k_div_clk = {
>  					    0),
>  };
>  
> -static SUNXI_CCU_GATE_HW(osc24M_32k_clk, "osc24M-32k", &osc24M_32k_div_clk.hw,
> -			 LOSC_OUT_GATING_REG, BIT(16), 0);
> +static struct clk_div_table osc24M_32k_div_a733_table[] = {
> +	{ .val = 0, .div = 732 },
> +	{ .val = 1, .div = 586 },
> +	{ .val = 2, .div = 793 },
> +	{ .val = 3, .div = 732 },
> +	{ /* Sentinel */ },
> +};
> +
> +static struct ccu_div osc24M_32k_div_a733_clk = {
> +	.enable = BIT(1),

[Severity: Critical]
This maps the master DCXO enable bit, BIT(1) defined earlier as
DCXO_CTRL_DCXO_EN, directly to the 32kHz divider's lifecycle.

If a board uses an external 32kHz crystal, the rtc-32k clock mux is
reparented to osc32k_clk, leaving this internal osc24M-32k-div clock with
zero active consumers.

During late boot, the clock framework's clk_disable_unused() routine walks
the clock tree and will disable this clock, clearing BIT(1) in DCXO_CTRL_REG.

Could this cause a hard system freeze? Since this bit physically turns off
the shared master 24MHz crystal (DCXO), does it immediately stop the CPU,
PLLs, and all peripherals?

> +	.div	= _SUNXI_CCU_DIV_TABLE(14, 2, osc24M_32k_div_a733_table),
> +	.common	= {
> +		.reg		= DCXO_CTRL_REG,
> +		.hw.init	= CLK_HW_INIT_PARENTS_DATA("osc24M-32k-div",
> +							   osc24M,
> +							   &ccu_rodiv_ops,
> +							   0),
> +	},
> +};
  
Jerome Brunet June 29, 2026, 2:24 p.m. UTC | #2
On lun. 29 juin 2026 at 13:15, sashiko-bot@kernel.org wrote:

> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Critical] The master DCXO enable bit is incorrectly mapped as the
> exclusive enable bit for the 32kHz divider. This will cause the clock
> framework to inadvertently turn off the master oscillator, freezing the
> system.
> --
>
> commit 83ebb861eeea7934e5df335b0d1620cad0b733bf
> Author: Jerome Brunet <jbrunet@baylibre.com>
>
> clk: sunxi-ng: sun6i-rtc: add a733 support
>
> This commit adds support for the sun60i a733 CCU RTC. It introduces a
> new auto-detecting oscillator divider and several phy reference clocks
> with dedicated clock gates.
>
>> diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
>> index 25dd87e78eb7a..2ec09e3464724 100644
>> --- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
>> +++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
>
> [ ... ]
>
>> @@ -227,8 +237,28 @@ static struct clk_fixed_factor osc24M_32k_div_clk = {
>>  					    0),
>>  };
>>  
>> -static SUNXI_CCU_GATE_HW(osc24M_32k_clk, "osc24M-32k", &osc24M_32k_div_clk.hw,
>> -			 LOSC_OUT_GATING_REG, BIT(16), 0);
>> +static struct clk_div_table osc24M_32k_div_a733_table[] = {
>> +	{ .val = 0, .div = 732 },
>> +	{ .val = 1, .div = 586 },
>> +	{ .val = 2, .div = 793 },
>> +	{ .val = 3, .div = 732 },
>> +	{ /* Sentinel */ },
>> +};
>> +
>> +static struct ccu_div osc24M_32k_div_a733_clk = {
>> +	.enable = BIT(1),
>
> [Severity: Critical]
> This maps the master DCXO enable bit, BIT(1) defined earlier as
> DCXO_CTRL_DCXO_EN, directly to the 32kHz divider's lifecycle.

Forgot to remove that unused define

>
> If a board uses an external 32kHz crystal, the rtc-32k clock mux is
> reparented to osc32k_clk, leaving this internal osc24M-32k-div clock with
> zero active consumers.
>
> During late boot, the clock framework's clk_disable_unused() routine walks
> the clock tree and will disable this clock, clearing BIT(1) in DCXO_CTRL_REG.
>
> Could this cause a hard system freeze? Since this bit physically turns off
> the shared master 24MHz crystal (DCXO), does it immediately stop the CPU,
> PLLs, and all peripherals?

I don't quite get what the AI is trying to report here. If there are no
user, the clock will be gated ... so ? Am I missing something ?

>
>> +	.div	= _SUNXI_CCU_DIV_TABLE(14, 2, osc24M_32k_div_a733_table),
>> +	.common	= {
>> +		.reg		= DCXO_CTRL_REG,
>> +		.hw.init	= CLK_HW_INIT_PARENTS_DATA("osc24M-32k-div",
>> +							   osc24M,
>> +							   &ccu_rodiv_ops,
>> +							   0),
>> +	},
>> +};
  
Chen-Yu Tsai June 29, 2026, 3:15 p.m. UTC | #3
On Mon, Jun 29, 2026 at 10:25 PM Jerome Brunet <jbrunet@baylibre.com> wrote:
>
> On lun. 29 juin 2026 at 13:15, sashiko-bot@kernel.org wrote:
>
> > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > - [Critical] The master DCXO enable bit is incorrectly mapped as the
> > exclusive enable bit for the 32kHz divider. This will cause the clock
> > framework to inadvertently turn off the master oscillator, freezing the
> > system.
> > --
> >
> > commit 83ebb861eeea7934e5df335b0d1620cad0b733bf
> > Author: Jerome Brunet <jbrunet@baylibre.com>
> >
> > clk: sunxi-ng: sun6i-rtc: add a733 support
> >
> > This commit adds support for the sun60i a733 CCU RTC. It introduces a
> > new auto-detecting oscillator divider and several phy reference clocks
> > with dedicated clock gates.
> >
> >> diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> >> index 25dd87e78eb7a..2ec09e3464724 100644
> >> --- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> >> +++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> >
> > [ ... ]
> >
> >> @@ -227,8 +237,28 @@ static struct clk_fixed_factor osc24M_32k_div_clk = {
> >>                                          0),
> >>  };
> >>
> >> -static SUNXI_CCU_GATE_HW(osc24M_32k_clk, "osc24M-32k", &osc24M_32k_div_clk.hw,
> >> -                     LOSC_OUT_GATING_REG, BIT(16), 0);
> >> +static struct clk_div_table osc24M_32k_div_a733_table[] = {
> >> +    { .val = 0, .div = 732 },
> >> +    { .val = 1, .div = 586 },
> >> +    { .val = 2, .div = 793 },
> >> +    { .val = 3, .div = 732 },
> >> +    { /* Sentinel */ },
> >> +};
> >> +
> >> +static struct ccu_div osc24M_32k_div_a733_clk = {
> >> +    .enable = BIT(1),
> >
> > [Severity: Critical]
> > This maps the master DCXO enable bit, BIT(1) defined earlier as
> > DCXO_CTRL_DCXO_EN, directly to the 32kHz divider's lifecycle.
>
> Forgot to remove that unused define
>
> >
> > If a board uses an external 32kHz crystal, the rtc-32k clock mux is
> > reparented to osc32k_clk, leaving this internal osc24M-32k-div clock with
> > zero active consumers.
> >
> > During late boot, the clock framework's clk_disable_unused() routine walks
> > the clock tree and will disable this clock, clearing BIT(1) in DCXO_CTRL_REG.
> >
> > Could this cause a hard system freeze? Since this bit physically turns off
> > the shared master 24MHz crystal (DCXO), does it immediately stop the CPU,
> > PLLs, and all peripherals?
>
> I don't quite get what the AI is trying to report here. If there are no
> user, the clock will be gated ... so ? Am I missing something ?

I think it's just getting confused. Since you said it was unused and you
plan to remove it, I would just ignore the AI.


ChenYu

> >
> >> +    .div    = _SUNXI_CCU_DIV_TABLE(14, 2, osc24M_32k_div_a733_table),
> >> +    .common = {
> >> +            .reg            = DCXO_CTRL_REG,
> >> +            .hw.init        = CLK_HW_INIT_PARENTS_DATA("osc24M-32k-div",
> >> +                                                       osc24M,
> >> +                                                       &ccu_rodiv_ops,
> >> +                                                       0),
> >> +    },
> >> +};
>
> --
> Jerome
>
  

Patch

diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
index 25dd87e78eb7..2ec09e346472 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
@@ -42,11 +42,16 @@ 
 #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
+
 struct sun6i_rtc_match_data {
 	bool				have_ext_osc32k		: 1;
 	bool				have_iosc_calibration	: 1;
+	bool				have_dcxo_status	: 1;
+	bool				have_phy_ref_gates	: 1;
 	bool				rtc_32k_single_parent	: 1;
 	const struct clk_parent_data	*osc32k_fanout_parents;
 	u8				osc32k_fanout_nparents;
@@ -213,7 +218,12 @@  static struct ccu_mux osc32k_clk = {
 	},
 };
 
-/* This falls back to the global name for fwnodes without a named reference. */
+/*
+ * This falls back to the global name for fwnodes without a named reference.
+ * NOTE: osc24M name might be misleading the oscillator could also be a 26MHz
+ * or a 19.2MHz one starting with the a733. The original name is kept anyway
+ * in case anything is relying on it.
+ */
 static const struct clk_parent_data osc24M[] = {
 	{ .fw_name = "hosc", .name = "osc24M" }
 };
@@ -227,8 +237,28 @@  static struct clk_fixed_factor osc24M_32k_div_clk = {
 					    0),
 };
 
-static SUNXI_CCU_GATE_HW(osc24M_32k_clk, "osc24M-32k", &osc24M_32k_div_clk.hw,
-			 LOSC_OUT_GATING_REG, BIT(16), 0);
+static struct clk_div_table osc24M_32k_div_a733_table[] = {
+	{ .val = 0, .div = 732 },
+	{ .val = 1, .div = 586 },
+	{ .val = 2, .div = 793 },
+	{ .val = 3, .div = 732 },
+	{ /* Sentinel */ },
+};
+
+static struct ccu_div osc24M_32k_div_a733_clk = {
+	.enable = BIT(1),
+	.div	= _SUNXI_CCU_DIV_TABLE(14, 2, osc24M_32k_div_a733_table),
+	.common	= {
+		.reg		= DCXO_CTRL_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_DATA("osc24M-32k-div",
+							   osc24M,
+							   &ccu_rodiv_ops,
+							   0),
+	},
+};
+
+static SUNXI_CCU_GATE(osc24M_32k_clk, "osc24M-32k", "osc24M-32k-div",
+		      LOSC_OUT_GATING_REG, BIT(16), 0);
 
 static const struct clk_hw *rtc_32k_parents[] = {
 	&osc32k_clk.common.hw,
@@ -267,6 +297,15 @@  static struct ccu_mux osc32k_fanout_clk = {
 	},
 };
 
+static SUNXI_CCU_GATE_FW(hosc_serdes1_clk, "hosc-serdes1", "hosc",
+		      DCXO_GATING_REG, BIT(5), 0);
+static SUNXI_CCU_GATE_FW(hosc_serdes0_clk, "hosc-serdes0", "hosc",
+		      DCXO_GATING_REG, BIT(4), 0);
+static SUNXI_CCU_GATE_FW(hosc_hdmi_clk, "hosc-hdmi", "hosc",
+		      DCXO_GATING_REG, BIT(1), 0);
+static SUNXI_CCU_GATE_FW(hosc_ufs_clk, "hosc-ufs", "hosc",
+		      DCXO_GATING_REG, BIT(0), 0);
+
 static struct ccu_common *sun6i_rtc_ccu_clks[] = {
 	&iosc_clk,
 	&iosc_32k_clk,
@@ -275,6 +314,11 @@  static struct ccu_common *sun6i_rtc_ccu_clks[] = {
 	&osc24M_32k_clk.common,
 	&rtc_32k_clk.common,
 	&osc32k_fanout_clk.common,
+	&osc24M_32k_div_a733_clk.common,
+	&hosc_serdes1_clk.common,
+	&hosc_serdes0_clk.common,
+	&hosc_hdmi_clk.common,
+	&hosc_ufs_clk.common,
 };
 
 static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = {
@@ -288,6 +332,10 @@  static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = {
 		[CLK_OSC24M_32K]	= &osc24M_32k_clk.common.hw,
 		[CLK_RTC_32K]		= &rtc_32k_clk.common.hw,
 		[CLK_OSC24M_32K_DIV]	= &osc24M_32k_div_clk.hw,
+		[CLK_HOSC_UFS]		= &hosc_ufs_clk.common.hw,
+		[CLK_HOSC_HDMI]		= &hosc_hdmi_clk.common.hw,
+		[CLK_HOSC_SERDES0]	= &hosc_serdes0_clk.common.hw,
+		[CLK_HOSC_SERDES1]	= &hosc_serdes1_clk.common.hw,
 	},
 };
 
@@ -330,6 +378,15 @@  static const struct sun6i_rtc_match_data sun55i_a523_rtc_ccu_data = {
 	.osc32k_fanout_nparents	= ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents),
 };
 
+static const struct sun6i_rtc_match_data sun60i_a733_rtc_ccu_data = {
+	.have_ext_osc32k	= true,
+	.have_iosc_calibration	= true,
+	.have_dcxo_status	= true,
+	.have_phy_ref_gates	= true,
+	.osc32k_fanout_parents	= sun50i_r329_osc32k_fanout_parents,
+	.osc32k_fanout_nparents	= ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents),
+};
+
 static const struct of_device_id sun6i_rtc_ccu_match[] = {
 	{
 		.compatible	= "allwinner,sun50i-h616-rtc",
@@ -343,6 +400,10 @@  static const struct of_device_id sun6i_rtc_ccu_match[] = {
 		.compatible	= "allwinner,sun55i-a523-rtc",
 		.data		= &sun55i_a523_rtc_ccu_data,
 	},
+	{
+		.compatible	= "allwinner,sun60i-a733-rtc",
+		.data		= &sun60i_a733_rtc_ccu_data,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, sun6i_rtc_ccu_match);
@@ -375,6 +436,13 @@  int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
 	osc32k_fanout_init_data.parent_data = data->osc32k_fanout_parents;
 	osc32k_fanout_init_data.num_parents = data->osc32k_fanout_nparents;
 
+	if (data->have_dcxo_status)
+		sun6i_rtc_ccu_hw_clks.hws[CLK_OSC24M_32K_DIV] =
+			&osc24M_32k_div_a733_clk.common.hw;
+
+	if (!data->have_phy_ref_gates)
+		sun6i_rtc_ccu_hw_clks.num = CLK_OSC24M_32K_DIV + 1;
+
 	return devm_sunxi_ccu_probe(dev, reg, &sun6i_rtc_ccu_desc);
 }
 
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
index ab7b92b47f59..4f4f4cb00f1d 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
@@ -11,6 +11,6 @@ 
 #define CLK_RTC_32K		6
 #define CLK_OSC24M_32K_DIV	7
 
-#define CLK_NUMBER		(CLK_OSC24M_32K_DIV + 1)
+#define CLK_NUMBER		(CLK_HOSC_SERDES1 + 1)
 
 #endif /* _CCU_SUN6I_RTC_H */