Messages in this thread Patch in this message |  | | From | Brian Masney <> | | Subject | [PATCH 1/2] clk: preserve original rate when a sibling clk changes it's rate | | Date | Tue, 20 May 2025 15:28:45 -0400 |
| |
When a clk requests a new rate, there are times when the requested rate cannot be fulfilled due to the current rate of the parent clk. If CLK_SET_RATE_PARENT is set, then the parent rate can also be changed. However, the clk core currently doesn't negotiate with any of the other children to see if the new parent rate is acceptable, and will currently just change the rates of the sibling clks.
When a parent changes it's rate, only ensure that the section of the clk tree where the rate change request propagated up is changed. All other sibling nodes should try to keep a rate close to where they were originally at. The rate will go through a recalc_rate() with the new parent rate, so the rate may possibly change.
This doesn't fix all of the issues where a clk can unknowingly change the rate of it's siblings, however this is a relatively small change that can fix some issues. A correct change that includes voting across the various nodes in the subtree, and works across the various types of clks will involve a much more elaborate patch set.
This change was tested with kunit tests, and also boot tested on a Lenovo Thinkpad x13s laptop.
Signed-off-by: Brian Masney <bmasney@redhat.com> --- drivers/clk/clk.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0565c87656cf..713d4d8a9b1e 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -72,6 +72,7 @@ struct clk_core { unsigned long rate; unsigned long req_rate; unsigned long new_rate; + bool rate_directly_changed; struct clk_core *new_parent; struct clk_core *new_child; unsigned long flags; @@ -2254,6 +2255,7 @@ static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, struct clk_core *new_parent, u8 p_index) { struct clk_core *child; + unsigned long tmp_rate; core->new_rate = new_rate; core->new_parent = new_parent; @@ -2264,7 +2266,14 @@ static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, new_parent->new_child = core; hlist_for_each_entry(child, &core->children, child_node) { - child->new_rate = clk_recalc(child, new_rate); + /* + * When a parent changes it's rate, only ensure that the section + * of the clk tree where the rate change request propagated up + * is changed. All other sibling nodes should try to keep a rate + * close to where they were originally at. + */ + tmp_rate = child->rate_directly_changed ? new_rate : child->rate; + child->new_rate = clk_recalc(child, tmp_rate); clk_calc_subtree(child, child->new_rate, NULL, 0); } } @@ -2346,8 +2355,10 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core, } if ((core->flags & CLK_SET_RATE_PARENT) && parent && - best_parent_rate != parent->rate) + best_parent_rate != parent->rate) { + parent->rate_directly_changed = true; top = clk_calc_new_rates(parent, best_parent_rate); + } out: clk_calc_subtree(core, new_rate, parent, p_index); @@ -2487,6 +2498,15 @@ static void clk_change_rate(struct clk_core *core) clk_pm_runtime_put(core); } +static void clk_clear_rate_flags(struct clk_core *top) +{ + struct clk_core *child; + + top->rate_directly_changed = false; + hlist_for_each_entry(child, &top->children, child_node) { + clk_clear_rate_flags(child); + } +} static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core, unsigned long req_rate) { @@ -2537,6 +2557,8 @@ static int clk_core_set_rate_nolock(struct clk_core *core, if (clk_core_rate_is_protected(core)) return -EBUSY; + core->rate_directly_changed = true; + /* calculate new rates and get the topmost changed clock */ top = clk_calc_new_rates(core, req_rate); if (!top) @@ -2559,6 +2581,8 @@ static int clk_core_set_rate_nolock(struct clk_core *core, /* change the rates */ clk_change_rate(top); + clk_clear_rate_flags(top); + core->req_rate = req_rate; err: clk_pm_runtime_put(core); -- 2.49.0
|  |