Commit 25cb20a2 authored by Stephen Boyd's avatar Stephen Boyd Committed by Viresh Kumar

PM / OPP: Support adjusting OPP voltages at runtime

On some SoCs the Adaptive Voltage Scaling (AVS) technique is
employed to optimize the operating voltage of a device. At a
given frequency, the hardware monitors dynamic factors and either
makes a suggestion for how much to adjust a voltage for the
current frequency, or it automatically adjusts the voltage
without software intervention. Add an API to the OPP library for
the former case, so that AVS type devices can update the voltages
for an OPP when the hardware determines the voltage should
change. The assumption is that drivers like CPUfreq or devfreq
will register for the OPP notifiers and adjust the voltage
according to suggestions that AVS makes.

This patch is derived from [1] submitted by Stephen.
[1] https://lore.kernel.org/patchwork/patch/599279/Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
[Roger Lu: Changed to rcu less implementation]
Signed-off-by: default avatarRoger Lu <roger.lu@mediatek.com>
[s.nawrocki@samsung.com: added handling of OPP min/max voltage]
Signed-off-by: default avatarSylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
parent b19c2355
...@@ -2102,6 +2102,75 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, ...@@ -2102,6 +2102,75 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
return r; return r;
} }
/**
* dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP
* @dev: device for which we do this operation
* @freq: OPP frequency to adjust voltage of
* @u_volt: new OPP target voltage
* @u_volt_min: new OPP min voltage
* @u_volt_max: new OPP max voltage
*
* Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
* copy operation, returns 0 if no modifcation was done OR modification was
* successful.
*/
int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
unsigned long u_volt, unsigned long u_volt_min,
unsigned long u_volt_max)
{
struct opp_table *opp_table;
struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV);
int r = 0;
/* Find the opp_table */
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
r = PTR_ERR(opp_table);
dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
return r;
}
mutex_lock(&opp_table->lock);
/* Do we have the frequency? */
list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
if (tmp_opp->rate == freq) {
opp = tmp_opp;
break;
}
}
if (IS_ERR(opp)) {
r = PTR_ERR(opp);
goto adjust_unlock;
}
/* Is update really needed? */
if (opp->supplies->u_volt == u_volt)
goto adjust_unlock;
opp->supplies->u_volt = u_volt;
opp->supplies->u_volt_min = u_volt_min;
opp->supplies->u_volt_max = u_volt_max;
dev_pm_opp_get(opp);
mutex_unlock(&opp_table->lock);
/* Notify the voltage change of the OPP */
blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADJUST_VOLTAGE,
opp);
dev_pm_opp_put(opp);
goto adjust_put_table;
adjust_unlock:
mutex_unlock(&opp_table->lock);
adjust_put_table:
dev_pm_opp_put_opp_table(opp_table);
return r;
}
/** /**
* dev_pm_opp_enable() - Enable a specific OPP * dev_pm_opp_enable() - Enable a specific OPP
* @dev: device for which we do this operation * @dev: device for which we do this operation
......
...@@ -22,6 +22,7 @@ struct opp_table; ...@@ -22,6 +22,7 @@ struct opp_table;
enum dev_pm_opp_event { enum dev_pm_opp_event {
OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
OPP_EVENT_ADJUST_VOLTAGE,
}; };
/** /**
...@@ -113,6 +114,10 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, ...@@ -113,6 +114,10 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq,
void dev_pm_opp_remove(struct device *dev, unsigned long freq); void dev_pm_opp_remove(struct device *dev, unsigned long freq);
void dev_pm_opp_remove_all_dynamic(struct device *dev); void dev_pm_opp_remove_all_dynamic(struct device *dev);
int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
unsigned long u_volt, unsigned long u_volt_min,
unsigned long u_volt_max);
int dev_pm_opp_enable(struct device *dev, unsigned long freq); int dev_pm_opp_enable(struct device *dev, unsigned long freq);
int dev_pm_opp_disable(struct device *dev, unsigned long freq); int dev_pm_opp_disable(struct device *dev, unsigned long freq);
...@@ -242,6 +247,14 @@ static inline void dev_pm_opp_remove_all_dynamic(struct device *dev) ...@@ -242,6 +247,14 @@ static inline void dev_pm_opp_remove_all_dynamic(struct device *dev)
{ {
} }
static inline int
dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
unsigned long u_volt, unsigned long u_volt_min,
unsigned long u_volt_max)
{
return 0;
}
static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq) static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
{ {
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment