diff options
Diffstat (limited to 'drivers/clk/clk-divider.c')
-rw-r--r-- | drivers/clk/clk-divider.c | 228 |
1 files changed, 144 insertions, 84 deletions
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index c0a842b335c5..db7f8bce7467 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c | |||
@@ -30,7 +30,7 @@ | |||
30 | 30 | ||
31 | #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw) | 31 | #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw) |
32 | 32 | ||
33 | #define div_mask(d) ((1 << ((d)->width)) - 1) | 33 | #define div_mask(width) ((1 << (width)) - 1) |
34 | 34 | ||
35 | static unsigned int _get_table_maxdiv(const struct clk_div_table *table) | 35 | static unsigned int _get_table_maxdiv(const struct clk_div_table *table) |
36 | { | 36 | { |
@@ -54,15 +54,16 @@ static unsigned int _get_table_mindiv(const struct clk_div_table *table) | |||
54 | return mindiv; | 54 | return mindiv; |
55 | } | 55 | } |
56 | 56 | ||
57 | static unsigned int _get_maxdiv(struct clk_divider *divider) | 57 | static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width, |
58 | unsigned long flags) | ||
58 | { | 59 | { |
59 | if (divider->flags & CLK_DIVIDER_ONE_BASED) | 60 | if (flags & CLK_DIVIDER_ONE_BASED) |
60 | return div_mask(divider); | 61 | return div_mask(width); |
61 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) | 62 | if (flags & CLK_DIVIDER_POWER_OF_TWO) |
62 | return 1 << div_mask(divider); | 63 | return 1 << div_mask(width); |
63 | if (divider->table) | 64 | if (table) |
64 | return _get_table_maxdiv(divider->table); | 65 | return _get_table_maxdiv(table); |
65 | return div_mask(divider) + 1; | 66 | return div_mask(width) + 1; |
66 | } | 67 | } |
67 | 68 | ||
68 | static unsigned int _get_table_div(const struct clk_div_table *table, | 69 | static unsigned int _get_table_div(const struct clk_div_table *table, |
@@ -76,14 +77,15 @@ static unsigned int _get_table_div(const struct clk_div_table *table, | |||
76 | return 0; | 77 | return 0; |
77 | } | 78 | } |
78 | 79 | ||
79 | static unsigned int _get_div(struct clk_divider *divider, unsigned int val) | 80 | static unsigned int _get_div(const struct clk_div_table *table, |
81 | unsigned int val, unsigned long flags) | ||
80 | { | 82 | { |
81 | if (divider->flags & CLK_DIVIDER_ONE_BASED) | 83 | if (flags & CLK_DIVIDER_ONE_BASED) |
82 | return val; | 84 | return val; |
83 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) | 85 | if (flags & CLK_DIVIDER_POWER_OF_TWO) |
84 | return 1 << val; | 86 | return 1 << val; |
85 | if (divider->table) | 87 | if (table) |
86 | return _get_table_div(divider->table, val); | 88 | return _get_table_div(table, val); |
87 | return val + 1; | 89 | return val + 1; |
88 | } | 90 | } |
89 | 91 | ||
@@ -98,29 +100,28 @@ static unsigned int _get_table_val(const struct clk_div_table *table, | |||
98 | return 0; | 100 | return 0; |
99 | } | 101 | } |
100 | 102 | ||
101 | static unsigned int _get_val(struct clk_divider *divider, unsigned int div) | 103 | static unsigned int _get_val(const struct clk_div_table *table, |
104 | unsigned int div, unsigned long flags) | ||
102 | { | 105 | { |
103 | if (divider->flags & CLK_DIVIDER_ONE_BASED) | 106 | if (flags & CLK_DIVIDER_ONE_BASED) |
104 | return div; | 107 | return div; |
105 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) | 108 | if (flags & CLK_DIVIDER_POWER_OF_TWO) |
106 | return __ffs(div); | 109 | return __ffs(div); |
107 | if (divider->table) | 110 | if (table) |
108 | return _get_table_val(divider->table, div); | 111 | return _get_table_val(table, div); |
109 | return div - 1; | 112 | return div - 1; |
110 | } | 113 | } |
111 | 114 | ||
112 | static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, | 115 | unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, |
113 | unsigned long parent_rate) | 116 | unsigned int val, |
117 | const struct clk_div_table *table, | ||
118 | unsigned long flags) | ||
114 | { | 119 | { |
115 | struct clk_divider *divider = to_clk_divider(hw); | 120 | unsigned int div; |
116 | unsigned int div, val; | ||
117 | 121 | ||
118 | val = clk_readl(divider->reg) >> divider->shift; | 122 | div = _get_div(table, val, flags); |
119 | val &= div_mask(divider); | ||
120 | |||
121 | div = _get_div(divider, val); | ||
122 | if (!div) { | 123 | if (!div) { |
123 | WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO), | 124 | WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO), |
124 | "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", | 125 | "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", |
125 | __clk_get_name(hw->clk)); | 126 | __clk_get_name(hw->clk)); |
126 | return parent_rate; | 127 | return parent_rate; |
@@ -128,6 +129,20 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, | |||
128 | 129 | ||
129 | return DIV_ROUND_UP(parent_rate, div); | 130 | return DIV_ROUND_UP(parent_rate, div); |
130 | } | 131 | } |
132 | EXPORT_SYMBOL_GPL(divider_recalc_rate); | ||
133 | |||
134 | static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, | ||
135 | unsigned long parent_rate) | ||
136 | { | ||
137 | struct clk_divider *divider = to_clk_divider(hw); | ||
138 | unsigned int val; | ||
139 | |||
140 | val = clk_readl(divider->reg) >> divider->shift; | ||
141 | val &= div_mask(divider->width); | ||
142 | |||
143 | return divider_recalc_rate(hw, parent_rate, val, divider->table, | ||
144 | divider->flags); | ||
145 | } | ||
131 | 146 | ||
132 | /* | 147 | /* |
133 | * The reverse of DIV_ROUND_UP: The maximum number which | 148 | * The reverse of DIV_ROUND_UP: The maximum number which |
@@ -146,12 +161,13 @@ static bool _is_valid_table_div(const struct clk_div_table *table, | |||
146 | return false; | 161 | return false; |
147 | } | 162 | } |
148 | 163 | ||
149 | static bool _is_valid_div(struct clk_divider *divider, unsigned int div) | 164 | static bool _is_valid_div(const struct clk_div_table *table, unsigned int div, |
165 | unsigned long flags) | ||
150 | { | 166 | { |
151 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) | 167 | if (flags & CLK_DIVIDER_POWER_OF_TWO) |
152 | return is_power_of_2(div); | 168 | return is_power_of_2(div); |
153 | if (divider->table) | 169 | if (table) |
154 | return _is_valid_table_div(divider->table, div); | 170 | return _is_valid_table_div(table, div); |
155 | return true; | 171 | return true; |
156 | } | 172 | } |
157 | 173 | ||
@@ -191,71 +207,76 @@ static int _round_down_table(const struct clk_div_table *table, int div) | |||
191 | return down; | 207 | return down; |
192 | } | 208 | } |
193 | 209 | ||
194 | static int _div_round_up(struct clk_divider *divider, | 210 | static int _div_round_up(const struct clk_div_table *table, |
195 | unsigned long parent_rate, unsigned long rate) | 211 | unsigned long parent_rate, unsigned long rate, |
212 | unsigned long flags) | ||
196 | { | 213 | { |
197 | int div = DIV_ROUND_UP(parent_rate, rate); | 214 | int div = DIV_ROUND_UP(parent_rate, rate); |
198 | 215 | ||
199 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) | 216 | if (flags & CLK_DIVIDER_POWER_OF_TWO) |
200 | div = __roundup_pow_of_two(div); | 217 | div = __roundup_pow_of_two(div); |
201 | if (divider->table) | 218 | if (table) |
202 | div = _round_up_table(divider->table, div); | 219 | div = _round_up_table(table, div); |
203 | 220 | ||
204 | return div; | 221 | return div; |
205 | } | 222 | } |
206 | 223 | ||
207 | static int _div_round_closest(struct clk_divider *divider, | 224 | static int _div_round_closest(const struct clk_div_table *table, |
208 | unsigned long parent_rate, unsigned long rate) | 225 | unsigned long parent_rate, unsigned long rate, |
226 | unsigned long flags) | ||
209 | { | 227 | { |
210 | int up, down, div; | 228 | int up, down, div; |
211 | 229 | ||
212 | up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate); | 230 | up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate); |
213 | 231 | ||
214 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) { | 232 | if (flags & CLK_DIVIDER_POWER_OF_TWO) { |
215 | up = __roundup_pow_of_two(div); | 233 | up = __roundup_pow_of_two(div); |
216 | down = __rounddown_pow_of_two(div); | 234 | down = __rounddown_pow_of_two(div); |
217 | } else if (divider->table) { | 235 | } else if (table) { |
218 | up = _round_up_table(divider->table, div); | 236 | up = _round_up_table(table, div); |
219 | down = _round_down_table(divider->table, div); | 237 | down = _round_down_table(table, div); |
220 | } | 238 | } |
221 | 239 | ||
222 | return (up - div) <= (div - down) ? up : down; | 240 | return (up - div) <= (div - down) ? up : down; |
223 | } | 241 | } |
224 | 242 | ||
225 | static int _div_round(struct clk_divider *divider, unsigned long parent_rate, | 243 | static int _div_round(const struct clk_div_table *table, |
226 | unsigned long rate) | 244 | unsigned long parent_rate, unsigned long rate, |
245 | unsigned long flags) | ||
227 | { | 246 | { |
228 | if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) | 247 | if (flags & CLK_DIVIDER_ROUND_CLOSEST) |
229 | return _div_round_closest(divider, parent_rate, rate); | 248 | return _div_round_closest(table, parent_rate, rate, flags); |
230 | 249 | ||
231 | return _div_round_up(divider, parent_rate, rate); | 250 | return _div_round_up(table, parent_rate, rate, flags); |
232 | } | 251 | } |
233 | 252 | ||
234 | static bool _is_best_div(struct clk_divider *divider, | 253 | static bool _is_best_div(unsigned long rate, unsigned long now, |
235 | unsigned long rate, unsigned long now, unsigned long best) | 254 | unsigned long best, unsigned long flags) |
236 | { | 255 | { |
237 | if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) | 256 | if (flags & CLK_DIVIDER_ROUND_CLOSEST) |
238 | return abs(rate - now) < abs(rate - best); | 257 | return abs(rate - now) < abs(rate - best); |
239 | 258 | ||
240 | return now <= rate && now > best; | 259 | return now <= rate && now > best; |
241 | } | 260 | } |
242 | 261 | ||
243 | static int _next_div(struct clk_divider *divider, int div) | 262 | static int _next_div(const struct clk_div_table *table, int div, |
263 | unsigned long flags) | ||
244 | { | 264 | { |
245 | div++; | 265 | div++; |
246 | 266 | ||
247 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) | 267 | if (flags & CLK_DIVIDER_POWER_OF_TWO) |
248 | return __roundup_pow_of_two(div); | 268 | return __roundup_pow_of_two(div); |
249 | if (divider->table) | 269 | if (table) |
250 | return _round_up_table(divider->table, div); | 270 | return _round_up_table(table, div); |
251 | 271 | ||
252 | return div; | 272 | return div; |
253 | } | 273 | } |
254 | 274 | ||
255 | static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | 275 | static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, |
256 | unsigned long *best_parent_rate) | 276 | unsigned long *best_parent_rate, |
277 | const struct clk_div_table *table, u8 width, | ||
278 | unsigned long flags) | ||
257 | { | 279 | { |
258 | struct clk_divider *divider = to_clk_divider(hw); | ||
259 | int i, bestdiv = 0; | 280 | int i, bestdiv = 0; |
260 | unsigned long parent_rate, best = 0, now, maxdiv; | 281 | unsigned long parent_rate, best = 0, now, maxdiv; |
261 | unsigned long parent_rate_saved = *best_parent_rate; | 282 | unsigned long parent_rate_saved = *best_parent_rate; |
@@ -263,19 +284,11 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | |||
263 | if (!rate) | 284 | if (!rate) |
264 | rate = 1; | 285 | rate = 1; |
265 | 286 | ||
266 | /* if read only, just return current value */ | 287 | maxdiv = _get_maxdiv(table, width, flags); |
267 | if (divider->flags & CLK_DIVIDER_READ_ONLY) { | ||
268 | bestdiv = readl(divider->reg) >> divider->shift; | ||
269 | bestdiv &= div_mask(divider); | ||
270 | bestdiv = _get_div(divider, bestdiv); | ||
271 | return bestdiv; | ||
272 | } | ||
273 | |||
274 | maxdiv = _get_maxdiv(divider); | ||
275 | 288 | ||
276 | if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { | 289 | if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { |
277 | parent_rate = *best_parent_rate; | 290 | parent_rate = *best_parent_rate; |
278 | bestdiv = _div_round(divider, parent_rate, rate); | 291 | bestdiv = _div_round(table, parent_rate, rate, flags); |
279 | bestdiv = bestdiv == 0 ? 1 : bestdiv; | 292 | bestdiv = bestdiv == 0 ? 1 : bestdiv; |
280 | bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; | 293 | bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; |
281 | return bestdiv; | 294 | return bestdiv; |
@@ -287,8 +300,8 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | |||
287 | */ | 300 | */ |
288 | maxdiv = min(ULONG_MAX / rate, maxdiv); | 301 | maxdiv = min(ULONG_MAX / rate, maxdiv); |
289 | 302 | ||
290 | for (i = 1; i <= maxdiv; i = _next_div(divider, i)) { | 303 | for (i = 1; i <= maxdiv; i = _next_div(table, i, flags)) { |
291 | if (!_is_valid_div(divider, i)) | 304 | if (!_is_valid_div(table, i, flags)) |
292 | continue; | 305 | continue; |
293 | if (rate * i == parent_rate_saved) { | 306 | if (rate * i == parent_rate_saved) { |
294 | /* | 307 | /* |
@@ -302,7 +315,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | |||
302 | parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), | 315 | parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), |
303 | MULT_ROUND_UP(rate, i)); | 316 | MULT_ROUND_UP(rate, i)); |
304 | now = DIV_ROUND_UP(parent_rate, i); | 317 | now = DIV_ROUND_UP(parent_rate, i); |
305 | if (_is_best_div(divider, rate, now, best)) { | 318 | if (_is_best_div(rate, now, best, flags)) { |
306 | bestdiv = i; | 319 | bestdiv = i; |
307 | best = now; | 320 | best = now; |
308 | *best_parent_rate = parent_rate; | 321 | *best_parent_rate = parent_rate; |
@@ -310,48 +323,79 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | |||
310 | } | 323 | } |
311 | 324 | ||
312 | if (!bestdiv) { | 325 | if (!bestdiv) { |
313 | bestdiv = _get_maxdiv(divider); | 326 | bestdiv = _get_maxdiv(table, width, flags); |
314 | *best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1); | 327 | *best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1); |
315 | } | 328 | } |
316 | 329 | ||
317 | return bestdiv; | 330 | return bestdiv; |
318 | } | 331 | } |
319 | 332 | ||
320 | static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, | 333 | long divider_round_rate(struct clk_hw *hw, unsigned long rate, |
321 | unsigned long *prate) | 334 | unsigned long *prate, const struct clk_div_table *table, |
335 | u8 width, unsigned long flags) | ||
322 | { | 336 | { |
323 | int div; | 337 | int div; |
324 | div = clk_divider_bestdiv(hw, rate, prate); | 338 | |
339 | div = clk_divider_bestdiv(hw, rate, prate, table, width, flags); | ||
325 | 340 | ||
326 | return DIV_ROUND_UP(*prate, div); | 341 | return DIV_ROUND_UP(*prate, div); |
327 | } | 342 | } |
343 | EXPORT_SYMBOL_GPL(divider_round_rate); | ||
328 | 344 | ||
329 | static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, | 345 | static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, |
330 | unsigned long parent_rate) | 346 | unsigned long *prate) |
331 | { | 347 | { |
332 | struct clk_divider *divider = to_clk_divider(hw); | 348 | struct clk_divider *divider = to_clk_divider(hw); |
349 | int bestdiv; | ||
350 | |||
351 | /* if read only, just return current value */ | ||
352 | if (divider->flags & CLK_DIVIDER_READ_ONLY) { | ||
353 | bestdiv = readl(divider->reg) >> divider->shift; | ||
354 | bestdiv &= div_mask(divider->width); | ||
355 | bestdiv = _get_div(divider->table, bestdiv, divider->flags); | ||
356 | return bestdiv; | ||
357 | } | ||
358 | |||
359 | return divider_round_rate(hw, rate, prate, divider->table, | ||
360 | divider->width, divider->flags); | ||
361 | } | ||
362 | |||
363 | int divider_get_val(unsigned long rate, unsigned long parent_rate, | ||
364 | const struct clk_div_table *table, u8 width, | ||
365 | unsigned long flags) | ||
366 | { | ||
333 | unsigned int div, value; | 367 | unsigned int div, value; |
334 | unsigned long flags = 0; | ||
335 | u32 val; | ||
336 | 368 | ||
337 | div = DIV_ROUND_UP(parent_rate, rate); | 369 | div = DIV_ROUND_UP(parent_rate, rate); |
338 | 370 | ||
339 | if (!_is_valid_div(divider, div)) | 371 | if (!_is_valid_div(table, div, flags)) |
340 | return -EINVAL; | 372 | return -EINVAL; |
341 | 373 | ||
342 | value = _get_val(divider, div); | 374 | value = _get_val(table, div, flags); |
375 | |||
376 | return min_t(unsigned int, value, div_mask(width)); | ||
377 | } | ||
378 | EXPORT_SYMBOL_GPL(divider_get_val); | ||
379 | |||
380 | static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, | ||
381 | unsigned long parent_rate) | ||
382 | { | ||
383 | struct clk_divider *divider = to_clk_divider(hw); | ||
384 | unsigned int value; | ||
385 | unsigned long flags = 0; | ||
386 | u32 val; | ||
343 | 387 | ||
344 | if (value > div_mask(divider)) | 388 | value = divider_get_val(rate, parent_rate, divider->table, |
345 | value = div_mask(divider); | 389 | divider->width, divider->flags); |
346 | 390 | ||
347 | if (divider->lock) | 391 | if (divider->lock) |
348 | spin_lock_irqsave(divider->lock, flags); | 392 | spin_lock_irqsave(divider->lock, flags); |
349 | 393 | ||
350 | if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { | 394 | if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { |
351 | val = div_mask(divider) << (divider->shift + 16); | 395 | val = div_mask(divider->width) << (divider->shift + 16); |
352 | } else { | 396 | } else { |
353 | val = clk_readl(divider->reg); | 397 | val = clk_readl(divider->reg); |
354 | val &= ~(div_mask(divider) << divider->shift); | 398 | val &= ~(div_mask(divider->width) << divider->shift); |
355 | } | 399 | } |
356 | val |= value << divider->shift; | 400 | val |= value << divider->shift; |
357 | clk_writel(val, divider->reg); | 401 | clk_writel(val, divider->reg); |
@@ -463,3 +507,19 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, | |||
463 | width, clk_divider_flags, table, lock); | 507 | width, clk_divider_flags, table, lock); |
464 | } | 508 | } |
465 | EXPORT_SYMBOL_GPL(clk_register_divider_table); | 509 | EXPORT_SYMBOL_GPL(clk_register_divider_table); |
510 | |||
511 | void clk_unregister_divider(struct clk *clk) | ||
512 | { | ||
513 | struct clk_divider *div; | ||
514 | struct clk_hw *hw; | ||
515 | |||
516 | hw = __clk_get_hw(clk); | ||
517 | if (!hw) | ||
518 | return; | ||
519 | |||
520 | div = to_clk_divider(hw); | ||
521 | |||
522 | clk_unregister(clk); | ||
523 | kfree(div); | ||
524 | } | ||
525 | EXPORT_SYMBOL_GPL(clk_unregister_divider); | ||