clk: multiplier: Prevent the multiplier from under / over flowing
authorMaxime Ripard <maxime.ripard@free-electrons.com>
Mon, 16 May 2016 12:47:02 +0000 (14:47 +0200)
committerMichael Turquette <mturquette@baylibre.com>
Mon, 20 Jun 2016 20:00:00 +0000 (13:00 -0700)
In the current multiplier base clock implementation, if the
CLK_SET_RATE_PARENT flag isn't set, the code will not make sure that the
multiplier computed remains within the boundaries of our clock.

This means that if the clock we want to reach is below the parent rate,
or if the multiplier is above the maximum that we can reach, we will end up
with a completely bogus one that the clock cannot achieve.

Fixes: f2e0a53271a4 ("clk: Add a basic multiplier clock")
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Michael Turquette <mturquette@baylibre.com>
Link: lkml.kernel.org/r/1463402840-17062-3-git-send-email-maxime.ripard@free-electrons.com

drivers/clk/clk-multiplier.c

index 9e449c7b751c328e23b074186e1bebf426172087..dc037c957acd8a1dc1f74f3c0407567a09fa66cd 100644 (file)
@@ -52,14 +52,28 @@ static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate,
                                unsigned long *best_parent_rate,
                                u8 width, unsigned long flags)
 {
+       struct clk_multiplier *mult = to_clk_multiplier(hw);
        unsigned long orig_parent_rate = *best_parent_rate;
        unsigned long parent_rate, current_rate, best_rate = ~0;
        unsigned int i, bestmult = 0;
+       unsigned int maxmult = (1 << width) - 1;
+
+       if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
+               bestmult = rate / orig_parent_rate;
+
+               /* Make sure we don't end up with a 0 multiplier */
+               if ((bestmult == 0) &&
+                   !(mult->flags & CLK_MULTIPLIER_ZERO_BYPASS))
+                       bestmult = 1;
 
-       if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
-               return rate / *best_parent_rate;
+               /* Make sure we don't overflow the multiplier */
+               if (bestmult > maxmult)
+                       bestmult = maxmult;
+
+               return bestmult;
+       }
 
-       for (i = 1; i < ((1 << width) - 1); i++) {
+       for (i = 1; i < maxmult; i++) {
                if (rate == orig_parent_rate * i) {
                        /*
                         * This is the best case for us if we have a