[v4,6/9] clk: sunxi-ng: div: add read-only operation support
Commit Message
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 | 42 ++++++++++++++++++++++++++++++++++++++++++
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, 48 insertions(+), 1 deletion(-)
Comments
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Inconsistent rate calculation between `determine_rate` and `recalc_rate` due to division order.
--
commit 39f872462c04ad724c3624b152234160de56a47e
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..d1c8c7baa12dc 100644
> --- a/drivers/clk/sunxi-ng/ccu_div.c
> +++ b/drivers/clk/sunxi-ng/ccu_div.c
[ ... ]
> @@ -84,6 +84,36 @@ static int ccu_div_determine_rate(struct clk_hw *hw,
> req, ccu_div_determine_rate_helper, cd);
> }
>
> +static int ccu_rodiv_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
> + struct ccu_div *cd = hw_to_ccu_div(hw);
> + unsigned long val;
> + u32 reg;
> + int ret;
> +
> + reg = readl(cd->common.base + cd->common.reg);
> + val = reg >> cd->div.shift;
> + val &= (1 << cd->div.width) - 1;
> +
> + req->rate = ccu_mux_helper_unapply_prediv(&cd->common, &cd->mux, -1,
> + req->rate);
> +
> + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
> + req->rate *= cd->fixed_post_div;
> +
> + ret = divider_ro_determine_rate(hw, req, cd->div.table,
> + cd->div.width, cd->div.flags, val);
> +
> + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
> + req->rate /= cd->fixed_post_div;
> +
> + req->rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
> + req->rate);
[Severity: Medium]
Will this division order cause inconsistent rate calculations compared to
ccu_div_recalc_rate()?
The divider_ro_determine_rate() helper applies DIV_ROUND_UP() to the unadjusted
parent rate, and then this function divides that result by fixed_post_div and
prediv.
However, ccu_div_recalc_rate() applies prediv to the parent rate first, then
calculates the divider rate, and finally divides by fixed_post_div.
Since the Common Clock Framework requires determine_rate and recalc_rate to be
mathematically consistent, could this discrepancy cause rate mismatches or
warnings for clocks using a pre-divider or fixed post-divider?
> +
> + return ret;
> +}
> +
On lun. 06 juil. 2026 at 10:06, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] Inconsistent rate calculation between `determine_rate` and `recalc_rate` due to division order.
> --
>
> commit 39f872462c04ad724c3624b152234160de56a47e
> 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..d1c8c7baa12dc 100644
>> --- a/drivers/clk/sunxi-ng/ccu_div.c
>> +++ b/drivers/clk/sunxi-ng/ccu_div.c
> [ ... ]
>> @@ -84,6 +84,36 @@ static int ccu_div_determine_rate(struct clk_hw *hw,
>> req, ccu_div_determine_rate_helper, cd);
>> }
>>
>> +static int ccu_rodiv_determine_rate(struct clk_hw *hw,
>> + struct clk_rate_request *req)
>> +{
>> + struct ccu_div *cd = hw_to_ccu_div(hw);
>> + unsigned long val;
>> + u32 reg;
>> + int ret;
>> +
>> + reg = readl(cd->common.base + cd->common.reg);
>> + val = reg >> cd->div.shift;
>> + val &= (1 << cd->div.width) - 1;
>> +
>> + req->rate = ccu_mux_helper_unapply_prediv(&cd->common, &cd->mux, -1,
>> + req->rate);
>> +
>> + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
>> + req->rate *= cd->fixed_post_div;
>> +
>> + ret = divider_ro_determine_rate(hw, req, cd->div.table,
>> + cd->div.width, cd->div.flags, val);
>> +
>> + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
>> + req->rate /= cd->fixed_post_div;
>> +
>> + req->rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
>> + req->rate);
>
> [Severity: Medium]
> Will this division order cause inconsistent rate calculations compared to
> ccu_div_recalc_rate()?
>
> The divider_ro_determine_rate() helper applies DIV_ROUND_UP() to the unadjusted
> parent rate, and then this function divides that result by fixed_post_div and
> prediv.
>
> However, ccu_div_recalc_rate() applies prediv to the parent rate first, then
> calculates the divider rate, and finally divides by fixed_post_div.
>
> Since the Common Clock Framework requires determine_rate and recalc_rate to be
> mathematically consistent, could this discrepancy cause rate mismatches or
> warnings for clocks using a pre-divider or fixed post-divider?
I think the RO side is correct and RW one is not.
The RO part correctly walk back the tree by un-applying the predivider
so, whatever CCF does with the parent rate value will be correct.
The RW part query the parent rate then applies the prediv. This
will be thrown away when/if clk_divider_bestdiv() calls
clk_hw_round_rate() on the parent. If that happens, the subsequent
un-apply of the prediv would make the matter even worse. So (if I
understand all this correctly) this case does not happen yet, otherwise
we would have noticed, and it can be fixed separately.
Side Note: I've not followed the sunxi history behind all those
prediv/postdiv but I find them a bit confusing. It seems like inserting
some fixed factor clocks in the clock trees would accomplish the same
thing while simplifying mux/divider quite a lot by droping these quirks ?
Side Note #2: I feel that while sashiko is pointing out valid things but we
are slowly getting off topic and down the rabbit hole with it ...
>
>> +
>> + return ret;
>> +}
>> +
On Mon, Jul 6, 2026 at 8:36 PM Jerome Brunet <jbrunet@baylibre.com> wrote:
>
> On lun. 06 juil. 2026 at 10:06, sashiko-bot@kernel.org wrote:
>
> > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > - [Medium] Inconsistent rate calculation between `determine_rate` and `recalc_rate` due to division order.
> > --
> >
> > commit 39f872462c04ad724c3624b152234160de56a47e
> > 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..d1c8c7baa12dc 100644
> >> --- a/drivers/clk/sunxi-ng/ccu_div.c
> >> +++ b/drivers/clk/sunxi-ng/ccu_div.c
> > [ ... ]
> >> @@ -84,6 +84,36 @@ static int ccu_div_determine_rate(struct clk_hw *hw,
> >> req, ccu_div_determine_rate_helper, cd);
> >> }
> >>
> >> +static int ccu_rodiv_determine_rate(struct clk_hw *hw,
> >> + struct clk_rate_request *req)
> >> +{
> >> + struct ccu_div *cd = hw_to_ccu_div(hw);
> >> + unsigned long val;
> >> + u32 reg;
> >> + int ret;
> >> +
> >> + reg = readl(cd->common.base + cd->common.reg);
> >> + val = reg >> cd->div.shift;
> >> + val &= (1 << cd->div.width) - 1;
> >> +
> >> + req->rate = ccu_mux_helper_unapply_prediv(&cd->common, &cd->mux, -1,
> >> + req->rate);
> >> +
> >> + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
> >> + req->rate *= cd->fixed_post_div;
> >> +
> >> + ret = divider_ro_determine_rate(hw, req, cd->div.table,
> >> + cd->div.width, cd->div.flags, val);
> >> +
> >> + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
> >> + req->rate /= cd->fixed_post_div;
> >> +
> >> + req->rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
> >> + req->rate);
> >
> > [Severity: Medium]
> > Will this division order cause inconsistent rate calculations compared to
> > ccu_div_recalc_rate()?
> >
> > The divider_ro_determine_rate() helper applies DIV_ROUND_UP() to the unadjusted
> > parent rate, and then this function divides that result by fixed_post_div and
> > prediv.
> >
> > However, ccu_div_recalc_rate() applies prediv to the parent rate first, then
> > calculates the divider rate, and finally divides by fixed_post_div.
> >
> > Since the Common Clock Framework requires determine_rate and recalc_rate to be
> > mathematically consistent, could this discrepancy cause rate mismatches or
> > warnings for clocks using a pre-divider or fixed post-divider?
>
> I think the RO side is correct and RW one is not.
>
> The RO part correctly walk back the tree by un-applying the predivider
> so, whatever CCF does with the parent rate value will be correct.
>
> The RW part query the parent rate then applies the prediv. This
> will be thrown away when/if clk_divider_bestdiv() calls
> clk_hw_round_rate() on the parent. If that happens, the subsequent
> un-apply of the prediv would make the matter even worse. So (if I
> understand all this correctly) this case does not happen yet, otherwise
> we would have noticed, and it can be fixed separately.
Looking at the history, ccu_div_determine_rate_helper() used to be
ccu_div_round_rate(), so it wouldn't have tried all the parents. Looks
like I missed this when looking at the conversion patches back in
January.
The fix is easy though: just reimplement divider_round_rate_parent().
Somehow we did this already for the multiplier but not the divider.
BTW, would it make sense to implement ccu_rodiv_determine_rate() using
ccu_mux_helper_determine_rate() as well? That would make both RW and RW
consistent, but it would mean another rewrite for you ...
> Side Note: I've not followed the sunxi history behind all those
> prediv/postdiv but I find them a bit confusing. It seems like inserting
> some fixed factor clocks in the clock trees would accomplish the same
> thing while simplifying mux/divider quite a lot by droping these quirks ?
I think the whole idea was that we didn't want all those extra intermediate
clocks. If you look at drivers/clk/sunxi/ you can see that we came from a
one device node per clock design, which was messy and bloated. The second
try ended up being the "one clock per useful hardware clock unit" design
you see now..
And we somehow tied the list of clks to the exported list of clks,
which probably made it worse for adding intermediates.
> Side Note #2: I feel that while sashiko is pointing out valid things but we
> are slowly getting off topic and down the rabbit hole with it ...
Yeah. That's the downside of Sashiko.
Thanks
ChenYu
@@ -84,6 +84,36 @@ static int ccu_div_determine_rate(struct clk_hw *hw,
req, ccu_div_determine_rate_helper, cd);
}
+static int ccu_rodiv_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+ unsigned long val;
+ u32 reg;
+ int ret;
+
+ reg = readl(cd->common.base + cd->common.reg);
+ val = reg >> cd->div.shift;
+ val &= (1 << cd->div.width) - 1;
+
+ req->rate = ccu_mux_helper_unapply_prediv(&cd->common, &cd->mux, -1,
+ req->rate);
+
+ if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ req->rate *= cd->fixed_post_div;
+
+ ret = divider_ro_determine_rate(hw, req, cd->div.table,
+ cd->div.width, cd->div.flags, val);
+
+ if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ req->rate /= cd->fixed_post_div;
+
+ req->rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
+ req->rate);
+
+ return ret;
+}
+
static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
@@ -143,3 +173,15 @@ 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,
+
+ .determine_rate = ccu_rodiv_determine_rate,
+ .recalc_rate = ccu_div_recalc_rate,
+};
+EXPORT_SYMBOL_NS_GPL(ccu_rodiv_ops, "SUNXI_CCU");
@@ -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_ */
@@ -68,7 +68,7 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
}
EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_apply_prediv, "SUNXI_CCU");
-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)
@@ -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,