lkml.org 
[lkml]   [2016]   [Nov]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH V4 06/10] PM / OPP: Add infrastructure to manage multiple regulators
    Date
    This patch adds infrastructure to manage multiple regulators and updates
    the only user (cpufreq-dt) of dev_pm_opp_set{put}_regulator().

    This is preparatory work for adding full support for devices with
    multiple regulators.

    Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
    Tested-by: Dave Gerlach <d-gerlach@ti.com>
    ---
    drivers/base/power/opp/core.c | 222 ++++++++++++++++++++++++++-------------
    drivers/base/power/opp/debugfs.c | 52 +++++++--
    drivers/base/power/opp/of.c | 103 +++++++++++++-----
    drivers/base/power/opp/opp.h | 10 +-
    drivers/cpufreq/cpufreq-dt.c | 9 +-
    include/linux/pm_opp.h | 8 +-
    6 files changed, 282 insertions(+), 122 deletions(-)

    diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
    index 37fad2eb0f47..2d5c726c920f 100644
    --- a/drivers/base/power/opp/core.c
    +++ b/drivers/base/power/opp/core.c
    @@ -93,6 +93,8 @@ struct opp_table *_find_opp_table(struct device *dev)
    * Return: voltage in micro volt corresponding to the opp, else
    * 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
    * 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
    @@ -112,7 +114,7 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
    if (IS_ERR_OR_NULL(tmp_opp))
    pr_err("%s: Invalid parameters\n", __func__);
    else
    - v = tmp_opp->supply.u_volt;
    + v = tmp_opp->supplies[0].u_volt;

    return v;
    }
    @@ -222,10 +224,13 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
    {
    struct opp_table *opp_table;
    struct dev_pm_opp *opp;
    - struct regulator *reg;
    + struct regulator *reg, **regulators;
    unsigned long latency_ns = 0;
    - unsigned long min_uV = ~0, max_uV = 0;
    - int ret;
    + int ret, size, i, count;
    + struct {
    + unsigned long min;
    + unsigned long max;
    + } *uV;

    rcu_read_lock();

    @@ -235,21 +240,41 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
    return 0;
    }

    - reg = opp_table->regulator;
    - if (IS_ERR(reg)) {
    + count = opp_table->regulator_count;
    +
    + if (!count) {
    /* Regulator may not be required for device */
    rcu_read_unlock();
    return 0;
    }

    - list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
    - if (!opp->available)
    - continue;
    + size = count * sizeof(*regulators);
    + regulators = kmemdup(opp_table->regulators, size, GFP_KERNEL);
    + if (!regulators) {
    + rcu_read_unlock();
    + return 0;
    + }
    +
    + uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL);
    + if (!uV) {
    + kfree(regulators);
    + rcu_read_unlock();
    + return 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;
    + for (i = 0; i < count; i++) {
    + uV[i].min = ~0;
    + uV[i].max = 0;
    +
    + 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();
    @@ -258,9 +283,14 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
    * The caller needs to ensure that opp_table (and hence the regulator)
    * 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;
    + }
    +
    + kfree(uV);
    + kfree(regulators);

    return latency_ns;
    }
    @@ -580,7 +610,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
    {
    struct opp_table *opp_table;
    struct dev_pm_opp *old_opp, *opp;
    - struct regulator *reg;
    + struct regulator *reg = ERR_PTR(-ENXIO);
    struct clk *clk;
    unsigned long freq, old_freq;
    struct dev_pm_opp_supply old_supply, new_supply;
    @@ -633,14 +663,23 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
    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))
    old_supply.u_volt = 0;
    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();

    @@ -764,9 +803,6 @@ static struct opp_table *_add_opp_table(struct device *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 */
    opp_table->clk = clk_get(dev, NULL);
    if (IS_ERR(opp_table->clk)) {
    @@ -815,7 +851,7 @@ static void _remove_opp_table(struct opp_table *opp_table)
    if (opp_table->prop_name)
    return;

    - if (!IS_ERR(opp_table->regulator))
    + if (opp_table->regulators)
    return;

    /* Release clk */
    @@ -924,35 +960,50 @@ struct dev_pm_opp *_allocate_opp(struct device *dev,
    struct opp_table **opp_table)
    {
    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;

    - 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;
    }

    + /* 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;
    }

    static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
    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;
    @@ -984,12 +1035,13 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,

    /* Duplicate OPPs */
    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 &&
    - 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;
    @@ -1056,9 +1108,9 @@ int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
    /* populate the opp table */
    new_opp->rate = freq;
    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->dynamic = dynamic;

    @@ -1303,12 +1355,14 @@ void dev_pm_opp_put_prop_name(struct device *dev)
    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.
    - * @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
    - * 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.
    *
    @@ -1318,11 +1372,12 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
    * that this function is *NOT* called under RCU protection or in contexts where
    * mutex cannot be locked.
    */
    -int dev_pm_opp_set_regulator(struct device *dev, const char *name)
    +int dev_pm_opp_set_regulators(struct device *dev, const char * const names[],
    + unsigned int count)
    {
    struct opp_table *opp_table;
    struct regulator *reg;
    - int ret;
    + int ret, i;

    mutex_lock(&opp_table_lock);

    @@ -1338,26 +1393,44 @@ int dev_pm_opp_set_regulator(struct device *dev, const char *name)
    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;
    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;
    }

    - 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: regulator (%s) not 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);
    return 0;

    +free_regulators:
    + while (i != 0)
    + regulator_put(opp_table->regulators[--i]);
    +
    + kfree(opp_table->regulators);
    + opp_table->regulators = NULL;
    err:
    _remove_opp_table(opp_table);
    unlock:
    @@ -1365,11 +1438,11 @@ int dev_pm_opp_set_regulator(struct device *dev, const char *name)

    return 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
    - * @dev: Device for which regulator was set.
    + * dev_pm_opp_put_regulators() - Releases resources blocked for regulators
    + * @dev: Device for which regulators were set.
    *
    * Locking: The internal opp_table and opp structures are RCU protected.
    * Hence this function internally uses RCU updater strategy with mutex locks
    @@ -1377,9 +1450,10 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
    * that this function is *NOT* called under RCU protection or in contexts where
    * mutex cannot be locked.
    */
    -void dev_pm_opp_put_regulator(struct device *dev)
    +void dev_pm_opp_put_regulators(struct device *dev)
    {
    struct opp_table *opp_table;
    + int i;

    mutex_lock(&opp_table_lock);

    @@ -1391,16 +1465,20 @@ void dev_pm_opp_put_regulator(struct device *dev)
    goto unlock;
    }

    - if (IS_ERR(opp_table->regulator)) {
    - dev_err(dev, "%s: Doesn't have regulator set\n", __func__);
    + if (!opp_table->regulators) {
    + dev_err(dev, "%s: Doesn't have regulators set\n", __func__);
    goto unlock;
    }

    /* Make sure there are no concurrent readers while updating opp_table */
    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 */
    _remove_opp_table(opp_table);
    @@ -1408,7 +1486,7 @@ void dev_pm_opp_put_regulator(struct device *dev)
    unlock:
    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
    diff --git a/drivers/base/power/opp/debugfs.c b/drivers/base/power/opp/debugfs.c
    index c897676ca35f..95f433db4ac7 100644
    --- a/drivers/base/power/opp/debugfs.c
    +++ b/drivers/base/power/opp/debugfs.c
    @@ -15,6 +15,7 @@
    #include <linux/err.h>
    #include <linux/init.h>
    #include <linux/limits.h>
    +#include <linux/slab.h>

    #include "opp.h"

    @@ -34,6 +35,46 @@ void opp_debug_remove_one(struct dev_pm_opp *opp)
    debugfs_remove_recursive(opp->dentry);
    }

    +static bool opp_debug_create_supplies(struct dev_pm_opp *opp,
    + struct opp_table *opp_table,
    + struct dentry *pdentry)
    +{
    + struct dentry *d;
    + int i = 0;
    + char *name;
    +
    + /* Always create at least supply-0 directory */
    + do {
    + name = kasprintf(GFP_KERNEL, "supply-%d", i);
    +
    + /* Create per-opp directory */
    + d = debugfs_create_dir(name, pdentry);
    +
    + kfree(name);
    +
    + if (!d)
    + return false;
    +
    + if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d,
    + &opp->supplies[i].u_volt))
    + return false;
    +
    + if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d,
    + &opp->supplies[i].u_volt_min))
    + return false;
    +
    + if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d,
    + &opp->supplies[i].u_volt_max))
    + return false;
    +
    + if (!debugfs_create_ulong("u_amp", S_IRUGO, d,
    + &opp->supplies[i].u_amp))
    + return false;
    + } while (++i < opp_table->regulator_count);
    +
    + return true;
    +}
    +
    int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
    {
    struct dentry *pdentry = opp_table->dentry;
    @@ -63,16 +104,7 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
    if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
    return -ENOMEM;

    - if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, &opp->supply.u_volt))
    - return -ENOMEM;
    -
    - if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, &opp->supply.u_volt_min))
    - return -ENOMEM;
    -
    - if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, &opp->supply.u_volt_max))
    - return -ENOMEM;
    -
    - if (!debugfs_create_ulong("u_amp", S_IRUGO, d, &opp->supply.u_amp))
    + if (!opp_debug_create_supplies(opp, opp_table, d))
    return -ENOMEM;

    if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
    diff --git a/drivers/base/power/opp/of.c b/drivers/base/power/opp/of.c
    index bdf409d42126..3f7d2591b173 100644
    --- a/drivers/base/power/opp/of.c
    +++ b/drivers/base/power/opp/of.c
    @@ -17,6 +17,7 @@
    #include <linux/errno.h>
    #include <linux/device.h>
    #include <linux/of.h>
    +#include <linux/slab.h>
    #include <linux/export.h>

    #include "opp.h"
    @@ -101,16 +102,16 @@ static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
    return true;
    }

    -/* TODO: Support multiple regulators */
    static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
    struct opp_table *opp_table)
    {
    - u32 microvolt[3] = {0};
    - u32 val;
    - int count, ret;
    + u32 *microvolt, *microamp = NULL;
    + int supplies, vcount, icount, ret, i, j;
    struct property *prop = NULL;
    char name[NAME_MAX];

    + supplies = opp_table->regulator_count ? opp_table->regulator_count : 1;
    +
    /* Search for "opp-microvolt-<name>" */
    if (opp_table->prop_name) {
    snprintf(name, sizeof(name), "opp-microvolt-%s",
    @@ -128,34 +129,29 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
    return 0;
    }

    - count = of_property_count_u32_elems(opp->np, name);
    - if (count < 0) {
    + vcount = of_property_count_u32_elems(opp->np, name);
    + if (vcount < 0) {
    dev_err(dev, "%s: Invalid %s property (%d)\n",
    - __func__, name, count);
    - return count;
    + __func__, name, vcount);
    + return vcount;
    }

    - /* There can be one or three elements here */
    - if (count != 1 && count != 3) {
    - dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n",
    - __func__, name, count);
    + /* There can be one or three elements per supply */
    + if (vcount != supplies && vcount != supplies * 3) {
    + dev_err(dev, "%s: Invalid number of elements in %s property (%d) with supplies (%d)\n",
    + __func__, name, vcount, supplies);
    return -EINVAL;
    }

    - ret = of_property_read_u32_array(opp->np, name, microvolt, count);
    + microvolt = kmalloc_array(vcount, sizeof(*microvolt), GFP_KERNEL);
    + if (!microvolt)
    + return -ENOMEM;
    +
    + ret = of_property_read_u32_array(opp->np, name, microvolt, vcount);
    if (ret) {
    dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret);
    - return -EINVAL;
    - }
    -
    - opp->supply.u_volt = microvolt[0];
    -
    - if (count == 1) {
    - opp->supply.u_volt_min = opp->supply.u_volt;
    - opp->supply.u_volt_max = opp->supply.u_volt;
    - } else {
    - opp->supply.u_volt_min = microvolt[1];
    - opp->supply.u_volt_max = microvolt[2];
    + ret = -EINVAL;
    + goto free_microvolt;
    }

    /* Search for "opp-microamp-<name>" */
    @@ -172,10 +168,59 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
    prop = of_find_property(opp->np, name, NULL);
    }

    - if (prop && !of_property_read_u32(opp->np, name, &val))
    - opp->supply.u_amp = val;
    + if (prop) {
    + icount = of_property_count_u32_elems(opp->np, name);
    + if (icount < 0) {
    + dev_err(dev, "%s: Invalid %s property (%d)\n", __func__,
    + name, icount);
    + ret = icount;
    + goto free_microvolt;
    + }

    - return 0;
    + if (icount != supplies) {
    + dev_err(dev, "%s: Invalid number of elements in %s property (%d) with supplies (%d)\n",
    + __func__, name, icount, supplies);
    + ret = -EINVAL;
    + goto free_microvolt;
    + }
    +
    + microamp = kmalloc_array(icount, sizeof(*microamp), GFP_KERNEL);
    + if (!microamp) {
    + ret = -EINVAL;
    + goto free_microvolt;
    + }
    +
    + ret = of_property_read_u32_array(opp->np, name, microamp,
    + icount);
    + if (ret) {
    + dev_err(dev, "%s: error parsing %s: %d\n", __func__,
    + name, ret);
    + ret = -EINVAL;
    + goto free_microamp;
    + }
    + }
    +
    + for (i = 0, j = 0; i < supplies; i++) {
    + opp->supplies[i].u_volt = microvolt[j++];
    +
    + if (vcount == supplies) {
    + opp->supplies[i].u_volt_min = opp->supplies[i].u_volt;
    + opp->supplies[i].u_volt_max = opp->supplies[i].u_volt;
    + } else {
    + opp->supplies[i].u_volt_min = microvolt[j++];
    + opp->supplies[i].u_volt_max = microvolt[j++];
    + }
    +
    + if (microamp)
    + opp->supplies[i].u_amp = microamp[i];
    + }
    +
    +free_microamp:
    + kfree(microamp);
    +free_microvolt:
    + kfree(microvolt);
    +
    + return ret;
    }

    /**
    @@ -304,8 +349,8 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)

    pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n",
    __func__, new_opp->turbo, new_opp->rate,
    - new_opp->supply.u_volt, new_opp->supply.u_volt_min,
    - new_opp->supply.u_volt_max, new_opp->clock_latency_ns);
    + new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min,
    + new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns);

    /*
    * Notify the changes in the availability of the operable
    diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h
    index 8a02516542c2..5b0f7e53bede 100644
    --- a/drivers/base/power/opp/opp.h
    +++ b/drivers/base/power/opp/opp.h
    @@ -61,7 +61,7 @@ extern struct list_head opp_tables;
    * @turbo: true if turbo (boost) OPP
    * @suspend: true if suspend OPP
    * @rate: Frequency in hertz
    - * @supply: Power supply voltage/current values
    + * @supplies: Power supplies voltage/current values
    * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
    * frequency from any other OPP's frequency.
    * @opp_table: points back to the opp_table struct this opp belongs to
    @@ -80,7 +80,7 @@ struct dev_pm_opp {
    bool suspend;
    unsigned long rate;

    - struct dev_pm_opp_supply supply;
    + struct dev_pm_opp_supply *supplies;

    unsigned long clock_latency_ns;

    @@ -139,7 +139,8 @@ enum opp_table_access {
    * @supported_hw_count: Number of elements in supported_hw array.
    * @prop_name: A name to postfix to many DT properties, while parsing them.
    * @clk: Device's clock handle
    - * @regulator: Supply regulator
    + * @regulators: Supply regulators
    + * @regulator_count: Number of power supply regulators
    * @dentry: debugfs dentry pointer of the real device directory (not links).
    * @dentry_name: Name of the real dentry.
    *
    @@ -174,7 +175,8 @@ struct opp_table {
    unsigned int supported_hw_count;
    const char *prop_name;
    struct clk *clk;
    - struct regulator *regulator;
    + struct regulator **regulators;
    + unsigned int regulator_count;

    #ifdef CONFIG_DEBUG_FS
    struct dentry *dentry;
    diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
    index 5c07ae05d69a..15cb26118dc7 100644
    --- a/drivers/cpufreq/cpufreq-dt.c
    +++ b/drivers/cpufreq/cpufreq-dt.c
    @@ -186,7 +186,10 @@ static int cpufreq_init(struct cpufreq_policy *policy)
    */
    name = find_supply_name(cpu_dev);
    if (name) {
    - ret = dev_pm_opp_set_regulator(cpu_dev, name);
    + const char *names[] = {name};
    +
    + ret = dev_pm_opp_set_regulators(cpu_dev, names,
    + ARRAY_SIZE(names));
    if (ret) {
    dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n",
    policy->cpu, ret);
    @@ -285,7 +288,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
    out_free_opp:
    dev_pm_opp_of_cpumask_remove_table(policy->cpus);
    if (name)
    - dev_pm_opp_put_regulator(cpu_dev);
    + dev_pm_opp_put_regulators(cpu_dev);
    out_put_clk:
    clk_put(cpu_clk);

    @@ -300,7 +303,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy)
    dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
    dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
    if (priv->reg_name)
    - dev_pm_opp_put_regulator(priv->cpu_dev);
    + dev_pm_opp_put_regulators(priv->cpu_dev);

    clk_put(policy->clk);
    kfree(priv);
    diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
    index f69126e2bb59..27eea9bfc5ed 100644
    --- a/include/linux/pm_opp.h
    +++ b/include/linux/pm_opp.h
    @@ -78,8 +78,8 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
    void dev_pm_opp_put_supported_hw(struct device *dev);
    int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
    void dev_pm_opp_put_prop_name(struct device *dev);
    -int dev_pm_opp_set_regulator(struct device *dev, const char *name);
    -void dev_pm_opp_put_regulator(struct device *dev);
    +int dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count);
    +void dev_pm_opp_put_regulators(struct device *dev);
    int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
    int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
    int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
    @@ -186,12 +186,12 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)

    static inline void dev_pm_opp_put_prop_name(struct device *dev) {}

    -static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)
    +static inline int dev_pm_opp_set_regulators(struct device *dev, const char *names[], unsigned int count)
    {
    return -ENOTSUPP;
    }

    -static inline void dev_pm_opp_put_regulator(struct device *dev) {}
    +static inline void dev_pm_opp_put_regulators(struct device *dev) {}

    static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
    {
    --
    2.7.1.410.g6faf27b
    \
     
     \ /
      Last update: 2016-11-24 12:38    [W:4.214 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site