clk: sunxi-ng: mux: fix determine helper rate propagation

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: 1c8d7af61b ("clk: sunxi-ng: convert from divider_round_rate_parent() to divider_determine_rate()")
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Link: https://patch.msgid.link/20260723-a733-rtc-v7-1-8fd68aab94ae@baylibre.com
Signed-off-by: Chen-Yu Tsai <wens@kernel.org>
This commit is contained in:
Jerome Brunet 2026-07-23 11:39:51 +02:00 committed by Chen-Yu Tsai
parent c52b5090bf
commit 4bcba49984

View File

@ -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;
}
}