[PATCH 1/1] CPU Frequency Scaling for VT8500/WM8505
Tony Prisk
service at prisktech.co.nz
Thu Mar 3 02:46:57 EST 2011
Add CPU Frequency Scaling support for VT8500/WM8505 based system.
Signed-off-by: Tony Prisk (linux at prisktech.co.nz)
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
index 7422e8a..e1df248 100644
--- a/arch/arm/mach-vt8500/Makefile
+++ b/arch/arm/mach-vt8500/Makefile
@@ -7,3 +7,7 @@ obj-$(CONFIG_MACH_BV07) += bv07.o
obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
obj-$(CONFIG_HAVE_PWM) += pwm.o
+
+ifeq ($(CONFIG_CPU_FREQ), y)
+ obj-y += cpufreq.o
+endif
diff --git a/arch/arm/mach-vt8500/cpufreq.c b/arch/arm/mach-vt8500/cpufreq.c
new file mode 100644
index 0000000..570d9e8
--- /dev/null
+++ b/arch/arm/mach-vt8500/cpufreq.c
@@ -0,0 +1,209 @@
+/*
+ * linux/arch/arm/mach-vt8500/cpufreq.c
+ *
+ * CPU frequency scaling
+ * Copyright (c) 2008 Wondermedia Technologies, Inc.
+ * Copyright (c) 2011 Tony Prisk.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * 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.
+ *
+ * You should have recieved a copy of the GNU PUblic License along with this
+ * program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <mach/wm8505_regs.h>
+
+#define wmt_cpufreq_read_reg(reg) readl(pmc_base + reg)
+#define wmt_cpufreq_write_reg(reg, val) writel(val, pmc_base + reg)
+
+struct wmt_freq_t {
+ u32 cpu_khz;
+ u32 pll_a;
+ u32 ahb;
+};
+
+static struct wmt_freq_t wmt_freqs[] = {
+ { 50000, 100000000, 50000000 },
+ { 75000, 150000000, 75000000 },
+ { 100000, 200000000, 100000000 },
+ { 125000, 250000000, 62500000 },
+ { 150000, 300000000, 75000000 },
+ { 175000, 350000000, 87500000 },
+ { 200000, 400000000, 100000000 },
+ { 225000, 450000000, 112500000 },
+ { 250000, 500000000, 125000000 },
+ { 275000, 550000000, 137500000 },
+ { 300000, 600000000, 150000000 },
+ { 325000, 650000000, 162500000 },
+ { 350000, 700000000, 175000000 },
+ { 375000, 750000000, 125000000 },
+ { 387500, 775000000, 129166666 },
+};
+
+#define WMT_NUM_FREQS ARRAY_SIZE(wmt_freqs)
+
+struct clk *cpufreq_clk_arm;
+struct clk *cpufreq_clk_ahb;
+
+static u32 wmt_freq_to_idx(u32 khz)
+{
+ int idx;
+
+ for (idx = 0; idx < WMT_NUM_FREQS; idx++)
+ if (wmt_freqs[idx].cpu_khz >= khz)
+ return idx;
+
+ return WMT_NUM_FREQS - 1;
+}
+
+static u32 wmt_idx_to_freq(u32 idx)
+{
+ if (idx < WMT_NUM_FREQS)
+ return wmt_freqs[idx].cpu_khz;
+ else
+ return wmt_freqs[WMT_NUM_FREQS - 1].cpu_khz;
+}
+
+static struct wmt_freq_t *wmt_idx_to_params(u32 idx)
+{
+ if (idx < WMT_NUM_FREQS)
+ return &wmt_freqs[idx];
+ else
+ return &wmt_freqs[WMT_NUM_FREQS - 1];
+}
+
+static unsigned int wmt_cpufreq_get_current(void)
+{
+ return (u32)(clk_get_rate(cpufreq_clk_arm) / 1000);
+}
+
+static unsigned int wmt_cpufreq_get(unsigned int cpu)
+{
+ return wmt_cpufreq_get_current();
+}
+
+static int wmt_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ u32 tmp;
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, \
+ policy->cpuinfo.max_freq);
+
+ tmp = wmt_freqs[wmt_freq_to_idx(policy->min)].cpu_khz;
+
+ if (tmp > policy->max)
+ policy->max = tmp;
+
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, \
+ policy->cpuinfo.max_freq);
+
+ return 0;
+}
+
+static void wmt_speedstep(unsigned int idx)
+{
+ struct wmt_freq_t *np;
+ np = wmt_idx_to_params(idx);
+
+ clk_set_rate(cpufreq_clk_arm, np->cpu_khz * 1000);
+ clk_set_rate(cpufreq_clk_ahb, np->ahb);
+}
+
+static int wmt_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int idx;
+ unsigned long flags;
+ struct cpufreq_freqs freqs;
+
+ idx = wmt_freq_to_idx(target_freq);
+
+ switch (relation) {
+ case CPUFREQ_RELATION_L:
+ if (wmt_idx_to_freq(idx) > policy->max)
+ idx--;
+ break;
+ case CPUFREQ_RELATION_H:
+ if ((wmt_idx_to_freq(idx) > target_freq) &&
+ (wmt_idx_to_freq(idx-1) >= policy->min))
+ idx--;
+ break;
+ }
+
+ freqs.old = wmt_cpufreq_get_current();
+ freqs.new = wmt_idx_to_freq(idx);
+ freqs.cpu = 0;
+
+ if (freqs.new != freqs.old) {
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ local_irq_save(flags);
+ wmt_speedstep(idx);
+ local_irq_restore(flags);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+
+ return 0;
+}
+
+static int wmt_cpufreq_init(struct cpufreq_policy *policy)
+{
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ cpufreq_clk_arm = clk_get(NULL, "arm");
+ if (IS_ERR(cpufreq_clk_arm))
+ return PTR_ERR(cpufreq_clk_arm);
+ cpufreq_clk_ahb = clk_get(NULL, "ahb");
+ if (IS_ERR(cpufreq_clk_ahb))
+ return PTR_ERR(cpufreq_clk_ahb);
+
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ policy->cpuinfo.min_freq = wmt_freqs[0].cpu_khz;
+ policy->cpuinfo.max_freq = wmt_freqs[WMT_NUM_FREQS - 1].cpu_khz;
+ policy->cpuinfo.transition_latency = 1000000; /* 1ms, assumed? */
+ policy->cur = wmt_cpufreq_get_current();
+ policy->min = 100000;
+ policy->max = 350000;
+
+ return 0;
+}
+
+static struct cpufreq_driver wmt_cpufreq_driver = {
+ .flags = CPUFREQ_STICKY,
+ .init = wmt_cpufreq_init,
+ .verify = wmt_cpufreq_verify,
+ .target = wmt_cpufreq_target,
+ .get = wmt_cpufreq_get,
+ .name = "wmt",
+};
+
+static int __init wmt_cpuf_init(void)
+{
+ return cpufreq_register_driver(&wmt_cpufreq_driver);
+}
+
+arch_initcall(wmt_cpuf_init);
More information about the linux-arm-kernel
mailing list