* 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);