diff options
author | Sekhar Nori <nsekhar@ti.com> | 2009-08-31 06:18:03 -0400 |
---|---|---|
committer | Kevin Hilman <khilman@deeprootsystems.com> | 2009-11-25 13:21:20 -0500 |
commit | d6a61563f9e934ef20a1338780082f63802c8908 (patch) | |
tree | 543d650b9c87c6a63974db565d803ad26bf1ee38 /arch/arm/mach-davinci/clock.c | |
parent | de381a91f544008f4f99571e2ef1f60b92d5f0cf (diff) |
davinci: support changing the clock rate in clock framework
clk_round_rate, clk_set_rate have been updated to handle dynamic
frequency changes.
The motivation behind the changes is to support dynamic CPU frequency
change.
davinci_set_pllrate() changes the PLL rate of a given PLL. This function
has been presented as a generic function though it has been tested only
on OMAP-L138 EVM. No other currently available DaVinci device will probably
use this function, but any future device specific changes will hopefully be
small enough to get taken care using a cpu_is_xxx() macro.
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
Diffstat (limited to 'arch/arm/mach-davinci/clock.c')
-rw-r--r-- | arch/arm/mach-davinci/clock.c | 114 |
1 files changed, 111 insertions, 3 deletions
diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c index 6de1e3428f1f..09e0e1c00a59 100644 --- a/arch/arm/mach-davinci/clock.c +++ b/arch/arm/mach-davinci/clock.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/mutex.h> | 19 | #include <linux/mutex.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
22 | #include <linux/delay.h> | ||
22 | 23 | ||
23 | #include <mach/hardware.h> | 24 | #include <mach/hardware.h> |
24 | 25 | ||
@@ -99,17 +100,44 @@ long clk_round_rate(struct clk *clk, unsigned long rate) | |||
99 | if (clk == NULL || IS_ERR(clk)) | 100 | if (clk == NULL || IS_ERR(clk)) |
100 | return -EINVAL; | 101 | return -EINVAL; |
101 | 102 | ||
103 | if (clk->round_rate) | ||
104 | return clk->round_rate(clk, rate); | ||
105 | |||
102 | return clk->rate; | 106 | return clk->rate; |
103 | } | 107 | } |
104 | EXPORT_SYMBOL(clk_round_rate); | 108 | EXPORT_SYMBOL(clk_round_rate); |
105 | 109 | ||
110 | /* Propagate rate to children */ | ||
111 | static void propagate_rate(struct clk *root) | ||
112 | { | ||
113 | struct clk *clk; | ||
114 | |||
115 | list_for_each_entry(clk, &root->children, childnode) { | ||
116 | if (clk->recalc) | ||
117 | clk->rate = clk->recalc(clk); | ||
118 | propagate_rate(clk); | ||
119 | } | ||
120 | } | ||
121 | |||
106 | int clk_set_rate(struct clk *clk, unsigned long rate) | 122 | int clk_set_rate(struct clk *clk, unsigned long rate) |
107 | { | 123 | { |
124 | unsigned long flags; | ||
125 | int ret = -EINVAL; | ||
126 | |||
108 | if (clk == NULL || IS_ERR(clk)) | 127 | if (clk == NULL || IS_ERR(clk)) |
109 | return -EINVAL; | 128 | return ret; |
110 | 129 | ||
111 | /* changing the clk rate is not supported */ | 130 | spin_lock_irqsave(&clockfw_lock, flags); |
112 | return -EINVAL; | 131 | if (clk->set_rate) |
132 | ret = clk->set_rate(clk, rate); | ||
133 | if (ret == 0) { | ||
134 | if (clk->recalc) | ||
135 | clk->rate = clk->recalc(clk); | ||
136 | propagate_rate(clk); | ||
137 | } | ||
138 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
139 | |||
140 | return ret; | ||
113 | } | 141 | } |
114 | EXPORT_SYMBOL(clk_set_rate); | 142 | EXPORT_SYMBOL(clk_set_rate); |
115 | 143 | ||
@@ -296,6 +324,86 @@ static unsigned long clk_pllclk_recalc(struct clk *clk) | |||
296 | return rate; | 324 | return rate; |
297 | } | 325 | } |
298 | 326 | ||
327 | /** | ||
328 | * davinci_set_pllrate - set the output rate of a given PLL. | ||
329 | * | ||
330 | * Note: Currently tested to work with OMAP-L138 only. | ||
331 | * | ||
332 | * @pll: pll whose rate needs to be changed. | ||
333 | * @prediv: The pre divider value. Passing 0 disables the pre-divider. | ||
334 | * @pllm: The multiplier value. Passing 0 leads to multiply-by-one. | ||
335 | * @postdiv: The post divider value. Passing 0 disables the post-divider. | ||
336 | */ | ||
337 | int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, | ||
338 | unsigned int mult, unsigned int postdiv) | ||
339 | { | ||
340 | u32 ctrl; | ||
341 | unsigned int locktime; | ||
342 | |||
343 | if (pll->base == NULL) | ||
344 | return -EINVAL; | ||
345 | |||
346 | /* | ||
347 | * PLL lock time required per OMAP-L138 datasheet is | ||
348 | * (2000 * prediv)/sqrt(pllm) OSCIN cycles. We approximate sqrt(pllm) | ||
349 | * as 4 and OSCIN cycle as 25 MHz. | ||
350 | */ | ||
351 | if (prediv) { | ||
352 | locktime = ((2000 * prediv) / 100); | ||
353 | prediv = (prediv - 1) | PLLDIV_EN; | ||
354 | } else { | ||
355 | locktime = 20; | ||
356 | } | ||
357 | if (postdiv) | ||
358 | postdiv = (postdiv - 1) | PLLDIV_EN; | ||
359 | if (mult) | ||
360 | mult = mult - 1; | ||
361 | |||
362 | ctrl = __raw_readl(pll->base + PLLCTL); | ||
363 | |||
364 | /* Switch the PLL to bypass mode */ | ||
365 | ctrl &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN); | ||
366 | __raw_writel(ctrl, pll->base + PLLCTL); | ||
367 | |||
368 | /* | ||
369 | * Wait for 4 OSCIN/CLKIN cycles to ensure that the PLLC has switched | ||
370 | * to bypass mode. Delay of 1us ensures we are good for all > 4MHz | ||
371 | * OSCIN/CLKIN inputs. Typically the input is ~25MHz. | ||
372 | */ | ||
373 | udelay(1); | ||
374 | |||
375 | /* Reset and enable PLL */ | ||
376 | ctrl &= ~(PLLCTL_PLLRST | PLLCTL_PLLDIS); | ||
377 | __raw_writel(ctrl, pll->base + PLLCTL); | ||
378 | |||
379 | if (pll->flags & PLL_HAS_PREDIV) | ||
380 | __raw_writel(prediv, pll->base + PREDIV); | ||
381 | |||
382 | __raw_writel(mult, pll->base + PLLM); | ||
383 | |||
384 | if (pll->flags & PLL_HAS_POSTDIV) | ||
385 | __raw_writel(postdiv, pll->base + POSTDIV); | ||
386 | |||
387 | /* | ||
388 | * Wait for PLL to reset properly, OMAP-L138 datasheet says | ||
389 | * 'min' time = 125ns | ||
390 | */ | ||
391 | udelay(1); | ||
392 | |||
393 | /* Bring PLL out of reset */ | ||
394 | ctrl |= PLLCTL_PLLRST; | ||
395 | __raw_writel(ctrl, pll->base + PLLCTL); | ||
396 | |||
397 | udelay(locktime); | ||
398 | |||
399 | /* Remove PLL from bypass mode */ | ||
400 | ctrl |= PLLCTL_PLLEN; | ||
401 | __raw_writel(ctrl, pll->base + PLLCTL); | ||
402 | |||
403 | return 0; | ||
404 | } | ||
405 | EXPORT_SYMBOL(davinci_set_pllrate); | ||
406 | |||
299 | int __init davinci_clk_init(struct davinci_clk *clocks) | 407 | int __init davinci_clk_init(struct davinci_clk *clocks) |
300 | { | 408 | { |
301 | struct davinci_clk *c; | 409 | struct davinci_clk *c; |