]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
Revert "drivers: clk: zynqmp: update divider round rate logic"
authorJoel Guittet <jguittet@witekio.com>
Mon, 25 Nov 2024 16:09:59 +0000 (17:09 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 14 Dec 2024 18:51:12 +0000 (19:51 +0100)
This reverts commit 9117fc44fd3a9538261e530ba5a022dfc9519620 which is
commit 1fe15be1fb613534ecbac5f8c3f8744f757d237d upstream.

It is reported to cause regressions in the 5.15.y tree, so revert it for
now.

Link: https://www.spinics.net/lists/kernel/msg5397998.html
Signed-off-by: Joel Guittet <jguittet.opensource@witekio.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/clk/zynqmp/divider.c

index e25c76ff2739109edee9f4f8cc891bb4aa4aeb53..47a199346ddfc5896d03dfd2e06a70913b7b9f3d 100644 (file)
@@ -110,6 +110,52 @@ static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw,
        return DIV_ROUND_UP_ULL(parent_rate, value);
 }
 
+static void zynqmp_get_divider2_val(struct clk_hw *hw,
+                                   unsigned long rate,
+                                   struct zynqmp_clk_divider *divider,
+                                   u32 *bestdiv)
+{
+       int div1;
+       int div2;
+       long error = LONG_MAX;
+       unsigned long div1_prate;
+       struct clk_hw *div1_parent_hw;
+       struct zynqmp_clk_divider *pdivider;
+       struct clk_hw *div2_parent_hw = clk_hw_get_parent(hw);
+
+       if (!div2_parent_hw)
+               return;
+
+       pdivider = to_zynqmp_clk_divider(div2_parent_hw);
+       if (!pdivider)
+               return;
+
+       div1_parent_hw = clk_hw_get_parent(div2_parent_hw);
+       if (!div1_parent_hw)
+               return;
+
+       div1_prate = clk_hw_get_rate(div1_parent_hw);
+       *bestdiv = 1;
+       for (div1 = 1; div1 <= pdivider->max_div;) {
+               for (div2 = 1; div2 <= divider->max_div;) {
+                       long new_error = ((div1_prate / div1) / div2) - rate;
+
+                       if (abs(new_error) < abs(error)) {
+                               *bestdiv = div2;
+                               error = new_error;
+                       }
+                       if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+                               div2 = div2 << 1;
+                       else
+                               div2++;
+               }
+               if (pdivider->flags & CLK_DIVIDER_POWER_OF_TWO)
+                       div1 = div1 << 1;
+               else
+                       div1++;
+       }
+}
+
 /**
  * zynqmp_clk_divider_round_rate() - Round rate of divider clock
  * @hw:                        handle between common and hardware-specific interfaces
@@ -128,7 +174,6 @@ static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
        u32 div_type = divider->div_type;
        u32 bestdiv;
        int ret;
-       u8 width;
 
        /* if read only, just return current value */
        if (divider->flags & CLK_DIVIDER_READ_ONLY) {
@@ -148,12 +193,23 @@ static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
                return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
        }
 
-       width = fls(divider->max_div);
+       bestdiv = zynqmp_divider_get_val(*prate, rate, divider->flags);
+
+       /*
+        * In case of two divisors, compute best divider values and return
+        * divider2 value based on compute value. div1 will  be automatically
+        * set to optimum based on required total divider value.
+        */
+       if (div_type == TYPE_DIV2 &&
+           (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
+               zynqmp_get_divider2_val(hw, rate, divider, &bestdiv);
+       }
 
-       rate = divider_round_rate(hw, rate, prate, NULL, width, divider->flags);
+       if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && divider->is_frac)
+               bestdiv = rate % *prate ? 1 : bestdiv;
 
-       if (divider->is_frac && (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && (rate % *prate))
-               *prate = rate;
+       bestdiv = min_t(u32, bestdiv, divider->max_div);
+       *prate = rate * bestdiv;
 
        return rate;
 }