Skip to contentSkip to conversation

clk: sunxi-ng: ccu_mp: fix clocks without P dividers

PATCH In review Enzo Adriano 5 participants 12 messages Jul 22 – Sep 23

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 clk: sunxi-ng: ccu_mp: fix clocks without P dividers

Some sunxi-ng MP clocks have an M divider but no P divider. The A523 MBUS, IOMMU and DRAM clocks use this layout and also require the update bit when changing their rate.

ccu_mp_set_rate() unconditionally builds and applies a mask for the P field. With a zero-width P field this produces an invalid GENMASK() range and can clear bits outside a P divider, including the clock gate.

The callback also ignores CCU_FEATURE_UPDATE_BIT, so hardware that requires the update bit may not latch the new divider value.

Only update the P field when it exists, and set CCU_SUNXI_UPDATE_BIT for MP clocks carrying the feature. This matches the existing sunxi-ng div, mux and gate helper behavior.

Fixes 6702d17f54a8 ("clk: sunxi-ng: a523: add video mod clocks")
Assisted-by Codex:gpt-5

---
Based on clk-next 8cdeaa50eae8 (Linux 7.2-rc2).
Tested with strict checkpatch and an arm64 W=1 build of ccu_mp.o. No hardware runtime claim is made.

 drivers/clk/sunxi-ng/ccu_mp.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)
drivers/clk/sunxi-ng/ccu_mp.c +10 -5
index 7cdb0eedc69b..aa6cb20447f1 100644
@@ -237,12 +237,17 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
 
 	reg = readl(cmp->common.base + cmp->common.reg);
 	reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift);
-	reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
+	if (cmp->p.width)
+		reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
+	if (cmp->common.features & CCU_FEATURE_UPDATE_BIT)
+		reg |= CCU_SUNXI_UPDATE_BIT;
 	reg |= (m - cmp->m.offset) << cmp->m.shift;
-	if (shift)
-		reg |= ilog2(p) << cmp->p.shift;
-	else
-		reg |= (p - cmp->p.offset) << cmp->p.shift;
+	if (cmp->p.width) {
+		if (shift)
+			reg |= ilog2(p) << cmp->p.shift;
+		else
+			reg |= (p - cmp->p.offset) << cmp->p.shift;
+	}
 
 	writel(reg, cmp->common.base + cmp->common.reg);
 
Signature · 1 line
2.53.0
#1 lore raw
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] The patch resolves an invalid GENMASK evaluation for 0-width P dividers but fails to apply the same check for 0-width M dividers, leaving a register corruption bug intact.
- [High] When an unreachable low clock rate is requested, ccu_mp_find_best fails to find a suitable divider and returns m = 0 and p = 0, leading to integer underflows that severely corrupt the CCU hardware register. --

--- Patch [1]: [PATCH] clk: sunxi-ng: ccu_mp: fix clocks without P dividers --- commit e8027d463bc82dfb3ec7ba4b45f1688fa59d1694
Author: Enzo Adriano <[email protected]>

clk: sunxi-ng: ccu_mp: fix clocks without P dividers

This commit modifies the sunxi-ng MP clock driver to conditionally apply the P divider mask only when the P field is present. It also ensures the update bit is set if required by the hardware. This prevents invalid GENMASK evaluations and missing hardware updates.

drivers/clk/sunxi-ng/ccu_mp.c static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, · line 239

Quoted 8 lines
drivers/clk/sunxi-ng/ccu_mp.c +0 -0
index 7cdb0eedc69b5..aa6cb20447f19 100644
@@ -237,12 +237,17 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
 
 	reg = readl(cmp->common.base + cmp->common.reg);
 	reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift);

[Severity: High]
This isn't a bug introduced by this patch, but since we are fixing the invalid GENMASK for 0-width P dividers, should we apply the same check for M dividers?

If an MP clock has no M divider (m.width == 0), the GENMASK will underflow and produce an all-ones mask. The ~GENMASK operation then zeroes out the entire hardware register, clearing critical fields like the clock gate.

drivers/clk/sunxi-ng/ccu_mp.c static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, · line 244

Quoted 6 lines
drivers/clk/sunxi-ng/ccu_mp.c +4 -1
-	reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
+	if (cmp->p.width)
+		reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
+	if (cmp->common.features & CCU_FEATURE_UPDATE_BIT)
+		reg |= CCU_SUNXI_UPDATE_BIT;
 	reg |= (m - cmp->m.offset) << cmp->m.shift;

[Severity: High]
This is a pre-existing issue, but can this operation corrupt the hardware register if an extremely low rate is requested?

If a driver or user requests a rate lower than the minimum achievable rate, ccu_mp_find_best() will skip all rates:

drivers/clk/sunxi-ng/ccu_mp.c:ccu_mp_find_best() {
    ...
	if (tmp_rate > rate)
		continue;
    ...
}

This causes it to return the initial values for m and p as 0. Back in ccu_mp_set_rate(), an m of 0 will cause m - cmp->m.offset to underflow (for example, 0 - 1 = 0xFFFFFFFF). This value is then left-shifted and OR'd into the register, overwriting adjacent hardware fields with 1s.

Quoted 12 lines
drivers/clk/sunxi-ng/ccu_mp.c +6 -4
-	if (shift)
-		reg |= ilog2(p) << cmp->p.shift;
-	else
-		reg |= (p - cmp->p.offset) << cmp->p.shift;
+	if (cmp->p.width) {
+		if (shift)
+			reg |= ilog2(p) << cmp->p.shift;
+		else
+			reg |= (p - cmp->p.offset) << cmp->p.shift;
+	}
 
 	writel(reg, cmp->common.base + cmp->common.reg);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
#2 lore raw
In reply to Enzo Adriano

Hi,

Quoted 20 lines · Enzo Adriano

Some sunxi-ng MP clocks have an M divider but no P divider. The A523 MBUS, IOMMU and DRAM clocks use this layout and also require the update bit when changing their rate.

ccu_mp_set_rate() unconditionally builds and applies a mask for the P field. With a zero-width P field this produces an invalid GENMASK() range and can clear bits outside a P divider, including the clock gate.

The callback also ignores CCU_FEATURE_UPDATE_BIT, so hardware that requires the update bit may not latch the new divider value.

Only update the P field when it exists, and set CCU_SUNXI_UPDATE_BIT for MP clocks carrying the feature. This matches the existing sunxi-ng div, mux and gate helper behavior.

Fixes 6702d17f54a8 ("clk: sunxi-ng: a523: add video mod clocks")
Assisted-by Codex:gpt-5
Quoted 4 lines

---
Based on clk-next 8cdeaa50eae8 (Linux 7.2-rc2).
Tested with strict checkpatch and an arm64 W=1 build of ccu_mp.o. No hardware runtime claim is made.

Does that mean it's not tested on hardware?

Quoted 13 lines
  drivers/clk/sunxi-ng/ccu_mp.c | 15 ++++++++++-----
  1 file changed, 10 insertions(+), 5 deletions(-)
drivers/clk/sunxi-ng/ccu_mp.c +0 -1
index 7cdb0eedc69b..aa6cb20447f1 100644
@@ -237,12 +237,17 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
 
  	reg = readl(cmp->common.base + cmp->common.reg);
  	reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift);
-	reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);

drivers/clk/sunxi-ng/ccu_mp.c static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, · line 245

Quoted 10 lines
drivers/clk/sunxi-ng/ccu_mp.c +5 -4
+	if (cmp->p.width)
+		reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
+	if (cmp->common.features & CCU_FEATURE_UPDATE_BIT)
+		reg |= CCU_SUNXI_UPDATE_BIT;
  	reg |= (m - cmp->m.offset) << cmp->m.shift;
-	if (shift)
-		reg |= ilog2(p) << cmp->p.shift;
-	else
-		reg |= (p - cmp->p.offset) << cmp->p.shift;
+	if (cmp->p.width) {

Can you merge that into the upper conditional branch? So that it's just one if statement?
And then apply the same treatment to the M divider, which is set to 0 by some A523 clocks (hstimer and r-timer).

Cheers,
Andre

Quoted 8 lines
drivers/clk/sunxi-ng/ccu_mp.c +5 -0
+		if (shift)
+			reg |= ilog2(p) << cmp->p.shift;
+		else
+			reg |= (p - cmp->p.offset) << cmp->p.shift;
+	}
 
  	writel(reg, cmp->common.base + cmp->common.reg);
 
maintainer
#3 lore raw
In reply to Enzo Adriano
Quoted 9 lines · Enzo Adriano

Some sunxi-ng MP clocks have an M divider but no P divider. The A523 MBUS, IOMMU and DRAM clocks use this layout and also require the update bit when changing their rate.

