From: 黄涛 <huangtao@rock-chips.com>
Date: Thu, 13 May 2010 09:01:08 +0000 (+0000)
Subject: add cpufreq support
X-Git-Tag: firefly_0821_release~11550
X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=82dd9cc400864655c733ee33999a8c24a5fa4423;p=firefly-linux-kernel-4.4.55.git

add cpufreq support
---

diff --git a/.config b/.config
index 4410605dce39..22afaf7228e7 100644
--- 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
 
 #
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 270b2c28b2c7..69c7e1becf06 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -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
diff --git a/arch/arm/mach-rk2818/clock.c b/arch/arm/mach-rk2818/clock.c
index 7435aa9512a7..4fa5b845a153 100644
--- a/arch/arm/mach-rk2818/clock.c
+++ b/arch/arm/mach-rk2818/clock.c
@@ -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
index 000000000000..36b85fa26951
--- /dev/null
+++ b/arch/arm/mach-rk2818/cpufreq.c
@@ -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);
+