Skip to content

Commit

Permalink
cpufreq: cpufreq-dt: disable unsupported OPPs
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Lucas Stach <[email protected]>
Acked-by: Viresh Kumar <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
  • Loading branch information
lynxeye-dev authored and rafaeljw committed Oct 27, 2014
1 parent cac7f24 commit 045ee45
Showing 1 changed file with 41 additions and 25 deletions.
66 changes: 41 additions & 25 deletions drivers/cpufreq/cpufreq-dt.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
struct device *cpu_dev;
struct regulator *cpu_reg;
struct clk *cpu_clk;
unsigned long min_uV = ~0, max_uV = 0;
unsigned int transition_latency;
int ret;

Expand All @@ -206,16 +207,10 @@ static int cpufreq_init(struct cpufreq_policy *policy)
/* OPPs might be populated at runtime, don't check for error here */
of_init_opp_table(cpu_dev);

ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
goto out_put_node;
}

priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto out_free_table;
goto out_put_node;
}

of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance);
Expand All @@ -224,30 +219,51 @@ static int cpufreq_init(struct cpufreq_policy *policy)
transition_latency = CPUFREQ_ETERNAL;

if (!IS_ERR(cpu_reg)) {
struct dev_pm_opp *opp;
unsigned long min_uV, max_uV;
int i;
unsigned long opp_freq = 0;

/*
* OPP is maintained in order of increasing frequency, and
* freq_table initialised from OPP is therefore sorted in the
* same order.
* Disable any OPPs where the connected regulator isn't able to
* provide the specified voltage and record minimum and maximum
* voltage levels.
*/
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
;
rcu_read_lock();
opp = dev_pm_opp_find_freq_exact(cpu_dev,
freq_table[0].frequency * 1000, true);
min_uV = dev_pm_opp_get_voltage(opp);
opp = dev_pm_opp_find_freq_exact(cpu_dev,
freq_table[i-1].frequency * 1000, true);
max_uV = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
while (1) {
struct dev_pm_opp *opp;
unsigned long opp_uV, tol_uV;

rcu_read_lock();
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &opp_freq);
if (IS_ERR(opp)) {
rcu_read_unlock();
break;
}
opp_uV = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();

tol_uV = opp_uV * priv->voltage_tolerance / 100;
if (regulator_is_supported_voltage(cpu_reg, opp_uV,
opp_uV + tol_uV)) {
if (opp_uV < min_uV)
min_uV = opp_uV;
if (opp_uV > max_uV)
max_uV = opp_uV;
} else {
dev_pm_opp_disable(cpu_dev, opp_freq);
}

opp_freq++;
}

ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
if (ret > 0)
transition_latency += ret * 1000;
}

ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
pr_err("failed to init cpufreq table: %d\n", ret);
goto out_free_priv;
}

/*
* For now, just loading the cooling device;
* thermal DT code takes care of matching them.
Expand Down Expand Up @@ -286,9 +302,9 @@ static int cpufreq_init(struct cpufreq_policy *policy)

out_cooling_unregister:
cpufreq_cooling_unregister(priv->cdev);
kfree(priv);
out_free_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_priv:
kfree(priv);
out_put_node:
of_node_put(np);
out_put_reg_clk:
Expand Down

0 comments on commit 045ee45

Please sign in to comment.