aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorTang Yuantian <yuantian.tang@freescale.com>2013-06-05 05:30:48 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-06-05 08:19:28 -0400
commitdefa4c738aa90e29e91eff43b0c1b3198367ce9c (patch)
treef571c9c29413859f392182d4f1494891bb3fa20e /drivers/cpufreq
parenta262e94cdcb961762e5d91e7fcb857bba7d420a0 (diff)
cpufreq: powerpc: Add cpufreq driver for Freescale e500mc SoCs
Add cpufreq driver for Freescale e500mc, e5500 and e6500 SoCs which are capable of changing the CPU frequency dynamically Signed-off-by: Tang Yuantian <Yuantian.Tang@freescale.com> Signed-off-by: Li Yang <leoli@freescale.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/Kconfig.powerpc10
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/ppc-corenet-cpufreq.c380
3 files changed, 391 insertions, 0 deletions
diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc
index 9c926ca0d718..88f629e5a393 100644
--- a/drivers/cpufreq/Kconfig.powerpc
+++ b/drivers/cpufreq/Kconfig.powerpc
@@ -23,3 +23,13 @@ config CPU_FREQ_MAPLE
23 help 23 help
24 This adds support for frequency switching on Maple 970FX 24 This adds support for frequency switching on Maple 970FX
25 Evaluation Board and compatible boards (IBM JS2x blades). 25 Evaluation Board and compatible boards (IBM JS2x blades).
26
27config PPC_CORENET_CPUFREQ
28 tristate "CPU frequency scaling driver for Freescale E500MC SoCs"
29 depends on PPC_E500MC && OF && COMMON_CLK
30 select CPU_FREQ_TABLE
31 select CLK_PPC_CORENET
32 help
33 This adds the CPUFreq driver support for Freescale e500mc,
34 e5500 and e6500 series SoCs which are capable of changing
35 the CPU's frequency dynamically.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 315b9231feb1..c956094ccc39 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_CPU_FREQ_CBE) += ppc-cbe-cpufreq.o
79ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o 79ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o
80obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o 80obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o
81obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o 81obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o
82obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o
82 83
83################################################################################## 84##################################################################################
84# Other platform drivers 85# Other platform drivers
diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c
new file mode 100644
index 000000000000..3cae4529f959
--- /dev/null
+++ b/drivers/cpufreq/ppc-corenet-cpufreq.c
@@ -0,0 +1,380 @@
1/*
2 * Copyright 2013 Freescale Semiconductor, Inc.
3 *
4 * CPU Frequency Scaling driver for Freescale PowerPC corenet SoCs.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12
13#include <linux/clk.h>
14#include <linux/cpufreq.h>
15#include <linux/errno.h>
16#include <sysdev/fsl_soc.h>
17#include <linux/init.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/mutex.h>
21#include <linux/of.h>
22#include <linux/slab.h>
23#include <linux/smp.h>
24
25/**
26 * struct cpu_data - per CPU data struct
27 * @clk: the clk of CPU
28 * @parent: the parent node of cpu clock
29 * @table: frequency table
30 */
31struct cpu_data {
32 struct clk *clk;
33 struct device_node *parent;
34 struct cpufreq_frequency_table *table;
35};
36
37/**
38 * struct soc_data - SoC specific data
39 * @freq_mask: mask the disallowed frequencies
40 * @flag: unique flags
41 */
42struct soc_data {
43 u32 freq_mask[4];
44 u32 flag;
45};
46
47#define FREQ_MASK 1
48/* see hardware specification for the allowed frqeuencies */
49static const struct soc_data sdata[] = {
50 { /* used by p2041 and p3041 */
51 .freq_mask = {0x8, 0x8, 0x2, 0x2},
52 .flag = FREQ_MASK,
53 },
54 { /* used by p5020 */
55 .freq_mask = {0x8, 0x2},
56 .flag = FREQ_MASK,
57 },
58 { /* used by p4080, p5040 */
59 .freq_mask = {0},
60 .flag = 0,
61 },
62};
63
64/*
65 * the minimum allowed core frequency, in Hz
66 * for chassis v1.0, >= platform frequency
67 * for chassis v2.0, >= platform frequency / 2
68 */
69static u32 min_cpufreq;
70static const u32 *fmask;
71
72/* serialize frequency changes */
73static DEFINE_MUTEX(cpufreq_lock);
74static DEFINE_PER_CPU(struct cpu_data *, cpu_data);
75
76/* cpumask in a cluster */
77static DEFINE_PER_CPU(cpumask_var_t, cpu_mask);
78
79#ifndef CONFIG_SMP
80static inline const struct cpumask *cpu_core_mask(int cpu)
81{
82 return cpumask_of(0);
83}
84#endif
85
86static unsigned int corenet_cpufreq_get_speed(unsigned int cpu)
87{
88 struct cpu_data *data = per_cpu(cpu_data, cpu);
89
90 return clk_get_rate(data->clk) / 1000;
91}
92
93/* reduce the duplicated frequencies in frequency table */
94static void freq_table_redup(struct cpufreq_frequency_table *freq_table,
95 int count)
96{
97 int i, j;
98
99 for (i = 1; i < count; i++) {
100 for (j = 0; j < i; j++) {
101 if (freq_table[j].frequency == CPUFREQ_ENTRY_INVALID ||
102 freq_table[j].frequency !=
103 freq_table[i].frequency)
104 continue;
105
106 freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
107 break;
108 }
109 }
110}
111
112/* sort the frequencies in frequency table in descenting order */
113static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
114 int count)
115{
116 int i, j, ind;
117 unsigned int freq, max_freq;
118 struct cpufreq_frequency_table table;
119 for (i = 0; i < count - 1; i++) {
120 max_freq = freq_table[i].frequency;
121 ind = i;
122 for (j = i + 1; j < count; j++) {
123 freq = freq_table[j].frequency;
124 if (freq == CPUFREQ_ENTRY_INVALID ||
125 freq <= max_freq)
126 continue;
127 ind = j;
128 max_freq = freq;
129 }
130
131 if (ind != i) {
132 /* exchange the frequencies */
133 table.driver_data = freq_table[i].driver_data;
134 table.frequency = freq_table[i].frequency;
135 freq_table[i].driver_data = freq_table[ind].driver_data;
136 freq_table[i].frequency = freq_table[ind].frequency;
137 freq_table[ind].driver_data = table.driver_data;
138 freq_table[ind].frequency = table.frequency;
139 }
140 }
141}
142
143static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
144{
145 struct device_node *np;
146 int i, count, ret;
147 u32 freq, mask;
148 struct clk *clk;
149 struct cpufreq_frequency_table *table;
150 struct cpu_data *data;
151 unsigned int cpu = policy->cpu;
152
153 np = of_get_cpu_node(cpu, NULL);
154 if (!np)
155 return -ENODEV;
156
157 data = kzalloc(sizeof(*data), GFP_KERNEL);
158 if (!data) {
159 pr_err("%s: no memory\n", __func__);
160 goto err_np;
161 }
162
163 data->clk = of_clk_get(np, 0);
164 if (IS_ERR(data->clk)) {
165 pr_err("%s: no clock information\n", __func__);
166 goto err_nomem2;
167 }
168
169 data->parent = of_parse_phandle(np, "clocks", 0);
170 if (!data->parent) {
171 pr_err("%s: could not get clock information\n", __func__);
172 goto err_nomem2;
173 }
174
175 count = of_property_count_strings(data->parent, "clock-names");
176 table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
177 if (!table) {
178 pr_err("%s: no memory\n", __func__);
179 goto err_node;
180 }
181
182 if (fmask)
183 mask = fmask[get_hard_smp_processor_id(cpu)];
184 else
185 mask = 0x0;
186
187 for (i = 0; i < count; i++) {
188 clk = of_clk_get(data->parent, i);
189 freq = clk_get_rate(clk);
190 /*
191 * the clock is valid if its frequency is not masked
192 * and large than minimum allowed frequency.
193 */
194 if (freq < min_cpufreq || (mask & (1 << i)))
195 table[i].frequency = CPUFREQ_ENTRY_INVALID;
196 else
197 table[i].frequency = freq / 1000;
198 table[i].driver_data = i;
199 }
200 freq_table_redup(table, count);
201 freq_table_sort(table, count);
202 table[i].frequency = CPUFREQ_TABLE_END;
203
204 /* set the min and max frequency properly */
205 ret = cpufreq_frequency_table_cpuinfo(policy, table);
206 if (ret) {
207 pr_err("invalid frequency table: %d\n", ret);
208 goto err_nomem1;
209 }
210
211 data->table = table;
212 per_cpu(cpu_data, cpu) = data;
213
214 /* update ->cpus if we have cluster, no harm if not */
215 cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu));
216 for_each_cpu(i, per_cpu(cpu_mask, cpu))
217 per_cpu(cpu_data, i) = data;
218
219 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
220 policy->cur = corenet_cpufreq_get_speed(policy->cpu);
221
222 cpufreq_frequency_table_get_attr(table, cpu);
223 of_node_put(np);
224
225 return 0;
226
227err_nomem1:
228 kfree(table);
229err_node:
230 of_node_put(data->parent);
231err_nomem2:
232 per_cpu(cpu_data, cpu) = NULL;
233 kfree(data);
234err_np:
235 of_node_put(np);
236
237 return -ENODEV;
238}
239
240static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy)
241{
242 struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
243 unsigned int cpu;
244
245 cpufreq_frequency_table_put_attr(policy->cpu);
246 of_node_put(data->parent);
247 kfree(data->table);
248 kfree(data);
249
250 for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu))
251 per_cpu(cpu_data, cpu) = NULL;
252
253 return 0;
254}
255
256static int corenet_cpufreq_verify(struct cpufreq_policy *policy)
257{
258 struct cpufreq_frequency_table *table =
259 per_cpu(cpu_data, policy->cpu)->table;
260
261 return cpufreq_frequency_table_verify(policy, table);
262}
263
264static int corenet_cpufreq_target(struct cpufreq_policy *policy,
265 unsigned int target_freq, unsigned int relation)
266{
267 struct cpufreq_freqs freqs;
268 unsigned int new;
269 struct clk *parent;
270 int ret;
271 struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
272
273 cpufreq_frequency_table_target(policy, data->table,
274 target_freq, relation, &new);
275
276 if (policy->cur == data->table[new].frequency)
277 return 0;
278
279 freqs.old = policy->cur;
280 freqs.new = data->table[new].frequency;
281
282 mutex_lock(&cpufreq_lock);
283 cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
284
285 parent = of_clk_get(data->parent, data->table[new].driver_data);
286 ret = clk_set_parent(data->clk, parent);
287 if (ret)
288 freqs.new = freqs.old;
289
290 cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
291 mutex_unlock(&cpufreq_lock);
292
293 return ret;
294}
295
296static struct freq_attr *corenet_cpufreq_attr[] = {
297 &cpufreq_freq_attr_scaling_available_freqs,
298 NULL,
299};
300
301static struct cpufreq_driver ppc_corenet_cpufreq_driver = {
302 .name = "ppc_cpufreq",
303 .owner = THIS_MODULE,
304 .flags = CPUFREQ_CONST_LOOPS,
305 .init = corenet_cpufreq_cpu_init,
306 .exit = __exit_p(corenet_cpufreq_cpu_exit),
307 .verify = corenet_cpufreq_verify,
308 .target = corenet_cpufreq_target,
309 .get = corenet_cpufreq_get_speed,
310 .attr = corenet_cpufreq_attr,
311};
312
313static const struct of_device_id node_matches[] __initdata = {
314 { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
315 { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
316 { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
317 { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
318 { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
319 { .compatible = "fsl,qoriq-clockgen-2.0", },
320 {}
321};
322
323static int __init ppc_corenet_cpufreq_init(void)
324{
325 int ret;
326 struct device_node *np;
327 const struct of_device_id *match;
328 const struct soc_data *data;
329 unsigned int cpu;
330
331 np = of_find_matching_node(NULL, node_matches);
332 if (!np)
333 return -ENODEV;
334
335 for_each_possible_cpu(cpu) {
336 if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL))
337 goto err_mask;
338 cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu));
339 }
340
341 match = of_match_node(node_matches, np);
342 data = match->data;
343 if (data) {
344 if (data->flag)
345 fmask = data->freq_mask;
346 min_cpufreq = fsl_get_sys_freq();
347 } else {
348 min_cpufreq = fsl_get_sys_freq() / 2;
349 }
350
351 of_node_put(np);
352
353 ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver);
354 if (!ret)
355 pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n");
356
357 return ret;
358
359err_mask:
360 for_each_possible_cpu(cpu)
361 free_cpumask_var(per_cpu(cpu_mask, cpu));
362
363 return -ENOMEM;
364}
365module_init(ppc_corenet_cpufreq_init);
366
367static void __exit ppc_corenet_cpufreq_exit(void)
368{
369 unsigned int cpu;
370
371 for_each_possible_cpu(cpu)
372 free_cpumask_var(per_cpu(cpu_mask, cpu));
373
374 cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver);
375}
376module_exit(ppc_corenet_cpufreq_exit);
377
378MODULE_LICENSE("GPL");
379MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
380MODULE_DESCRIPTION("cpufreq driver for Freescale e500mc series SoCs");