aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBoris BREZILLON <boris.brezillon@free-electrons.com>2014-09-02 03:50:15 -0400
committerMike Turquette <mturquette@linaro.org>2014-09-02 18:37:11 -0400
commit3ef9dd2bab7d6a013f75f9fb226d0191e9981288 (patch)
tree057ff3bdfe0a9df7b759991f529d0cc75da8b82a
parent078a3eb519dacf28cb7c9bb2ad2f62e19ca6dcc2 (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.c147
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
261static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, 268static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,