From patchwork Mon Jul 6 13:13:20 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sekhar Nori X-Patchwork-Id: 34241 Received: from arroyo.ext.ti.com (arroyo.ext.ti.com [192.94.94.40]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n66DFOFG003054 for ; Mon, 6 Jul 2009 13:15:25 GMT Received: from dlep35.itg.ti.com ([157.170.170.118]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id n66DDRft017852; Mon, 6 Jul 2009 08:13:32 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep35.itg.ti.com (8.13.7/8.13.7) with ESMTP id n66DDPEf018227; Mon, 6 Jul 2009 08:13:26 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id 63DB680627; Mon, 6 Jul 2009 08:13:25 -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 C5EF680626 for ; Mon, 6 Jul 2009 08:13:23 -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 n66DDLDX011749; Mon, 6 Jul 2009 18:43:22 +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 n66DDL09002319; Mon, 6 Jul 2009 18:43:21 +0530 Received: (from a0875516@localhost) by psplinux051.india.ti.com (8.13.1/8.13.1/Submit) id n66DDLrf002316; Mon, 6 Jul 2009 18:43:21 +0530 From: Sekhar Nori To: davinci-linux-open-source@linux.davincidsp.com Date: Mon, 6 Jul 2009 18:43:20 +0530 Message-Id: <1246886001-2285-1-git-send-email-nsekhar@ti.com> X-Mailer: git-send-email 1.6.2.4 Cc: Subject: [PATCH 1/2] davinci: 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 This patch adds minimal cpufreq support for DaVinci devices. The patch implements cpufreq driver, support to change PLL output rate and recalculation of the rates of PLL derived clocks Tested using OMAP-L138 EVM. Signed-off-by: Sekhar Nori --- This patch set depends on the two patch set posted by Sudhakar adding DA850/OMAP-L138 support. arch/arm/Kconfig | 2 +- arch/arm/mach-davinci/Makefile | 3 + arch/arm/mach-davinci/clock.c | 105 +++++++++++++++- arch/arm/mach-davinci/clock.h | 9 ++ arch/arm/mach-davinci/cpu-davinci.c | 179 +++++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/common.h | 3 + 6 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 arch/arm/mach-davinci/cpu-davinci.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 915b393..79411ce 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1189,7 +1189,7 @@ endmenu menu "CPU Power Management" -if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA) +if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA || ARCH_DAVINCI) source "drivers/cpufreq/Kconfig" diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile index 2e11e84..14b9527 100644 --- a/arch/arm/mach-davinci/Makefile +++ b/arch/arm/mach-davinci/Makefile @@ -29,3 +29,6 @@ obj-$(CONFIG_MACH_DAVINCI_DM6467_EVM) += board-dm646x-evm.o obj-$(CONFIG_MACH_DAVINCI_DM365_EVM) += board-dm365-evm.o obj-$(CONFIG_MACH_DAVINCI_DA830_EVM) += board-da830-evm.o obj-$(CONFIG_MACH_DAVINCI_DA850_EVM) += board-da850-evm.o + +# Power Management +obj-$(CONFIG_CPU_FREQ) += cpu-davinci.o diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c index 83d54d5..c284a17 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,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; - /* 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); @@ -273,6 +284,94 @@ 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 + * + * @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 = 4/25 us ~= 1 us + */ + 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); + + /* Bring PLL out of reset */ + ctrl |= PLLCTL_PLLRST; + __raw_writel(ctrl, pll->base + PLLCTL); + + /* + * Wait for PLL to lock. FIXME: justify this value. TRM remains + * ambiguous as well + */ + udelay(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; diff --git a/arch/arm/mach-davinci/cpu-davinci.c b/arch/arm/mach-davinci/cpu-davinci.c new file mode 100644 index 0000000..52527f1 --- /dev/null +++ b/arch/arm/mach-davinci/cpu-davinci.c @@ -0,0 +1,179 @@ +/* + * CPU frequency scaling for DaVinci + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * Based on linux/arch/arm/plat-omap/cpu-omap.c. Original Copyright follows: + * + * Copyright (C) 2005 Nokia Corporation + * Written by Tony Lindgren + * + * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King + * + * Copyright (C) 2007-2008 Texas Instruments, Inc. + * Updated to support OMAP3 + * Rajendra Nayak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "clock.h" + +#define VERY_HI_RATE 900000000 + +static struct cpufreq_frequency_table *freq_table; +static struct clk *armclk; + +static int davinci_verify_speed(struct cpufreq_policy *policy) +{ + if (freq_table) + return cpufreq_frequency_table_verify(policy, freq_table); + + if (policy->cpu) + return -EINVAL; + + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + + policy->min = clk_round_rate(armclk, policy->min * 1000) / 1000; + policy->max = clk_round_rate(armclk, policy->max * 1000) / 1000; + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + return 0; +} + +static unsigned int davinci_getspeed(unsigned int cpu) +{ + unsigned long rate; + + if (cpu) + return 0; + + rate = clk_get_rate(armclk) / 1000; + + return rate; +} + +static int davinci_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct cpufreq_freqs freqs; + int ret = 0; + + /* Ensure desired rate is within allowed range. Some govenors + * (ondemand) will just pass target_freq=0 to get the minimum. */ + if (target_freq < policy->cpuinfo.min_freq) + target_freq = policy->cpuinfo.min_freq; + if (target_freq > policy->cpuinfo.max_freq) + target_freq = policy->cpuinfo.max_freq; + + freqs.old = davinci_getspeed(0); + freqs.new = clk_round_rate(armclk, target_freq * 1000) / 1000; + freqs.cpu = 0; + + if (freqs.old == freqs.new) + return ret; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); +#ifdef CONFIG_CPU_FREQ_DEBUG + printk(KERN_DEBUG "cpufreq-davinci: transition: %u --> %u\n", + freqs.old, freqs.new); +#endif + ret = clk_set_rate(armclk, freqs.new * 1000); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return ret; +} + +static int __init davinci_cpu_init(struct cpufreq_policy *policy) +{ + int result = 0; + + armclk = clk_get(NULL, "arm"); + if (IS_ERR(armclk)) { + printk(KERN_ERR "cpufreq-davinci: Unable to get ARM clock\n"); + return PTR_ERR(armclk); + } + + if (policy->cpu != 0) { + clk_put(armclk); + return -EINVAL; + } + + policy->cur = policy->min = policy->max = davinci_getspeed(0); + + davinci_soc_info.init_cpufreq_table(&freq_table); + if (freq_table) { + result = cpufreq_frequency_table_cpuinfo(policy, freq_table); + if (!result) + cpufreq_frequency_table_get_attr(freq_table, + policy->cpu); + } else { + policy->cpuinfo.min_freq = clk_round_rate(armclk, 0) / 1000; + policy->cpuinfo.max_freq = clk_round_rate(armclk, + VERY_HI_RATE) / 1000; + } + + clk_set_rate(armclk, policy->cpuinfo.max_freq * 1000); + + policy->min = policy->cpuinfo.min_freq; + policy->max = policy->cpuinfo.max_freq; + policy->cur = davinci_getspeed(0); + + /* FIXME: what's the actual transition time? */ + policy->cpuinfo.transition_latency = 1 * 1000 * 1000; + return 0; +} + +static int davinci_cpu_exit(struct cpufreq_policy *policy) +{ + clk_put(armclk); + return 0; +} + +static struct freq_attr *davinci_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver davinci_driver = { + .flags = CPUFREQ_STICKY, + .verify = davinci_verify_speed, + .target = davinci_target, + .get = davinci_getspeed, + .init = davinci_cpu_init, + .exit = davinci_cpu_exit, + .name = "davinci", + .attr = davinci_cpufreq_attr, +}; + +static int __init davinci_cpufreq_init(void) +{ + return cpufreq_register_driver(&davinci_driver); +} + +late_initcall(davinci_cpufreq_init); + +/* + * if ever we want to remove this, upon cleanup call: + * + * cpufreq_unregister_driver() + * cpufreq_frequency_table_put_attr() + */ + diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index 1fd3917..1487a57 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -12,6 +12,8 @@ #ifndef __ARCH_ARM_MACH_DAVINCI_COMMON_H #define __ARCH_ARM_MACH_DAVINCI_COMMON_H +#include + struct sys_timer; extern struct sys_timer davinci_timer; @@ -68,6 +70,7 @@ struct davinci_soc_info { struct emac_platform_data *emac_pdata; dma_addr_t sram_dma; unsigned sram_len; + void (*init_cpufreq_table) (struct cpufreq_frequency_table **); }; extern struct davinci_soc_info davinci_soc_info;