aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-davinci/clock.c
diff options
context:
space:
mode:
authorSekhar Nori <nsekhar@ti.com>2009-08-31 06:18:03 -0400
committerKevin Hilman <khilman@deeprootsystems.com>2009-11-25 13:21:20 -0500
commitd6a61563f9e934ef20a1338780082f63802c8908 (patch)
tree543d650b9c87c6a63974db565d803ad26bf1ee38 /arch/arm/mach-davinci/clock.c
parentde381a91f544008f4f99571e2ef1f60b92d5f0cf (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.c114
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}
104EXPORT_SYMBOL(clk_round_rate); 108EXPORT_SYMBOL(clk_round_rate);
105 109
110/* Propagate rate to children */
111static 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
106int clk_set_rate(struct clk *clk, unsigned long rate) 122int 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}
114EXPORT_SYMBOL(clk_set_rate); 142EXPORT_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 */
337int 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}
405EXPORT_SYMBOL(davinci_set_pllrate);
406
299int __init davinci_clk_init(struct davinci_clk *clocks) 407int __init davinci_clk_init(struct davinci_clk *clocks)
300 { 408 {
301 struct davinci_clk *c; 409 struct davinci_clk *c;