Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.16 248 lines 6.6 kB view raw
1/* 2 * Copyright (C) 2012 Freescale Semiconductor, Inc. 3 * 4 * The OPP code in function cpu0_set_target() is reused from 5 * drivers/cpufreq/omap-cpufreq.c 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 14#include <linux/clk.h> 15#include <linux/cpu.h> 16#include <linux/cpu_cooling.h> 17#include <linux/cpufreq.h> 18#include <linux/cpumask.h> 19#include <linux/err.h> 20#include <linux/module.h> 21#include <linux/of.h> 22#include <linux/pm_opp.h> 23#include <linux/platform_device.h> 24#include <linux/regulator/consumer.h> 25#include <linux/slab.h> 26#include <linux/thermal.h> 27 28static unsigned int transition_latency; 29static unsigned int voltage_tolerance; /* in percentage */ 30 31static struct device *cpu_dev; 32static struct clk *cpu_clk; 33static struct regulator *cpu_reg; 34static struct cpufreq_frequency_table *freq_table; 35static struct thermal_cooling_device *cdev; 36 37static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) 38{ 39 struct dev_pm_opp *opp; 40 unsigned long volt = 0, volt_old = 0, tol = 0; 41 unsigned int old_freq, new_freq; 42 long freq_Hz, freq_exact; 43 int ret; 44 45 freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); 46 if (freq_Hz <= 0) 47 freq_Hz = freq_table[index].frequency * 1000; 48 49 freq_exact = freq_Hz; 50 new_freq = freq_Hz / 1000; 51 old_freq = clk_get_rate(cpu_clk) / 1000; 52 53 if (!IS_ERR(cpu_reg)) { 54 rcu_read_lock(); 55 opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); 56 if (IS_ERR(opp)) { 57 rcu_read_unlock(); 58 pr_err("failed to find OPP for %ld\n", freq_Hz); 59 return PTR_ERR(opp); 60 } 61 volt = dev_pm_opp_get_voltage(opp); 62 rcu_read_unlock(); 63 tol = volt * voltage_tolerance / 100; 64 volt_old = regulator_get_voltage(cpu_reg); 65 } 66 67 pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n", 68 old_freq / 1000, volt_old ? volt_old / 1000 : -1, 69 new_freq / 1000, volt ? volt / 1000 : -1); 70 71 /* scaling up? scale voltage before frequency */ 72 if (!IS_ERR(cpu_reg) && new_freq > old_freq) { 73 ret = regulator_set_voltage_tol(cpu_reg, volt, tol); 74 if (ret) { 75 pr_err("failed to scale voltage up: %d\n", ret); 76 return ret; 77 } 78 } 79 80 ret = clk_set_rate(cpu_clk, freq_exact); 81 if (ret) { 82 pr_err("failed to set clock rate: %d\n", ret); 83 if (!IS_ERR(cpu_reg)) 84 regulator_set_voltage_tol(cpu_reg, volt_old, tol); 85 return ret; 86 } 87 88 /* scaling down? scale voltage after frequency */ 89 if (!IS_ERR(cpu_reg) && new_freq < old_freq) { 90 ret = regulator_set_voltage_tol(cpu_reg, volt, tol); 91 if (ret) { 92 pr_err("failed to scale voltage down: %d\n", ret); 93 clk_set_rate(cpu_clk, old_freq * 1000); 94 } 95 } 96 97 return ret; 98} 99 100static int cpu0_cpufreq_init(struct cpufreq_policy *policy) 101{ 102 policy->clk = cpu_clk; 103 return cpufreq_generic_init(policy, freq_table, transition_latency); 104} 105 106static struct cpufreq_driver cpu0_cpufreq_driver = { 107 .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, 108 .verify = cpufreq_generic_frequency_table_verify, 109 .target_index = cpu0_set_target, 110 .get = cpufreq_generic_get, 111 .init = cpu0_cpufreq_init, 112 .name = "generic_cpu0", 113 .attr = cpufreq_generic_attr, 114}; 115 116static int cpu0_cpufreq_probe(struct platform_device *pdev) 117{ 118 struct device_node *np; 119 int ret; 120 121 cpu_dev = get_cpu_device(0); 122 if (!cpu_dev) { 123 pr_err("failed to get cpu0 device\n"); 124 return -ENODEV; 125 } 126 127 np = of_node_get(cpu_dev->of_node); 128 if (!np) { 129 pr_err("failed to find cpu0 node\n"); 130 return -ENOENT; 131 } 132 133 cpu_reg = regulator_get_optional(cpu_dev, "cpu0"); 134 if (IS_ERR(cpu_reg)) { 135 /* 136 * If cpu0 regulator supply node is present, but regulator is 137 * not yet registered, we should try defering probe. 138 */ 139 if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { 140 dev_err(cpu_dev, "cpu0 regulator not ready, retry\n"); 141 ret = -EPROBE_DEFER; 142 goto out_put_node; 143 } 144 pr_warn("failed to get cpu0 regulator: %ld\n", 145 PTR_ERR(cpu_reg)); 146 } 147 148 cpu_clk = clk_get(cpu_dev, NULL); 149 if (IS_ERR(cpu_clk)) { 150 ret = PTR_ERR(cpu_clk); 151 pr_err("failed to get cpu0 clock: %d\n", ret); 152 goto out_put_reg; 153 } 154 155 /* OPPs might be populated at runtime, don't check for error here */ 156 of_init_opp_table(cpu_dev); 157 158 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); 159 if (ret) { 160 pr_err("failed to init cpufreq table: %d\n", ret); 161 goto out_put_clk; 162 } 163 164 of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance); 165 166 if (of_property_read_u32(np, "clock-latency", &transition_latency)) 167 transition_latency = CPUFREQ_ETERNAL; 168 169 if (!IS_ERR(cpu_reg)) { 170 struct dev_pm_opp *opp; 171 unsigned long min_uV, max_uV; 172 int i; 173 174 /* 175 * OPP is maintained in order of increasing frequency, and 176 * freq_table initialised from OPP is therefore sorted in the 177 * same order. 178 */ 179 for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) 180 ; 181 rcu_read_lock(); 182 opp = dev_pm_opp_find_freq_exact(cpu_dev, 183 freq_table[0].frequency * 1000, true); 184 min_uV = dev_pm_opp_get_voltage(opp); 185 opp = dev_pm_opp_find_freq_exact(cpu_dev, 186 freq_table[i-1].frequency * 1000, true); 187 max_uV = dev_pm_opp_get_voltage(opp); 188 rcu_read_unlock(); 189 ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); 190 if (ret > 0) 191 transition_latency += ret * 1000; 192 } 193 194 ret = cpufreq_register_driver(&cpu0_cpufreq_driver); 195 if (ret) { 196 pr_err("failed register driver: %d\n", ret); 197 goto out_free_table; 198 } 199 200 /* 201 * For now, just loading the cooling device; 202 * thermal DT code takes care of matching them. 203 */ 204 if (of_find_property(np, "#cooling-cells", NULL)) { 205 cdev = of_cpufreq_cooling_register(np, cpu_present_mask); 206 if (IS_ERR(cdev)) 207 pr_err("running cpufreq without cooling device: %ld\n", 208 PTR_ERR(cdev)); 209 } 210 211 of_node_put(np); 212 return 0; 213 214out_free_table: 215 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); 216out_put_clk: 217 if (!IS_ERR(cpu_clk)) 218 clk_put(cpu_clk); 219out_put_reg: 220 if (!IS_ERR(cpu_reg)) 221 regulator_put(cpu_reg); 222out_put_node: 223 of_node_put(np); 224 return ret; 225} 226 227static int cpu0_cpufreq_remove(struct platform_device *pdev) 228{ 229 cpufreq_cooling_unregister(cdev); 230 cpufreq_unregister_driver(&cpu0_cpufreq_driver); 231 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); 232 233 return 0; 234} 235 236static struct platform_driver cpu0_cpufreq_platdrv = { 237 .driver = { 238 .name = "cpufreq-cpu0", 239 .owner = THIS_MODULE, 240 }, 241 .probe = cpu0_cpufreq_probe, 242 .remove = cpu0_cpufreq_remove, 243}; 244module_platform_driver(cpu0_cpufreq_platdrv); 245 246MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); 247MODULE_DESCRIPTION("Generic CPU0 cpufreq driver"); 248MODULE_LICENSE("GPL");