From patchwork Tue Aug 18 14:52:16 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sekhar Nori X-Patchwork-Id: 42337 Received: from comal.ext.ti.com (comal.ext.ti.com [198.47.26.152]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n7IEsFX5015821 for ; Tue, 18 Aug 2009 14:54:15 GMT Received: from dlep33.itg.ti.com ([157.170.170.112]) by comal.ext.ti.com (8.13.7/8.13.7) with ESMTP id n7IEqT9v017091; Tue, 18 Aug 2009 09:52:34 -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 n7IEqSg7015204; Tue, 18 Aug 2009 09:52:29 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id C432980627; Tue, 18 Aug 2009 09:52:27 -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 68C6580627 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 n7IEqILi027063; 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 n7IEqIYC006143; 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 n7IEqI4g006140; Tue, 18 Aug 2009 20:22:18 +0530 From: Sekhar Nori To: davinci-linux-open-source@linux.davincidsp.com Date: Tue, 18 Aug 2009 20:22:16 +0530 Message-Id: <1250607137-5965-4-git-send-email-nsekhar@ti.com> X-Mailer: git-send-email 1.6.2.4 In-Reply-To: <1250607137-5965-3-git-send-email-nsekhar@ti.com> References: <1250607137-5965-1-git-send-email-nsekhar@ti.com> <1250607137-5965-2-git-send-email-nsekhar@ti.com> <1250607137-5965-3-git-send-email-nsekhar@ti.com> Cc: Subject: [PATCH v3 4/5] davinci: DA850/OMAP-L138: add 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 Adds basic CPUFreq support for DA850/OMAP-L138. Currently, frequency scaling only on PLL0 is supported. No scaling of PLL1 or voltage levels as yet. Peripherals like MMC/SD which have a clock input synchronous with ARM clock will not work well since the clock will change behind their backs. Support for notification to such devices to adjust themselves to the new frequency will be added in later patches. Current defconfigs keep CPUFreq disabled so it will not affect normal operation. The OPP defintions assume clock input of 24MHz to the SoC. This is inline with hardcoding of input frequency in the .c files. At some point this will need to move into board dependent code as new boards appear with a different input clock. Tested with ondemand governer and a shell script to vary processor load. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/da850.c | 171 ++++++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 1 + 2 files changed, 172 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 4123ea5..5f379c4 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -40,6 +41,10 @@ #define DA850_REF_FREQ 24000000 #define CFGCHIP3_ASYNC3_CLKSRC BIT(4) +#define CFGCHIP0_PLL_MASTER_LOCK BIT(4) + +static int da850_set_armrate(struct clk *clk, unsigned long rate); +static int da850_round_armrate(struct clk *clk, unsigned long rate); static struct pll_data pll0_data = { .num = 1, @@ -283,6 +288,8 @@ static struct clk arm_clk = { .parent = &pll0_sysclk6, .lpsc = DA8XX_LPSC0_ARM, .flags = ALWAYS_ENABLED, + .set_rate = da850_set_armrate, + .round_rate = da850_round_armrate, }; static struct clk rmii_clk = { @@ -798,6 +805,148 @@ static struct davinci_timer_info da850_timer_info = { }; #ifdef CONFIG_CPU_FREQ +/* + * Notes: + * According to the TRM, minimum PLLM results in maximum power savings. + * The OPP definitions below should keep the PLLM as low as possible. + * + * The output of the PLLM must be between 400 to 600 MHz. + * This rules out prediv of anything but divide-by-one for 24Mhz OSC input. + */ +struct da850_opp { + unsigned int freq; /* in KHz */ + unsigned int prediv; + unsigned int mult; + unsigned int postdiv; +}; + +static const struct da850_opp da850_opp_300 = { + .freq = 300000, + .prediv = 1, + .mult = 25, + .postdiv = 2, +}; + +static const struct da850_opp da850_opp_200 = { + .freq = 200000, + .prediv = 1, + .mult = 25, + .postdiv = 3, +}; + +static const struct da850_opp da850_opp_96 = { + .freq = 96000, + .prediv = 1, + .mult = 20, + .postdiv = 5, +}; + +#define OPP(freq) \ + { \ + .index = (unsigned int) &da850_opp_##freq, \ + .frequency = freq * 1000, \ + } + +static struct cpufreq_frequency_table da850_freq_table[] = { + OPP(300), + OPP(200), + OPP(96), + { + .index = 0, + .frequency = CPUFREQ_TABLE_END, + }, +}; + +static struct davinci_clk da850_pll0_clks[ARRAY_SIZE(da850_clks)]; + +static void da850_init_cpufreq_table(struct cpufreq_frequency_table **table) +{ + *table = &da850_freq_table[0]; +} + +static void da850_init_pll0_childlist(void) +{ + struct davinci_clk *c, *v = da850_pll0_clks; + + for (c = da850_clks; c->lk.clk; c++) { + struct clk *clk = c->lk.clk; + struct clk *pll = clk_get_parent_pll(clk); + + if (pll && !strcmp(pll->name, "pll0")) + *v++ = *c; + } +} + +static int da850_round_armrate(struct clk *clk, unsigned long rate) +{ + int i, ret = 0, diff; + unsigned int best = (unsigned int) -1; + + rate /= 1000; /* convert to kHz */ + + for (i = 0; da850_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + diff = da850_freq_table[i].frequency - rate; + if (diff < 0) + diff = -diff; + + if (diff < best) { + best = diff; + ret = da850_freq_table[i].frequency; + } + } + + return ret * 1000; +} + +static int da850_set_armrate(struct clk *clk, unsigned long rate) +{ + int i; + unsigned int prediv, mult, postdiv; + struct da850_opp *opp; + struct clk *pllclk = &pll0_clk; + struct pll_data *pll = pllclk->pll_data; + unsigned int v; + int ret; + + /* convert to KHz */ + rate /= 1000; + + for (i = 0; da850_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + if (rate == da850_freq_table[i].frequency) + break; + } + + if (da850_freq_table[i].frequency == CPUFREQ_TABLE_END) { + printk(KERN_WARNING "%s: Unsupported ARM clock rate %ld\n", + __func__, rate); + return -EINVAL; + } + + opp = (struct da850_opp *) da850_freq_table[i].index; + prediv = opp->prediv; + mult = opp->mult; + postdiv = opp->postdiv; + + /* Unlock writing to PLL registers */ + v = __raw_readl(IO_ADDRESS(DA8XX_CFGCHIP0_REG)); + v &= ~CFGCHIP0_PLL_MASTER_LOCK; + __raw_writel(v, IO_ADDRESS(DA8XX_CFGCHIP0_REG)); + + ret = davinci_set_pllrate(pll, prediv, mult, postdiv); + if (WARN_ON(ret)) + return ret; + + /* Propogate new rate */ + pllclk->rate = pllclk->parent->rate; + pllclk->rate /= prediv; + pllclk->rate *= mult; + pllclk->rate /= postdiv; + + davinci_clk_recalc_rates(da850_pll0_clks); + + return 0; +} + static void da850_set_async3_src(int pllnum) { struct clk *clk, *newparent = pllnum ? &pll1_sysclk2 : &pll0_sysclk2; @@ -822,6 +971,25 @@ static void da850_set_async3_src(int pllnum) __raw_writel(v, IO_ADDRESS(DA8XX_CFGCHIP3_REG)); } #else +static void da850_init_cpufreq_table(struct cpufreq_frequency_table **table) +{ + *table = NULL; +} + +static void da850_init_pll0_childlist(void) +{ +} + +static int da850_set_armrate(struct clk *clk, unsigned long rate) +{ + return -EINVAL; +} + +static int da850_round_armrate(struct clk *clk, unsigned long rate) +{ + return clk->rate; +} + static void da850_set_async3_src(int pllnum) { } @@ -849,6 +1017,7 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .gpio_irq = IRQ_DA8XX_GPIO0, .serial_dev = &da8xx_serial_device, .emac_pdata = &da8xx_emac_pdata, + .init_cpufreq_table = da850_init_cpufreq_table, }; void __init da850_init(void) @@ -856,4 +1025,6 @@ void __init da850_init(void) davinci_common_init(&davinci_soc_info_da850); da850_set_async3_src(1); + + da850_init_pll0_childlist(); } diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index e3eb953..bc411b2 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -30,6 +30,7 @@ #define DA8XX_CP_INTC_VIRT (IO_VIRT - DA8XX_CP_INTC_SIZE - SZ_4K) #define DA8XX_BOOT_CFG_BASE (IO_PHYS + 0x14000) +#define DA8XX_CFGCHIP0_REG (DA8XX_BOOT_CFG_BASE + 0x17c) #define DA8XX_CFGCHIP3_REG (DA8XX_BOOT_CFG_BASE + 0x188) #define DA8XX_PSC0_BASE 0x01c10000