From patchwork Tue Aug 18 14:52:13 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sekhar Nori X-Patchwork-Id: 42336 Received: from devils.ext.ti.com (devils.ext.ti.com [198.47.26.153]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n7IEsDo4015811 for ; Tue, 18 Aug 2009 14:54:14 GMT Received: from dlep33.itg.ti.com ([157.170.170.112]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id n7IEqQub021186; Tue, 18 Aug 2009 09:52:31 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep33.itg.ti.com (8.13.7/8.13.7) with ESMTP id n7IEqMcl015163; Tue, 18 Aug 2009 09:52:22 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id A0E1180627; Tue, 18 Aug 2009 09:52:22 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dbdp31.itg.ti.com (dbdp31.itg.ti.com [172.24.170.98]) by linux.omap.com (Postfix) with ESMTP id 5FE1980626 for ; Tue, 18 Aug 2009 09:52:20 -0500 (CDT) Received: from psplinux051.india.ti.com (localhost [127.0.0.1]) by dbdp31.itg.ti.com (8.13.8/8.13.8) with ESMTP id n7IEqI4a027060; Tue, 18 Aug 2009 20:22:18 +0530 (IST) Received: from psplinux051.india.ti.com (localhost [127.0.0.1]) by psplinux051.india.ti.com (8.13.1/8.13.1) with ESMTP id n7IEqIQZ006125; Tue, 18 Aug 2009 20:22:18 +0530 Received: (from a0875516@localhost) by psplinux051.india.ti.com (8.13.1/8.13.1/Submit) id n7IEqHCd006121; Tue, 18 Aug 2009 20:22:17 +0530 From: Sekhar Nori To: davinci-linux-open-source@linux.davincidsp.com Date: Tue, 18 Aug 2009 20:22:13 +0530 Message-Id: <1250607137-5965-1-git-send-email-nsekhar@ti.com> X-Mailer: git-send-email 1.6.2.4 Cc: Subject: [PATCH v3 1/5] davinci: clock framework updates for CPUFreq support X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.4 Precedence: list List-Id: davinci-linux-open-source.linux.davincidsp.com List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com Update the clock framework for dynamic CPU frequency change. clk_round_rate, clk_set_rate have been updated to handle dynamic frequency changes. 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. The clk_set_parent() API is implemented to reparent clocks to asynchronous domains where possible and insulate them from frequency changes. An inline function is introduced to get to the parent PLL of a given clock. This is useful in finding out all the clocks which gets affected by a PLL rate change. Finally, another function is implemented to recalculate the PLL derived rates after the PLL rate has been changed. Tested on OMAP-L138 EVM. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/clock.c | 140 ++++++++++++++++++++++++++++++++++++++++- arch/arm/mach-davinci/clock.h | 18 +++++ 2 files changed, 155 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c index 83d54d5..5cbb95e 100644 --- a/arch/arm/mach-davinci/clock.c +++ b/arch/arm/mach-davinci/clock.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -99,20 +100,50 @@ long clk_round_rate(struct clk *clk, unsigned long rate) if (clk == NULL || IS_ERR(clk)) return -EINVAL; + if (clk->round_rate) + return clk->round_rate(clk, rate); + return clk->rate; } EXPORT_SYMBOL(clk_round_rate); int clk_set_rate(struct clk *clk, unsigned long rate) { + unsigned long flags; + int ret = -EINVAL; + if (clk == NULL || IS_ERR(clk)) - return -EINVAL; + return ret; - /* changing the clk rate is not supported */ - return -EINVAL; + spin_lock_irqsave(&clockfw_lock, flags); + if (clk->set_rate) + ret = clk->set_rate(clk, rate); + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; } EXPORT_SYMBOL(clk_set_rate); +/* Currently only 'leaf' clocks are supported */ +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + unsigned long flags; + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk)) + return ret; + + spin_lock_irqsave(&clockfw_lock, flags); + if (clk->parent && !(clk->flags & CLK_PLL)) { + clk->parent = parent; + clk->rate = clk->parent->rate; + ret = 0; + } + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; +} + int clk_register(struct clk *clk) { if (clk == NULL || IS_ERR(clk)) @@ -273,6 +304,109 @@ static void __init clk_pll_init(struct clk *clk) pr_debug("] --> %lu MHz output.\n", clk->rate / 1000000); } +/** + * davinci_set_pllrate - set the output rate of a given PLL. + * + * Note: Currently tested to work with OMAP-L138 only. + * + * @pll: pll whose rate needs to be changed. + * @prediv: The pre divider value. Passing 0 disables the pre-divider. + * @pllm: The multiplier value. Passing 0 leads to multiply-by-one. + * @postdiv: The post divider value. Passing 0 disables the post-divider. + */ +int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, + unsigned int mult, unsigned int postdiv) +{ + u32 ctrl; + unsigned int locktime; + + if (pll->base == NULL) + return -EINVAL; + + /* + * PLL lock time required per OMAP-L138 datasheet is + * (2000 * prediv)/sqrt(pllm) OSCIN cycles. We approximate sqrt(pllm) + * as 4 and OSCIN cycle as 25 MHz. + */ + if (prediv) { + locktime = ((2000 * prediv) / 100); + prediv = (prediv - 1) | PLLDIV_EN; + } else { + locktime = 20; + } + if (postdiv) + postdiv = (postdiv - 1) | PLLDIV_EN; + if (mult) + mult = mult - 1; + + ctrl = __raw_readl(pll->base + PLLCTL); + + /* Switch the PLL to bypass mode */ + ctrl &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN); + __raw_writel(ctrl, pll->base + PLLCTL); + + /* + * Wait for 4 OSCIN/CLKIN cycles to ensure that the PLLC has switched + * to bypass mode. Delay of 1us ensures we are good for all > 4MHz + * OSCIN/CLKIN inputs. Typically the input is ~25MHz. + */ + udelay(1); + + /* Reset and enable PLL */ + ctrl &= ~(PLLCTL_PLLRST | PLLCTL_PLLDIS); + __raw_writel(ctrl, pll->base + PLLCTL); + + if (pll->flags & PLL_HAS_PREDIV) + __raw_writel(prediv, pll->base + PREDIV); + + __raw_writel(mult, pll->base + PLLM); + + if (pll->flags & PLL_HAS_POSTDIV) + __raw_writel(postdiv, pll->base + POSTDIV); + + /* + * Wait for PLL to reset properly, OMAP-L138 datasheet says + * 'min' time = 125ns + */ + udelay(1); + + /* Bring PLL out of reset */ + ctrl |= PLLCTL_PLLRST; + __raw_writel(ctrl, pll->base + PLLCTL); + + udelay(locktime); + + /* Remove PLL from bypass mode */ + ctrl |= PLLCTL_PLLEN; + __raw_writel(ctrl, pll->base + PLLCTL); + + return 0; +} +EXPORT_SYMBOL(davinci_set_pllrate); + +/** + * davinci_clk_recalc_rates - Re-calculate rates of PLL derived clocks of + * the given clock tree. + * + * @clocks: pointer to the clock tree + */ +int davinci_clk_recalc_rates(struct davinci_clk *clocks) +{ + struct davinci_clk *c; + + for (c = clocks; c->lk.clk; c++) { + struct clk *clk = c->lk.clk; + + if (!clk->pll_data && clk->flags & CLK_PLL) + clk_sysclk_recalc(clk); + else if (clk->parent && !(clk->flags & CLK_PLL)) + clk->rate = clk->parent->rate; + } + + return 0; +} +EXPORT_SYMBOL(davinci_clk_recalc_rates); + int __init davinci_clk_init(struct davinci_clk *clocks) { struct davinci_clk *c; diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h index 27233cb..74e0389 100644 --- a/arch/arm/mach-davinci/clock.h +++ b/arch/arm/mach-davinci/clock.h @@ -22,6 +22,10 @@ /* PLL/Reset register offsets */ #define PLLCTL 0x100 #define PLLCTL_PLLEN BIT(0) +#define PLLCTL_PLLPWRDN BIT(1) +#define PLLCTL_PLLRST BIT(3) +#define PLLCTL_PLLDIS BIT(4) +#define PLLCTL_PLLENSRC BIT(5) #define PLLCTL_CLKMODE BIT(8) #define PLLM 0x110 @@ -71,6 +75,8 @@ struct clk { struct clk *parent; struct pll_data *pll_data; u32 div_reg; + int (*set_rate) (struct clk *clk, unsigned long rate); + int (*round_rate) (struct clk *clk, unsigned long rate); }; /* Clock flags */ @@ -94,6 +100,18 @@ struct davinci_clk { } int davinci_clk_init(struct davinci_clk *clocks); +int davinci_clk_recalc_rates(struct davinci_clk *clocks); +int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, + unsigned int mult, unsigned int postdiv); + +/* returns clk structure of the PLL clk if PLL derived clock, NULL otherwise */ +static inline struct clk *clk_get_parent_pll(struct clk* clk) +{ + while (clk && !clk->pll_data) + clk = clk->parent; + + return clk; +} extern struct platform_device davinci_wdt_device;