From b39639b820ca64e55c39b5217773919ca7973cec Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Tue, 20 Jul 2010 16:46:49 +0530 Subject: 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 Signed-off-by: Kevin Hilman --- arch/arm/mach-davinci/clock.c | 73 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'arch/arm/mach-davinci/clock.c') 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)) -- cgit v1.2.3-55-g7522