@@ -14,6 +14,16 @@ Optional properties:
- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt for
details. OPPs *must* be supplied either via DT, i.e. this property, or
populated at runtime.
+- boost-opps: Certain CPUs can be operated in optional 'boost' mode (sometimes
+ also referred as overclocking) in which the CPU can operate at frequencies
+ which are not specified by the manufacturer as CPU's operating frequency.
+ CPU usually can operate in 'boost' mode for limited amount of time which
+ depends on thermal conditions. This makes the boost operating points
+ separate from normal ones which can be used at any time. This property
+ consists of an array of 2-tuples items, and each item consists of frequency
+ and voltage like <freq-kHz vol-uV>.
+ freq: clock frequency in kHz
+ vol: voltage in microvolt
- clock-latency: Specify the possible maximum transition latency for clock,
in unit of nanoseconds.
- voltage-tolerance: Specify the CPU voltage tolerance in percentage.
@@ -38,6 +48,10 @@ cpus {
396000 950000
198000 850000
>;
+ boost-opps = <
+ /* kHz uV */
+ 891000 1150000
+ >;
clock-latency = <61036>; /* two CLK32 periods */
#cooling-cells = <2>;
cooling-min-level = <0>;
@@ -60,17 +60,22 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)
if (!IS_ERR(cpu_reg)) {
unsigned long opp_freq;
- rcu_read_lock();
- opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
- if (IS_ERR(opp)) {
+ if (freq_table[index].flags & CPUFREQ_BOOST_FREQ) {
+ volt = freq_table[index].driver_data;
+ opp_freq = freq_table[index].frequency * 1000;
+ } else {
+ rcu_read_lock();
+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
+ if (IS_ERR(opp)) {
+ rcu_read_unlock();
+ dev_err(cpu_dev, "failed to find OPP for %ld\n",
+ freq_Hz);
+ return PTR_ERR(opp);
+ }
+ volt = dev_pm_opp_get_voltage(opp);
+ opp_freq = dev_pm_opp_get_freq(opp);
rcu_read_unlock();
- dev_err(cpu_dev, "failed to find OPP for %ld\n",
- freq_Hz);
- return PTR_ERR(opp);
}
- volt = dev_pm_opp_get_voltage(opp);
- opp_freq = dev_pm_opp_get_freq(opp);
- rcu_read_unlock();
tol = volt * priv->voltage_tolerance / 100;
volt_old = regulator_get_voltage(cpu_reg);
dev_dbg(cpu_dev, "Found OPP: %ld kHz, %ld uV\n",
@@ -182,6 +187,11 @@ try_again:
return ret;
}
+struct boost_opp {
+ unsigned long rate;
+ unsigned long u_volt;
+};
+
static int cpufreq_init(struct cpufreq_policy *policy)
{
struct cpufreq_dt_platform_data *pd;
@@ -191,9 +201,11 @@ static int cpufreq_init(struct cpufreq_policy *policy)
struct device *cpu_dev;
struct regulator *cpu_reg;
struct clk *cpu_clk;
+ struct boost_opp *boost_opps = NULL;
unsigned long min_uV = ~0, max_uV = 0;
+ unsigned int extra_opps = 0;
unsigned int transition_latency;
- int ret;
+ int nr, i, ret;
ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk);
if (ret) {
@@ -234,7 +246,10 @@ static int cpufreq_init(struct cpufreq_policy *policy)
transition_latency = CPUFREQ_ETERNAL;
if (!IS_ERR(cpu_reg)) {
+ const struct property *prop;
+ unsigned long opp_uV, tol_uV;
unsigned long opp_freq = 0;
+ const __be32 *val;
/*
* Disable any OPPs where the connected regulator isn't able to
@@ -243,7 +258,6 @@ static int cpufreq_init(struct cpufreq_policy *policy)
*/
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);
@@ -268,17 +282,86 @@ static int cpufreq_init(struct cpufreq_policy *policy)
opp_freq++;
}
+ prop = of_find_property(np, "boost-opps", NULL);
+ if (!prop || !prop->value)
+ goto set_cpu_reg;
+
+ nr = prop->length / sizeof(u32);
+ if (nr % 2) {
+ dev_err(cpu_dev, "%s: Invalid boost-opps list\n",
+ __func__);
+ goto set_cpu_reg;
+ }
+
+ boost_opps = kzalloc(nr / 2 * sizeof(*boost_opps),
+ GFP_KERNEL);
+ if (!boost_opps)
+ goto set_cpu_reg;
+
+ val = prop->value;
+ while (nr) {
+ unsigned long rate = be32_to_cpup(val++) * 1000;
+ unsigned long u_volt = be32_to_cpup(val++);
+
+ if (rate < opp_freq) {
+ nr -= 2;
+ continue;
+ } else {
+ opp_freq = rate + 1;
+ opp_uV = u_volt;
+ }
+
+ 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 {
+ nr -= 2;
+ continue;
+ }
+
+ boost_opps[extra_opps].rate = rate;
+ boost_opps[extra_opps].u_volt = u_volt;
+ extra_opps++;
+ nr -= 2;
+ }
+set_cpu_reg:
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);
+ ret = __dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table,
+ extra_opps);
if (ret) {
pr_err("failed to init cpufreq table: %d\n", ret);
+ kfree(boost_opps);
goto out_free_priv;
}
+ if (extra_opps) {
+ nr = 0;
+ while (1) {
+ if (freq_table[nr].frequency == CPUFREQ_TABLE_END)
+ break;
+ nr++;
+ }
+
+ for (i = 0; i < extra_opps; i++) {
+ freq_table[nr + i].flags |= CPUFREQ_BOOST_FREQ;
+ freq_table[nr + i].driver_data = boost_opps[i].u_volt;
+ freq_table[nr + i].frequency =
+ boost_opps[i].rate / 1000;
+ }
+
+ freq_table[nr + i].frequency = CPUFREQ_TABLE_END;
+ }
+
+ kfree(boost_opps);
+
priv->cpu_dev = cpu_dev;
priv->cpu_reg = cpu_reg;
policy->driver_data = priv;
@@ -372,6 +455,7 @@ static struct cpufreq_driver dt_cpufreq_driver = {
static int dt_cpufreq_probe(struct platform_device *pdev)
{
+ struct cpufreq_dt_platform_data *pd;
struct device *cpu_dev;
struct regulator *cpu_reg;
struct clk *cpu_clk;
@@ -392,7 +476,11 @@ static int dt_cpufreq_probe(struct platform_device *pdev)
if (!IS_ERR(cpu_reg))
regulator_put(cpu_reg);
- dt_cpufreq_driver.driver_data = dev_get_platdata(&pdev->dev);
+ pd = dev_get_platdata(&pdev->dev);
+ dt_cpufreq_driver.driver_data = pd;
+
+ if (pd)
+ dt_cpufreq_driver.boost_supported = pd->boost_supported;
ret = cpufreq_register_driver(&dt_cpufreq_driver);
if (ret)
@@ -17,6 +17,7 @@ struct cpufreq_dt_platform_data {
* clock.
*/
bool independent_clocks;
+ bool boost_supported;
};
#endif /* __CPUFREQ_DT_H__ */
Add 'boost' mode frequencies support: - add boost-opps binding to cpufreq-dt driver bindings - make cpufreq_init() adjust freq_table accordingly - fix set_target() to handle boost frequencies - add boost_supported field to struct cpufreq_dt_platform_data - set dt_cpufreq_driver.boost_supported in dt_cpufreq_probe() This patch makes cpufreq-dt driver aware of 'boost' mode frequencies and prepares it for adding support for Exynos4x12 'boost' support. boost-opps binding is currently limited to cpufreq-dt but once there is a need for cpufreq wide and/or generic Linux device support for 'boost' mode cpufreq-dt can be updated to handle the new code without changing the binding itself. The decision to make 'boost' mode support limited to cpufreq-dt driver for now was taken because 'boost' mode is currently a niche feature and code needed for parsing boost-opps binding is minimal and simple. More generic (i.e. separate 'boost' OPPs list in struct device and generic cpufreq convertion of them to freq_table format) support would need far more code and effort to make it work. Doing it without a demonstrated real need would be on overengineering IMHO. Cc: Tomasz Figa <tomasz.figa@gmail.com> Cc: Mike Turquette <mturquette@linaro.org> Cc: Javier Martinez Canillas <javier.martinez@collabora.co.uk> Cc: Thomas Abraham <thomas.ab@samsung.com> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> --- .../devicetree/bindings/cpufreq/cpufreq-dt.txt | 14 +++ drivers/cpufreq/cpufreq-dt.c | 114 +++++++++++++++++--- include/linux/cpufreq-dt.h | 1 + 3 files changed, 116 insertions(+), 13 deletions(-)