]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/cpufreq/ppc-corenet-cpufreq.c
Merge tag 'char-misc-4.0-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[mirror_ubuntu-bionic-kernel.git] / drivers / cpufreq / ppc-corenet-cpufreq.c
CommitLineData
defa4c73
TY
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>
defa4c73
TY
16#include <linux/init.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/mutex.h>
20#include <linux/of.h>
21#include <linux/slab.h>
22#include <linux/smp.h>
bfa709bc 23#include <sysdev/fsl_soc.h>
defa4c73 24
5877b4f4
GU
25#include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */
26
defa4c73
TY
27/**
28 * struct cpu_data - per CPU data struct
defa4c73
TY
29 * @parent: the parent node of cpu clock
30 * @table: frequency table
31 */
32struct cpu_data {
defa4c73
TY
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
defa4c73
TY
72static DEFINE_PER_CPU(struct cpu_data *, cpu_data);
73
74/* cpumask in a cluster */
75static DEFINE_PER_CPU(cpumask_var_t, cpu_mask);
76
77#ifndef CONFIG_SMP
78static inline const struct cpumask *cpu_core_mask(int cpu)
79{
80 return cpumask_of(0);
81}
82#endif
83
defa4c73
TY
84/* reduce the duplicated frequencies in frequency table */
85static void freq_table_redup(struct cpufreq_frequency_table *freq_table,
86 int count)
87{
88 int i, j;
89
90 for (i = 1; i < count; i++) {
91 for (j = 0; j < i; j++) {
92 if (freq_table[j].frequency == CPUFREQ_ENTRY_INVALID ||
93 freq_table[j].frequency !=
94 freq_table[i].frequency)
95 continue;
96
97 freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
98 break;
99 }
100 }
101}
102
103/* sort the frequencies in frequency table in descenting order */
104static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
105 int count)
106{
107 int i, j, ind;
108 unsigned int freq, max_freq;
109 struct cpufreq_frequency_table table;
110 for (i = 0; i < count - 1; i++) {
111 max_freq = freq_table[i].frequency;
112 ind = i;
113 for (j = i + 1; j < count; j++) {
114 freq = freq_table[j].frequency;
115 if (freq == CPUFREQ_ENTRY_INVALID ||
116 freq <= max_freq)
117 continue;
118 ind = j;
119 max_freq = freq;
120 }
121
122 if (ind != i) {
123 /* exchange the frequencies */
124 table.driver_data = freq_table[i].driver_data;
125 table.frequency = freq_table[i].frequency;
126 freq_table[i].driver_data = freq_table[ind].driver_data;
127 freq_table[i].frequency = freq_table[ind].frequency;
128 freq_table[ind].driver_data = table.driver_data;
129 freq_table[ind].frequency = table.frequency;
130 }
131 }
132}
133
134static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
135{
136 struct device_node *np;
137 int i, count, ret;
138 u32 freq, mask;
139 struct clk *clk;
140 struct cpufreq_frequency_table *table;
141 struct cpu_data *data;
142 unsigned int cpu = policy->cpu;
906fe033 143 u64 u64temp;
defa4c73
TY
144
145 np = of_get_cpu_node(cpu, NULL);
146 if (!np)
147 return -ENODEV;
148
149 data = kzalloc(sizeof(*data), GFP_KERNEL);
150 if (!data) {
151 pr_err("%s: no memory\n", __func__);
152 goto err_np;
153 }
154
652ed95d
VK
155 policy->clk = of_clk_get(np, 0);
156 if (IS_ERR(policy->clk)) {
defa4c73
TY
157 pr_err("%s: no clock information\n", __func__);
158 goto err_nomem2;
159 }
160
161 data->parent = of_parse_phandle(np, "clocks", 0);
162 if (!data->parent) {
163 pr_err("%s: could not get clock information\n", __func__);
164 goto err_nomem2;
165 }
166
167 count = of_property_count_strings(data->parent, "clock-names");
168 table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
169 if (!table) {
170 pr_err("%s: no memory\n", __func__);
171 goto err_node;
172 }
173
174 if (fmask)
175 mask = fmask[get_hard_smp_processor_id(cpu)];
176 else
177 mask = 0x0;
178
179 for (i = 0; i < count; i++) {
180 clk = of_clk_get(data->parent, i);
181 freq = clk_get_rate(clk);
182 /*
183 * the clock is valid if its frequency is not masked
184 * and large than minimum allowed frequency.
185 */
186 if (freq < min_cpufreq || (mask & (1 << i)))
187 table[i].frequency = CPUFREQ_ENTRY_INVALID;
188 else
189 table[i].frequency = freq / 1000;
190 table[i].driver_data = i;
191 }
192 freq_table_redup(table, count);
193 freq_table_sort(table, count);
194 table[i].frequency = CPUFREQ_TABLE_END;
195
196 /* set the min and max frequency properly */
6b4147db 197 ret = cpufreq_table_validate_and_show(policy, table);
defa4c73
TY
198 if (ret) {
199 pr_err("invalid frequency table: %d\n", ret);
200 goto err_nomem1;
201 }
202
203 data->table = table;
defa4c73
TY
204
205 /* update ->cpus if we have cluster, no harm if not */
206 cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu));
207 for_each_cpu(i, per_cpu(cpu_mask, cpu))
208 per_cpu(cpu_data, i) = data;
209
906fe033
ES
210 /* Minimum transition latency is 12 platform clocks */
211 u64temp = 12ULL * NSEC_PER_SEC;
212 do_div(u64temp, fsl_get_sys_freq());
213 policy->cpuinfo.transition_latency = u64temp + 1;
6712d293 214
defa4c73
TY
215 of_node_put(np);
216
217 return 0;
218
219err_nomem1:
220 kfree(table);
221err_node:
222 of_node_put(data->parent);
223err_nomem2:
224 per_cpu(cpu_data, cpu) = NULL;
225 kfree(data);
226err_np:
227 of_node_put(np);
228
229 return -ENODEV;
230}
231
232static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy)
233{
234 struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
235 unsigned int cpu;
236
defa4c73
TY
237 of_node_put(data->parent);
238 kfree(data->table);
239 kfree(data);
240
241 for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu))
242 per_cpu(cpu_data, cpu) = NULL;
243
244 return 0;
245}
246
defa4c73 247static int corenet_cpufreq_target(struct cpufreq_policy *policy,
9c0ebcf7 248 unsigned int index)
defa4c73 249{
defa4c73 250 struct clk *parent;
defa4c73
TY
251 struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
252
9c0ebcf7 253 parent = of_clk_get(data->parent, data->table[index].driver_data);
652ed95d 254 return clk_set_parent(policy->clk, parent);
defa4c73
TY
255}
256
defa4c73
TY
257static struct cpufreq_driver ppc_corenet_cpufreq_driver = {
258 .name = "ppc_cpufreq",
defa4c73
TY
259 .flags = CPUFREQ_CONST_LOOPS,
260 .init = corenet_cpufreq_cpu_init,
261 .exit = __exit_p(corenet_cpufreq_cpu_exit),
dc2398d7 262 .verify = cpufreq_generic_frequency_table_verify,
9c0ebcf7 263 .target_index = corenet_cpufreq_target,
652ed95d 264 .get = cpufreq_generic_get,
dc2398d7 265 .attr = cpufreq_generic_attr,
defa4c73
TY
266};
267
268static const struct of_device_id node_matches[] __initdata = {
269 { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
270 { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
271 { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
272 { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
273 { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
274 { .compatible = "fsl,qoriq-clockgen-2.0", },
275 {}
276};
277
278static int __init ppc_corenet_cpufreq_init(void)
279{
280 int ret;
281 struct device_node *np;
282 const struct of_device_id *match;
283 const struct soc_data *data;
284 unsigned int cpu;
285
286 np = of_find_matching_node(NULL, node_matches);
287 if (!np)
288 return -ENODEV;
289
290 for_each_possible_cpu(cpu) {
291 if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL))
292 goto err_mask;
293 cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu));
294 }
295
296 match = of_match_node(node_matches, np);
297 data = match->data;
298 if (data) {
299 if (data->flag)
300 fmask = data->freq_mask;
301 min_cpufreq = fsl_get_sys_freq();
302 } else {
303 min_cpufreq = fsl_get_sys_freq() / 2;
304 }
305
306 of_node_put(np);
307
308 ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver);
309 if (!ret)
310 pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n");
311
312 return ret;
313
314err_mask:
315 for_each_possible_cpu(cpu)
316 free_cpumask_var(per_cpu(cpu_mask, cpu));
317
318 return -ENOMEM;
319}
320module_init(ppc_corenet_cpufreq_init);
321
322static void __exit ppc_corenet_cpufreq_exit(void)
323{
324 unsigned int cpu;
325
326 for_each_possible_cpu(cpu)
327 free_cpumask_var(per_cpu(cpu_mask, cpu));
328
329 cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver);
330}
331module_exit(ppc_corenet_cpufreq_exit);
332
333MODULE_LICENSE("GPL");
334MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
335MODULE_DESCRIPTION("cpufreq driver for Freescale e500mc series SoCs");