diff options
author | Ranjani Vaidyanathan <ra5478@freescale.com> | 2013-08-16 13:19:16 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:01:16 -0400 |
commit | f2c66915dcc5a6ce309ed3c78efd930786373125 (patch) | |
tree | 36744bccbbd6f3672314d97db123640bc756b264 /drivers/cpufreq | |
parent | 2a7b3ac19150caeee1eac35e2dcc2fefd6155d0a (diff) |
ENGR00273792-1 Cpufreq:iMX6x:Improve CPUFREQ driver.
Add support for VDDSOC/VDDPU operating points that track
the VDDARM cap to the device tree.
Add the description for soc-operating-points that need to be added to
the device tree files.
Signed-off-by: Ranjani Vaidyanathan <ra5478@freescale.com>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r-- | drivers/cpufreq/imx6q-cpufreq.c | 184 |
1 files changed, 144 insertions, 40 deletions
diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index b78bc35973ba..23c0125e8253 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c | |||
@@ -15,10 +15,7 @@ | |||
15 | #include <linux/opp.h> | 15 | #include <linux/opp.h> |
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/regulator/consumer.h> | 17 | #include <linux/regulator/consumer.h> |
18 | 18 | #include <linux/slab.h> | |
19 | #define PU_SOC_VOLTAGE_NORMAL 1250000 | ||
20 | #define PU_SOC_VOLTAGE_HIGH 1275000 | ||
21 | #define FREQ_1P2_GHZ 1200000000 | ||
22 | 19 | ||
23 | static struct regulator *arm_reg; | 20 | static struct regulator *arm_reg; |
24 | static struct regulator *pu_reg; | 21 | static struct regulator *pu_reg; |
@@ -33,6 +30,13 @@ static struct clk *pll2_pfd2_396m_clk; | |||
33 | static struct device *cpu_dev; | 30 | static struct device *cpu_dev; |
34 | static struct cpufreq_frequency_table *freq_table; | 31 | static struct cpufreq_frequency_table *freq_table; |
35 | static unsigned int transition_latency; | 32 | static unsigned int transition_latency; |
33 | struct soc_opp { | ||
34 | u32 arm_freq; | ||
35 | u32 soc_volt; | ||
36 | }; | ||
37 | |||
38 | static struct soc_opp *imx6q_soc_opp; | ||
39 | static u32 soc_opp_count; | ||
36 | 40 | ||
37 | static int imx6q_verify_speed(struct cpufreq_policy *policy) | 41 | static int imx6q_verify_speed(struct cpufreq_policy *policy) |
38 | { | 42 | { |
@@ -50,7 +54,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, | |||
50 | struct cpufreq_freqs freqs; | 54 | struct cpufreq_freqs freqs; |
51 | struct opp *opp; | 55 | struct opp *opp; |
52 | unsigned long freq_hz, volt, volt_old; | 56 | unsigned long freq_hz, volt, volt_old; |
53 | unsigned int index; | 57 | unsigned int index, soc_opp_index = 0; |
54 | int ret; | 58 | int ret; |
55 | 59 | ||
56 | ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, | 60 | ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, |
@@ -82,28 +86,46 @@ static int imx6q_set_target(struct cpufreq_policy *policy, | |||
82 | rcu_read_unlock(); | 86 | rcu_read_unlock(); |
83 | volt_old = regulator_get_voltage(arm_reg); | 87 | volt_old = regulator_get_voltage(arm_reg); |
84 | 88 | ||
89 | /* Find the matching VDDSOC/VDDPU operating voltage */ | ||
90 | while (soc_opp_index < soc_opp_count) { | ||
91 | if (freqs.new == imx6q_soc_opp[soc_opp_index].arm_freq) | ||
92 | break; | ||
93 | soc_opp_index++; | ||
94 | } | ||
95 | if (soc_opp_index >= soc_opp_count) { | ||
96 | dev_err(cpu_dev, | ||
97 | "Cannot find matching imx6q_soc_opp voltage\n"); | ||
98 | return -EINVAL; | ||
99 | } | ||
100 | |||
85 | dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", | 101 | dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", |
86 | freqs.old / 1000, volt_old / 1000, | 102 | freqs.old / 1000, volt_old / 1000, |
87 | freqs.new / 1000, volt / 1000); | 103 | freqs.new / 1000, volt / 1000); |
88 | 104 | ||
89 | /* scaling up? scale voltage before frequency */ | 105 | /* scaling up? scale voltage before frequency */ |
90 | if (freqs.new > freqs.old) { | 106 | if (freqs.new > freqs.old) { |
107 | if (regulator_is_enabled(pu_reg)) { | ||
108 | ret = regulator_set_voltage_tol(pu_reg, | ||
109 | imx6q_soc_opp[soc_opp_index].soc_volt, | ||
110 | 0); | ||
111 | if (ret) { | ||
112 | dev_err(cpu_dev, | ||
113 | "failed to scale vddpu up: %d\n", ret); | ||
114 | goto err1; | ||
115 | } | ||
116 | } | ||
117 | ret = regulator_set_voltage_tol(soc_reg, | ||
118 | imx6q_soc_opp[soc_opp_index].soc_volt, 0); | ||
119 | if (ret) { | ||
120 | dev_err(cpu_dev, | ||
121 | "failed to scale vddsoc up: %d\n", ret); | ||
122 | goto err1; | ||
123 | } | ||
91 | ret = regulator_set_voltage_tol(arm_reg, volt, 0); | 124 | ret = regulator_set_voltage_tol(arm_reg, volt, 0); |
92 | if (ret) { | 125 | if (ret) { |
93 | dev_err(cpu_dev, | 126 | dev_err(cpu_dev, |
94 | "failed to scale vddarm up: %d\n", ret); | 127 | "failed to scale vddarm up: %d\n", ret); |
95 | return ret; | 128 | goto err1; |
96 | } | ||
97 | |||
98 | /* | ||
99 | * Need to increase vddpu and vddsoc for safety | ||
100 | * if we are about to run at 1.2 GHz. | ||
101 | */ | ||
102 | if (freqs.new == FREQ_1P2_GHZ / 1000) { | ||
103 | regulator_set_voltage_tol(pu_reg, | ||
104 | PU_SOC_VOLTAGE_HIGH, 0); | ||
105 | regulator_set_voltage_tol(soc_reg, | ||
106 | PU_SOC_VOLTAGE_HIGH, 0); | ||
107 | } | 129 | } |
108 | } | 130 | } |
109 | 131 | ||
@@ -144,28 +166,43 @@ static int imx6q_set_target(struct cpufreq_policy *policy, | |||
144 | ret = clk_set_rate(arm_clk, freqs.new * 1000); | 166 | ret = clk_set_rate(arm_clk, freqs.new * 1000); |
145 | if (ret) { | 167 | if (ret) { |
146 | dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); | 168 | dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); |
147 | regulator_set_voltage_tol(arm_reg, volt_old, 0); | 169 | goto err1; |
148 | return ret; | ||
149 | } | 170 | } |
150 | 171 | ||
151 | /* scaling down? scale voltage after frequency */ | 172 | /* scaling down? scale voltage after frequency */ |
152 | if (freqs.new < freqs.old) { | 173 | if (freqs.new < freqs.old) { |
153 | ret = regulator_set_voltage_tol(arm_reg, volt, 0); | 174 | ret = regulator_set_voltage_tol(arm_reg, volt, 0); |
154 | if (ret) | 175 | if (ret) { |
155 | dev_warn(cpu_dev, | 176 | dev_warn(cpu_dev, |
156 | "failed to scale vddarm down: %d\n", ret); | 177 | "failed to scale vddarm down: %d\n", ret); |
178 | goto err1; | ||
179 | } | ||
157 | 180 | ||
158 | if (freqs.old == FREQ_1P2_GHZ / 1000) { | 181 | ret = regulator_set_voltage_tol(soc_reg, |
159 | regulator_set_voltage_tol(pu_reg, | 182 | imx6q_soc_opp[soc_opp_index].soc_volt, 0); |
160 | PU_SOC_VOLTAGE_NORMAL, 0); | 183 | if (ret) { |
161 | regulator_set_voltage_tol(soc_reg, | 184 | dev_err(cpu_dev, |
162 | PU_SOC_VOLTAGE_NORMAL, 0); | 185 | "failed to scale vddsoc down: %d\n", ret); |
186 | goto err1; | ||
163 | } | 187 | } |
164 | } | ||
165 | 188 | ||
189 | if (regulator_is_enabled(pu_reg)) { | ||
190 | ret = regulator_set_voltage_tol(pu_reg, | ||
191 | imx6q_soc_opp[soc_opp_index].soc_volt, | ||
192 | 0); | ||
193 | if (ret) { | ||
194 | dev_err(cpu_dev, | ||
195 | "failed to scale vddpu down: %d\n", ret); | ||
196 | goto err1; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | } | ||
166 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); | 201 | cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); |
167 | 202 | ||
168 | return 0; | 203 | return 0; |
204 | err1: | ||
205 | return -1; | ||
169 | } | 206 | } |
170 | 207 | ||
171 | static int imx6q_cpufreq_init(struct cpufreq_policy *policy) | 208 | static int imx6q_cpufreq_init(struct cpufreq_policy *policy) |
@@ -211,8 +248,11 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) | |||
211 | { | 248 | { |
212 | struct device_node *np; | 249 | struct device_node *np; |
213 | struct opp *opp; | 250 | struct opp *opp; |
214 | unsigned long min_volt, max_volt; | 251 | unsigned long min_volt = 0, max_volt = 0; |
215 | int num, ret; | 252 | int num, ret; |
253 | const struct property *prop; | ||
254 | const __be32 *val; | ||
255 | u32 nr, i; | ||
216 | 256 | ||
217 | cpu_dev = &pdev->dev; | 257 | cpu_dev = &pdev->dev; |
218 | 258 | ||
@@ -259,10 +299,86 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) | |||
259 | goto put_node; | 299 | goto put_node; |
260 | } | 300 | } |
261 | 301 | ||
302 | prop = of_find_property(np, "fsl,soc-operating-points", NULL); | ||
303 | if (!prop) { | ||
304 | dev_err(cpu_dev, | ||
305 | "fsl,soc-operating-points node not found\n"); | ||
306 | goto free_freq_table; | ||
307 | } | ||
308 | if (!prop->value) { | ||
309 | dev_err(cpu_dev, | ||
310 | "No entries in fsl-soc-operating-points node.\n"); | ||
311 | goto free_freq_table; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * Each OPP is a set of tuples consisting of frequency and | ||
316 | * voltage like <freq-kHz vol-uV>. | ||
317 | */ | ||
318 | nr = prop->length / sizeof(u32); | ||
319 | if (nr % 2) { | ||
320 | dev_err(cpu_dev, "Invalid fsl-soc-operating-points list\n"); | ||
321 | goto free_freq_table; | ||
322 | } | ||
323 | |||
324 | /* Get the VDDSOC/VDDPU voltages that need to track the CPU voltages. */ | ||
325 | imx6q_soc_opp = devm_kzalloc(cpu_dev, | ||
326 | sizeof(struct soc_opp) * (nr / 2), | ||
327 | GFP_KERNEL); | ||
328 | |||
329 | if (imx6q_soc_opp == NULL) { | ||
330 | dev_err(cpu_dev, "No Memory for VDDSOC/PU table\n"); | ||
331 | goto free_freq_table; | ||
332 | } | ||
333 | |||
334 | rcu_read_lock(); | ||
335 | val = prop->value; | ||
336 | |||
337 | for (i = 0; i < nr / 2; i++) { | ||
338 | unsigned long freq = be32_to_cpup(val++); | ||
339 | unsigned long volt = be32_to_cpup(val++); | ||
340 | |||
341 | if (i == 0) | ||
342 | min_volt = max_volt = volt; | ||
343 | if (volt < min_volt) | ||
344 | min_volt = volt; | ||
345 | if (volt > max_volt) | ||
346 | max_volt = volt; | ||
347 | opp = opp_find_freq_exact(cpu_dev, | ||
348 | freq * 1000, true); | ||
349 | if (IS_ERR(opp)) { | ||
350 | opp = opp_find_freq_exact(cpu_dev, | ||
351 | freq * 1000, false); | ||
352 | if (IS_ERR(opp)) { | ||
353 | dev_err(cpu_dev, | ||
354 | "freq in soc-operating-points does not \ | ||
355 | match cpufreq table\n"); | ||
356 | rcu_read_unlock(); | ||
357 | goto free_freq_table; | ||
358 | } | ||
359 | } | ||
360 | imx6q_soc_opp[i].arm_freq = freq; | ||
361 | imx6q_soc_opp[i].soc_volt = volt; | ||
362 | soc_opp_count++; | ||
363 | } | ||
364 | rcu_read_unlock(); | ||
365 | |||
262 | if (of_property_read_u32(np, "clock-latency", &transition_latency)) | 366 | if (of_property_read_u32(np, "clock-latency", &transition_latency)) |
263 | transition_latency = CPUFREQ_ETERNAL; | 367 | transition_latency = CPUFREQ_ETERNAL; |
264 | 368 | ||
265 | /* | 369 | /* |
370 | * Calculate the ramp time for max voltage change in the | ||
371 | * VDDSOC and VDDPU regulators. | ||
372 | */ | ||
373 | ret = regulator_set_voltage_time(soc_reg, min_volt, max_volt); | ||
374 | if (ret > 0) | ||
375 | transition_latency += ret * 1000; | ||
376 | |||
377 | ret = regulator_set_voltage_time(pu_reg, min_volt, max_volt); | ||
378 | if (ret > 0) | ||
379 | transition_latency += ret * 1000; | ||
380 | |||
381 | /* | ||
266 | * OPP is maintained in order of increasing frequency, and | 382 | * OPP is maintained in order of increasing frequency, and |
267 | * freq_table initialised from OPP is therefore sorted in the | 383 | * freq_table initialised from OPP is therefore sorted in the |
268 | * same order. | 384 | * same order. |
@@ -272,25 +388,13 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) | |||
272 | freq_table[0].frequency * 1000, true); | 388 | freq_table[0].frequency * 1000, true); |
273 | min_volt = opp_get_voltage(opp); | 389 | min_volt = opp_get_voltage(opp); |
274 | opp = opp_find_freq_exact(cpu_dev, | 390 | opp = opp_find_freq_exact(cpu_dev, |
275 | freq_table[--num].frequency * 1000, true); | 391 | freq_table[num - 1].frequency * 1000, true); |
276 | max_volt = opp_get_voltage(opp); | 392 | max_volt = opp_get_voltage(opp); |
277 | rcu_read_unlock(); | 393 | rcu_read_unlock(); |
278 | ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt); | 394 | ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt); |
279 | if (ret > 0) | 395 | if (ret > 0) |
280 | transition_latency += ret * 1000; | 396 | transition_latency += ret * 1000; |
281 | 397 | ||
282 | /* Count vddpu and vddsoc latency in for 1.2 GHz support */ | ||
283 | if (freq_table[num].frequency == FREQ_1P2_GHZ / 1000) { | ||
284 | ret = regulator_set_voltage_time(pu_reg, PU_SOC_VOLTAGE_NORMAL, | ||
285 | PU_SOC_VOLTAGE_HIGH); | ||
286 | if (ret > 0) | ||
287 | transition_latency += ret * 1000; | ||
288 | ret = regulator_set_voltage_time(soc_reg, PU_SOC_VOLTAGE_NORMAL, | ||
289 | PU_SOC_VOLTAGE_HIGH); | ||
290 | if (ret > 0) | ||
291 | transition_latency += ret * 1000; | ||
292 | } | ||
293 | |||
294 | ret = cpufreq_register_driver(&imx6q_cpufreq_driver); | 398 | ret = cpufreq_register_driver(&imx6q_cpufreq_driver); |
295 | if (ret) { | 399 | if (ret) { |
296 | dev_err(cpu_dev, "failed register driver: %d\n", ret); | 400 | dev_err(cpu_dev, "failed register driver: %d\n", ret); |