@@ -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"
@@ -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
@@ -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;
- /* 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;
@@ -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;
new file mode 100644
@@ -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 <tony@atomide.com>
+ *
+ * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Updated to support OMAP3
+ * Rajendra Nayak <rnayak@ti.com>
+ *
+ * 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 <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/system.h>
+#include <mach/clock.h>
+#include <mach/common.h>
+
+#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()
+ */
+
@@ -12,6 +12,8 @@
#ifndef __ARCH_ARM_MACH_DAVINCI_COMMON_H
#define __ARCH_ARM_MACH_DAVINCI_COMMON_H
+#include <linux/cpufreq.h>
+
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;
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 <nsekhar@ti.com> --- 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