ccu_mp_set_rate() unconditionally builds and applies a mask for the P field. With a zero-width P field this produces an invalid GENMASK() range and can clear bits outside a P divider, including the clock gate.

Quoted 6 lines

The callback also ignores CCU_FEATURE_UPDATE_BIT, so hardware that requires the update bit may not latch the new divider value.

Only update the P field when it exists, and set CCU_SUNXI_UPDATE_BIT for MP clocks carrying the feature. This matches the existing sunxi-ng div, mux and gate helper behavior.

No. If the clock only has one divider, then it should use the single divider clk class. That one also supports CCU_FEATURE_UPDATE_BIT.

ChenYu

Quoted 42 lines
Fixes 6702d17f54a8 ("clk: sunxi-ng: a523: add video mod clocks")
Assisted-by Codex:gpt-5

---
Based on clk-next 8cdeaa50eae8 (Linux 7.2-rc2).
Tested with strict checkpatch and an arm64 W=1 build of ccu_mp.o. No hardware runtime claim is made.

 drivers/clk/sunxi-ng/ccu_mp.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)
drivers/clk/sunxi-ng/ccu_mp.c +10 -6
index 7cdb0eedc69b..aa6cb20447f1 100644
@@ -237,12 +237,17 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
 
        reg = readl(cmp->common.base + cmp->common.reg);
        reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift);
-       reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
+       if (cmp->p.width)
+               reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
+       if (cmp->common.features & CCU_FEATURE_UPDATE_BIT)
+               reg |= CCU_SUNXI_UPDATE_BIT;
        reg |= (m - cmp->m.offset) << cmp->m.shift;
-       if (shift)
-               reg |= ilog2(p) << cmp->p.shift;
-       else
-               reg |= (p - cmp->p.offset) << cmp->p.shift;
+       if (cmp->p.width) {
+               if (shift)
+                       reg |= ilog2(p) << cmp->p.shift;
+               else
+                       reg |= (p - cmp->p.offset) << cmp->p.shift;
+       }
 
        writel(reg, cmp->common.base + cmp->common.reg);
 
--

2.53.0

#4 lore raw

Hi Andre, Chen-Yu,

Correct, this was not tested on hardware; it was build-tested only.

Thanks for the review. Chen-Yu's point is right: these one-divider clocks should use the single-divider clock class, which already supports CCU_FEATURE_UPDATE_BIT. I'll drop this generic ccu_mp patch; there will be no v2.

This analysis was done with AI assistance and each finding was checked against the cited sources.

Regards,
Enzo

maintainer
#5 lore raw
Quoted 4 lines · Enzo Adriano

Hi Andre, Chen-Yu,

Correct, this was not tested on hardware; it was build-tested only.

For future patches, if you only build-tested it (which is a hard requirement), please mark the patches with "RFT" (so the subject prefix is "[PATCH RFT]") and note in the footer that it needs to be tested on actual hardware.

ChenYu

Quoted 10 lines

Thanks for the review. Chen-Yu's point is right: these one-divider clocks should use the single-divider clock class, which already supports CCU_FEATURE_UPDATE_BIT. I'll drop this generic ccu_mp patch; there will be no v2.

This analysis was done with AI assistance and each finding was checked against the cited sources.

Regards,
Enzo

#6 lore raw
In reply to Enzo Adriano
Quoted 8 lines · On Wed, 22 Jul 2026 13:37:14 -0400 Enzo Adriano <[email protected]>…

Hi Andre, Chen-Yu,

Correct, this was not tested on hardware; it was build-tested only.

Thanks for the review. Chen-Yu's point is right: these one-divider clocks should use the single-divider clock class, which already supports CCU_FEATURE_UPDATE_BIT. I'll drop this generic ccu_mp patch; there will be no v2.

But there clocks that use a _MP_ initialiser with width = 0, both for M and P. Which leads to GENMASK(-1, 0), as you mentioned. So we need patches to fix: to convert the HSTIMER clocks to use SUNXI_CCU_P_DATA_WITH_MUX_GATE, which is a pure div clock that doesn't have that problem. Same with the r-timer clocks in ccu-sun55i-a523-r.c. And there is mbus, iommu and dram that have no P.

Are you going to make and post patches for these?

Cheers,
Andre

Quoted 7 lines

This analysis was done with AI assistance and each finding was checked against the cited sources.

Regards,
Enzo

#7 lore raw

cover clk: sunxi-ng: fix A523/T527 single-divider clocks

