diff options
author | Boris BREZILLON <boris.brezillon@free-electrons.com> | 2014-09-02 03:50:15 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-09-02 18:37:11 -0400 |
commit | 3ef9dd2bab7d6a013f75f9fb226d0191e9981288 (patch) | |
tree | 057ff3bdfe0a9df7b759991f529d0cc75da8b82a | |
parent | 078a3eb519dacf28cb7c9bb2ad2f62e19ca6dcc2 (diff) |
clk: at91: rework PLL rate calculation
The AT91 PLL rate configuration is done by configuring a multiplier/divider
pair.
The previous calculation was over-complicated (and apparently buggy).
Simplify the implementation and add some comments to explain what is done
here.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Reported-by: Gaël PORTAY <gael.portay@gmail.com>
Tested-by: Gaël PORTAY <gael.portay@gmail.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
-rw-r--r-- | drivers/clk/at91/clk-pll.c | 147 |
1 files changed, 77 insertions, 70 deletions
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c index 7b453b915c72..a1adcf186023 100644 --- a/drivers/clk/at91/clk-pll.c +++ b/drivers/clk/at91/clk-pll.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/of_address.h> | 15 | #include <linux/of_address.h> |
16 | #include <linux/of_irq.h> | 16 | #include <linux/of_irq.h> |
17 | #include <linux/io.h> | 17 | #include <linux/io.h> |
18 | #include <linux/kernel.h> | ||
18 | #include <linux/wait.h> | 19 | #include <linux/wait.h> |
19 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
20 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
@@ -29,6 +30,9 @@ | |||
29 | #define PLL_DIV(reg) ((reg) & PLL_DIV_MASK) | 30 | #define PLL_DIV(reg) ((reg) & PLL_DIV_MASK) |
30 | #define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \ | 31 | #define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \ |
31 | (layout)->mul_mask) | 32 | (layout)->mul_mask) |
33 | #define PLL_MUL_MIN 2 | ||
34 | #define PLL_MUL_MASK(layout) ((layout)->mul_mask) | ||
35 | #define PLL_MUL_MAX(layout) (PLL_MUL_MASK(layout) + 1) | ||
32 | #define PLL_ICPR_SHIFT(id) ((id) * 16) | 36 | #define PLL_ICPR_SHIFT(id) ((id) * 16) |
33 | #define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id)) | 37 | #define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id)) |
34 | #define PLL_MAX_COUNT 0x3f | 38 | #define PLL_MAX_COUNT 0x3f |
@@ -163,99 +167,102 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate, | |||
163 | unsigned long parent_rate, | 167 | unsigned long parent_rate, |
164 | u32 *div, u32 *mul, | 168 | u32 *div, u32 *mul, |
165 | u32 *index) { | 169 | u32 *index) { |
166 | unsigned long maxrate; | ||
167 | unsigned long minrate; | ||
168 | unsigned long divrate; | ||
169 | unsigned long bestdiv = 1; | ||
170 | unsigned long bestmul; | ||
171 | unsigned long tmpdiv; | ||
172 | unsigned long roundup; | ||
173 | unsigned long rounddown; | ||
174 | unsigned long remainder; | ||
175 | unsigned long bestremainder; | ||
176 | unsigned long maxmul; | ||
177 | unsigned long maxdiv; | ||
178 | unsigned long mindiv; | ||
179 | int i = 0; | ||
180 | const struct clk_pll_layout *layout = pll->layout; | 170 | const struct clk_pll_layout *layout = pll->layout; |
181 | const struct clk_pll_characteristics *characteristics = | 171 | const struct clk_pll_characteristics *characteristics = |
182 | pll->characteristics; | 172 | pll->characteristics; |
173 | unsigned long bestremainder = ULONG_MAX; | ||
174 | unsigned long maxdiv, mindiv, tmpdiv; | ||
175 | long bestrate = -ERANGE; | ||
176 | unsigned long bestdiv; | ||
177 | unsigned long bestmul; | ||
178 | int i = 0; | ||
183 | 179 | ||
184 | /* Minimum divider = 1 */ | 180 | /* Check if parent_rate is a valid input rate */ |
185 | /* Maximum multiplier = max_mul */ | ||
186 | maxmul = layout->mul_mask + 1; | ||
187 | maxrate = (parent_rate * maxmul) / 1; | ||
188 | |||
189 | /* Maximum divider = max_div */ | ||
190 | /* Minimum multiplier = 2 */ | ||
191 | maxdiv = PLL_DIV_MAX; | ||
192 | minrate = (parent_rate * 2) / maxdiv; | ||
193 | |||
194 | if (parent_rate < characteristics->input.min || | 181 | if (parent_rate < characteristics->input.min || |
195 | parent_rate < characteristics->input.max) | 182 | parent_rate > characteristics->input.max) |
196 | return -ERANGE; | ||
197 | |||
198 | if (parent_rate < minrate || parent_rate > maxrate) | ||
199 | return -ERANGE; | 183 | return -ERANGE; |
200 | 184 | ||
201 | for (i = 0; i < characteristics->num_output; i++) { | 185 | /* |
202 | if (parent_rate >= characteristics->output[i].min && | 186 | * Calculate minimum divider based on the minimum multiplier, the |
203 | parent_rate <= characteristics->output[i].max) | 187 | * parent_rate and the requested rate. |
204 | break; | 188 | * Should always be 2 according to the input and output characteristics |
205 | } | 189 | * of the PLL blocks. |
206 | 190 | */ | |
207 | if (i >= characteristics->num_output) | 191 | mindiv = (parent_rate * PLL_MUL_MIN) / rate; |
208 | return -ERANGE; | 192 | if (!mindiv) |
209 | 193 | mindiv = 1; | |
210 | bestmul = rate / parent_rate; | 194 | |
211 | rounddown = parent_rate % rate; | 195 | /* |
212 | roundup = rate - rounddown; | 196 | * Calculate the maximum divider which is limited by PLL register |
213 | bestremainder = roundup < rounddown ? roundup : rounddown; | 197 | * layout (limited by the MUL or DIV field size). |
214 | 198 | */ | |
215 | if (!bestremainder) { | 199 | maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate); |
216 | if (div) | 200 | if (maxdiv > PLL_DIV_MAX) |
217 | *div = bestdiv; | 201 | maxdiv = PLL_DIV_MAX; |
218 | if (mul) | 202 | |
219 | *mul = bestmul; | 203 | /* |
220 | if (index) | 204 | * Iterate over the acceptable divider values to find the best |
221 | *index = i; | 205 | * divider/multiplier pair (the one that generates the closest |
222 | return rate; | 206 | * rate to the requested one). |
223 | } | 207 | */ |
224 | 208 | for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) { | |
225 | maxdiv = 255 / (bestmul + 1); | 209 | unsigned long remainder; |
226 | if (parent_rate / maxdiv < characteristics->input.min) | 210 | unsigned long tmprate; |
227 | maxdiv = parent_rate / characteristics->input.min; | 211 | unsigned long tmpmul; |
228 | mindiv = parent_rate / characteristics->input.max; | 212 | |
229 | if (parent_rate % characteristics->input.max) | 213 | /* |
230 | mindiv++; | 214 | * Calculate the multiplier associated with the current |
231 | 215 | * divider that provide the closest rate to the requested one. | |
232 | for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) { | 216 | */ |
233 | divrate = parent_rate / tmpdiv; | 217 | tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv); |
234 | 218 | tmprate = (parent_rate / tmpdiv) * tmpmul; | |
235 | rounddown = rate % divrate; | 219 | if (tmprate > rate) |
236 | roundup = divrate - rounddown; | 220 | remainder = tmprate - rate; |
237 | remainder = roundup < rounddown ? roundup : rounddown; | 221 | else |
238 | 222 | remainder = rate - tmprate; | |
223 | |||
224 | /* | ||
225 | * Compare the remainder with the best remainder found until | ||
226 | * now and elect a new best multiplier/divider pair if the | ||
227 | * current remainder is smaller than the best one. | ||
228 | */ | ||
239 | if (remainder < bestremainder) { | 229 | if (remainder < bestremainder) { |
240 | bestremainder = remainder; | 230 | bestremainder = remainder; |
241 | bestmul = rate / divrate; | ||
242 | bestdiv = tmpdiv; | 231 | bestdiv = tmpdiv; |
232 | bestmul = tmpmul; | ||
233 | bestrate = tmprate; | ||
243 | } | 234 | } |
244 | 235 | ||
236 | /* | ||
237 | * We've found a perfect match! | ||
238 | * Stop searching now and use this multiplier/divider pair. | ||
239 | */ | ||
245 | if (!remainder) | 240 | if (!remainder) |
246 | break; | 241 | break; |
247 | } | 242 | } |
248 | 243 | ||
249 | rate = (parent_rate / bestdiv) * bestmul; | 244 | /* We haven't found any multiplier/divider pair => return -ERANGE */ |
245 | if (bestrate < 0) | ||
246 | return bestrate; | ||
247 | |||
248 | /* Check if bestrate is a valid output rate */ | ||
249 | for (i = 0; i < characteristics->num_output; i++) { | ||
250 | if (bestrate >= characteristics->output[i].min && | ||
251 | bestrate <= characteristics->output[i].max) | ||
252 | break; | ||
253 | } | ||
254 | |||
255 | if (i >= characteristics->num_output) | ||
256 | return -ERANGE; | ||
250 | 257 | ||
251 | if (div) | 258 | if (div) |
252 | *div = bestdiv; | 259 | *div = bestdiv; |
253 | if (mul) | 260 | if (mul) |
254 | *mul = bestmul; | 261 | *mul = bestmul - 1; |
255 | if (index) | 262 | if (index) |
256 | *index = i; | 263 | *index = i; |
257 | 264 | ||
258 | return rate; | 265 | return bestrate; |
259 | } | 266 | } |
260 | 267 | ||
261 | static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, | 268 | static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, |