diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/power/opp.c | 55 |
1 files changed, 28 insertions, 27 deletions
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 25538675d59e..38b43bb20878 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -617,53 +617,54 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_disable); * the table if any of the mentioned functions have been invoked in the interim. * * Locking: The internal device_opp and opp structures are RCU protected. - * To simplify the logic, we pretend we are updater and hold relevant mutex here - * Callers should ensure that this function is *NOT* called under RCU protection - * or in contexts where mutex locking cannot be used. + * Since we just use the regular accessor functions to access the internal data + * structures, we use RCU read lock inside this function. As a result, users of + * this function DONOT need to use explicit locks for invoking. */ int dev_pm_opp_init_cpufreq_table(struct device *dev, struct cpufreq_frequency_table **table) { - struct device_opp *dev_opp; struct dev_pm_opp *opp; - struct cpufreq_frequency_table *freq_table; - int i = 0; + struct cpufreq_frequency_table *freq_table = NULL; + int i, max_opps, ret = 0; + unsigned long rate; - /* Pretend as if I am an updater */ - mutex_lock(&dev_opp_list_lock); + rcu_read_lock(); - dev_opp = find_device_opp(dev); - if (IS_ERR(dev_opp)) { - int r = PTR_ERR(dev_opp); - mutex_unlock(&dev_opp_list_lock); - dev_err(dev, "%s: Device OPP not found (%d)\n", __func__, r); - return r; + max_opps = dev_pm_opp_get_opp_count(dev); + if (max_opps <= 0) { + ret = max_opps ? max_opps : -ENODATA; + goto out; } - freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * - (dev_pm_opp_get_opp_count(dev) + 1), GFP_KERNEL); + freq_table = kzalloc(sizeof(*freq_table) * (max_opps + 1), GFP_KERNEL); if (!freq_table) { - mutex_unlock(&dev_opp_list_lock); - dev_warn(dev, "%s: Unable to allocate frequency table\n", - __func__); - return -ENOMEM; + ret = -ENOMEM; + goto out; } - list_for_each_entry(opp, &dev_opp->opp_list, node) { - if (opp->available) { - freq_table[i].driver_data = i; - freq_table[i].frequency = opp->rate / 1000; - i++; + for (i = 0, rate = 0; i < max_opps; i++, rate++) { + /* find next rate */ + opp = dev_pm_opp_find_freq_ceil(dev, &rate); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + goto out; } + freq_table[i].driver_data = i; + freq_table[i].frequency = rate / 1000; } - mutex_unlock(&dev_opp_list_lock); freq_table[i].driver_data = i; freq_table[i].frequency = CPUFREQ_TABLE_END; *table = &freq_table[0]; - return 0; +out: + rcu_read_unlock(); + if (ret) + kfree(freq_table); + + return ret; } EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table); |