aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorLucas Stach <l.stach@pengutronix.de>2014-10-24 09:05:55 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-10-27 13:41:34 -0400
commit045ee45c4ff2422a6f47e9fad7dd6cb537de940c (patch)
treeb6118c3066934e7cb2b6601a3c2a8a8055c0df0c /drivers/cpufreq
parentcac7f2429872d3733dc3f9915857b1691da2eb2f (diff)
cpufreq: cpufreq-dt: disable unsupported OPPs
If the regulator connected to the CPU voltage plane doesn't support an OPP specified voltage with the acceptable tolerance it's better to just disable the OPP instead of constantly failing the voltage scaling later on. Includes a fix to move initialization of opp_freq outside the loop to avoid an endless loop from Geert Uytterhoeven. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Signed-off-by: Lucas Stach <l.stach@pengutronix.de> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/cpufreq-dt.c66
1 files changed, 41 insertions, 25 deletions
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 92c162af5045..7789affa7eb8 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -187,6 +187,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
187 struct device *cpu_dev; 187 struct device *cpu_dev;
188 struct regulator *cpu_reg; 188 struct regulator *cpu_reg;
189 struct clk *cpu_clk; 189 struct clk *cpu_clk;
190 unsigned long min_uV = ~0, max_uV = 0;
190 unsigned int transition_latency; 191 unsigned int transition_latency;
191 int ret; 192 int ret;
192 193
@@ -206,16 +207,10 @@ static int cpufreq_init(struct cpufreq_policy *policy)
206 /* OPPs might be populated at runtime, don't check for error here */ 207 /* OPPs might be populated at runtime, don't check for error here */
207 of_init_opp_table(cpu_dev); 208 of_init_opp_table(cpu_dev);
208 209
209 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
210 if (ret) {
211 dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
212 goto out_put_node;
213 }
214
215 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 210 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
216 if (!priv) { 211 if (!priv) {
217 ret = -ENOMEM; 212 ret = -ENOMEM;
218 goto out_free_table; 213 goto out_put_node;
219 } 214 }
220 215
221 of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); 216 of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance);
@@ -224,30 +219,51 @@ static int cpufreq_init(struct cpufreq_policy *policy)
224 transition_latency = CPUFREQ_ETERNAL; 219 transition_latency = CPUFREQ_ETERNAL;
225 220
226 if (!IS_ERR(cpu_reg)) { 221 if (!IS_ERR(cpu_reg)) {
227 struct dev_pm_opp *opp; 222 unsigned long opp_freq = 0;
228 unsigned long min_uV, max_uV;
229 int i;
230 223
231 /* 224 /*
232 * OPP is maintained in order of increasing frequency, and 225 * Disable any OPPs where the connected regulator isn't able to
233 * freq_table initialised from OPP is therefore sorted in the 226 * provide the specified voltage and record minimum and maximum
234 * same order. 227 * voltage levels.
235 */ 228 */
236 for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) 229 while (1) {
237 ; 230 struct dev_pm_opp *opp;
238 rcu_read_lock(); 231 unsigned long opp_uV, tol_uV;
239 opp = dev_pm_opp_find_freq_exact(cpu_dev, 232
240 freq_table[0].frequency * 1000, true); 233 rcu_read_lock();
241 min_uV = dev_pm_opp_get_voltage(opp); 234 opp = dev_pm_opp_find_freq_ceil(cpu_dev, &opp_freq);
242 opp = dev_pm_opp_find_freq_exact(cpu_dev, 235 if (IS_ERR(opp)) {
243 freq_table[i-1].frequency * 1000, true); 236 rcu_read_unlock();
244 max_uV = dev_pm_opp_get_voltage(opp); 237 break;
245 rcu_read_unlock(); 238 }
239 opp_uV = dev_pm_opp_get_voltage(opp);
240 rcu_read_unlock();
241
242 tol_uV = opp_uV * priv->voltage_tolerance / 100;
243 if (regulator_is_supported_voltage(cpu_reg, opp_uV,
244 opp_uV + tol_uV)) {
245 if (opp_uV < min_uV)
246 min_uV = opp_uV;
247 if (opp_uV > max_uV)
248 max_uV = opp_uV;
249 } else {
250 dev_pm_opp_disable(cpu_dev, opp_freq);
251 }
252
253 opp_freq++;
254 }
255
246 ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); 256 ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
247 if (ret > 0) 257 if (ret > 0)
248 transition_latency += ret * 1000; 258 transition_latency += ret * 1000;
249 } 259 }
250 260
261 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
262 if (ret) {
263 pr_err("failed to init cpufreq table: %d\n", ret);
264 goto out_free_priv;
265 }
266
251 /* 267 /*
252 * For now, just loading the cooling device; 268 * For now, just loading the cooling device;
253 * thermal DT code takes care of matching them. 269 * thermal DT code takes care of matching them.
@@ -286,9 +302,9 @@ static int cpufreq_init(struct cpufreq_policy *policy)
286 302
287out_cooling_unregister: 303out_cooling_unregister:
288 cpufreq_cooling_unregister(priv->cdev); 304 cpufreq_cooling_unregister(priv->cdev);
289 kfree(priv);
290out_free_table:
291 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); 305 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
306out_free_priv:
307 kfree(priv);
292out_put_node: 308out_put_node:
293 of_node_put(np); 309 of_node_put(np);
294out_put_reg_clk: 310out_put_reg_clk: