|
@@ -93,6 +93,8 @@ struct opp_table *_find_opp_table(struct device *dev)
|
|
* Return: voltage in micro volt corresponding to the opp, else
|
|
* Return: voltage in micro volt corresponding to the opp, else
|
|
* return 0
|
|
* return 0
|
|
*
|
|
*
|
|
|
|
+ * This is useful only for devices with single power supply.
|
|
|
|
+ *
|
|
* Locking: This function must be called under rcu_read_lock(). opp is a rcu
|
|
* Locking: This function must be called under rcu_read_lock(). opp is a rcu
|
|
* protected pointer. This means that opp which could have been fetched by
|
|
* protected pointer. This means that opp which could have been fetched by
|
|
* opp_find_freq_{exact,ceil,floor} functions is valid as long as we are
|
|
* opp_find_freq_{exact,ceil,floor} functions is valid as long as we are
|
|
@@ -112,7 +114,7 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
|
|
if (IS_ERR_OR_NULL(tmp_opp))
|
|
if (IS_ERR_OR_NULL(tmp_opp))
|
|
pr_err("%s: Invalid parameters\n", __func__);
|
|
pr_err("%s: Invalid parameters\n", __func__);
|
|
else
|
|
else
|
|
- v = tmp_opp->supply.u_volt;
|
|
|
|
|
|
+ v = tmp_opp->supplies[0].u_volt;
|
|
|
|
|
|
return v;
|
|
return v;
|
|
}
|
|
}
|
|
@@ -210,6 +212,24 @@ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency);
|
|
EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency);
|
|
|
|
|
|
|
|
+static int _get_regulator_count(struct device *dev)
|
|
|
|
+{
|
|
|
|
+ struct opp_table *opp_table;
|
|
|
|
+ int count;
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+
|
|
|
|
+ opp_table = _find_opp_table(dev);
|
|
|
|
+ if (!IS_ERR(opp_table))
|
|
|
|
+ count = opp_table->regulator_count;
|
|
|
|
+ else
|
|
|
|
+ count = 0;
|
|
|
|
+
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+
|
|
|
|
+ return count;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds
|
|
* dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds
|
|
* @dev: device for which we do this operation
|
|
* @dev: device for which we do this operation
|
|
@@ -222,34 +242,51 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
|
|
{
|
|
{
|
|
struct opp_table *opp_table;
|
|
struct opp_table *opp_table;
|
|
struct dev_pm_opp *opp;
|
|
struct dev_pm_opp *opp;
|
|
- struct regulator *reg;
|
|
|
|
|
|
+ struct regulator *reg, **regulators;
|
|
unsigned long latency_ns = 0;
|
|
unsigned long latency_ns = 0;
|
|
- unsigned long min_uV = ~0, max_uV = 0;
|
|
|
|
- int ret;
|
|
|
|
|
|
+ int ret, i, count;
|
|
|
|
+ struct {
|
|
|
|
+ unsigned long min;
|
|
|
|
+ unsigned long max;
|
|
|
|
+ } *uV;
|
|
|
|
+
|
|
|
|
+ count = _get_regulator_count(dev);
|
|
|
|
+
|
|
|
|
+ /* Regulator may not be required for the device */
|
|
|
|
+ if (!count)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ regulators = kmalloc_array(count, sizeof(*regulators), GFP_KERNEL);
|
|
|
|
+ if (!regulators)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL);
|
|
|
|
+ if (!uV)
|
|
|
|
+ goto free_regulators;
|
|
|
|
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
|
|
|
|
opp_table = _find_opp_table(dev);
|
|
opp_table = _find_opp_table(dev);
|
|
if (IS_ERR(opp_table)) {
|
|
if (IS_ERR(opp_table)) {
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
- return 0;
|
|
|
|
|
|
+ goto free_uV;
|
|
}
|
|
}
|
|
|
|
|
|
- reg = opp_table->regulator;
|
|
|
|
- if (IS_ERR(reg)) {
|
|
|
|
- /* Regulator may not be required for device */
|
|
|
|
- rcu_read_unlock();
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
|
|
+ memcpy(regulators, opp_table->regulators, count * sizeof(*regulators));
|
|
|
|
|
|
- list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
|
|
|
|
- if (!opp->available)
|
|
|
|
- continue;
|
|
|
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
|
+ uV[i].min = ~0;
|
|
|
|
+ uV[i].max = 0;
|
|
|
|
|
|
- if (opp->supply.u_volt_min < min_uV)
|
|
|
|
- min_uV = opp->supply.u_volt_min;
|
|
|
|
- if (opp->supply.u_volt_max > max_uV)
|
|
|
|
- max_uV = opp->supply.u_volt_max;
|
|
|
|
|
|
+ list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
|
|
|
|
+ if (!opp->available)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (opp->supplies[i].u_volt_min < uV[i].min)
|
|
|
|
+ uV[i].min = opp->supplies[i].u_volt_min;
|
|
|
|
+ if (opp->supplies[i].u_volt_max > uV[i].max)
|
|
|
|
+ uV[i].max = opp->supplies[i].u_volt_max;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
@@ -258,9 +295,16 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
|
|
* The caller needs to ensure that opp_table (and hence the regulator)
|
|
* The caller needs to ensure that opp_table (and hence the regulator)
|
|
* isn't freed, while we are executing this routine.
|
|
* isn't freed, while we are executing this routine.
|
|
*/
|
|
*/
|
|
- ret = regulator_set_voltage_time(reg, min_uV, max_uV);
|
|
|
|
- if (ret > 0)
|
|
|
|
- latency_ns = ret * 1000;
|
|
|
|
|
|
+ for (i = 0; reg = regulators[i], i < count; i++) {
|
|
|
|
+ ret = regulator_set_voltage_time(reg, uV[i].min, uV[i].max);
|
|
|
|
+ if (ret > 0)
|
|
|
|
+ latency_ns += ret * 1000;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+free_uV:
|
|
|
|
+ kfree(uV);
|
|
|
|
+free_regulators:
|
|
|
|
+ kfree(regulators);
|
|
|
|
|
|
return latency_ns;
|
|
return latency_ns;
|
|
}
|
|
}
|
|
@@ -580,7 +624,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
|
{
|
|
{
|
|
struct opp_table *opp_table;
|
|
struct opp_table *opp_table;
|
|
struct dev_pm_opp *old_opp, *opp;
|
|
struct dev_pm_opp *old_opp, *opp;
|
|
- struct regulator *reg;
|
|
|
|
|
|
+ struct regulator *reg = ERR_PTR(-ENXIO);
|
|
struct clk *clk;
|
|
struct clk *clk;
|
|
unsigned long freq, old_freq;
|
|
unsigned long freq, old_freq;
|
|
struct dev_pm_opp_supply old_supply, new_supply;
|
|
struct dev_pm_opp_supply old_supply, new_supply;
|
|
@@ -633,14 +677,23 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (opp_table->regulators) {
|
|
|
|
+ /* This function only supports single regulator per device */
|
|
|
|
+ if (WARN_ON(opp_table->regulator_count > 1)) {
|
|
|
|
+ dev_err(dev, "multiple regulators not supported\n");
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ reg = opp_table->regulators[0];
|
|
|
|
+ }
|
|
|
|
+
|
|
if (IS_ERR(old_opp))
|
|
if (IS_ERR(old_opp))
|
|
old_supply.u_volt = 0;
|
|
old_supply.u_volt = 0;
|
|
else
|
|
else
|
|
- memcpy(&old_supply, &old_opp->supply, sizeof(old_supply));
|
|
|
|
-
|
|
|
|
- memcpy(&new_supply, &opp->supply, sizeof(new_supply));
|
|
|
|
|
|
+ memcpy(&old_supply, old_opp->supplies, sizeof(old_supply));
|
|
|
|
|
|
- reg = opp_table->regulator;
|
|
|
|
|
|
+ memcpy(&new_supply, opp->supplies, sizeof(new_supply));
|
|
|
|
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
|
|
|
|
@@ -764,9 +817,6 @@ static struct opp_table *_add_opp_table(struct device *dev)
|
|
|
|
|
|
_of_init_opp_table(opp_table, dev);
|
|
_of_init_opp_table(opp_table, dev);
|
|
|
|
|
|
- /* Set regulator to a non-NULL error value */
|
|
|
|
- opp_table->regulator = ERR_PTR(-ENXIO);
|
|
|
|
-
|
|
|
|
/* Find clk for the device */
|
|
/* Find clk for the device */
|
|
opp_table->clk = clk_get(dev, NULL);
|
|
opp_table->clk = clk_get(dev, NULL);
|
|
if (IS_ERR(opp_table->clk)) {
|
|
if (IS_ERR(opp_table->clk)) {
|
|
@@ -815,7 +865,7 @@ static void _remove_opp_table(struct opp_table *opp_table)
|
|
if (opp_table->prop_name)
|
|
if (opp_table->prop_name)
|
|
return;
|
|
return;
|
|
|
|
|
|
- if (!IS_ERR(opp_table->regulator))
|
|
|
|
|
|
+ if (opp_table->regulators)
|
|
return;
|
|
return;
|
|
|
|
|
|
/* Release clk */
|
|
/* Release clk */
|
|
@@ -924,35 +974,50 @@ struct dev_pm_opp *_allocate_opp(struct device *dev,
|
|
struct opp_table **opp_table)
|
|
struct opp_table **opp_table)
|
|
{
|
|
{
|
|
struct dev_pm_opp *opp;
|
|
struct dev_pm_opp *opp;
|
|
|
|
+ int count, supply_size;
|
|
|
|
+ struct opp_table *table;
|
|
|
|
|
|
- /* allocate new OPP node */
|
|
|
|
- opp = kzalloc(sizeof(*opp), GFP_KERNEL);
|
|
|
|
- if (!opp)
|
|
|
|
|
|
+ table = _add_opp_table(dev);
|
|
|
|
+ if (!table)
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|
|
- INIT_LIST_HEAD(&opp->node);
|
|
|
|
|
|
+ /* Allocate space for at least one supply */
|
|
|
|
+ count = table->regulator_count ? table->regulator_count : 1;
|
|
|
|
+ supply_size = sizeof(*opp->supplies) * count;
|
|
|
|
|
|
- *opp_table = _add_opp_table(dev);
|
|
|
|
- if (!*opp_table) {
|
|
|
|
- kfree(opp);
|
|
|
|
|
|
+ /* allocate new OPP node and supplies structures */
|
|
|
|
+ opp = kzalloc(sizeof(*opp) + supply_size, GFP_KERNEL);
|
|
|
|
+ if (!opp) {
|
|
|
|
+ kfree(table);
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* Put the supplies at the end of the OPP structure as an empty array */
|
|
|
|
+ opp->supplies = (struct dev_pm_opp_supply *)(opp + 1);
|
|
|
|
+ INIT_LIST_HEAD(&opp->node);
|
|
|
|
+
|
|
|
|
+ *opp_table = table;
|
|
|
|
+
|
|
return opp;
|
|
return opp;
|
|
}
|
|
}
|
|
|
|
|
|
static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
|
|
static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
|
|
struct opp_table *opp_table)
|
|
struct opp_table *opp_table)
|
|
{
|
|
{
|
|
- struct regulator *reg = opp_table->regulator;
|
|
|
|
-
|
|
|
|
- if (!IS_ERR(reg) &&
|
|
|
|
- !regulator_is_supported_voltage(reg, opp->supply.u_volt_min,
|
|
|
|
- opp->supply.u_volt_max)) {
|
|
|
|
- pr_warn("%s: OPP minuV: %lu maxuV: %lu, not supported by regulator\n",
|
|
|
|
- __func__, opp->supply.u_volt_min,
|
|
|
|
- opp->supply.u_volt_max);
|
|
|
|
- return false;
|
|
|
|
|
|
+ struct regulator *reg;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < opp_table->regulator_count; i++) {
|
|
|
|
+ reg = opp_table->regulators[i];
|
|
|
|
+
|
|
|
|
+ if (!regulator_is_supported_voltage(reg,
|
|
|
|
+ opp->supplies[i].u_volt_min,
|
|
|
|
+ opp->supplies[i].u_volt_max)) {
|
|
|
|
+ pr_warn("%s: OPP minuV: %lu maxuV: %lu, not supported by regulator\n",
|
|
|
|
+ __func__, opp->supplies[i].u_volt_min,
|
|
|
|
+ opp->supplies[i].u_volt_max);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
return true;
|
|
return true;
|
|
@@ -984,12 +1049,13 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
|
|
|
|
|
|
/* Duplicate OPPs */
|
|
/* Duplicate OPPs */
|
|
dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
|
|
dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
|
|
- __func__, opp->rate, opp->supply.u_volt,
|
|
|
|
- opp->available, new_opp->rate, new_opp->supply.u_volt,
|
|
|
|
- new_opp->available);
|
|
|
|
|
|
+ __func__, opp->rate, opp->supplies[0].u_volt,
|
|
|
|
+ opp->available, new_opp->rate,
|
|
|
|
+ new_opp->supplies[0].u_volt, new_opp->available);
|
|
|
|
|
|
|
|
+ /* Should we compare voltages for all regulators here ? */
|
|
return opp->available &&
|
|
return opp->available &&
|
|
- new_opp->supply.u_volt == opp->supply.u_volt ? 0 : -EEXIST;
|
|
|
|
|
|
+ new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? 0 : -EEXIST;
|
|
}
|
|
}
|
|
|
|
|
|
new_opp->opp_table = opp_table;
|
|
new_opp->opp_table = opp_table;
|
|
@@ -1056,9 +1122,9 @@ int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
|
|
/* populate the opp table */
|
|
/* populate the opp table */
|
|
new_opp->rate = freq;
|
|
new_opp->rate = freq;
|
|
tol = u_volt * opp_table->voltage_tolerance_v1 / 100;
|
|
tol = u_volt * opp_table->voltage_tolerance_v1 / 100;
|
|
- new_opp->supply.u_volt = u_volt;
|
|
|
|
- new_opp->supply.u_volt_min = u_volt - tol;
|
|
|
|
- new_opp->supply.u_volt_max = u_volt + tol;
|
|
|
|
|
|
+ new_opp->supplies[0].u_volt = u_volt;
|
|
|
|
+ new_opp->supplies[0].u_volt_min = u_volt - tol;
|
|
|
|
+ new_opp->supplies[0].u_volt_max = u_volt + tol;
|
|
new_opp->available = true;
|
|
new_opp->available = true;
|
|
new_opp->dynamic = dynamic;
|
|
new_opp->dynamic = dynamic;
|
|
|
|
|
|
@@ -1303,12 +1369,14 @@ unlock:
|
|
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
|
|
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
|
|
|
|
|
|
/**
|
|
/**
|
|
- * dev_pm_opp_set_regulator() - Set regulator name for the device
|
|
|
|
|
|
+ * dev_pm_opp_set_regulators() - Set regulator names for the device
|
|
* @dev: Device for which regulator name is being set.
|
|
* @dev: Device for which regulator name is being set.
|
|
- * @name: Name of the regulator.
|
|
|
|
|
|
+ * @names: Array of pointers to the names of the regulator.
|
|
|
|
+ * @count: Number of regulators.
|
|
*
|
|
*
|
|
* In order to support OPP switching, OPP layer needs to know the name of the
|
|
* In order to support OPP switching, OPP layer needs to know the name of the
|
|
- * device's regulator, as the core would be required to switch voltages as well.
|
|
|
|
|
|
+ * device's regulators, as the core would be required to switch voltages as
|
|
|
|
+ * well.
|
|
*
|
|
*
|
|
* This must be called before any OPPs are initialized for the device.
|
|
* This must be called before any OPPs are initialized for the device.
|
|
*
|
|
*
|
|
@@ -1318,11 +1386,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
|
|
* that this function is *NOT* called under RCU protection or in contexts where
|
|
* that this function is *NOT* called under RCU protection or in contexts where
|
|
* mutex cannot be locked.
|
|
* mutex cannot be locked.
|
|
*/
|
|
*/
|
|
-struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name)
|
|
|
|
|
|
+struct opp_table *dev_pm_opp_set_regulators(struct device *dev,
|
|
|
|
+ const char * const names[],
|
|
|
|
+ unsigned int count)
|
|
{
|
|
{
|
|
struct opp_table *opp_table;
|
|
struct opp_table *opp_table;
|
|
struct regulator *reg;
|
|
struct regulator *reg;
|
|
- int ret;
|
|
|
|
|
|
+ int ret, i;
|
|
|
|
|
|
mutex_lock(&opp_table_lock);
|
|
mutex_lock(&opp_table_lock);
|
|
|
|
|
|
@@ -1338,26 +1408,44 @@ struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name)
|
|
goto err;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
|
|
- /* Already have a regulator set */
|
|
|
|
- if (WARN_ON(!IS_ERR(opp_table->regulator))) {
|
|
|
|
|
|
+ /* Already have regulators set */
|
|
|
|
+ if (WARN_ON(opp_table->regulators)) {
|
|
ret = -EBUSY;
|
|
ret = -EBUSY;
|
|
goto err;
|
|
goto err;
|
|
}
|
|
}
|
|
- /* Allocate the regulator */
|
|
|
|
- reg = regulator_get_optional(dev, name);
|
|
|
|
- if (IS_ERR(reg)) {
|
|
|
|
- ret = PTR_ERR(reg);
|
|
|
|
- if (ret != -EPROBE_DEFER)
|
|
|
|
- dev_err(dev, "%s: no regulator (%s) found: %d\n",
|
|
|
|
- __func__, name, ret);
|
|
|
|
|
|
+
|
|
|
|
+ opp_table->regulators = kmalloc_array(count,
|
|
|
|
+ sizeof(*opp_table->regulators),
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ if (!opp_table->regulators) {
|
|
|
|
+ ret = -ENOMEM;
|
|
goto err;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
|
|
- opp_table->regulator = reg;
|
|
|
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
|
+ reg = regulator_get_optional(dev, names[i]);
|
|
|
|
+ if (IS_ERR(reg)) {
|
|
|
|
+ ret = PTR_ERR(reg);
|
|
|
|
+ if (ret != -EPROBE_DEFER)
|
|
|
|
+ dev_err(dev, "%s: no regulator (%s) found: %d\n",
|
|
|
|
+ __func__, names[i], ret);
|
|
|
|
+ goto free_regulators;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ opp_table->regulators[i] = reg;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ opp_table->regulator_count = count;
|
|
|
|
|
|
mutex_unlock(&opp_table_lock);
|
|
mutex_unlock(&opp_table_lock);
|
|
return opp_table;
|
|
return opp_table;
|
|
|
|
|
|
|
|
+free_regulators:
|
|
|
|
+ while (i != 0)
|
|
|
|
+ regulator_put(opp_table->regulators[--i]);
|
|
|
|
+
|
|
|
|
+ kfree(opp_table->regulators);
|
|
|
|
+ opp_table->regulators = NULL;
|
|
err:
|
|
err:
|
|
_remove_opp_table(opp_table);
|
|
_remove_opp_table(opp_table);
|
|
unlock:
|
|
unlock:
|
|
@@ -1365,11 +1453,11 @@ unlock:
|
|
|
|
|
|
return ERR_PTR(ret);
|
|
return ERR_PTR(ret);
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
|
|
|
|
|
|
+EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulators);
|
|
|
|
|
|
/**
|
|
/**
|
|
- * dev_pm_opp_put_regulator() - Releases resources blocked for regulator
|
|
|
|
- * @opp_table: OPP table returned from dev_pm_opp_set_regulator().
|
|
|
|
|
|
+ * dev_pm_opp_put_regulators() - Releases resources blocked for regulator
|
|
|
|
+ * @opp_table: OPP table returned from dev_pm_opp_set_regulators().
|
|
*
|
|
*
|
|
* Locking: The internal opp_table and opp structures are RCU protected.
|
|
* Locking: The internal opp_table and opp structures are RCU protected.
|
|
* Hence this function internally uses RCU updater strategy with mutex locks
|
|
* Hence this function internally uses RCU updater strategy with mutex locks
|
|
@@ -1377,20 +1465,26 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
|
|
* that this function is *NOT* called under RCU protection or in contexts where
|
|
* that this function is *NOT* called under RCU protection or in contexts where
|
|
* mutex cannot be locked.
|
|
* mutex cannot be locked.
|
|
*/
|
|
*/
|
|
-void dev_pm_opp_put_regulator(struct opp_table *opp_table)
|
|
|
|
|
|
+void dev_pm_opp_put_regulators(struct opp_table *opp_table)
|
|
{
|
|
{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
mutex_lock(&opp_table_lock);
|
|
mutex_lock(&opp_table_lock);
|
|
|
|
|
|
- if (IS_ERR(opp_table->regulator)) {
|
|
|
|
- pr_err("%s: Doesn't have regulator set\n", __func__);
|
|
|
|
|
|
+ if (!opp_table->regulators) {
|
|
|
|
+ pr_err("%s: Doesn't have regulators set\n", __func__);
|
|
goto unlock;
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
|
|
/* Make sure there are no concurrent readers while updating opp_table */
|
|
/* Make sure there are no concurrent readers while updating opp_table */
|
|
WARN_ON(!list_empty(&opp_table->opp_list));
|
|
WARN_ON(!list_empty(&opp_table->opp_list));
|
|
|
|
|
|
- regulator_put(opp_table->regulator);
|
|
|
|
- opp_table->regulator = ERR_PTR(-ENXIO);
|
|
|
|
|
|
+ for (i = opp_table->regulator_count - 1; i >= 0; i--)
|
|
|
|
+ regulator_put(opp_table->regulators[i]);
|
|
|
|
+
|
|
|
|
+ kfree(opp_table->regulators);
|
|
|
|
+ opp_table->regulators = NULL;
|
|
|
|
+ opp_table->regulator_count = 0;
|
|
|
|
|
|
/* Try freeing opp_table if this was the last blocking resource */
|
|
/* Try freeing opp_table if this was the last blocking resource */
|
|
_remove_opp_table(opp_table);
|
|
_remove_opp_table(opp_table);
|
|
@@ -1398,7 +1492,7 @@ void dev_pm_opp_put_regulator(struct opp_table *opp_table)
|
|
unlock:
|
|
unlock:
|
|
mutex_unlock(&opp_table_lock);
|
|
mutex_unlock(&opp_table_lock);
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulator);
|
|
|
|
|
|
+EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators);
|
|
|
|
|
|
/**
|
|
/**
|
|
* dev_pm_opp_add() - Add an OPP table from a table definitions
|
|
* dev_pm_opp_add() - Add an OPP table from a table definitions
|