[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250520192846.9614-2-bmasney@redhat.com>
Date: Tue, 20 May 2025 15:28:45 -0400
From: Brian Masney <bmasney@...hat.com>
To: sboyd@...nel.org
Cc: mturquette@...libre.com,
mripard@...nel.org,
linux-clk@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH 1/2] clk: preserve original rate when a sibling clk changes it's rate
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@...hat.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
Powered by blists - more mailing lists