Root/target/linux/xburst/patches-3.2/0012-MIPS-JZ4740-Add-cpufreq-support.patch

1From d0f0d5739a31c12d349980ed05a670fa1e84696d Mon Sep 17 00:00:00 2001
2From: Maarten ter Huurne <maarten@treewalker.org>
3Date: Wed, 16 Mar 2011 03:16:04 +0100
4Subject: [PATCH 12/28] MIPS: JZ4740: Add cpufreq support.
5
6This is a squashed version of Uli's driver that was further developed in the opendingux-kernel repository.
7---
8 arch/mips/Kconfig | 1 +
9 arch/mips/jz4740/Makefile | 1 +
10 arch/mips/jz4740/cpufreq.c | 226 ++++++++++++++++++++++++++++++++++++++
11 arch/mips/kernel/cpufreq/Kconfig | 13 ++-
12 4 files changed, 240 insertions(+), 1 deletions(-)
13 create mode 100644 arch/mips/jz4740/cpufreq.c
14
15diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
16index d46f1da..8128df7 100644
17--- a/arch/mips/Kconfig
18+++ b/arch/mips/Kconfig
19@@ -209,6 +209,7 @@ config MACH_JZ4740
20     select HAVE_PWM
21     select HAVE_CLK
22     select GENERIC_IRQ_CHIP
23+ select CPU_SUPPORTS_CPUFREQ
24 
25 config LANTIQ
26     bool "Lantiq based platforms"
27diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
28index a9dff33..15f828e 100644
29--- a/arch/mips/jz4740/Makefile
30+++ b/arch/mips/jz4740/Makefile
31@@ -16,5 +16,6 @@ obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o
32 # PM support
33 
34 obj-$(CONFIG_PM) += pm.o
35+obj-$(CONFIG_CPU_FREQ_JZ) += cpufreq.o
36 
37 ccflags-y := -Werror -Wall
38diff --git a/arch/mips/jz4740/cpufreq.c b/arch/mips/jz4740/cpufreq.c
39new file mode 100644
40index 0000000..aa41e9f
41--- /dev/null
42+++ b/arch/mips/jz4740/cpufreq.c
43@@ -0,0 +1,226 @@
44+/*
45+ * linux/arch/mips/jz4740/cpufreq.c
46+ *
47+ * cpufreq driver for JZ4740
48+ *
49+ * Copyright (c) 2010 Ulrich Hecht <ulrich.hecht@gmail.com>
50+ * Copyright (c) 2010 Maarten ter Huurne <maarten@treewalker.org>
51+ *
52+ * This program is free software; you can redistribute it and/or modify
53+ * it under the terms of the GNU General Public License version 2 as
54+ * published by the Free Software Foundation.
55+ */
56+
57+#include <linux/kernel.h>
58+#include <linux/module.h>
59+#include <linux/init.h>
60+#include <linux/err.h>
61+
62+#include <linux/cpufreq.h>
63+
64+#include <linux/clk.h>
65+#include <asm/mach-jz4740/base.h>
66+
67+#include "clock.h"
68+
69+#define DEBUG_CPUFREQ
70+
71+#ifdef DEBUG_CPUFREQ
72+#define dprintk(X...) printk(KERN_INFO X)
73+#else
74+#define dprintk(X...) do { } while(0)
75+#endif
76+
77+#define HCLK_MIN 30000
78+/* TODO: The maximum MCLK most likely depends on the SDRAM chips used,
79+ so it is board-specific. */
80+#define MCLK_MAX 140000
81+
82+/* Same as jz_clk_main_divs, but with 24 and 32 removed because the hardware
83+ spec states those dividers must not be used for CCLK or HCLK. */
84+static const unsigned int jz4740_freq_cpu_divs[] = {1, 2, 3, 4, 6, 8, 12, 16};
85+
86+struct jz4740_freq_percpu_info {
87+ unsigned int pll_rate;
88+ struct cpufreq_frequency_table table[
89+ ARRAY_SIZE(jz4740_freq_cpu_divs) + 1];
90+};
91+
92+static struct clk *pll;
93+static struct clk *cclk;
94+
95+static struct jz4740_freq_percpu_info jz4740_freq_info;
96+
97+static struct cpufreq_driver cpufreq_jz4740_driver;
98+
99+static void jz4740_freq_fill_table(struct cpufreq_policy *policy,
100+ unsigned int pll_rate)
101+{
102+ struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0];
103+ int i;
104+
105+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
106+ /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */
107+ static bool init = false;
108+ if (init)
109+ cpufreq_frequency_table_put_attr(policy->cpu);
110+ else
111+ init = true;
112+#endif
113+
114+ jz4740_freq_info.pll_rate = pll_rate;
115+
116+ for (i = 0; i < ARRAY_SIZE(jz4740_freq_cpu_divs); i++) {
117+ unsigned int freq = pll_rate / jz4740_freq_cpu_divs[i];
118+ if (freq < HCLK_MIN) break;
119+ table[i].index = i;
120+ table[i].frequency = freq;
121+ }
122+ table[i].index = i;
123+ table[i].frequency = CPUFREQ_TABLE_END;
124+
125+ policy->min = table[i - 1].frequency;
126+ policy->max = table[0].frequency;
127+
128+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
129+ cpufreq_frequency_table_get_attr(table, policy->cpu);
130+#endif
131+}
132+
133+static unsigned int jz4740_freq_get(unsigned int cpu)
134+{
135+ return clk_get_rate(cclk) / 1000;
136+}
137+
138+static int jz4740_freq_verify(struct cpufreq_policy *policy)
139+{
140+ unsigned int new_pll;
141+
142+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
143+ policy->cpuinfo.max_freq);
144+
145+ new_pll = clk_round_rate(pll, policy->max * 1000) / 1000;
146+ if (jz4740_freq_info.pll_rate != new_pll)
147+ jz4740_freq_fill_table(policy, new_pll);
148+
149+ return 0;
150+}
151+
152+static int jz4740_freq_target(struct cpufreq_policy *policy,
153+ unsigned int target_freq,
154+ unsigned int relation)
155+{
156+ struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0];
157+ struct cpufreq_freqs freqs;
158+ unsigned int new_index = 0;
159+ unsigned int old_pll = clk_get_rate(pll) / 1000;
160+ unsigned int new_pll = jz4740_freq_info.pll_rate;
161+ int ret = 0;
162+
163+ if (cpufreq_frequency_table_target(policy, table,
164+ target_freq, relation, &new_index))
165+ return -EINVAL;
166+ freqs = (struct cpufreq_freqs) {
167+ .old = jz4740_freq_get(policy->cpu),
168+ .new = table[new_index].frequency,
169+ .cpu = policy->cpu,
170+ .flags = cpufreq_jz4740_driver.flags,
171+ };
172+ if (freqs.new != freqs.old || new_pll != old_pll) {
173+ unsigned int cdiv, hdiv, mdiv, pdiv;
174+ cdiv = jz4740_freq_cpu_divs[new_index];
175+ hdiv = (cdiv == 3 || cdiv == 6) ? cdiv * 2 : cdiv * 3;
176+ while (new_pll < HCLK_MIN * hdiv)
177+ hdiv -= cdiv;
178+ mdiv = hdiv;
179+ if (new_pll > MCLK_MAX * mdiv) {
180+ /* 4,4 performs better than 3,6 */
181+ if (new_pll > MCLK_MAX * 4)
182+ mdiv *= 2;
183+ else
184+ hdiv = mdiv = cdiv * 4;
185+ }
186+ pdiv = mdiv;
187+ dprintk(KERN_INFO "%s: cclk %p, setting from %d to %d, "
188+ "dividers %d, %d, %d, %d\n",
189+ __FUNCTION__, cclk, freqs.old, freqs.new,
190+ cdiv, hdiv, mdiv, pdiv);
191+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
192+ ret = clk_main_set_dividers(new_pll == old_pll,
193+ cdiv, hdiv, mdiv, pdiv);
194+ if (ret) {
195+ dprintk(KERN_INFO "failed to set dividers\n");
196+ } else if (new_pll != old_pll) {
197+ dprintk(KERN_INFO "%s: pll %p, setting from %d to %d\n",
198+ __FUNCTION__, pll, old_pll, new_pll);
199+ ret = clk_set_rate(pll, new_pll * 1000);
200+ }
201+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
202+ }
203+
204+ return ret;
205+}
206+
207+static int jz4740_cpufreq_driver_init(struct cpufreq_policy *policy)
208+{
209+ int ret;
210+
211+ dprintk(KERN_INFO "Jz4740 cpufreq driver\n");
212+
213+ if (policy->cpu != 0)
214+ return -EINVAL;
215+
216+ pll = clk_get(NULL, "pll");
217+ if (IS_ERR(pll)) {
218+ ret = PTR_ERR(pll);
219+ goto err_exit;
220+ }
221+
222+ cclk = clk_get(NULL, "cclk");
223+ if (IS_ERR(cclk)) {
224+ ret = PTR_ERR(cclk);
225+ goto err_clk_put_pll;
226+ }
227+
228+ policy->cpuinfo.min_freq = HCLK_MIN;
229+ policy->cpuinfo.max_freq = 500000;
230+ policy->cpuinfo.transition_latency = 100000; /* in nanoseconds */
231+ policy->cur = jz4740_freq_get(policy->cpu);
232+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
233+ /* min and max are set by jz4740_freq_fill_table() */
234+
235+ jz4740_freq_fill_table(policy, clk_get_rate(pll) / 1000 /* in kHz */);
236+
237+ return 0;
238+
239+err_clk_put_pll:
240+ clk_put(pll);
241+err_exit:
242+ return ret;
243+}
244+
245+static struct cpufreq_driver cpufreq_jz4740_driver = {
246+ .init = jz4740_cpufreq_driver_init,
247+ .verify = jz4740_freq_verify,
248+ .target = jz4740_freq_target,
249+ .get = jz4740_freq_get,
250+ .name = "jz4740",
251+};
252+
253+static int __init jz4740_cpufreq_init(void)
254+{
255+ return cpufreq_register_driver(&cpufreq_jz4740_driver);
256+}
257+
258+static void __exit jz4740_cpufreq_exit(void)
259+{
260+ cpufreq_unregister_driver(&cpufreq_jz4740_driver);
261+}
262+
263+module_init(jz4740_cpufreq_init);
264+module_exit(jz4740_cpufreq_exit);
265+
266+MODULE_AUTHOR("Ulrich Hecht <ulrich.hecht@gmail.com>, "
267+ "Maarten ter Huurne <maarten@treewalker.org>");
268+MODULE_DESCRIPTION("cpufreq driver for Jz4740");
269+MODULE_LICENSE("GPL");
270diff --git a/arch/mips/kernel/cpufreq/Kconfig b/arch/mips/kernel/cpufreq/Kconfig
271index 58c601e..11af8e8 100644
272--- a/arch/mips/kernel/cpufreq/Kconfig
273+++ b/arch/mips/kernel/cpufreq/Kconfig
274@@ -8,7 +8,7 @@ config MIPS_EXTERNAL_TIMER
275 config MIPS_CPUFREQ
276     bool
277     default y
278- depends on CPU_SUPPORTS_CPUFREQ && MIPS_EXTERNAL_TIMER
279+ depends on CPU_SUPPORTS_CPUFREQ
280 
281 if MIPS_CPUFREQ
282 
283@@ -24,6 +24,7 @@ config LOONGSON2_CPUFREQ
284     tristate "Loongson2 CPUFreq Driver"
285     select CPU_FREQ_TABLE
286     depends on MIPS_CPUFREQ
287+ depends on MIPS_EXTERNAL_TIMER
288     help
289       This option adds a CPUFreq driver for loongson processors which
290       support software configurable cpu frequency.
291@@ -34,6 +35,16 @@ config LOONGSON2_CPUFREQ
292 
293       If in doubt, say N.
294 
295+config CPU_FREQ_JZ
296+ tristate "CPUfreq driver for JZ CPUs"
297+ select CPU_FREQ_TABLE
298+ depends on MACH_JZ4740
299+ default n
300+ help
301+ This enables the CPUfreq driver for JZ CPUs.
302+
303+ If in doubt, say N.
304+
305 endif # CPU_FREQ
306 
307 endmenu
308--
3091.7.5.4
310
311

Archive Download this file



interactive