aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorSekhar Nori <nsekhar@ti.com>2010-07-20 07:16:49 -0400
committerKevin Hilman <khilman@deeprootsystems.com>2010-09-24 10:40:24 -0400
commitb39639b820ca64e55c39b5217773919ca7973cec (patch)
tree53b2eba184359e1aa748e40891b3fd4149228697 /arch/arm
parent0a477f6b8c1849701981f6e00e7afcd35af75546 (diff)
davinci: clock: add support for setting sysclk rate
Setting sysclk rate will be useful in cases where the sysclk is not at a fixed ratio to the PLL output but can asynchronously be changed. This support forms the basis of attempt to keep the AEMIF clock constant on OMAP-L138 even as PLL0 output changes as ARM clock is changed to save power. This patch has been tested on OMAP-L138. Signed-off-by: Sekhar Nori <nsekhar@ti.com> Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-davinci/clock.c73
-rw-r--r--arch/arm/mach-davinci/clock.h5
2 files changed, 78 insertions, 0 deletions
diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c
index 5b0cb62a058f..01ba080433db 100644
--- a/arch/arm/mach-davinci/clock.c
+++ b/arch/arm/mach-davinci/clock.c
@@ -287,6 +287,79 @@ static unsigned long clk_sysclk_recalc(struct clk *clk)
287 return rate; 287 return rate;
288} 288}
289 289
290int davinci_set_sysclk_rate(struct clk *clk, unsigned long rate)
291{
292 unsigned v;
293 struct pll_data *pll;
294 unsigned long input;
295 unsigned ratio = 0;
296
297 /* If this is the PLL base clock, wrong function to call */
298 if (clk->pll_data)
299 return -EINVAL;
300
301 /* There must be a parent... */
302 if (WARN_ON(!clk->parent))
303 return -EINVAL;
304
305 /* ... the parent must be a PLL... */
306 if (WARN_ON(!clk->parent->pll_data))
307 return -EINVAL;
308
309 /* ... and this clock must have a divider. */
310 if (WARN_ON(!clk->div_reg))
311 return -EINVAL;
312
313 pll = clk->parent->pll_data;
314
315 input = clk->parent->rate;
316
317 /* If pre-PLL, source clock is before the multiplier and divider(s) */
318 if (clk->flags & PRE_PLL)
319 input = pll->input_rate;
320
321 if (input > rate) {
322 /*
323 * Can afford to provide an output little higher than requested
324 * only if maximum rate supported by hardware on this sysclk
325 * is known.
326 */
327 if (clk->maxrate) {
328 ratio = DIV_ROUND_CLOSEST(input, rate);
329 if (input / ratio > clk->maxrate)
330 ratio = 0;
331 }
332
333 if (ratio == 0)
334 ratio = DIV_ROUND_UP(input, rate);
335
336 ratio--;
337 }
338
339 if (ratio > PLLDIV_RATIO_MASK)
340 return -EINVAL;
341
342 do {
343 v = __raw_readl(pll->base + PLLSTAT);
344 } while (v & PLLSTAT_GOSTAT);
345
346 v = __raw_readl(pll->base + clk->div_reg);
347 v &= ~PLLDIV_RATIO_MASK;
348 v |= ratio | PLLDIV_EN;
349 __raw_writel(v, pll->base + clk->div_reg);
350
351 v = __raw_readl(pll->base + PLLCMD);
352 v |= PLLCMD_GOSET;
353 __raw_writel(v, pll->base + PLLCMD);
354
355 do {
356 v = __raw_readl(pll->base + PLLSTAT);
357 } while (v & PLLSTAT_GOSTAT);
358
359 return 0;
360}
361EXPORT_SYMBOL(davinci_set_sysclk_rate);
362
290static unsigned long clk_leafclk_recalc(struct clk *clk) 363static unsigned long clk_leafclk_recalc(struct clk *clk)
291{ 364{
292 if (WARN_ON(!clk->parent)) 365 if (WARN_ON(!clk->parent))
diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h
index 01e36483ac3d..11099980b58b 100644
--- a/arch/arm/mach-davinci/clock.h
+++ b/arch/arm/mach-davinci/clock.h
@@ -70,6 +70,9 @@
70#include <linux/list.h> 70#include <linux/list.h>
71#include <asm/clkdev.h> 71#include <asm/clkdev.h>
72 72
73#define PLLSTAT_GOSTAT BIT(0)
74#define PLLCMD_GOSET BIT(0)
75
73struct pll_data { 76struct pll_data {
74 u32 phys_base; 77 u32 phys_base;
75 void __iomem *base; 78 void __iomem *base;
@@ -86,6 +89,7 @@ struct clk {
86 struct module *owner; 89 struct module *owner;
87 const char *name; 90 const char *name;
88 unsigned long rate; 91 unsigned long rate;
92 unsigned long maxrate; /* H/W supported max rate */
89 u8 usecount; 93 u8 usecount;
90 u8 lpsc; 94 u8 lpsc;
91 u8 gpsc; 95 u8 gpsc;
@@ -118,6 +122,7 @@ struct clk {
118int davinci_clk_init(struct clk_lookup *clocks); 122int davinci_clk_init(struct clk_lookup *clocks);
119int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, 123int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv,
120 unsigned int mult, unsigned int postdiv); 124 unsigned int mult, unsigned int postdiv);
125int davinci_set_sysclk_rate(struct clk *clk, unsigned long rate);
121 126
122extern struct platform_device davinci_wdt_device; 127extern struct platform_device davinci_wdt_device;
123extern void davinci_watchdog_reset(struct platform_device *); 128extern void davinci_watchdog_reset(struct platform_device *);