clk: zynqmp: Fix divider2 calculation
authorTejas Patel <tejas.patel@xilinx.com>
Mon, 2 Mar 2020 21:50:41 +0000 (13:50 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Jun 2020 15:48:57 +0000 (17:48 +0200)
[ Upstream commit b8c1049c68d634a412ed5980ae666ed7c8839305 ]

zynqmp_get_divider2_val() calculates, divider value of type DIV2 clock,
considering best possible combination of DIV1 and DIV2.

To find best possible values of DIV1 and DIV2, DIV1's parent rate
should be consider and not DIV2's parent rate since it would rate of
div1 clock. Consider a below topology,

out_clk->div2_clk->div1_clk->fixed_parent

where out_clk = (fixed_parent/div1_clk) / div2_clk, so parent clock
of div1_clk (i.e. out_clk) should be divided by div1_clk and div2_clk.

Existing code divides parent rate of div2_clk's clock instead of
div1_clk's parent rate, which is wrong.

Fix the same by considering div1's parent clock rate.

Fixes: 4ebd92d2e228 ("clk: zynqmp: Fix divider calculation")
Signed-off-by: Tejas Patel <tejas.patel@xilinx.com>
Signed-off-by: Jolly Shah <jolly.shah@xilinx.com>
Link: https://lkml.kernel.org/r/1583185843-20707-3-git-send-email-jolly.shah@xilinx.com
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/clk/zynqmp/divider.c

index 4be2cc76aa2e787f2409f76b52a28aa67b78846e..9bc4f9409aea0f8a8e054a5b5b6e4265d021177f 100644 (file)
@@ -111,23 +111,30 @@ static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw,
 
 static void zynqmp_get_divider2_val(struct clk_hw *hw,
                                    unsigned long rate,
-                                   unsigned long parent_rate,
                                    struct zynqmp_clk_divider *divider,
                                    int *bestdiv)
 {
        int div1;
        int div2;
        long error = LONG_MAX;
-       struct clk_hw *parent_hw = clk_hw_get_parent(hw);
-       struct zynqmp_clk_divider *pdivider = to_zynqmp_clk_divider(parent_hw);
+       unsigned long div1_prate;
+       struct clk_hw *div1_parent_hw;
+       struct clk_hw *div2_parent_hw = clk_hw_get_parent(hw);
+       struct zynqmp_clk_divider *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 = ((parent_rate / div1) / div2) - rate;
+                       long new_error = ((div1_prate / div1) / div2) - rate;
 
                        if (abs(new_error) < abs(error)) {
                                *bestdiv = div2;
@@ -192,7 +199,7 @@ static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
         */
        if (div_type == TYPE_DIV2 &&
            (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
-               zynqmp_get_divider2_val(hw, rate, *prate, divider, &bestdiv);
+               zynqmp_get_divider2_val(hw, rate, divider, &bestdiv);
        }
 
        if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && divider->is_frac)