From f43bed1fb1ddf7b350ceb7c371c624795a7820e8 Mon Sep 17 00:00:00 2001 From: Junhui Liu Date: Sun, 6 Sep 2026 15:06:38 +0800 Subject: [PATCH 1/5] clk: sunxi-ng: div: Add feature support to M clock macro Add a variant of SUNXI_CCU_M_DATA_WITH_MUX_GATE() that accepts CCU feature flags, such as CCU_FEATURE_UPDATE_BIT. Signed-off-by: Junhui Liu Link: https://patch.msgid.link/20260906-sunxi-clk-no-p-v1-1-cbde21c9fe69@pigmoral.tech Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_div.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h index a30a92780a05..6d3541a3b519 100644 --- a/drivers/clk/sunxi-ng/ccu_div.h +++ b/drivers/clk/sunxi-ng/ccu_div.h @@ -212,16 +212,17 @@ 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), \ .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \ .common = { \ .reg = _reg, \ + .features = _features, \ .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \ _parents, \ &ccu_div_ops, \ @@ -229,6 +230,15 @@ struct ccu_div { }, \ } +#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, \ From 59dced16a82d1ec685b88365c64dde27a0a0b46a Mon Sep 17 00:00:00 2001 From: Junhui Liu Date: Sun, 6 Sep 2026 15:06:39 +0800 Subject: [PATCH 2/5] clk: sunxi-ng: sun55i-a523-ccu: Use M-only clocks for MBUS, IOMMU and DRAM The MBUS, IOMMU, and DRAM clocks only have an M divider, but are currently modeled as MP clocks with a zero-width P divider. ccu_mp_set_rate() generates an invalid mask for the zero-width P divider, clearing unrelated register fields. It also ignores CCU_FEATURE_UPDATE_BIT, so the new divider value does not take effect. Model these clocks as M-only clocks instead. This avoids accessing a nonexistent P divider and uses ccu_div_set_rate(), which handles the update bit. Fixes: 74b0443a0d0a ("clk: sunxi-ng: a523: add system mod clocks") Signed-off-by: Junhui Liu Link: https://patch.msgid.link/20260906-sunxi-clk-no-p-v1-2-cbde21c9fe69@pigmoral.tech Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu-sun55i-a523.c | 45 ++++++++++++-------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c index 20dad06b37ca..9bc3ff0ab12d 100644 --- a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c +++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c @@ -380,14 +380,13 @@ static const struct clk_parent_data mbus_parents[] = { { .hw = &pll_periph1_150M_clk.hw }, { .fw_name = "hosc" }, }; -static SUNXI_CCU_MP_DATA_WITH_MUX_GATE_FEAT(mbus_clk, "mbus", mbus_parents, - 0x540, - 0, 5, /* M */ - 0, 0, /* no P */ - 24, 3, /* mux */ - BIT(31), /* gate */ - CLK_IS_CRITICAL, - CCU_FEATURE_UPDATE_BIT); +static SUNXI_CCU_M_DATA_WITH_MUX_GATE_FEAT(mbus_clk, "mbus", mbus_parents, + 0x540, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_IS_CRITICAL, + CCU_FEATURE_UPDATE_BIT); static const struct clk_hw *mbus_hws[] = { &mbus_clk.common.hw }; @@ -584,14 +583,13 @@ static const struct clk_parent_data iommu_parents[] = { { .fw_name = "hosc" }, }; -static SUNXI_CCU_MP_DATA_WITH_MUX_GATE_FEAT(iommu_clk, "iommu", iommu_parents, - 0x7b0, - 0, 5, /* M */ - 0, 0, /* no P */ - 24, 3, /* mux */ - BIT(31), /* gate */ - CLK_SET_RATE_PARENT, - CCU_FEATURE_UPDATE_BIT); +static SUNXI_CCU_M_DATA_WITH_MUX_GATE_FEAT(iommu_clk, "iommu", iommu_parents, + 0x7b0, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT, + CCU_FEATURE_UPDATE_BIT); static SUNXI_CCU_GATE_HWS(bus_iommu_clk, "bus-iommu", apb0_hws, 0x7bc, BIT(0), 0); @@ -603,14 +601,13 @@ static const struct clk_parent_data dram_parents[] = { { .hw = &pll_periph0_400M_clk.hw }, { .hw = &pll_periph0_150M_clk.hw }, }; -static SUNXI_CCU_MP_DATA_WITH_MUX_GATE_FEAT(dram_clk, "dram", dram_parents, - 0x800, - 0, 5, /* M */ - 0, 0, /* no P */ - 24, 3, /* mux */ - BIT(31), /* gate */ - CLK_IS_CRITICAL, - CCU_FEATURE_UPDATE_BIT); +static SUNXI_CCU_M_DATA_WITH_MUX_GATE_FEAT(dram_clk, "dram", dram_parents, + 0x800, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_IS_CRITICAL, + CCU_FEATURE_UPDATE_BIT); static SUNXI_CCU_GATE_HWS(mbus_dma_clk, "mbus-dma", mbus_hws, 0x804, BIT(0), 0); From f2aee33d06d5e1363596ae166459aa4354a8b5ae Mon Sep 17 00:00:00 2001 From: Junhui Liu Date: Sun, 6 Sep 2026 15:06:40 +0800 Subject: [PATCH 3/5] clk: sunxi-ng: sun55i-a523-ccu: Use P-only clocks for high-speed timers The high-speed timer clocks only have a P divider, but are currently modeled as MP clocks with a zero-width M divider. ccu_mp_set_rate() generates an invalid mask for the zero-width M divider, clearing unrelated register fields. Model these clocks as P-only clocks to avoid accessing a nonexistent M divider. Fixes: 74b0443a0d0a ("clk: sunxi-ng: a523: add system mod clocks") Signed-off-by: Junhui Liu Link: https://patch.msgid.link/20260906-sunxi-clk-no-p-v1-3-cbde21c9fe69@pigmoral.tech Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu-sun55i-a523.c | 83 +++++++++++--------------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c index 9bc3ff0ab12d..0714090cc3b0 100644 --- a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c +++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c @@ -512,58 +512,47 @@ static const struct clk_parent_data hstimer_parents[] = { { .fw_name = "losc" }, { .hw = &pll_periph0_200M_clk.hw }, }; -static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(hstimer0_clk, "hstimer0", - hstimer_parents, 0x730, - 0, 0, /* M */ - 0, 3, /* P */ - 24, 3, /* mux */ - BIT(31), /* gate */ - 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(hstimer0_clk, "hstimer0", + hstimer_parents, 0x730, + 0, 3, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); -static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(hstimer1_clk, "hstimer1", - hstimer_parents, - 0x734, - 0, 0, /* M */ - 0, 3, /* P */ - 24, 3, /* mux */ - BIT(31), /* gate */ - 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(hstimer1_clk, "hstimer1", + hstimer_parents, 0x734, + 0, 3, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); -static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(hstimer2_clk, "hstimer2", - hstimer_parents, - 0x738, - 0, 0, /* M */ - 0, 3, /* P */ - 24, 3, /* mux */ - BIT(31), /* gate */ - 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(hstimer2_clk, "hstimer2", + hstimer_parents, 0x738, + 0, 3, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); -static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(hstimer3_clk, "hstimer3", - hstimer_parents, - 0x73c, - 0, 0, /* M */ - 0, 3, /* P */ - 24, 3, /* mux */ - BIT(31), /* gate */ - 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(hstimer3_clk, "hstimer3", + hstimer_parents, 0x73c, + 0, 3, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); -static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(hstimer4_clk, "hstimer4", - hstimer_parents, - 0x740, - 0, 0, /* M */ - 0, 3, /* P */ - 24, 3, /* mux */ - BIT(31), /* gate */ - 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(hstimer4_clk, "hstimer4", + hstimer_parents, 0x740, + 0, 3, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); -static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(hstimer5_clk, "hstimer5", - hstimer_parents, - 0x744, - 0, 0, /* M */ - 0, 3, /* P */ - 24, 3, /* mux */ - BIT(31), /* gate */ - 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(hstimer5_clk, "hstimer5", + hstimer_parents, 0x744, + 0, 3, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); static SUNXI_CCU_GATE_HWS(bus_hstimer_clk, "bus-hstimer", ahb_hws, 0x74c, BIT(0), 0); From 1002cc9d60942f8f294d8de7f2805f82f4ebdda0 Mon Sep 17 00:00:00 2001 From: Junhui Liu Date: Sun, 6 Sep 2026 15:06:41 +0800 Subject: [PATCH 4/5] clk: sunxi-ng: sun55i-a523-r-ccu: Use P-only clocks for timers The R timer clocks only have a P divider, but are currently modeled as MP clocks with a zero-width M divider. ccu_mp_set_rate() generates an invalid mask for the zero-width M divider, clearing unrelated register fields. Model these clocks as P-only clocks to avoid accessing a nonexistent M divider. Fixes: 8cea339cfb81 ("clk: sunxi-ng: add support for the A523/T527 PRCM CCU") Signed-off-by: Junhui Liu Link: https://patch.msgid.link/20260906-sunxi-clk-no-p-v1-4-cbde21c9fe69@pigmoral.tech Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c | 37 +++++++++++------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c index db0e36d8838e..349230e28c1c 100644 --- a/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c @@ -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); From 9ef62592b80c7ffb8cd987d889f659c1dc4f9f16 Mon Sep 17 00:00:00 2001 From: Alastair D'Silva Date: Wed, 16 Sep 2026 14:29:10 +1000 Subject: [PATCH 5/5] clk: sunxi-ng: ccu_common: Use readl_relaxed_poll_timeout_atomic for PLL lock The Allwinner sunxi-ng CCU driver currently uses readl_relaxed_poll_timeout() in ccu_helper_wait_for_lock() to wait for PLLs to lock. This non-atomic poll macro relies on ktime_get() and the kernel timekeeping infrastructure. During early boot or sensitive clock transitions (such as CPUfreq DVFS scaling) where timer interrupts or timekeeping may be suspended or unstable, ktime_get() fails to advance. This turns the timeout calculation into an infinite busy-wait loop, causing RCU stalls and system freezes if the PLL lock bit does not assert immediately. Switch to readl_relaxed_poll_timeout_atomic(). This atomic variant relies on udelay() rather than timekeeping, making it safe to execute even when timekeeping is suspended or uninitialized. Assisted-by: LLM Signed-off-by: Alastair D'Silva Link: https://patch.msgid.link/20260916042910.382744-1-alastair@d-silva.org Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c index 43d8eca6abee..248e58c9562d 100644 --- a/drivers/clk/sunxi-ng/ccu_common.c +++ b/drivers/clk/sunxi-ng/ccu_common.c @@ -35,7 +35,7 @@ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) else addr = common->base + common->reg; - WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000)); + WARN_ON(readl_relaxed_poll_timeout_atomic(addr, reg, reg & lock, 100, 70000)); } EXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock, "SUNXI_CCU");