Skip to content

Commit 25cb20a

Browse files
bebarinovireshk
authored andcommitted
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: Stephen Boyd <[email protected]> [Roger Lu: Changed to rcu less implementation] Signed-off-by: Roger Lu <[email protected]> [[email protected]: added handling of OPP min/max voltage] Signed-off-by: Sylwester Nawrocki <[email protected]> Signed-off-by: Viresh Kumar <[email protected]>
1 parent b19c235 commit 25cb20a

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

drivers/opp/core.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,6 +2102,75 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
21022102
return r;
21032103
}
21042104

2105+
/**
2106+
* dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP
2107+
* @dev: device for which we do this operation
2108+
* @freq: OPP frequency to adjust voltage of
2109+
* @u_volt: new OPP target voltage
2110+
* @u_volt_min: new OPP min voltage
2111+
* @u_volt_max: new OPP max voltage
2112+
*
2113+
* Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
2114+
* copy operation, returns 0 if no modifcation was done OR modification was
2115+
* successful.
2116+
*/
2117+
int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
2118+
unsigned long u_volt, unsigned long u_volt_min,
2119+
unsigned long u_volt_max)
2120+
2121+
{
2122+
struct opp_table *opp_table;
2123+
struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV);
2124+
int r = 0;
2125+
2126+
/* Find the opp_table */
2127+
opp_table = _find_opp_table(dev);
2128+
if (IS_ERR(opp_table)) {
2129+
r = PTR_ERR(opp_table);
2130+
dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
2131+
return r;
2132+
}
2133+
2134+
mutex_lock(&opp_table->lock);
2135+
2136+
/* Do we have the frequency? */
2137+
list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
2138+
if (tmp_opp->rate == freq) {
2139+
opp = tmp_opp;
2140+
break;
2141+
}
2142+
}
2143+
2144+
if (IS_ERR(opp)) {
2145+
r = PTR_ERR(opp);
2146+
goto adjust_unlock;
2147+
}
2148+
2149+
/* Is update really needed? */
2150+
if (opp->supplies->u_volt == u_volt)
2151+
goto adjust_unlock;
2152+
2153+
opp->supplies->u_volt = u_volt;
2154+
opp->supplies->u_volt_min = u_volt_min;
2155+
opp->supplies->u_volt_max = u_volt_max;
2156+
2157+
dev_pm_opp_get(opp);
2158+
mutex_unlock(&opp_table->lock);
2159+
2160+
/* Notify the voltage change of the OPP */
2161+
blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADJUST_VOLTAGE,
2162+
opp);
2163+
2164+
dev_pm_opp_put(opp);
2165+
goto adjust_put_table;
2166+
2167+
adjust_unlock:
2168+
mutex_unlock(&opp_table->lock);
2169+
adjust_put_table:
2170+
dev_pm_opp_put_opp_table(opp_table);
2171+
return r;
2172+
}
2173+
21052174
/**
21062175
* dev_pm_opp_enable() - Enable a specific OPP
21072176
* @dev: device for which we do this operation

include/linux/pm_opp.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct opp_table;
2222

2323
enum dev_pm_opp_event {
2424
OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
25+
OPP_EVENT_ADJUST_VOLTAGE,
2526
};
2627

2728
/**
@@ -113,6 +114,10 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq,
113114
void dev_pm_opp_remove(struct device *dev, unsigned long freq);
114115
void dev_pm_opp_remove_all_dynamic(struct device *dev);
115116

117+
int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
118+
unsigned long u_volt, unsigned long u_volt_min,
119+
unsigned long u_volt_max);
120+
116121
int dev_pm_opp_enable(struct device *dev, unsigned long freq);
117122

118123
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)
242247
{
243248
}
244249

250+
static inline int
251+
dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
252+
unsigned long u_volt, unsigned long u_volt_min,
253+
unsigned long u_volt_max)
254+
{
255+
return 0;
256+
}
257+
245258
static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
246259
{
247260
return 0;

0 commit comments

Comments
 (0)