Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.13-rc2 339 lines 8.3 kB view raw
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 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 84static unsigned int corenet_cpufreq_get_speed(unsigned int cpu) 85{ 86 struct cpu_data *data = per_cpu(cpu_data, cpu); 87 88 return clk_get_rate(data->clk) / 1000; 89} 90 91/* reduce the duplicated frequencies in frequency table */ 92static void freq_table_redup(struct cpufreq_frequency_table *freq_table, 93 int count) 94{ 95 int i, j; 96 97 for (i = 1; i < count; i++) { 98 for (j = 0; j < i; j++) { 99 if (freq_table[j].frequency == CPUFREQ_ENTRY_INVALID || 100 freq_table[j].frequency != 101 freq_table[i].frequency) 102 continue; 103 104 freq_table[i].frequency = CPUFREQ_ENTRY_INVALID; 105 break; 106 } 107 } 108} 109 110/* sort the frequencies in frequency table in descenting order */ 111static void freq_table_sort(struct cpufreq_frequency_table *freq_table, 112 int count) 113{ 114 int i, j, ind; 115 unsigned int freq, max_freq; 116 struct cpufreq_frequency_table table; 117 for (i = 0; i < count - 1; i++) { 118 max_freq = freq_table[i].frequency; 119 ind = i; 120 for (j = i + 1; j < count; j++) { 121 freq = freq_table[j].frequency; 122 if (freq == CPUFREQ_ENTRY_INVALID || 123 freq <= max_freq) 124 continue; 125 ind = j; 126 max_freq = freq; 127 } 128 129 if (ind != i) { 130 /* exchange the frequencies */ 131 table.driver_data = freq_table[i].driver_data; 132 table.frequency = freq_table[i].frequency; 133 freq_table[i].driver_data = freq_table[ind].driver_data; 134 freq_table[i].frequency = freq_table[ind].frequency; 135 freq_table[ind].driver_data = table.driver_data; 136 freq_table[ind].frequency = table.frequency; 137 } 138 } 139} 140 141static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) 142{ 143 struct device_node *np; 144 int i, count, ret; 145 u32 freq, mask; 146 struct clk *clk; 147 struct cpufreq_frequency_table *table; 148 struct cpu_data *data; 149 unsigned int cpu = policy->cpu; 150 151 np = of_get_cpu_node(cpu, NULL); 152 if (!np) 153 return -ENODEV; 154 155 data = kzalloc(sizeof(*data), GFP_KERNEL); 156 if (!data) { 157 pr_err("%s: no memory\n", __func__); 158 goto err_np; 159 } 160 161 data->clk = of_clk_get(np, 0); 162 if (IS_ERR(data->clk)) { 163 pr_err("%s: no clock information\n", __func__); 164 goto err_nomem2; 165 } 166 167 data->parent = of_parse_phandle(np, "clocks", 0); 168 if (!data->parent) { 169 pr_err("%s: could not get clock information\n", __func__); 170 goto err_nomem2; 171 } 172 173 count = of_property_count_strings(data->parent, "clock-names"); 174 table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL); 175 if (!table) { 176 pr_err("%s: no memory\n", __func__); 177 goto err_node; 178 } 179 180 if (fmask) 181 mask = fmask[get_hard_smp_processor_id(cpu)]; 182 else 183 mask = 0x0; 184 185 for (i = 0; i < count; i++) { 186 clk = of_clk_get(data->parent, i); 187 freq = clk_get_rate(clk); 188 /* 189 * the clock is valid if its frequency is not masked 190 * and large than minimum allowed frequency. 191 */ 192 if (freq < min_cpufreq || (mask & (1 << i))) 193 table[i].frequency = CPUFREQ_ENTRY_INVALID; 194 else 195 table[i].frequency = freq / 1000; 196 table[i].driver_data = i; 197 } 198 freq_table_redup(table, count); 199 freq_table_sort(table, count); 200 table[i].frequency = CPUFREQ_TABLE_END; 201 202 /* set the min and max frequency properly */ 203 ret = cpufreq_table_validate_and_show(policy, table); 204 if (ret) { 205 pr_err("invalid frequency table: %d\n", ret); 206 goto err_nomem1; 207 } 208 209 data->table = table; 210 per_cpu(cpu_data, cpu) = data; 211 212 /* update ->cpus if we have cluster, no harm if not */ 213 cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu)); 214 for_each_cpu(i, per_cpu(cpu_mask, cpu)) 215 per_cpu(cpu_data, i) = data; 216 217 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; 218 of_node_put(np); 219 220 return 0; 221 222err_nomem1: 223 kfree(table); 224err_node: 225 of_node_put(data->parent); 226err_nomem2: 227 per_cpu(cpu_data, cpu) = NULL; 228 kfree(data); 229err_np: 230 of_node_put(np); 231 232 return -ENODEV; 233} 234 235static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy) 236{ 237 struct cpu_data *data = per_cpu(cpu_data, policy->cpu); 238 unsigned int cpu; 239 240 cpufreq_frequency_table_put_attr(policy->cpu); 241 of_node_put(data->parent); 242 kfree(data->table); 243 kfree(data); 244 245 for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu)) 246 per_cpu(cpu_data, cpu) = NULL; 247 248 return 0; 249} 250 251static int corenet_cpufreq_target(struct cpufreq_policy *policy, 252 unsigned int index) 253{ 254 struct clk *parent; 255 struct cpu_data *data = per_cpu(cpu_data, policy->cpu); 256 257 parent = of_clk_get(data->parent, data->table[index].driver_data); 258 return clk_set_parent(data->clk, parent); 259} 260 261static struct cpufreq_driver ppc_corenet_cpufreq_driver = { 262 .name = "ppc_cpufreq", 263 .flags = CPUFREQ_CONST_LOOPS, 264 .init = corenet_cpufreq_cpu_init, 265 .exit = __exit_p(corenet_cpufreq_cpu_exit), 266 .verify = cpufreq_generic_frequency_table_verify, 267 .target_index = corenet_cpufreq_target, 268 .get = corenet_cpufreq_get_speed, 269 .attr = cpufreq_generic_attr, 270}; 271 272static const struct of_device_id node_matches[] __initdata = { 273 { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], }, 274 { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], }, 275 { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], }, 276 { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], }, 277 { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], }, 278 { .compatible = "fsl,qoriq-clockgen-2.0", }, 279 {} 280}; 281 282static int __init ppc_corenet_cpufreq_init(void) 283{ 284 int ret; 285 struct device_node *np; 286 const struct of_device_id *match; 287 const struct soc_data *data; 288 unsigned int cpu; 289 290 np = of_find_matching_node(NULL, node_matches); 291 if (!np) 292 return -ENODEV; 293 294 for_each_possible_cpu(cpu) { 295 if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL)) 296 goto err_mask; 297 cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu)); 298 } 299 300 match = of_match_node(node_matches, np); 301 data = match->data; 302 if (data) { 303 if (data->flag) 304 fmask = data->freq_mask; 305 min_cpufreq = fsl_get_sys_freq(); 306 } else { 307 min_cpufreq = fsl_get_sys_freq() / 2; 308 } 309 310 of_node_put(np); 311 312 ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver); 313 if (!ret) 314 pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n"); 315 316 return ret; 317 318err_mask: 319 for_each_possible_cpu(cpu) 320 free_cpumask_var(per_cpu(cpu_mask, cpu)); 321 322 return -ENOMEM; 323} 324module_init(ppc_corenet_cpufreq_init); 325 326static void __exit ppc_corenet_cpufreq_exit(void) 327{ 328 unsigned int cpu; 329 330 for_each_possible_cpu(cpu) 331 free_cpumask_var(per_cpu(cpu_mask, cpu)); 332 333 cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver); 334} 335module_exit(ppc_corenet_cpufreq_exit); 336 337MODULE_LICENSE("GPL"); 338MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>"); 339MODULE_DESCRIPTION("cpufreq driver for Freescale e500mc series SoCs");