add cpufreq support
author黄涛 <huangtao@rock-chips.com>
Thu, 13 May 2010 09:01:08 +0000 (09:01 +0000)
committer黄涛 <huangtao@rock-chips.com>
Mon, 21 Jun 2010 05:34:52 +0000 (13:34 +0800)
.config
arch/arm/Kconfig
arch/arm/mach-rk2818/clock.c
arch/arm/mach-rk2818/cpufreq.c [new file with mode: 0755]

diff --git a/.config b/.config
index 4410605dce396b5ff869b0049cec4508562cebd5..22afaf7228e7cb6192a71360b3e0d5bfb0af3bfa 100644 (file)
--- a/.config
+++ b/.config
@@ -17,6 +17,7 @@ CONFIG_TRACE_IRQFLAGS_SUPPORT=y
 CONFIG_HARDIRQS_SW_RESEND=y
 CONFIG_GENERIC_IRQ_PROBE=y
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
 CONFIG_GENERIC_HWEIGHT=y
 CONFIG_GENERIC_CALIBRATE_DELAY=y
 CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
@@ -277,6 +278,21 @@ CONFIG_CMDLINE="mem=128M console=ttyS1,115200n8"
 #
 # CPU Power Management
 #
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
 # CONFIG_CPU_IDLE is not set
 
 #
index 270b2c28b2c79576748c27e2b18871c1be332b78..69c7e1becf063f9ce61072a00a1185d6c9ed9581 100644 (file)
@@ -708,6 +708,7 @@ config ARCH_RK2818
        select HAVE_TCM
        select HAVE_CLK
        select COMMON_CLKDEV
+       select ARCH_HAS_CPUFREQ
         select GENERIC_TIME
         select GENERIC_CLOCKEVENTS
         select ARCH_REQUIRE_GPIOLIB
index 7435aa9512a7a707a392d269926518613b438c05..4fa5b845a153e2443a0f637193610bda300ad9e5 100644 (file)
@@ -1249,6 +1249,15 @@ static void clk_enable_init_clocks(void)
        }
 }
 
+#ifdef CONFIG_CPU_FREQ
+#include <linux/cpufreq.h>
+
+void clk_init_cpufreq_table(struct cpufreq_frequency_table **table)
+{
+}
+EXPORT_SYMBOL(clk_init_cpufreq_table);
+#endif /* CONFIG_CPU_FREQ */
+
 static unsigned int __initdata armclk;
 
 /*
diff --git a/arch/arm/mach-rk2818/cpufreq.c b/arch/arm/mach-rk2818/cpufreq.c
new file mode 100755 (executable)
index 0000000..36b85fa
--- /dev/null
@@ -0,0 +1,107 @@
+/* arch/arm/mach-rk2818/cpufreq.c
+ *
+ * Copyright (C) 2010 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/init.h>
+
+static struct cpufreq_frequency_table *freq_table;
+static struct clk *arm_clk;
+
+static int rk2818_cpufreq_verify(struct cpufreq_policy *policy)
+{
+       return cpufreq_frequency_table_verify(policy, freq_table);
+}
+
+static int rk2818_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation)
+{
+       int index;
+       struct cpufreq_freqs freqs;
+
+       if (policy->cpu != 0)
+               return -EINVAL;
+       if (cpufreq_frequency_table_target(policy, freq_table, target_freq, relation, &index)) {
+               pr_err("cpufreq: invalid target_freq: %d\n", target_freq);
+               return -EINVAL;
+       }
+
+       if (policy->cur == freq_table[index].frequency)
+               return 0;
+
+#ifdef CONFIG_CPU_FREQ_DEBUG
+       printk("%s %d r %d (%d-%d) selected %d\n", __func__, target_freq, relation, policy->min, policy->max, freq_table[index].frequency);
+#endif
+       freqs.old = policy->cur;
+       freqs.new = freq_table[index].frequency;
+       freqs.cpu = 0;
+       cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+       clk_set_rate(arm_clk, freqs.new * 1000);
+       cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+       return 0;
+}
+
+extern void clk_init_cpufreq_table(struct cpufreq_frequency_table **table);
+
+static int __init rk2818_cpufreq_init(struct cpufreq_policy *policy)
+{
+       arm_clk = clk_get(NULL, "arm");
+       if (IS_ERR(arm_clk))
+               return PTR_ERR(arm_clk);
+
+       if (policy->cpu != 0)
+               return -EINVAL;
+
+       clk_init_cpufreq_table(&freq_table);
+       if (!freq_table)
+               return -EINVAL;
+
+       BUG_ON(cpufreq_frequency_table_cpuinfo(policy, freq_table));
+       cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
+       policy->cur = clk_get_rate(arm_clk) / 1000;
+       policy->cpuinfo.transition_latency = 300 * NSEC_PER_USEC; // FIXME: 0.3ms?
+
+       return 0;
+}
+
+static int rk2818_cpufreq_exit(struct cpufreq_policy *policy)
+{
+       clk_put(arm_clk);
+       return 0;
+}
+
+static struct freq_attr *rk2818_cpufreq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
+static struct cpufreq_driver rk2818_cpufreq_driver = {
+       .flags          = CPUFREQ_STICKY,
+       .init           = rk2818_cpufreq_init,
+       .exit           = rk2818_cpufreq_exit,
+       .verify         = rk2818_cpufreq_verify,
+       .target         = rk2818_cpufreq_target,
+       .name           = "rk2818",
+       .attr           = rk2818_cpufreq_attr,
+};
+
+static int __init rk2818_cpufreq_register(void)
+{
+       return cpufreq_register_driver(&rk2818_cpufreq_driver);
+}
+
+late_initcall(rk2818_cpufreq_register);
+