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


Reply via email to