summaryrefslogtreecommitdiff
path: root/arch/arm/mach-davinci/clock.c
diff options
context:
space:
mode:
authorSekhar Nori <nsekhar@ti.com>2010-07-20 15:16:49 +0400
committerKevin Hilman <khilman@deeprootsystems.com>2010-09-24 18:40:24 +0400
commitb39639b820ca64e55c39b5217773919ca7973cec (patch)
tree53b2eba184359e1aa748e40891b3fd4149228697 /arch/arm/mach-davinci/clock.c
parent0a477f6b8c1849701981f6e00e7afcd35af75546 (diff)
downloadlinux-b39639b820ca64e55c39b5217773919ca7973cec.tar.xz
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/mach-davinci/clock.c')
-rw-r--r--arch/arm/mach-davinci/clock.c73
1 files changed, 73 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)
return rate;
}
+int davinci_set_sysclk_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned v;
+ struct pll_data *pll;
+ unsigned long input;
+ unsigned ratio = 0;
+
+ /* If this is the PLL base clock, wrong function to call */
+ if (clk->pll_data)
+ return -EINVAL;
+
+ /* There must be a parent... */
+ if (WARN_ON(!clk->parent))
+ return -EINVAL;
+
+ /* ... the parent must be a PLL... */
+ if (WARN_ON(!clk->parent->pll_data))
+ return -EINVAL;
+
+ /* ... and this clock must have a divider. */
+ if (WARN_ON(!clk->div_reg))
+ return -EINVAL;
+
+ pll = clk->parent->pll_data;
+
+ input = clk->parent->rate;
+
+ /* If pre-PLL, source clock is before the multiplier and divider(s) */
+ if (clk->flags & PRE_PLL)
+ input = pll->input_rate;
+
+ if (input > rate) {
+ /*
+ * Can afford to provide an output little higher than requested
+ * only if maximum rate supported by hardware on this sysclk
+ * is known.
+ */
+ if (clk->maxrate) {
+ ratio = DIV_ROUND_CLOSEST(input, rate);
+ if (input / ratio > clk->maxrate)
+ ratio = 0;
+ }
+
+ if (ratio == 0)
+ ratio = DIV_ROUND_UP(input, rate);
+
+ ratio--;
+ }
+
+ if (ratio > PLLDIV_RATIO_MASK)
+ return -EINVAL;
+
+ do {
+ v = __raw_readl(pll->base + PLLSTAT);
+ } while (v & PLLSTAT_GOSTAT);
+
+ v = __raw_readl(pll->base + clk->div_reg);
+ v &= ~PLLDIV_RATIO_MASK;
+ v |= ratio | PLLDIV_EN;
+ __raw_writel(v, pll->base + clk->div_reg);
+
+ v = __raw_readl(pll->base + PLLCMD);
+ v |= PLLCMD_GOSET;
+ __raw_writel(v, pll->base + PLLCMD);
+
+ do {
+ v = __raw_readl(pll->base + PLLSTAT);
+ } while (v & PLLSTAT_GOSTAT);
+
+ return 0;
+}
+EXPORT_SYMBOL(davinci_set_sysclk_rate);
+
static unsigned long clk_leafclk_recalc(struct clk *clk)
{
if (WARN_ON(!clk->parent))