aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2013-12-19 09:16:47 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-01-06 07:33:45 -0500
commitb4573d1d657aae28bedf3a9a1f5367e09c80d1d6 (patch)
tree07314dfeeada2c88b04a0f03f30b4ef0cebc25bd /drivers/cpufreq
parentab1b1c4e8223f9ee66aa93aaf64c36e77cadffac (diff)
cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed
on i.MX6Q, cpu freq change need to follow below flows: 1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint table from dts; 2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before VDDARM, if VDDPU is off, no need to change it; 3. when cpu freq is scaling down, need to decrease VDDARM voltage before VDDSOC/PU, if VDDPU is off, no need to change it; normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will use fixed value for vddsoc/pu voltage setting. Signed-off-by: Anson Huang <b20788@freescale.com> Acked-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/imx6q-cpufreq.c106
1 files changed, 77 insertions, 29 deletions
diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c
index 4b3f18e5f36b..c29198fbc9b7 100644
--- a/drivers/cpufreq/imx6q-cpufreq.c
+++ b/drivers/cpufreq/imx6q-cpufreq.c
@@ -35,6 +35,9 @@ static struct device *cpu_dev;
35static struct cpufreq_frequency_table *freq_table; 35static struct cpufreq_frequency_table *freq_table;
36static unsigned int transition_latency; 36static unsigned int transition_latency;
37 37
38static u32 *imx6_soc_volt;
39static u32 soc_opp_count;
40
38static unsigned int imx6q_get_speed(unsigned int cpu) 41static unsigned int imx6q_get_speed(unsigned int cpu)
39{ 42{
40 return clk_get_rate(arm_clk) / 1000; 43 return clk_get_rate(arm_clk) / 1000;
@@ -69,23 +72,22 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
69 72
70 /* scaling up? scale voltage before frequency */ 73 /* scaling up? scale voltage before frequency */
71 if (new_freq > old_freq) { 74 if (new_freq > old_freq) {
75 ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0);
76 if (ret) {
77 dev_err(cpu_dev, "failed to scale vddpu up: %d\n", ret);
78 return ret;
79 }
80 ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0);
81 if (ret) {
82 dev_err(cpu_dev, "failed to scale vddsoc up: %d\n", ret);
83 return ret;
84 }
72 ret = regulator_set_voltage_tol(arm_reg, volt, 0); 85 ret = regulator_set_voltage_tol(arm_reg, volt, 0);
73 if (ret) { 86 if (ret) {
74 dev_err(cpu_dev, 87 dev_err(cpu_dev,
75 "failed to scale vddarm up: %d\n", ret); 88 "failed to scale vddarm up: %d\n", ret);
76 return ret; 89 return ret;
77 } 90 }
78
79 /*
80 * Need to increase vddpu and vddsoc for safety
81 * if we are about to run at 1.2 GHz.
82 */
83 if (new_freq == FREQ_1P2_GHZ / 1000) {
84 regulator_set_voltage_tol(pu_reg,
85 PU_SOC_VOLTAGE_HIGH, 0);
86 regulator_set_voltage_tol(soc_reg,
87 PU_SOC_VOLTAGE_HIGH, 0);
88 }
89 } 91 }
90 92
91 /* 93 /*
@@ -120,12 +122,15 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
120 "failed to scale vddarm down: %d\n", ret); 122 "failed to scale vddarm down: %d\n", ret);
121 ret = 0; 123 ret = 0;
122 } 124 }
123 125 ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0);
124 if (old_freq == FREQ_1P2_GHZ / 1000) { 126 if (ret) {
125 regulator_set_voltage_tol(pu_reg, 127 dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret);
126 PU_SOC_VOLTAGE_NORMAL, 0); 128 ret = 0;
127 regulator_set_voltage_tol(soc_reg, 129 }
128 PU_SOC_VOLTAGE_NORMAL, 0); 130 ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0);
131 if (ret) {
132 dev_warn(cpu_dev, "failed to scale vddpu down: %d\n", ret);
133 ret = 0;
129 } 134 }
130 } 135 }
131 136
@@ -153,6 +158,9 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
153 struct dev_pm_opp *opp; 158 struct dev_pm_opp *opp;
154 unsigned long min_volt, max_volt; 159 unsigned long min_volt, max_volt;
155 int num, ret; 160 int num, ret;
161 const struct property *prop;
162 const __be32 *val;
163 u32 nr, i, j;
156 164
157 cpu_dev = get_cpu_device(0); 165 cpu_dev = get_cpu_device(0);
158 if (!cpu_dev) { 166 if (!cpu_dev) {
@@ -201,10 +209,62 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
201 goto put_node; 209 goto put_node;
202 } 210 }
203 211
212 /* Make imx6_soc_volt array's size same as arm opp number */
213 imx6_soc_volt = devm_kzalloc(cpu_dev, sizeof(*imx6_soc_volt) * num, GFP_KERNEL);
214 if (imx6_soc_volt == NULL) {
215 ret = -ENOMEM;
216 goto free_freq_table;
217 }
218
219 prop = of_find_property(np, "fsl,soc-operating-points", NULL);
220 if (!prop || !prop->value)
221 goto soc_opp_out;
222
223 /*
224 * Each OPP is a set of tuples consisting of frequency and
225 * voltage like <freq-kHz vol-uV>.
226 */
227 nr = prop->length / sizeof(u32);
228 if (nr % 2 || (nr / 2) < num)
229 goto soc_opp_out;
230
231 for (j = 0; j < num; j++) {
232 val = prop->value;
233 for (i = 0; i < nr / 2; i++) {
234 unsigned long freq = be32_to_cpup(val++);
235 unsigned long volt = be32_to_cpup(val++);
236 if (freq_table[j].frequency == freq) {
237 imx6_soc_volt[soc_opp_count++] = volt;
238 break;
239 }
240 }
241 }
242
243soc_opp_out:
244 /* use fixed soc opp volt if no valid soc opp info found in dtb */
245 if (soc_opp_count != num) {
246 dev_warn(cpu_dev, "can NOT find valid fsl,soc-operating-points property in dtb, use default value!\n");
247 for (j = 0; j < num; j++)
248 imx6_soc_volt[j] = PU_SOC_VOLTAGE_NORMAL;
249 if (freq_table[num - 1].frequency * 1000 == FREQ_1P2_GHZ)
250 imx6_soc_volt[num - 1] = PU_SOC_VOLTAGE_HIGH;
251 }
252
204 if (of_property_read_u32(np, "clock-latency", &transition_latency)) 253 if (of_property_read_u32(np, "clock-latency", &transition_latency))
205 transition_latency = CPUFREQ_ETERNAL; 254 transition_latency = CPUFREQ_ETERNAL;
206 255
207 /* 256 /*
257 * Calculate the ramp time for max voltage change in the
258 * VDDSOC and VDDPU regulators.
259 */
260 ret = regulator_set_voltage_time(soc_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]);
261 if (ret > 0)
262 transition_latency += ret * 1000;
263 ret = regulator_set_voltage_time(pu_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]);
264 if (ret > 0)
265 transition_latency += ret * 1000;
266
267 /*
208 * OPP is maintained in order of increasing frequency, and 268 * OPP is maintained in order of increasing frequency, and
209 * freq_table initialised from OPP is therefore sorted in the 269 * freq_table initialised from OPP is therefore sorted in the
210 * same order. 270 * same order.
@@ -221,18 +281,6 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
221 if (ret > 0) 281 if (ret > 0)
222 transition_latency += ret * 1000; 282 transition_latency += ret * 1000;
223 283
224 /* Count vddpu and vddsoc latency in for 1.2 GHz support */
225 if (freq_table[num].frequency == FREQ_1P2_GHZ / 1000) {
226 ret = regulator_set_voltage_time(pu_reg, PU_SOC_VOLTAGE_NORMAL,
227 PU_SOC_VOLTAGE_HIGH);
228 if (ret > 0)
229 transition_latency += ret * 1000;
230 ret = regulator_set_voltage_time(soc_reg, PU_SOC_VOLTAGE_NORMAL,
231 PU_SOC_VOLTAGE_HIGH);
232 if (ret > 0)
233 transition_latency += ret * 1000;
234 }
235
236 ret = cpufreq_register_driver(&imx6q_cpufreq_driver); 284 ret = cpufreq_register_driver(&imx6q_cpufreq_driver);
237 if (ret) { 285 if (ret) {
238 dev_err(cpu_dev, "failed register driver: %d\n", ret); 286 dev_err(cpu_dev, "failed register driver: %d\n", ret);