If the v2 rate negotiation logic is enabled in this portion of the clk subtree, then use the Lowest Common Multiple (LCM) of all of the child rates to determine what the parent rate should be. Make this change for clk_divider_bestdiv (used by clk_divider_determine_rate), and divider_ro_determine_rate.
Note that the v2 rate negotiation logic is disabled by default, unless a clk in this portion of the subtree has the flag CLK_V2_RATE_NEGOTIATION. This change was tested on a Thinkpad x13s laptop. Some clks used this new code, however this needs to be tested on more real systems. Reviewed-by: Maxime Ripard <[email protected]> Link: https://lore.kernel.org/linux-clk/[email protected]/ Link: https://lpc.events/event/19/contributions/2152/ Signed-off-by: Brian Masney <[email protected]> --- drivers/clk/clk-divider.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 45e7ebde4a8b4d6572aa9d867a6f12f6caf8aae4..17646a06420e099b5fa3ec99d92175356c65afa0 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -315,6 +315,19 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent, return bestdiv; } + if (clk_has_v2_rate_negotiation(parent->core)) { + unsigned long lcm_rate; + + lcm_rate = clk_hw_get_children_lcm(parent, hw, rate); + if (lcm_rate > 0) { + *best_parent_rate = lcm_rate; + bestdiv = _div_round(table, lcm_rate, rate, flags); + bestdiv = bestdiv == 0 ? 1 : bestdiv; + bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; + return bestdiv; + } + } + /* * The maximum divider we can use without overflowing * unsigned long in rate * i below @@ -377,8 +390,19 @@ int divider_ro_determine_rate(struct clk_hw *hw, struct clk_rate_request *req, if (!req->best_parent_hw) return -EINVAL; - req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw, - req->rate * div); + if (clk_has_v2_rate_negotiation(req->best_parent_hw->core)) { + unsigned long lcm_rate; + + lcm_rate = clk_hw_get_children_lcm(req->best_parent_hw, hw, req->rate); + if (lcm_rate > 0) + req->best_parent_rate = lcm_rate; + else + req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw, + req->rate * div); + } else { + req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw, + req->rate * div); + } } req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div); -- 2.53.0