Andre asked whether we would post the A523/T527 conversions for the clocks whose ccu_mp descriptors declare one divider field with width zero. This series implements those conversions.

Patch 1 adds a feature-aware M-divider helper so M-only clocks can retain the CCU update-bit handling. Patch 2 converts MBUS, IOMMU, and DRAM to that M-only class and converts HSTIMER0-5 and R-TIMER0-2 to the existing P-only class.

I do not have matching A523/T527 hardware, so this is deliberately an RFT series and carries no Tested-by claim. The series has passed semantic and register-bit preservation models, KUnit tests, strict checkpatch, sparse, GCC and Clang builds, full arm64 Image/modules/DTB builds, reproducibility checks, and hostile review. It also applies and builds cleanly together with the current A523/T527 GPU clock-model v2 series.

not tested on A523/T527 hardware; testing on actual hardware is needed.

In particular, testing of clock rate changes and affected users of MBUS, IOMMU, DRAM, HSTIMER0-5, and R-TIMER0-2 would be appreciated.

---

Enzo Adriano (2):
clk: sunxi-ng: add feature-aware M divider helper
clk: sunxi-ng: a523: use single-divider clock helpers

 drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c |  37 +++---
 drivers/clk/sunxi-ng/ccu-sun55i-a523.c   | 136 +++++++++++------------
 drivers/clk/sunxi-ng/ccu_div.h           |  18 ++-
 3 files changed, 96 insertions(+), 95 deletions(-)

base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda

Signature · 1 line
2.53.0
#8 lore raw

PATCH 1/2 clk: sunxi-ng: add feature-aware M divider helper

Allow single M-divider clocks to retain common CCU features such as the hardware update bit without using the two-divider MP class.

Assisted-by Codex:gpt-5

---
not tested on A523/T527 hardware; testing on actual hardware is needed.

 drivers/clk/sunxi-ng/ccu_div.h | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/clk/sunxi-ng/ccu_div.h +14 -4
index be00b3277e976f..fd222ef0b8af90 100644
@@ -212,10 +212,10 @@ struct ccu_div {
 	SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg,		\
 			      _mshift, _mwidth, 0, _flags)
 
-#define SUNXI_CCU_M_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg,	\
-				       _mshift, _mwidth,		\
-				       _muxshift, _muxwidth,		\
-				       _gate, _flags)			\
+#define SUNXI_CCU_M_DATA_WITH_MUX_GATE_FEAT(_struct, _name, _parents, _reg, \
+					    _mshift, _mwidth,		\
+					    _muxshift, _muxwidth,	\
+					    _gate, _flags, _features)	\
 	struct ccu_div _struct = {					\
 		.enable	= _gate,					\
 		.div	= _SUNXI_CCU_DIV(_mshift, _mwidth),		\
@@ -226,9 +226,19 @@ struct ccu_div {
 								   _parents, \
 								   &ccu_div_ops, \
 								   _flags), \
+			.features	= _features,			\
 		},							\
 	}
 
+#define SUNXI_CCU_M_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg,	\
+				       _mshift, _mwidth,		\
+				       _muxshift, _muxwidth,		\
+				       _gate, _flags)			\
+	SUNXI_CCU_M_DATA_WITH_MUX_GATE_FEAT(_struct, _name, _parents,	\
+					    _reg, _mshift, _mwidth,	\
+					    _muxshift, _muxwidth,	\
+					    _gate, _flags, 0)
+
 #define SUNXI_CCU_M_DATA_WITH_MUX(_struct, _name, _parents, _reg,	\
 				  _mshift, _mwidth,			\
 				  _muxshift, _muxwidth,			\
Signature · 1 line
2.53.0
#9 lore raw

PATCH 2/2 clk: sunxi-ng: a523: use single-divider clock helpers

In reply to Enzo Adriano

The A523 MBUS, IOMMU, and DRAM clocks have an M divider but no P divider, while HSTIMER0-5 and R-TIMER0-2 have a P divider but no M divider. They currently use the two-divider ccu_mp class by declaring the missing field with width zero.

ccu_mp_set_rate() unconditionally masks both divider fields, so a zero width forms an invalid GENMASK() range and can clear unrelated register bits. The M-only clocks also require the hardware update bit, which ccu_mp operations do not handle.

Use the single-divider M helper for MBUS, IOMMU, and DRAM, and the power-of-two P helper for the timer clocks. Preserve the parents, registers, real divider fields, muxes, gates, clock flags, and update-bit features.

