diff options
author | Maxime COQUELIN <maxime.coquelin@st.com> | 2014-01-29 11:24:06 -0500 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-04-30 14:44:00 -0400 |
commit | dd23c2cd38da2c64af381b19795d2c4f115e8ecb (patch) | |
tree | 285ce925cb3c9a99720f34b40e42f7e70972d05e /drivers/clk/clk-divider.c | |
parent | e813d49d2a477e3b64a9ff32ca7db5737d36cd91 (diff) |
clk: divider: Fix best div calculation for power-of-two and table dividers
The divider returned by clk_divider_bestdiv() is likely to be invalid in case
of power-of-two and table dividers when CLK_SET_RATE_PARENT flag isn't set.
Fixes boot on STiH416 platform.
Signed-off-by: Maxime Coquelin <maxime.coquelin@st.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
[mturquette@linaro.org: trivial merge conflict & updated changelog]
Diffstat (limited to 'drivers/clk/clk-divider.c')
-rw-r--r-- | drivers/clk/clk-divider.c | 37 |
1 files changed, 36 insertions, 1 deletions
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index ec22112e569f..4637697c139f 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c | |||
@@ -144,6 +144,37 @@ static bool _is_valid_div(struct clk_divider *divider, unsigned int div) | |||
144 | return true; | 144 | return true; |
145 | } | 145 | } |
146 | 146 | ||
147 | static int _round_up_table(const struct clk_div_table *table, int div) | ||
148 | { | ||
149 | const struct clk_div_table *clkt; | ||
150 | int up = _get_table_maxdiv(table); | ||
151 | |||
152 | for (clkt = table; clkt->div; clkt++) { | ||
153 | if (clkt->div == div) | ||
154 | return clkt->div; | ||
155 | else if (clkt->div < div) | ||
156 | continue; | ||
157 | |||
158 | if ((clkt->div - div) < (up - div)) | ||
159 | up = clkt->div; | ||
160 | } | ||
161 | |||
162 | return up; | ||
163 | } | ||
164 | |||
165 | static int _div_round_up(struct clk_divider *divider, | ||
166 | unsigned long parent_rate, unsigned long rate) | ||
167 | { | ||
168 | int div = DIV_ROUND_UP(parent_rate, rate); | ||
169 | |||
170 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) | ||
171 | div = __roundup_pow_of_two(div); | ||
172 | if (divider->table) | ||
173 | div = _round_up_table(divider->table, div); | ||
174 | |||
175 | return div; | ||
176 | } | ||
177 | |||
147 | static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | 178 | static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, |
148 | unsigned long *best_parent_rate) | 179 | unsigned long *best_parent_rate) |
149 | { | 180 | { |
@@ -159,7 +190,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | |||
159 | 190 | ||
160 | if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { | 191 | if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { |
161 | parent_rate = *best_parent_rate; | 192 | parent_rate = *best_parent_rate; |
162 | bestdiv = DIV_ROUND_UP(parent_rate, rate); | 193 | bestdiv = _div_round_up(divider, parent_rate, rate); |
163 | bestdiv = bestdiv == 0 ? 1 : bestdiv; | 194 | bestdiv = bestdiv == 0 ? 1 : bestdiv; |
164 | bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; | 195 | bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; |
165 | return bestdiv; | 196 | return bestdiv; |
@@ -219,6 +250,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, | |||
219 | u32 val; | 250 | u32 val; |
220 | 251 | ||
221 | div = DIV_ROUND_UP(parent_rate, rate); | 252 | div = DIV_ROUND_UP(parent_rate, rate); |
253 | |||
254 | if (!_is_valid_div(divider, div)) | ||
255 | return -EINVAL; | ||
256 | |||
222 | value = _get_val(divider, div); | 257 | value = _get_val(divider, div); |
223 | 258 | ||
224 | if (value > div_mask(divider)) | 259 | if (value > div_mask(divider)) |