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

Archive Download this file



interactive