Fixes 6702d17f54a8 ("clk: sunxi-ng: a523: add video mod clocks")
Fixes 74b0443a0d0a ("clk: sunxi-ng: a523: add system mod clocks")
Fixes 8cea339cfb81 ("clk: sunxi-ng: add support for the A523/T527 PRCM CCU")
Assisted-by Codex:gpt-5

---
not tested on A523/T527 hardware; testing on actual hardware is needed.

 drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c |  37 +++---
 drivers/clk/sunxi-ng/ccu-sun55i-a523.c   | 136 +++++++++++------------
 2 files changed, 82 insertions(+), 91 deletions(-)
drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c +17 -20
index db0e36d8838e74..349230e28c1c3a 100644
@@ -43,27 +43,24 @@ static SUNXI_CCU_M_DATA_WITH_MUX(r_apb1_clk, "r-apb1",
 				 24, 3,	/* mux */
 				 0);
 
-static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer0, "r-timer0",
-				       r_ahb_apb_parents, 0x100,
-				       0, 0,	/* no M */
-				       1, 3,	/* P */
-				       4, 3,	/* mux */
-				       BIT(0),
+static SUNXI_CCU_P_DATA_WITH_MUX_GATE(r_cpu_timer0, "r-timer0",
+				      r_ahb_apb_parents, 0x100,
+				      1, 3,	/* P */
+				      4, 3,	/* mux */
+				      BIT(0),
+				      0);
+static SUNXI_CCU_P_DATA_WITH_MUX_GATE(r_cpu_timer1, "r-timer1",
+				      r_ahb_apb_parents, 0x104,
+				      1, 3,	/* P */
+				      4, 3,	/* mux */
+				      BIT(0),
+				      0);
+static SUNXI_CCU_P_DATA_WITH_MUX_GATE(r_cpu_timer2, "r-timer2",
+				      r_ahb_apb_parents, 0x108,
+				      1, 3,	/* P */
+				      4, 3,	/* mux */
+				      BIT(0),
 				      0);
-static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer1, "r-timer1",
-				       r_ahb_apb_parents, 0x104,
-				       0, 0,	/* no M */
-				       1, 3,	/* P */
-				       4, 3,	/* mux */
-				       BIT(0),
-				       0);
-static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer2, "r-timer2",
-				       r_ahb_apb_parents, 0x108,
-				       0, 0,	/* no M */
-				       1, 3,	/* P */
-				       4, 3,	/* mux */
-				       BIT(0),
-				       0);
 
 static SUNXI_CCU_GATE_HW(bus_r_timer_clk, "bus-r-timer", &r_ahb_clk.common.hw,
 			 0x11c, BIT(0), 0);
drivers/clk/sunxi-ng/ccu-sun55i-a523.c +65 -71

165 lines not on this page · raw message

Signature · 1 line
2.53.0
#10 lore raw

Re: [PATCH RFT 0/2] clk: sunxi-ng: fix A523/T527 single-divider clocks

In reply to Enzo Adriano

Hi all,

This RFT series has been superseded by Junhui Liu's tested four-patch series:

https://lore.kernel.org/r/[email protected]

That series implements the M-only and P-only clock conversions, was tested on the Radxa Cubie A5E, and has been applied to sunxi/clk-for-7.4. Please disregard this RFT series.

Thanks to Junhui for completing and testing the fix.

Assisted-by Codex:gpt-5

I reviewed this reply and take responsibility for its contents.

Regards,
Enzo

#11 lore raw

Re: [PATCH RFT 0/2] clk: sunxi-ng: fix A523/T527 single-divider clocks

Hi Enzo,

Quoted 4 lines · Enzo Adriano

Hi all,

This RFT series has been superseded by Junhui Liu's tested four-patch series:

I just realized that you had already posted an equivalent fix before I sent mine. I have been quite busy recently and missed your series, so I ended up posting a duplicate. I am sorry about that.

I will make sure to check the mailing list more carefully before posting patches in the future.

Quoted 14 lines

https://lore.kernel.org/r/[email protected]

That series implements the M-only and P-only clock conversions, was tested on the Radxa Cubie A5E, and has been applied to sunxi/clk-for-7.4. Please disregard this RFT series.

Thanks to Junhui for completing and testing the fix.

Assisted-by Codex:gpt-5

I reviewed this reply and take responsibility for its contents.

Regards,
Enzo

Signature · 2 lines
Best regards,
Junhui Liu