* [PATCH v6 1/4] clk: sunxi-ng: mux: fix determine helper rate propagation
2026-07-22 14:48 [PATCH v6 0/4] clk: sun6i-rtc: Add support for Allwinner A733 SoC Jerome Brunet
@ 2026-07-22 14:48 ` Jerome Brunet
2026-07-22 15:07 ` sashiko-bot
2026-07-22 14:48 ` [PATCH v6 2/4] clk: sunxi-ng: div: add read-only operation support Jerome Brunet
` (2 subsequent siblings)
3 siblings, 1 reply; 11+ messages in thread
From: Jerome Brunet @ 2026-07-22 14:48 UTC (permalink / raw)
To: Junhui Liu, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Michael Turquette, Stephen Boyd, Maxime Ripard
Cc: linux-rtc, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-clk, Jerome Brunet
Applying the pre divider on the parent rate is wrong because, while
handling rate propagation through determine_rate(), the framework will
likely round the parent rate again while cycling through the possibilities,
throwing away the prediv applied. This means, the parent rate will then
be wrong when the prediv is unapplied from a parent rate on which it
was never applied to begin with.
The right way to do it is to unapply the prediv from the requested rate,
which is the wanted rate at the input on the clock element, and pass this
to framework to do its thing.
Change the determine rate mux helper in this way.
Fixes: 1c8d7af61b37 ("clk: sunxi-ng: convert from divider_round_rate_parent() to divider_determine_rate()")
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
drivers/clk/sunxi-ng/ccu_mux.c | 57 +++++++++++++++++++++---------------------
1 file changed, 28 insertions(+), 29 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index 09230728c400..4503c9780c39 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -92,66 +92,65 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
struct clk_rate_request adj_req = *req;
best_parent = clk_hw_get_parent(hw);
- best_parent_rate = clk_hw_get_rate(best_parent);
-
+ adj_req.best_parent_rate = clk_hw_get_rate(best_parent);
adj_req.best_parent_hw = best_parent;
- adj_req.best_parent_rate = ccu_mux_helper_apply_prediv(common, cm, -1,
- best_parent_rate);
+
+ /*
+ * This effectively treats the predivider as a postdivider.
+ * It stays mathematically correct and ensures whatever
+ * round() will do stays correct while walking the tree.
+ * It may query the parent rate too while handling rate
+ * propagation.
+ */
+ adj_req.rate = ccu_mux_helper_unapply_prediv(common, cm, -1,
+ req->rate);
ret = round(cm, &adj_req, data);
if (ret)
return ret;
- best_rate = adj_req.rate;
-
/*
- * best_parent_rate might have been modified by our clock.
- * Unapply the pre-divider if there's one, and give
- * the actual frequency the parent needs to run at.
+ * parent_rate might have been modified by our clock as part
+ * of the rate propagation mechanism. Same goes below.
*/
- best_parent_rate = ccu_mux_helper_unapply_prediv(common, cm, -1,
- adj_req.best_parent_rate);
+ best_parent_rate = adj_req.best_parent_rate;
+ best_rate = ccu_mux_helper_apply_prediv(common, cm, -1,
+ adj_req.rate);
goto out;
}
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
struct clk_rate_request tmp_req = *req;
- unsigned long parent_rate;
+ unsigned long rate;
struct clk_hw *parent;
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
- parent_rate = ccu_mux_helper_apply_prediv(common, cm, i,
- clk_hw_get_rate(parent));
-
tmp_req.best_parent_hw = parent;
- tmp_req.best_parent_rate = parent_rate;
+ tmp_req.best_parent_rate = clk_hw_get_rate(parent);
+ tmp_req.rate = ccu_mux_helper_unapply_prediv(common, cm, i,
+ req->rate);
ret = round(cm, &tmp_req, data);
if (ret)
continue;
- /*
- * parent_rate might have been modified by our clock.
- * Unapply the pre-divider if there's one, and give
- * the actual frequency the parent needs to run at.
- */
- parent_rate = ccu_mux_helper_unapply_prediv(common, cm, i,
- tmp_req.best_parent_rate);
+ rate = ccu_mux_helper_apply_prediv(common, cm, i,
+ tmp_req.rate);
- if (tmp_req.rate == req->rate) {
+ if (rate == req->rate) {
best_parent = parent;
- best_parent_rate = parent_rate;
- best_rate = tmp_req.rate;
+ best_parent_rate = tmp_req.best_parent_rate;
+ best_rate = rate;
goto out;
}
- if (ccu_is_better_rate(common, req->rate, tmp_req.rate, best_rate)) {
- best_rate = tmp_req.rate;
- best_parent_rate = parent_rate;
+ if (ccu_is_better_rate(common, req->rate, rate, best_rate)) {
+ best_rate = rate;
+ best_parent_rate = tmp_req.best_parent_rate;
best_parent = parent;
}
}
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v6 1/4] clk: sunxi-ng: mux: fix determine helper rate propagation
2026-07-22 14:48 ` [PATCH v6 1/4] clk: sunxi-ng: mux: fix determine helper rate propagation Jerome Brunet
@ 2026-07-22 15:07 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-22 15:07 UTC (permalink / raw)
To: Jerome Brunet
Cc: robh, linux-sunxi, devicetree, linux-rtc, Alexandre Belloni,
conor+dt
> Applying the pre divider on the parent rate is wrong because, while
> handling rate propagation through determine_rate(), the framework will
> likely round the parent rate again while cycling through the possibilities,
> throwing away the prediv applied. This means, the parent rate will then
> be wrong when the prediv is unapplied from a parent rate on which it
> was never applied to begin with.
>
> The right way to do it is to unapply the prediv from the requested rate,
> which is the wanted rate at the input on the clock element, and pass this
> to framework to do its thing.
>
> Change the determine rate mux helper in this way.
>
> Fixes: 1c8d7af61b37 ("clk: sunxi-ng: convert from divider_round_rate_parent() to divider_determine_rate()")
> Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260722-a733-rtc-v6-0-631cca28cc9a@baylibre.com?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v6 2/4] clk: sunxi-ng: div: add read-only operation support
2026-07-22 14:48 [PATCH v6 0/4] clk: sun6i-rtc: Add support for Allwinner A733 SoC Jerome Brunet
2026-07-22 14:48 ` [PATCH v6 1/4] clk: sunxi-ng: mux: fix determine helper rate propagation Jerome Brunet
@ 2026-07-22 14:48 ` Jerome Brunet
2026-07-22 15:00 ` sashiko-bot
2026-07-22 15:40 ` Chen-Yu Tsai
2026-07-22 14:48 ` [PATCH v6 3/4] clk: sunxi-ng: sun6i-rtc: split main oscillator div and gate Jerome Brunet
2026-07-22 14:48 ` [PATCH v6 4/4] clk: sunxi-ng: sun6i-rtc: add a733 support Jerome Brunet
3 siblings, 2 replies; 11+ messages in thread
From: Jerome Brunet @ 2026-07-22 14:48 UTC (permalink / raw)
To: Junhui Liu, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Michael Turquette, Stephen Boyd, Maxime Ripard
Cc: linux-rtc, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-clk, Jerome Brunet
Add support for sunxi-ng read-only dividers. This will be
useful to the a733 oscillator detection logic.
Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
drivers/clk/sunxi-ng/ccu_div.c | 31 +++++++++++++++++++++++++++++--
drivers/clk/sunxi-ng/ccu_div.h | 1 +
drivers/clk/sunxi-ng/ccu_mux.c | 2 +-
drivers/clk/sunxi-ng/ccu_mux.h | 4 ++++
4 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c
index 62d680ccb524..c385e0160f13 100644
--- a/drivers/clk/sunxi-ng/ccu_div.c
+++ b/drivers/clk/sunxi-ng/ccu_div.c
@@ -20,8 +20,22 @@ static int ccu_div_determine_rate_helper(struct ccu_mux_internal *mux,
if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
req->rate *= cd->fixed_post_div;
- ret = divider_determine_rate(&cd->common.hw, req, cd->div.table,
- cd->div.width, cd->div.flags);
+ if (cd->div.flags & CLK_DIVIDER_READ_ONLY) {
+ unsigned long val;
+ u32 reg;
+
+ reg = readl(cd->common.base + cd->common.reg);
+ val = reg >> cd->div.shift;
+ val &= (1 << cd->div.width) - 1;
+
+ ret = divider_ro_determine_rate(&cd->common.hw, req, cd->div.table,
+ cd->div.width, cd->div.flags, val);
+
+ } else {
+ ret = divider_determine_rate(&cd->common.hw, req, cd->div.table,
+ cd->div.width, cd->div.flags);
+ }
+
if (ret)
return ret;
@@ -143,3 +157,16 @@ const struct clk_ops ccu_div_ops = {
.set_rate = ccu_div_set_rate,
};
EXPORT_SYMBOL_NS_GPL(ccu_div_ops, "SUNXI_CCU");
+
+const struct clk_ops ccu_rodiv_ops = {
+ .disable = ccu_div_disable,
+ .enable = ccu_div_enable,
+ .is_enabled = ccu_div_is_enabled,
+
+ .get_parent = ccu_div_get_parent,
+ .set_parent = ccu_div_set_parent,
+
+ .determine_rate = ccu_div_determine_rate,
+ .recalc_rate = ccu_div_recalc_rate,
+};
+EXPORT_SYMBOL_NS_GPL(ccu_rodiv_ops, "SUNXI_CCU");
diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h
index be00b3277e97..a30a92780a05 100644
--- a/drivers/clk/sunxi-ng/ccu_div.h
+++ b/drivers/clk/sunxi-ng/ccu_div.h
@@ -300,5 +300,6 @@ static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw)
}
extern const struct clk_ops ccu_div_ops;
+extern const struct clk_ops ccu_rodiv_ops;
#endif /* _CCU_DIV_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index 4503c9780c39..12bfc99c9172 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -67,7 +67,7 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
}
-static unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
+unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
struct ccu_mux_internal *cm,
int parent_index,
unsigned long parent_rate)
diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h
index c94a4bde5d01..272a2c36a8f2 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.h
+++ b/drivers/clk/sunxi-ng/ccu_mux.h
@@ -134,6 +134,10 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
struct ccu_mux_internal *cm,
int parent_index,
unsigned long parent_rate);
+unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index,
+ unsigned long parent_rate);
int ccu_mux_helper_determine_rate(struct ccu_common *common,
struct ccu_mux_internal *cm,
struct clk_rate_request *req,
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v6 2/4] clk: sunxi-ng: div: add read-only operation support
2026-07-22 14:48 ` [PATCH v6 2/4] clk: sunxi-ng: div: add read-only operation support Jerome Brunet
@ 2026-07-22 15:00 ` sashiko-bot
2026-07-22 15:40 ` Chen-Yu Tsai
1 sibling, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-22 15:00 UTC (permalink / raw)
To: Jerome Brunet
Cc: Alexandre Belloni, devicetree, linux-rtc, linux-sunxi, conor+dt,
robh
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Potential division by zero due to unvalidated hardware register value propagating to `divider_ro_determine_rate`.
- [Low] Undocumented API exposure and dead code due to exporting `ccu_mux_helper_unapply_prediv`.
--
--- Patch [2]: [PATCH v6 2/4] clk: sunxi-ng: div: add read-only operation support ---
commit 14ce0824b93b836a325dbcab101b0319dddbe86a
Author: Jerome Brunet <jbrunet@baylibre.com>
clk: sunxi-ng: div: add read-only operation support
Add support for sunxi-ng read-only dividers. This will be
useful to the a733 oscillator detection logic.
> diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c
> index 62d680ccb524b..c385e0160f13b 100644
> --- a/drivers/clk/sunxi-ng/ccu_div.c
> +++ b/drivers/clk/sunxi-ng/ccu_div.c
> @@ -20,8 +20,22 @@ static int ccu_div_determine_rate_helper(struct ccu_mux_internal *mux,
> if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
> req->rate *= cd->fixed_post_div;
>
> - ret = divider_determine_rate(&cd->common.hw, req, cd->div.table,
> - cd->div.width, cd->div.flags);
> + if (cd->div.flags & CLK_DIVIDER_READ_ONLY) {
> + unsigned long val;
> + u32 reg;
> +
> + reg = readl(cd->common.base + cd->common.reg);
> + val = reg >> cd->div.shift;
> + val &= (1 << cd->div.width) - 1;
> +
> + ret = divider_ro_determine_rate(&cd->common.hw, req, cd->div.table,
> + cd->div.width, cd->div.flags, val);
[Severity: High]
Can this lead to a division by zero in divider_ro_determine_rate()?
When ccu_div_determine_rate_helper() reads the hardware register and passes
the unvalidated value to divider_ro_determine_rate(), _get_div() could
return 0 if the register value is 0 (with CLK_DIVIDER_ONE_BASED) or if the
value is not found in the table.
Unlike divider_recalc_rate(), divider_ro_determine_rate() does not have an
if (!div) check:
drivers/clk/clk-divider.c:divider_ro_determine_rate() {
...
div = _get_div(table, val, flags, width);
...
req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div);
...
}
Will this cause a panic if the hardware is in an unexpected reset state or
misconfigured?
[ ... ]
> diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
> index 4503c9780c39f..12bfc99c91721 100644
> --- a/drivers/clk/sunxi-ng/ccu_mux.c
> +++ b/drivers/clk/sunxi-ng/ccu_mux.c
> @@ -67,7 +67,7 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
> return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
> }
>
> -static unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
> +unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
> struct ccu_mux_internal *cm,
> int parent_index,
> unsigned long parent_rate)
[Severity: Low]
Is this function intended to be exported?
The commit message only mentions adding support for read-only operation in
sunxi-ng dividers and provides no justification for modifying mux helpers.
Additionally, ccu_mux_helper_unapply_prediv appears to be completely unused
outside of ccu_mux.c in this patch series. Could this change be an
undocumented API export that accidentally leaked from earlier development?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260722-a733-rtc-v6-0-631cca28cc9a@baylibre.com?part=2
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v6 2/4] clk: sunxi-ng: div: add read-only operation support
2026-07-22 14:48 ` [PATCH v6 2/4] clk: sunxi-ng: div: add read-only operation support Jerome Brunet
2026-07-22 15:00 ` sashiko-bot
@ 2026-07-22 15:40 ` Chen-Yu Tsai
2026-07-22 16:28 ` Jerome Brunet
1 sibling, 1 reply; 11+ messages in thread
From: Chen-Yu Tsai @ 2026-07-22 15:40 UTC (permalink / raw)
To: Jerome Brunet
Cc: Junhui Liu, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jernej Skrabec, Samuel Holland, Michael Turquette,
Stephen Boyd, Maxime Ripard, linux-rtc, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel, linux-clk
On Wed, Jul 22, 2026 at 10:48 PM Jerome Brunet <jbrunet@baylibre.com> wrote:
>
> Add support for sunxi-ng read-only dividers. This will be
> useful to the a733 oscillator detection logic.
>
> Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
> Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
> ---
> drivers/clk/sunxi-ng/ccu_div.c | 31 +++++++++++++++++++++++++++++--
> drivers/clk/sunxi-ng/ccu_div.h | 1 +
> drivers/clk/sunxi-ng/ccu_mux.c | 2 +-
> drivers/clk/sunxi-ng/ccu_mux.h | 4 ++++
Left over stuff?
> 4 files changed, 35 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c
> index 62d680ccb524..c385e0160f13 100644
> --- a/drivers/clk/sunxi-ng/ccu_div.c
> +++ b/drivers/clk/sunxi-ng/ccu_div.c
> @@ -20,8 +20,22 @@ static int ccu_div_determine_rate_helper(struct ccu_mux_internal *mux,
> if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
> req->rate *= cd->fixed_post_div;
>
> - ret = divider_determine_rate(&cd->common.hw, req, cd->div.table,
> - cd->div.width, cd->div.flags);
> + if (cd->div.flags & CLK_DIVIDER_READ_ONLY) {
> + unsigned long val;
> + u32 reg;
> +
> + reg = readl(cd->common.base + cd->common.reg);
> + val = reg >> cd->div.shift;
> + val &= (1 << cd->div.width) - 1;
> +
> + ret = divider_ro_determine_rate(&cd->common.hw, req, cd->div.table,
> + cd->div.width, cd->div.flags, val);
> +
> + } else {
> + ret = divider_determine_rate(&cd->common.hw, req, cd->div.table,
> + cd->div.width, cd->div.flags);
> + }
> +
> if (ret)
> return ret;
>
> @@ -143,3 +157,16 @@ const struct clk_ops ccu_div_ops = {
> .set_rate = ccu_div_set_rate,
> };
> EXPORT_SYMBOL_NS_GPL(ccu_div_ops, "SUNXI_CCU");
> +
> +const struct clk_ops ccu_rodiv_ops = {
> + .disable = ccu_div_disable,
> + .enable = ccu_div_enable,
> + .is_enabled = ccu_div_is_enabled,
> +
> + .get_parent = ccu_div_get_parent,
> + .set_parent = ccu_div_set_parent,
> +
> + .determine_rate = ccu_div_determine_rate,
> + .recalc_rate = ccu_div_recalc_rate,
> +};
> +EXPORT_SYMBOL_NS_GPL(ccu_rodiv_ops, "SUNXI_CCU");
> diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h
> index be00b3277e97..a30a92780a05 100644
> --- a/drivers/clk/sunxi-ng/ccu_div.h
> +++ b/drivers/clk/sunxi-ng/ccu_div.h
> @@ -300,5 +300,6 @@ static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw)
> }
>
> extern const struct clk_ops ccu_div_ops;
> +extern const struct clk_ops ccu_rodiv_ops;
>
> #endif /* _CCU_DIV_H_ */
> diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
> index 4503c9780c39..12bfc99c9172 100644
> --- a/drivers/clk/sunxi-ng/ccu_mux.c
> +++ b/drivers/clk/sunxi-ng/ccu_mux.c
> @@ -67,7 +67,7 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
> return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
> }
>
> -static unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
> +unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
> struct ccu_mux_internal *cm,
> int parent_index,
> unsigned long parent_rate)
> diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h
> index c94a4bde5d01..272a2c36a8f2 100644
> --- a/drivers/clk/sunxi-ng/ccu_mux.h
> +++ b/drivers/clk/sunxi-ng/ccu_mux.h
> @@ -134,6 +134,10 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
> struct ccu_mux_internal *cm,
> int parent_index,
> unsigned long parent_rate);
> +unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
> + struct ccu_mux_internal *cm,
> + int parent_index,
> + unsigned long parent_rate);
> int ccu_mux_helper_determine_rate(struct ccu_common *common,
> struct ccu_mux_internal *cm,
> struct clk_rate_request *req,
>
> --
> 2.47.3
>
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v6 2/4] clk: sunxi-ng: div: add read-only operation support
2026-07-22 15:40 ` Chen-Yu Tsai
@ 2026-07-22 16:28 ` Jerome Brunet
0 siblings, 0 replies; 11+ messages in thread
From: Jerome Brunet @ 2026-07-22 16:28 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Junhui Liu, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jernej Skrabec, Samuel Holland, Michael Turquette,
Stephen Boyd, Maxime Ripard, linux-rtc, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel, linux-clk
On mer. 22 juil. 2026 at 23:40, Chen-Yu Tsai <wens@kernel.org> wrote:
> On Wed, Jul 22, 2026 at 10:48 PM Jerome Brunet <jbrunet@baylibre.com> wrote:
>>
>> Add support for sunxi-ng read-only dividers. This will be
>> useful to the a733 oscillator detection logic.
>>
>> Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
>> Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
>> ---
>> drivers/clk/sunxi-ng/ccu_div.c | 31 +++++++++++++++++++++++++++++--
>> drivers/clk/sunxi-ng/ccu_div.h | 1 +
>
>> drivers/clk/sunxi-ng/ccu_mux.c | 2 +-
>> drivers/clk/sunxi-ng/ccu_mux.h | 4 ++++
>
> Left over stuff?
>
Argh indeed, that's no longer necessary
>
>> 4 files changed, 35 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c
>> index 62d680ccb524..c385e0160f13 100644
>> --- a/drivers/clk/sunxi-ng/ccu_div.c
>> +++ b/drivers/clk/sunxi-ng/ccu_div.c
>> @@ -20,8 +20,22 @@ static int ccu_div_determine_rate_helper(struct ccu_mux_internal *mux,
>> if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
>> req->rate *= cd->fixed_post_div;
>>
>> - ret = divider_determine_rate(&cd->common.hw, req, cd->div.table,
>> - cd->div.width, cd->div.flags);
>> + if (cd->div.flags & CLK_DIVIDER_READ_ONLY) {
>> + unsigned long val;
>> + u32 reg;
>> +
>> + reg = readl(cd->common.base + cd->common.reg);
>> + val = reg >> cd->div.shift;
>> + val &= (1 << cd->div.width) - 1;
>> +
>> + ret = divider_ro_determine_rate(&cd->common.hw, req, cd->div.table,
>> + cd->div.width, cd->div.flags, val);
>> +
>> + } else {
>> + ret = divider_determine_rate(&cd->common.hw, req, cd->div.table,
>> + cd->div.width, cd->div.flags);
>> + }
>> +
>> if (ret)
>> return ret;
>>
>> @@ -143,3 +157,16 @@ const struct clk_ops ccu_div_ops = {
>> .set_rate = ccu_div_set_rate,
>> };
>> EXPORT_SYMBOL_NS_GPL(ccu_div_ops, "SUNXI_CCU");
>> +
>> +const struct clk_ops ccu_rodiv_ops = {
>> + .disable = ccu_div_disable,
>> + .enable = ccu_div_enable,
>> + .is_enabled = ccu_div_is_enabled,
>> +
>> + .get_parent = ccu_div_get_parent,
>> + .set_parent = ccu_div_set_parent,
>> +
>> + .determine_rate = ccu_div_determine_rate,
>> + .recalc_rate = ccu_div_recalc_rate,
>> +};
>> +EXPORT_SYMBOL_NS_GPL(ccu_rodiv_ops, "SUNXI_CCU");
>> diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h
>> index be00b3277e97..a30a92780a05 100644
>> --- a/drivers/clk/sunxi-ng/ccu_div.h
>> +++ b/drivers/clk/sunxi-ng/ccu_div.h
>> @@ -300,5 +300,6 @@ static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw)
>> }
>>
>> extern const struct clk_ops ccu_div_ops;
>> +extern const struct clk_ops ccu_rodiv_ops;
>>
>> #endif /* _CCU_DIV_H_ */
>> diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
>> index 4503c9780c39..12bfc99c9172 100644
>> --- a/drivers/clk/sunxi-ng/ccu_mux.c
>> +++ b/drivers/clk/sunxi-ng/ccu_mux.c
>> @@ -67,7 +67,7 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
>> return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
>> }
>>
>> -static unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
>> +unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
>> struct ccu_mux_internal *cm,
>> int parent_index,
>> unsigned long parent_rate)
>> diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h
>> index c94a4bde5d01..272a2c36a8f2 100644
>> --- a/drivers/clk/sunxi-ng/ccu_mux.h
>> +++ b/drivers/clk/sunxi-ng/ccu_mux.h
>> @@ -134,6 +134,10 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
>> struct ccu_mux_internal *cm,
>> int parent_index,
>> unsigned long parent_rate);
>> +unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
>> + struct ccu_mux_internal *cm,
>> + int parent_index,
>> + unsigned long parent_rate);
>> int ccu_mux_helper_determine_rate(struct ccu_common *common,
>> struct ccu_mux_internal *cm,
>> struct clk_rate_request *req,
>>
>> --
>> 2.47.3
>>
--
Jerome
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v6 3/4] clk: sunxi-ng: sun6i-rtc: split main oscillator div and gate
2026-07-22 14:48 [PATCH v6 0/4] clk: sun6i-rtc: Add support for Allwinner A733 SoC Jerome Brunet
2026-07-22 14:48 ` [PATCH v6 1/4] clk: sunxi-ng: mux: fix determine helper rate propagation Jerome Brunet
2026-07-22 14:48 ` [PATCH v6 2/4] clk: sunxi-ng: div: add read-only operation support Jerome Brunet
@ 2026-07-22 14:48 ` Jerome Brunet
2026-07-22 14:57 ` sashiko-bot
2026-07-22 14:48 ` [PATCH v6 4/4] clk: sunxi-ng: sun6i-rtc: add a733 support Jerome Brunet
3 siblings, 1 reply; 11+ messages in thread
From: Jerome Brunet @ 2026-07-22 14:48 UTC (permalink / raw)
To: Junhui Liu, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Michael Turquette, Stephen Boyd, Maxime Ripard
Cc: linux-rtc, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-clk, Jerome Brunet
On the a733 the "osc24M-32k" clock has the same gate bits as the previously
supported SoC but a different divider implementation.
Instead of a fixed 750 divider, the divider is selected based on the
rate of the oscillator. It can be seen as a simple read-only divider.
On the A733 the gate and the divider are on different register which is
not supported by sunxi combined clocks. These element will be split on the
A733. Split them for the older SoC as well to keep on sharing the gate
definition.
Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 20 +++++++++++---------
drivers/clk/sunxi-ng/ccu-sun6i-rtc.h | 3 ++-
2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
index b24c8b196e66..25dd87e78eb7 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
@@ -218,17 +218,18 @@ static const struct clk_parent_data osc24M[] = {
{ .fw_name = "hosc", .name = "osc24M" }
};
-static struct ccu_gate osc24M_32k_clk = {
- .enable = BIT(16),
- .common = {
- .reg = LOSC_OUT_GATING_REG,
- .prediv = 750,
- .features = CCU_FEATURE_ALL_PREDIV,
- .hw.init = CLK_HW_INIT_PARENTS_DATA("osc24M-32k", osc24M,
- &ccu_gate_ops, 0),
- },
+static struct clk_fixed_factor osc24M_32k_div_clk = {
+ .mult = 1,
+ .div = 750,
+ .hw.init = CLK_HW_INIT_PARENTS_DATA("osc24M-32k-div",
+ osc24M,
+ &clk_fixed_factor_ops,
+ 0),
};
+static SUNXI_CCU_GATE_HW(osc24M_32k_clk, "osc24M-32k", &osc24M_32k_div_clk.hw,
+ LOSC_OUT_GATING_REG, BIT(16), 0);
+
static const struct clk_hw *rtc_32k_parents[] = {
&osc32k_clk.common.hw,
&osc24M_32k_clk.common.hw
@@ -286,6 +287,7 @@ static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = {
[CLK_EXT_OSC32K_GATE] = &ext_osc32k_gate_clk.common.hw,
[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,
},
};
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
index 9ae821fc2599..ab7b92b47f59 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
@@ -9,7 +9,8 @@
#define CLK_EXT_OSC32K_GATE 4
#define CLK_OSC24M_32K 5
#define CLK_RTC_32K 6
+#define CLK_OSC24M_32K_DIV 7
-#define CLK_NUMBER (CLK_RTC_32K + 1)
+#define CLK_NUMBER (CLK_OSC24M_32K_DIV + 1)
#endif /* _CCU_SUN6I_RTC_H */
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v6 4/4] clk: sunxi-ng: sun6i-rtc: add a733 support
2026-07-22 14:48 [PATCH v6 0/4] clk: sun6i-rtc: Add support for Allwinner A733 SoC Jerome Brunet
` (2 preceding siblings ...)
2026-07-22 14:48 ` [PATCH v6 3/4] clk: sunxi-ng: sun6i-rtc: split main oscillator div and gate Jerome Brunet
@ 2026-07-22 14:48 ` Jerome Brunet
2026-07-22 14:57 ` sashiko-bot
3 siblings, 1 reply; 11+ messages in thread
From: Jerome Brunet @ 2026-07-22 14:48 UTC (permalink / raw)
To: Junhui Liu, Alexandre Belloni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Michael Turquette, Stephen Boyd, Maxime Ripard
Cc: linux-rtc, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-clk, Jerome Brunet
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.
Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 77 ++++++++++++++++++++++++++++++++++--
drivers/clk/sunxi-ng/ccu-sun6i-rtc.h | 2 +-
2 files changed, 75 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
index 25dd87e78eb7..98ae10307f43 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
@@ -44,9 +44,15 @@
#define DCXO_CTRL_REG 0x160
#define DCXO_CTRL_CLK16M_RC_EN BIT(0)
+#define DCXO_GATING_REG 0x16c
+
+#define CLK_NUMBER_NO_GATES (CLK_OSC24M_32K_DIV + 1)
+
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 +219,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 +238,30 @@ 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_FLAGS(14, 2,
+ osc24M_32k_div_a733_table,
+ CLK_DIVIDER_READ_ONLY),
+ .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 +300,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 +317,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 +335,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 +381,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 +403,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 +439,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_NUMBER_NO_GATES;
+
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 */
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread