clk: axi-clkgen: Round closest in round_rate() and recalc_rate()
authorLars-Peter Clausen <lars@metafoo.de>
Tue, 5 Sep 2017 09:32:41 +0000 (11:32 +0200)
committerStephen Boyd <sboyd@codeaurora.org>
Fri, 22 Dec 2017 02:07:53 +0000 (18:07 -0800)
To minimize the rounding error round to the closest integer when
calculating the result in the recalc_rate() and set_rate() callbacks.

Also in order to improve precision multiply first and then divide.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
drivers/clk/clk-axi-clkgen.c

index 95a6e98343920a5f08137f46bbaf9583af846c49..48d11f2598e844660a84570a7389df06563dbea9 100644 (file)
@@ -302,13 +302,17 @@ static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
        unsigned long *parent_rate)
 {
        unsigned int d, m, dout;
+       unsigned long long tmp;
 
        axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
 
        if (d == 0 || dout == 0 || m == 0)
                return -EINVAL;
 
-       return *parent_rate / d * m / dout;
+       tmp = (unsigned long long)*parent_rate * m;
+       tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d);
+
+       return min_t(unsigned long long, tmp, LONG_MAX);
 }
 
 static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
@@ -344,8 +348,8 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
        if (d == 0 || dout == 0)
                return 0;
 
-       tmp = (unsigned long long)(parent_rate / d) * m;
-       do_div(tmp, dout);
+       tmp = (unsigned long long)parent_rate * m;
+       tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d);
 
        return min_t(unsigned long long, tmp, ULONG_MAX);
 }