Message ID | 1248155796-9920-2-git-send-email-nsekhar@ti.com (mailing list archive) |
---|---|
State | Rejected |
Headers | show |
Sekhar Nori <nsekhar@ti.com> writes: > 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. > > 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 <nsekhar@ti.com> I think this is a good starting point. As Dave pointed out, there will need to be some more sanity checking built into this, but I still think this is a good place to start. Pushing today. Kevin > --- > arch/arm/mach-davinci/clock.c | 115 +++++++++++++++++++++++++++++++++++++++- > arch/arm/mach-davinci/clock.h | 9 +++ > 2 files changed, 121 insertions(+), 3 deletions(-) > > diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c > index 83d54d5..8c108eb 100644 > --- a/arch/arm/mach-davinci/clock.c > +++ b/arch/arm/mach-davinci/clock.c > @@ -19,6 +19,7 @@ > #include <linux/mutex.h> > #include <linux/platform_device.h> > #include <linux/io.h> > +#include <linux/delay.h> > > #include <mach/hardware.h> > > @@ -99,17 +100,27 @@ 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; > + > + spin_lock_irqsave(&clockfw_lock, flags); > + if (clk->set_rate) > + ret = clk->set_rate(clk, rate); > + spin_unlock_irqrestore(&clockfw_lock, flags); > > - /* changing the clk rate is not supported */ > - return -EINVAL; > + return ret; > } > EXPORT_SYMBOL(clk_set_rate); > > @@ -273,6 +284,104 @@ 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: prediv value to be programmed. Passing 0 disables prediv. > + * @pllm: pllm value to be programmed. Passing 0 leads to multiply-by-one. > + * @postdiv: postdiv value to be programmed. Passing 0 disables postdiv. > + */ > +int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, > + unsigned int mult, unsigned int postdiv) > +{ > + u32 ctrl; > + > + BUG_ON(pll->base == NULL); > + > + if (prediv) > + prediv = (prediv - 1) | PLLDIV_EN; > + 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); > + > + /* > + * Wait for PLL to 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. > + */ > + udelay((2000 * (prediv + 1)) / 100); > + > + /* Remove PLL from bypass mode */ > + ctrl |= PLLCTL_PLLEN; > + __raw_writel(ctrl, pll->base + PLLCTL); > + > + return 0; > +} > +EXPORT_SYMBOL(davinci_set_pllrate); > + > +/** > + * davinci_clk_recal_rates - recalculate rates of the davinci clock tree > + * > + * @clocks: pointer to the clock tree > + */ > +int davinci_clk_recalc_rates(struct davinci_clk *clocks) > +{ > + struct davinci_clk *c; > + struct clk *clk; > + > + for (c = clocks; c->lk.clk; c++) { > + clk = c->lk.clk; > + > + /* Re-calculate rates for PLL-derived clocks */ > + if (!clk->pll_data && clk->flags & CLK_PLL) > + clk_sysclk_recalc(clk); > + else if (clk->flags & CLK_PSC) > + 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..f772e6e 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,9 @@ 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); > > extern struct platform_device davinci_wdt_device; > > -- > 1.6.2.4 > > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source@linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c index 83d54d5..8c108eb 100644 --- a/arch/arm/mach-davinci/clock.c +++ b/arch/arm/mach-davinci/clock.c @@ -19,6 +19,7 @@ #include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/delay.h> #include <mach/hardware.h> @@ -99,17 +100,27 @@ 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; + + spin_lock_irqsave(&clockfw_lock, flags); + if (clk->set_rate) + ret = clk->set_rate(clk, rate); + spin_unlock_irqrestore(&clockfw_lock, flags); - /* changing the clk rate is not supported */ - return -EINVAL; + return ret; } EXPORT_SYMBOL(clk_set_rate); @@ -273,6 +284,104 @@ 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: prediv value to be programmed. Passing 0 disables prediv. + * @pllm: pllm value to be programmed. Passing 0 leads to multiply-by-one. + * @postdiv: postdiv value to be programmed. Passing 0 disables postdiv. + */ +int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, + unsigned int mult, unsigned int postdiv) +{ + u32 ctrl; + + BUG_ON(pll->base == NULL); + + if (prediv) + prediv = (prediv - 1) | PLLDIV_EN; + 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); + + /* + * Wait for PLL to 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. + */ + udelay((2000 * (prediv + 1)) / 100); + + /* Remove PLL from bypass mode */ + ctrl |= PLLCTL_PLLEN; + __raw_writel(ctrl, pll->base + PLLCTL); + + return 0; +} +EXPORT_SYMBOL(davinci_set_pllrate); + +/** + * davinci_clk_recal_rates - recalculate rates of the davinci clock tree + * + * @clocks: pointer to the clock tree + */ +int davinci_clk_recalc_rates(struct davinci_clk *clocks) +{ + struct davinci_clk *c; + struct clk *clk; + + for (c = clocks; c->lk.clk; c++) { + clk = c->lk.clk; + + /* Re-calculate rates for PLL-derived clocks */ + if (!clk->pll_data && clk->flags & CLK_PLL) + clk_sysclk_recalc(clk); + else if (clk->flags & CLK_PSC) + 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..f772e6e 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,9 @@ 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); extern struct platform_device davinci_wdt_device;
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. 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 <nsekhar@ti.com> --- arch/arm/mach-davinci/clock.c | 115 +++++++++++++++++++++++++++++++++++++++- arch/arm/mach-davinci/clock.h | 9 +++ 2 files changed, 121 insertions(+), 3 deletions(-)