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