diff options
author | Sekhar Nori <nsekhar@ti.com> | 2010-07-20 07:16:49 -0400 |
---|---|---|
committer | Kevin Hilman <khilman@deeprootsystems.com> | 2010-09-24 10:40:24 -0400 |
commit | b39639b820ca64e55c39b5217773919ca7973cec (patch) | |
tree | 53b2eba184359e1aa748e40891b3fd4149228697 /arch/arm | |
parent | 0a477f6b8c1849701981f6e00e7afcd35af75546 (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.c | 73 | ||||
-rw-r--r-- | arch/arm/mach-davinci/clock.h | 5 |
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 | ||
290 | int 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 | } | ||
361 | EXPORT_SYMBOL(davinci_set_sysclk_rate); | ||
362 | |||
290 | static unsigned long clk_leafclk_recalc(struct clk *clk) | 363 | static 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 | |||
73 | struct pll_data { | 76 | struct 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 { | |||
118 | int davinci_clk_init(struct clk_lookup *clocks); | 122 | int davinci_clk_init(struct clk_lookup *clocks); |
119 | int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, | 123 | int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, |
120 | unsigned int mult, unsigned int postdiv); | 124 | unsigned int mult, unsigned int postdiv); |
125 | int davinci_set_sysclk_rate(struct clk *clk, unsigned long rate); | ||
121 | 126 | ||
122 | extern struct platform_device davinci_wdt_device; | 127 | extern struct platform_device davinci_wdt_device; |
123 | extern void davinci_watchdog_reset(struct platform_device *); | 128 | extern void davinci_watchdog_reset(struct platform_device *); |