diff options
author | Anson Huang <b20788@freescale.com> | 2013-12-19 09:16:47 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-01-06 07:33:45 -0500 |
commit | b4573d1d657aae28bedf3a9a1f5367e09c80d1d6 (patch) | |
tree | 07314dfeeada2c88b04a0f03f30b4ef0cebc25bd /drivers/cpufreq | |
parent | ab1b1c4e8223f9ee66aa93aaf64c36e77cadffac (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.c | 106 |
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; | |||
35 | static struct cpufreq_frequency_table *freq_table; | 35 | static struct cpufreq_frequency_table *freq_table; |
36 | static unsigned int transition_latency; | 36 | static unsigned int transition_latency; |
37 | 37 | ||
38 | static u32 *imx6_soc_volt; | ||
39 | static u32 soc_opp_count; | ||
40 | |||
38 | static unsigned int imx6q_get_speed(unsigned int cpu) | 41 | static 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 | |||
243 | soc_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); |