xburst: add support for 3.3
[openwrt/svn-archive/archive.git] / target / linux / xburst / patches-3.3 / 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 --- 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