[target/xburst] level up target xburst to linux kernel version 3.2.1
[openwrt.git] / target / linux / xburst / patches-3.2 / 0012-MIPS-JZ4740-Add-cpufreq-support.patch
1 From d0f0d5739a31c12d349980ed05a670fa1e84696d Mon Sep 17 00:00:00 2001
2 From: Maarten ter Huurne <maarten@treewalker.org>
3 Date: Wed, 16 Mar 2011 03:16:04 +0100
4 Subject: [PATCH 12/21] MIPS: JZ4740: Add cpufreq support.
5
6 This 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 diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
16 index 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"
27 diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
28 index 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
38 diff --git a/arch/mips/jz4740/cpufreq.c b/arch/mips/jz4740/cpufreq.c
39 new file mode 100644
40 index 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");
270 diff --git a/arch/mips/kernel/cpufreq/Kconfig b/arch/mips/kernel/cpufreq/Kconfig
271 index 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 --
309 1.7.5.4
310
This page took 0.067496 seconds and 5 git commands to generate.