diff options
Diffstat (limited to 'drivers/base/power/opp/core.c')
-rw-r--r-- | drivers/base/power/opp/core.c | 1014 |
1 files changed, 386 insertions, 628 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index 35ff06283738..dae61720b314 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -32,13 +32,7 @@ LIST_HEAD(opp_tables); /* Lock to allow exclusive modification to the device and opp lists */ DEFINE_MUTEX(opp_table_lock); -#define opp_rcu_lockdep_assert() \ -do { \ - RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ - !lockdep_is_held(&opp_table_lock), \ - "Missing rcu_read_lock() or " \ - "opp_table_lock protection"); \ -} while (0) +static void dev_pm_opp_get(struct dev_pm_opp *opp); static struct opp_device *_find_opp_dev(const struct device *dev, struct opp_table *opp_table) @@ -52,38 +46,46 @@ static struct opp_device *_find_opp_dev(const struct device *dev, return NULL; } +static struct opp_table *_find_opp_table_unlocked(struct device *dev) +{ + struct opp_table *opp_table; + + list_for_each_entry(opp_table, &opp_tables, node) { + if (_find_opp_dev(dev, opp_table)) { + _get_opp_table_kref(opp_table); + + return opp_table; + } + } + + return ERR_PTR(-ENODEV); +} + /** * _find_opp_table() - find opp_table struct using device pointer * @dev: device pointer used to lookup OPP table * - * Search OPP table for one containing matching device. Does a RCU reader - * operation to grab the pointer needed. + * Search OPP table for one containing matching device. * * Return: pointer to 'struct opp_table' if found, otherwise -ENODEV or * -EINVAL based on type of error. * - * Locking: For readers, this function must be called under rcu_read_lock(). - * opp_table is a RCU protected pointer, which means that opp_table is valid - * as long as we are under RCU lock. - * - * For Writers, this function must be called with opp_table_lock held. + * The callers must call dev_pm_opp_put_opp_table() after the table is used. */ struct opp_table *_find_opp_table(struct device *dev) { struct opp_table *opp_table; - opp_rcu_lockdep_assert(); - if (IS_ERR_OR_NULL(dev)) { pr_err("%s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } - list_for_each_entry_rcu(opp_table, &opp_tables, node) - if (_find_opp_dev(dev, opp_table)) - return opp_table; + mutex_lock(&opp_table_lock); + opp_table = _find_opp_table_unlocked(dev); + mutex_unlock(&opp_table_lock); - return ERR_PTR(-ENODEV); + return opp_table; } /** @@ -94,29 +96,15 @@ struct opp_table *_find_opp_table(struct device *dev) * 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 - * under RCU lock. The pointer returned by the opp_find_freq family must be - * used in the same section as the usage of this function with the pointer - * prior to unlocking with rcu_read_unlock() to maintain the integrity of the - * pointer. */ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) { - struct dev_pm_opp *tmp_opp; - unsigned long v = 0; - - opp_rcu_lockdep_assert(); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp)) + if (IS_ERR_OR_NULL(opp)) { pr_err("%s: Invalid parameters\n", __func__); - else - v = tmp_opp->supplies[0].u_volt; + return 0; + } - return v; + return opp->supplies[0].u_volt; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); @@ -126,29 +114,15 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); * * Return: frequency in hertz corresponding to the opp, else * return 0 - * - * 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 - * under RCU lock. The pointer returned by the opp_find_freq family must be - * used in the same section as the usage of this function with the pointer - * prior to unlocking with rcu_read_unlock() to maintain the integrity of the - * pointer. */ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) { - struct dev_pm_opp *tmp_opp; - unsigned long f = 0; - - opp_rcu_lockdep_assert(); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) + if (IS_ERR_OR_NULL(opp) || !opp->available) { pr_err("%s: Invalid parameters\n", __func__); - else - f = tmp_opp->rate; + return 0; + } - return f; + return opp->rate; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); @@ -161,28 +135,15 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); * quickly. Running on them for longer times may overheat the chip. * * Return: true if opp is turbo opp, else false. - * - * 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 - * under RCU lock. The pointer returned by the opp_find_freq family must be - * used in the same section as the usage of this function with the pointer - * prior to unlocking with rcu_read_unlock() to maintain the integrity of the - * pointer. */ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) { - struct dev_pm_opp *tmp_opp; - - opp_rcu_lockdep_assert(); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) { + if (IS_ERR_OR_NULL(opp) || !opp->available) { pr_err("%s: Invalid parameters\n", __func__); return false; } - return tmp_opp->turbo; + return opp->turbo; } EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); @@ -191,52 +152,29 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); * @dev: device for which we do this operation * * Return: This function returns the max clock latency in nanoseconds. - * - * Locking: This function takes rcu_read_lock(). */ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) { struct opp_table *opp_table; unsigned long clock_latency_ns; - rcu_read_lock(); - opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) - clock_latency_ns = 0; - else - clock_latency_ns = opp_table->clock_latency_ns_max; - - rcu_read_unlock(); - return clock_latency_ns; -} -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(); + return 0; - opp_table = _find_opp_table(dev); - if (!IS_ERR(opp_table)) - count = opp_table->regulator_count; - else - count = 0; + clock_latency_ns = opp_table->clock_latency_ns_max; - rcu_read_unlock(); + dev_pm_opp_put_opp_table(opp_table); - return count; + return clock_latency_ns; } +EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); /** * dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max voltage latency in nanoseconds. - * - * Locking: This function takes rcu_read_lock(). */ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) { @@ -250,35 +188,33 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) unsigned long max; } *uV; - count = _get_regulator_count(dev); + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) + return 0; + + count = opp_table->regulator_count; /* Regulator may not be required for the device */ if (!count) - return 0; + goto put_opp_table; regulators = kmalloc_array(count, sizeof(*regulators), GFP_KERNEL); if (!regulators) - return 0; + goto put_opp_table; uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL); if (!uV) goto free_regulators; - rcu_read_lock(); - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - rcu_read_unlock(); - goto free_uV; - } - memcpy(regulators, opp_table->regulators, count * sizeof(*regulators)); + mutex_lock(&opp_table->lock); + 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) { + list_for_each_entry(opp, &opp_table->opp_list, node) { if (!opp->available) continue; @@ -289,22 +225,24 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) } } - rcu_read_unlock(); + mutex_unlock(&opp_table->lock); /* * The caller needs to ensure that opp_table (and hence the regulator) * isn't freed, while we are executing this routine. */ - for (i = 0; reg = regulators[i], i < count; i++) { + for (i = 0; i < count; i++) { + reg = regulators[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); +put_opp_table: + dev_pm_opp_put_opp_table(opp_table); return latency_ns; } @@ -317,8 +255,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_volt_latency); * * Return: This function returns the max transition latency, in nanoseconds, to * switch from one OPP to other. - * - * Locking: This function takes rcu_read_lock(). */ unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev) { @@ -328,32 +264,29 @@ unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev) EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_transition_latency); /** - * dev_pm_opp_get_suspend_opp() - Get suspend opp + * dev_pm_opp_get_suspend_opp_freq() - Get frequency of suspend opp in Hz * @dev: device for which we do this operation * - * Return: This function returns pointer to the suspend opp if it is - * defined and available, otherwise it returns NULL. - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * Return: This function returns the frequency of the OPP marked as suspend_opp + * if one is available, else returns 0; */ -struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) +unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev) { struct opp_table *opp_table; - - opp_rcu_lockdep_assert(); + unsigned long freq = 0; opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table) || !opp_table->suspend_opp || - !opp_table->suspend_opp->available) - return NULL; + if (IS_ERR(opp_table)) + return 0; + + if (opp_table->suspend_opp && opp_table->suspend_opp->available) + freq = dev_pm_opp_get_freq(opp_table->suspend_opp); - return opp_table->suspend_opp; + dev_pm_opp_put_opp_table(opp_table); + + return freq; } -EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); +EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq); /** * dev_pm_opp_get_opp_count() - Get number of opps available in the opp table @@ -361,8 +294,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); * * Return: This function returns the number of available opps if there are any, * else returns 0 if none or the corresponding error value. - * - * Locking: This function takes rcu_read_lock(). */ int dev_pm_opp_get_opp_count(struct device *dev) { @@ -370,23 +301,24 @@ int dev_pm_opp_get_opp_count(struct device *dev) struct dev_pm_opp *temp_opp; int count = 0; - rcu_read_lock(); - opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { count = PTR_ERR(opp_table); dev_err(dev, "%s: OPP table not found (%d)\n", __func__, count); - goto out_unlock; + return count; } - list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available) count++; } -out_unlock: - rcu_read_unlock(); + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); + return count; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); @@ -411,11 +343,8 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); * This provides a mechanism to enable an opp which is not available currently * or the opposite as well. * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. */ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, @@ -424,8 +353,6 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, struct opp_table *opp_table; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - opp_rcu_lockdep_assert(); - opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { int r = PTR_ERR(opp_table); @@ -434,14 +361,22 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, return ERR_PTR(r); } - list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available == available && temp_opp->rate == freq) { opp = temp_opp; + + /* Increment the reference count of OPP */ + dev_pm_opp_get(opp); break; } } + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); + return opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); @@ -451,14 +386,21 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, { struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available && temp_opp->rate >= *freq) { opp = temp_opp; *freq = opp->rate; + + /* Increment the reference count of OPP */ + dev_pm_opp_get(opp); break; } } + mutex_unlock(&opp_table->lock); + return opp; } @@ -477,18 +419,14 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, * ERANGE: no match found for search * ENODEV: if device not found in list of registered devices * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. */ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq) { struct opp_table *opp_table; - - opp_rcu_lockdep_assert(); + struct dev_pm_opp *opp; if (!dev || !freq) { dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); @@ -499,7 +437,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, if (IS_ERR(opp_table)) return ERR_CAST(opp_table); - return _find_freq_ceil(opp_table, freq); + opp = _find_freq_ceil(opp_table, freq); + + dev_pm_opp_put_opp_table(opp_table); + + return opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); @@ -518,11 +460,8 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); * ERANGE: no match found for search * ENODEV: if device not found in list of registered devices * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. */ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) @@ -530,8 +469,6 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, struct opp_table *opp_table; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - opp_rcu_lockdep_assert(); - if (!dev || !freq) { dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); return ERR_PTR(-EINVAL); @@ -541,7 +478,9 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, if (IS_ERR(opp_table)) return ERR_CAST(opp_table); - list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available) { /* go to the next node, before choosing prev */ if (temp_opp->rate > *freq) @@ -550,6 +489,13 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, opp = temp_opp; } } + + /* Increment the reference count of OPP */ + if (!IS_ERR(opp)) + dev_pm_opp_get(opp); + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); + if (!IS_ERR(opp)) *freq = opp->rate; @@ -557,34 +503,6 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); -/* - * The caller needs to ensure that opp_table (and hence the clk) isn't freed, - * while clk returned here is used. - */ -static struct clk *_get_opp_clk(struct device *dev) -{ - struct opp_table *opp_table; - struct clk *clk; - - rcu_read_lock(); - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - dev_err(dev, "%s: device opp doesn't exist\n", __func__); - clk = ERR_CAST(opp_table); - goto unlock; - } - - clk = opp_table->clk; - if (IS_ERR(clk)) - dev_err(dev, "%s: No clock available for the device\n", - __func__); - -unlock: - rcu_read_unlock(); - return clk; -} - static int _set_opp_voltage(struct device *dev, struct regulator *reg, struct dev_pm_opp_supply *supply) { @@ -680,8 +598,6 @@ restore_voltage: * * This configures the power-supplies and clock source to the levels specified * by the OPP corresponding to the target_freq. - * - * Locking: This function takes rcu_read_lock(). */ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) { @@ -700,9 +616,19 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) return -EINVAL; } - clk = _get_opp_clk(dev); - if (IS_ERR(clk)) - return PTR_ERR(clk); + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "%s: device opp doesn't exist\n", __func__); + return PTR_ERR(opp_table); + } + + clk = opp_table->clk; + if (IS_ERR(clk)) { + dev_err(dev, "%s: No clock available for the device\n", + __func__); + ret = PTR_ERR(clk); + goto put_opp_table; + } freq = clk_round_rate(clk, target_freq); if ((long)freq <= 0) @@ -714,16 +640,8 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) if (old_freq == freq) { dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do\n", __func__, freq); - return 0; - } - - rcu_read_lock(); - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - dev_err(dev, "%s: device opp doesn't exist\n", __func__); - rcu_read_unlock(); - return PTR_ERR(opp_table); + ret = 0; + goto put_opp_table; } old_opp = _find_freq_ceil(opp_table, &old_freq); @@ -737,8 +655,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) ret = PTR_ERR(opp); dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n", __func__, freq, ret); - rcu_read_unlock(); - return ret; + goto put_old_opp; } dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__, @@ -748,8 +665,8 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) /* Only frequency scaling */ if (!regulators) { - rcu_read_unlock(); - return _generic_set_opp_clk_only(dev, clk, old_freq, freq); + ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); + goto put_opps; } if (opp_table->set_opp) @@ -773,28 +690,26 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) data->new_opp.rate = freq; memcpy(data->new_opp.supplies, opp->supplies, size); - rcu_read_unlock(); + ret = set_opp(data); - return set_opp(data); +put_opps: + dev_pm_opp_put(opp); +put_old_opp: + if (!IS_ERR(old_opp)) + dev_pm_opp_put(old_opp); +put_opp_table: + dev_pm_opp_put_opp_table(opp_table); + return ret; } EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); /* OPP-dev Helpers */ -static void _kfree_opp_dev_rcu(struct rcu_head *head) -{ - struct opp_device *opp_dev; - - opp_dev = container_of(head, struct opp_device, rcu_head); - kfree_rcu(opp_dev, rcu_head); -} - static void _remove_opp_dev(struct opp_device *opp_dev, struct opp_table *opp_table) { opp_debug_unregister(opp_dev, opp_table); list_del(&opp_dev->node); - call_srcu(&opp_table->srcu_head.srcu, &opp_dev->rcu_head, - _kfree_opp_dev_rcu); + kfree(opp_dev); } struct opp_device *_add_opp_dev(const struct device *dev, @@ -809,7 +724,7 @@ struct opp_device *_add_opp_dev(const struct device *dev, /* Initialize opp-dev */ opp_dev->dev = dev; - list_add_rcu(&opp_dev->node, &opp_table->dev_list); + list_add(&opp_dev->node, &opp_table->dev_list); /* Create debugfs entries for the opp_table */ ret = opp_debug_register(opp_dev, opp_table); @@ -820,26 +735,12 @@ struct opp_device *_add_opp_dev(const struct device *dev, return opp_dev; } -/** - * _add_opp_table() - Find OPP table or allocate a new one - * @dev: device for which we do this operation - * - * It tries to find an existing table first, if it couldn't find one, it - * allocates a new OPP table and returns that. - * - * Return: valid opp_table pointer if success, else NULL. - */ -static struct opp_table *_add_opp_table(struct device *dev) +static struct opp_table *_allocate_opp_table(struct device *dev) { struct opp_table *opp_table; struct opp_device *opp_dev; int ret; - /* Check for existing table for 'dev' first */ - opp_table = _find_opp_table(dev); - if (!IS_ERR(opp_table)) - return opp_table; - /* * Allocate a new OPP table. In the infrequent case where a new * device is needed to be added, we pay this penalty. @@ -867,50 +768,45 @@ static struct opp_table *_add_opp_table(struct device *dev) ret); } - srcu_init_notifier_head(&opp_table->srcu_head); + BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head); INIT_LIST_HEAD(&opp_table->opp_list); + mutex_init(&opp_table->lock); + kref_init(&opp_table->kref); /* Secure the device table modification */ - list_add_rcu(&opp_table->node, &opp_tables); + list_add(&opp_table->node, &opp_tables); return opp_table; } -/** - * _kfree_device_rcu() - Free opp_table RCU handler - * @head: RCU head - */ -static void _kfree_device_rcu(struct rcu_head *head) +void _get_opp_table_kref(struct opp_table *opp_table) { - struct opp_table *opp_table = container_of(head, struct opp_table, - rcu_head); - - kfree_rcu(opp_table, rcu_head); + kref_get(&opp_table->kref); } -/** - * _remove_opp_table() - Removes a OPP table - * @opp_table: OPP table to be removed. - * - * Removes/frees OPP table if it doesn't contain any OPPs. - */ -static void _remove_opp_table(struct opp_table *opp_table) +struct opp_table *dev_pm_opp_get_opp_table(struct device *dev) { - struct opp_device *opp_dev; + struct opp_table *opp_table; - if (!list_empty(&opp_table->opp_list)) - return; + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); - if (opp_table->supported_hw) - return; + opp_table = _find_opp_table_unlocked(dev); + if (!IS_ERR(opp_table)) + goto unlock; - if (opp_table->prop_name) - return; + opp_table = _allocate_opp_table(dev); - if (opp_table->regulators) - return; +unlock: + mutex_unlock(&opp_table_lock); - if (opp_table->set_opp) - return; + return opp_table; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_table); + +static void _opp_table_kref_release(struct kref *kref) +{ + struct opp_table *opp_table = container_of(kref, struct opp_table, kref); + struct opp_device *opp_dev; /* Release clk */ if (!IS_ERR(opp_table->clk)) @@ -924,63 +820,60 @@ static void _remove_opp_table(struct opp_table *opp_table) /* dev_list must be empty now */ WARN_ON(!list_empty(&opp_table->dev_list)); - list_del_rcu(&opp_table->node); - call_srcu(&opp_table->srcu_head.srcu, &opp_table->rcu_head, - _kfree_device_rcu); + mutex_destroy(&opp_table->lock); + list_del(&opp_table->node); + kfree(opp_table); + + mutex_unlock(&opp_table_lock); } -/** - * _kfree_opp_rcu() - Free OPP RCU handler - * @head: RCU head - */ -static void _kfree_opp_rcu(struct rcu_head *head) +void dev_pm_opp_put_opp_table(struct opp_table *opp_table) { - struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head); + kref_put_mutex(&opp_table->kref, _opp_table_kref_release, + &opp_table_lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_put_opp_table); - kfree_rcu(opp, rcu_head); +void _opp_free(struct dev_pm_opp *opp) +{ + kfree(opp); } -/** - * _opp_remove() - Remove an OPP from a table definition - * @opp_table: points back to the opp_table struct this opp belongs to - * @opp: pointer to the OPP to remove - * @notify: OPP_EVENT_REMOVE notification should be sent or not - * - * This function removes an opp definition from the opp table. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * It is assumed that the caller holds required mutex for an RCU updater - * strategy. - */ -void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp, - bool notify) +static void _opp_kref_release(struct kref *kref) { + struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref); + struct opp_table *opp_table = opp->opp_table; + /* * Notify the changes in the availability of the operable * frequency/voltage list. */ - if (notify) - srcu_notifier_call_chain(&opp_table->srcu_head, - OPP_EVENT_REMOVE, opp); + blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_REMOVE, opp); opp_debug_remove_one(opp); - list_del_rcu(&opp->node); - call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); + list_del(&opp->node); + kfree(opp); - _remove_opp_table(opp_table); + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); +} + +static void dev_pm_opp_get(struct dev_pm_opp *opp) +{ + kref_get(&opp->kref); } +void dev_pm_opp_put(struct dev_pm_opp *opp) +{ + kref_put_mutex(&opp->kref, _opp_kref_release, &opp->opp_table->lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_put); + /** * dev_pm_opp_remove() - Remove an OPP from OPP table * @dev: device for which we do this operation * @freq: OPP to remove with matching 'freq' * * This function removes an opp from the opp table. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ void dev_pm_opp_remove(struct device *dev, unsigned long freq) { @@ -988,12 +881,11 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) struct opp_table *opp_table; bool found = false; - /* Hold our table modification lock here */ - mutex_lock(&opp_table_lock); - opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) - goto unlock; + return; + + mutex_lock(&opp_table->lock); list_for_each_entry(opp, &opp_table->opp_list, node) { if (opp->rate == freq) { @@ -1002,28 +894,23 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) } } - if (!found) { + mutex_unlock(&opp_table->lock); + + if (found) { + dev_pm_opp_put(opp); + } else { dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n", __func__, freq); - goto unlock; } - _opp_remove(opp_table, opp, true); -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); } EXPORT_SYMBOL_GPL(dev_pm_opp_remove); -struct dev_pm_opp *_allocate_opp(struct device *dev, - struct opp_table **opp_table) +struct dev_pm_opp *_opp_allocate(struct opp_table *table) { struct dev_pm_opp *opp; int count, supply_size; - struct opp_table *table; - - table = _add_opp_table(dev); - if (!table) - return NULL; /* Allocate space for at least one supply */ count = table->regulator_count ? table->regulator_count : 1; @@ -1031,17 +918,13 @@ struct dev_pm_opp *_allocate_opp(struct device *dev, /* allocate new OPP node and supplies structures */ opp = kzalloc(sizeof(*opp) + supply_size, GFP_KERNEL); - if (!opp) { - kfree(table); + if (!opp) 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; } @@ -1067,11 +950,21 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp, return true; } +/* + * Returns: + * 0: On success. And appropriate error message for duplicate OPPs. + * -EBUSY: For OPP with same freq/volt and is available. The callers of + * _opp_add() must return 0 if they receive -EBUSY from it. This is to make + * sure we don't print error messages unnecessarily if different parts of + * kernel try to initialize the OPP table. + * -EEXIST: For OPP with same freq but different volt or is unavailable. This + * should be considered an error by the callers of _opp_add(). + */ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table) { struct dev_pm_opp *opp; - struct list_head *head = &opp_table->opp_list; + struct list_head *head; int ret; /* @@ -1082,7 +975,10 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * loop, don't replace it with head otherwise it will become an infinite * loop. */ - list_for_each_entry_rcu(opp, &opp_table->opp_list, node) { + mutex_lock(&opp_table->lock); + head = &opp_table->opp_list; + + list_for_each_entry(opp, &opp_table->opp_list, node) { if (new_opp->rate > opp->rate) { head = &opp->node; continue; @@ -1098,12 +994,21 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, new_opp->supplies[0].u_volt, new_opp->available); /* Should we compare voltages for all regulators here ? */ - return opp->available && - new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? 0 : -EEXIST; + ret = opp->available && + new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? -EBUSY : -EEXIST; + + mutex_unlock(&opp_table->lock); + return ret; } + list_add(&new_opp->node, head); + mutex_unlock(&opp_table->lock); + new_opp->opp_table = opp_table; - list_add_rcu(&new_opp->node, head); + kref_init(&new_opp->kref); + + /* Get a reference to the OPP table */ + _get_opp_table_kref(opp_table); ret = opp_debug_create_one(new_opp, opp_table); if (ret) @@ -1121,6 +1026,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, /** * _opp_add_v1() - Allocate a OPP based on v1 bindings. + * @opp_table: OPP table * @dev: device for which we do this operation * @freq: Frequency in Hz for this OPP * @u_volt: Voltage in uVolts for this OPP @@ -1133,12 +1039,6 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table * and freed by dev_pm_opp_of_remove_table. * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * * Return: * 0 On success OR * Duplicate OPPs (both freq and volt are same) and opp->available @@ -1146,22 +1046,16 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * Duplicate OPPs (both freq and volt are same) and !opp->available * -ENOMEM Memory allocation failure */ -int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, - bool dynamic) +int _opp_add_v1(struct opp_table *opp_table, struct device *dev, + unsigned long freq, long u_volt, bool dynamic) { - struct opp_table *opp_table; struct dev_pm_opp *new_opp; unsigned long tol; int ret; - /* Hold our table modification lock here */ - mutex_lock(&opp_table_lock); - - new_opp = _allocate_opp(dev, &opp_table); - if (!new_opp) { - ret = -ENOMEM; - goto unlock; - } + new_opp = _opp_allocate(opp_table); + if (!new_opp) + return -ENOMEM; /* populate the opp table */ new_opp->rate = freq; @@ -1173,22 +1067,23 @@ int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, new_opp->dynamic = dynamic; ret = _opp_add(dev, new_opp, opp_table); - if (ret) + if (ret) { + /* Don't return error for duplicate OPPs */ + if (ret == -EBUSY) + ret = 0; goto free_opp; - - mutex_unlock(&opp_table_lock); + } /* * Notify the changes in the availability of the operable * frequency/voltage list. */ - srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp); + blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp); return 0; free_opp: - _opp_remove(opp_table, new_opp, false); -unlock: - mutex_unlock(&opp_table_lock); + _opp_free(new_opp); + return ret; } @@ -1202,27 +1097,16 @@ unlock: * specify the hierarchy of versions it supports. OPP layer will then enable * OPPs, which are available for those versions, based on its 'opp-supported-hw' * property. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ -int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, - unsigned int count) +struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, + const u32 *versions, unsigned int count) { struct opp_table *opp_table; - int ret = 0; - - /* Hold our table modification lock here */ - mutex_lock(&opp_table_lock); + int ret; - opp_table = _add_opp_table(dev); - if (!opp_table) { - ret = -ENOMEM; - goto unlock; - } + opp_table = dev_pm_opp_get_opp_table(dev); + if (!opp_table) + return ERR_PTR(-ENOMEM); /* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); @@ -1243,65 +1127,40 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, } opp_table->supported_hw_count = count; - mutex_unlock(&opp_table_lock); - return 0; + + return opp_table; err: - _remove_opp_table(opp_table); -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); - return ret; + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw); /** * dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw - * @dev: Device for which supported-hw has to be put. + * @opp_table: OPP table returned by dev_pm_opp_set_supported_hw(). * * This is required only for the V2 bindings, and is called for a matching * dev_pm_opp_set_supported_hw(). Until this is called, the opp_table structure * will not be freed. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ -void dev_pm_opp_put_supported_hw(struct device *dev) +void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) { - struct opp_table *opp_table; - - /* Hold our table modification lock here */ - mutex_lock(&opp_table_lock); - - /* Check for existing table for 'dev' first */ - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - dev_err(dev, "Failed to find opp_table: %ld\n", - PTR_ERR(opp_table)); - goto unlock; - } - /* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); if (!opp_table->supported_hw) { - dev_err(dev, "%s: Doesn't have supported hardware list\n", - __func__); - goto unlock; + pr_err("%s: Doesn't have supported hardware list\n", + __func__); + return; } kfree(opp_table->supported_hw); opp_table->supported_hw = NULL; opp_table->supported_hw_count = 0; - /* Try freeing opp_table if this was the last blocking resource */ - _remove_opp_table(opp_table); - -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); } EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); @@ -1314,26 +1173,15 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); * specify the extn to be used for certain property names. The properties to * which the extension will apply are opp-microvolt and opp-microamp. OPP core * should postfix the property name with -<name> while looking for them. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ -int dev_pm_opp_set_prop_name(struct device *dev, const char *name) +struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) { struct opp_table *opp_table; - int ret = 0; - - /* Hold our table modification lock here */ - mutex_lock(&opp_table_lock); + int ret; - opp_table = _add_opp_table(dev); - if (!opp_table) { - ret = -ENOMEM; - goto unlock; - } + opp_table = dev_pm_opp_get_opp_table(dev); + if (!opp_table) + return ERR_PTR(-ENOMEM); /* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); @@ -1352,63 +1200,37 @@ int dev_pm_opp_set_prop_name(struct device *dev, const char *name) goto err; } - mutex_unlock(&opp_table_lock); - return 0; + return opp_table; err: - _remove_opp_table(opp_table); -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); - return ret; + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name); /** * dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name - * @dev: Device for which the prop-name has to be put. + * @opp_table: OPP table returned by dev_pm_opp_set_prop_name(). * * This is required only for the V2 bindings, and is called for a matching * dev_pm_opp_set_prop_name(). Until this is called, the opp_table structure * will not be freed. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ -void dev_pm_opp_put_prop_name(struct device *dev) +void dev_pm_opp_put_prop_name(struct opp_table *opp_table) { - struct opp_table *opp_table; - - /* Hold our table modification lock here */ - mutex_lock(&opp_table_lock); - - /* Check for existing table for 'dev' first */ - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - dev_err(dev, "Failed to find opp_table: %ld\n", - PTR_ERR(opp_table)); - goto unlock; - } - /* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); if (!opp_table->prop_name) { - dev_err(dev, "%s: Doesn't have a prop-name\n", __func__); - goto unlock; + pr_err("%s: Doesn't have a prop-name\n", __func__); + return; } kfree(opp_table->prop_name); opp_table->prop_name = NULL; - /* Try freeing opp_table if this was the last blocking resource */ - _remove_opp_table(opp_table); - -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); } EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); @@ -1455,12 +1277,6 @@ static void _free_set_opp_data(struct opp_table *opp_table) * well. * * This must be called before any OPPs are initialized for the device. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], @@ -1470,13 +1286,9 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, struct regulator *reg; int ret, i; - mutex_lock(&opp_table_lock); - - opp_table = _add_opp_table(dev); - if (!opp_table) { - ret = -ENOMEM; - goto unlock; - } + opp_table = dev_pm_opp_get_opp_table(dev); + if (!opp_table) + return ERR_PTR(-ENOMEM); /* This should be called before OPPs are initialized */ if (WARN_ON(!list_empty(&opp_table->opp_list))) { @@ -1518,7 +1330,6 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, if (ret) goto free_regulators; - mutex_unlock(&opp_table_lock); return opp_table; free_regulators: @@ -1529,9 +1340,7 @@ free_regulators: opp_table->regulators = NULL; opp_table->regulator_count = 0; err: - _remove_opp_table(opp_table); -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); return ERR_PTR(ret); } @@ -1540,22 +1349,14 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulators); /** * 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. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ void dev_pm_opp_put_regulators(struct opp_table *opp_table) { int i; - mutex_lock(&opp_table_lock); - if (!opp_table->regulators) { pr_err("%s: Doesn't have regulators set\n", __func__); - goto unlock; + return; } /* Make sure there are no concurrent readers while updating opp_table */ @@ -1570,11 +1371,7 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table) 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); - -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); } EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); @@ -1587,29 +1384,19 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); * regulators per device), instead of the generic OPP set rate helper. * * This must be called before any OPPs are initialized for the device. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ -int dev_pm_opp_register_set_opp_helper(struct device *dev, +struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)) { struct opp_table *opp_table; int ret; if (!set_opp) - return -EINVAL; - - mutex_lock(&opp_table_lock); + return ERR_PTR(-EINVAL); - opp_table = _add_opp_table(dev); - if (!opp_table) { - ret = -ENOMEM; - goto unlock; - } + opp_table = dev_pm_opp_get_opp_table(dev); + if (!opp_table) + return ERR_PTR(-ENOMEM); /* This should be called before OPPs are initialized */ if (WARN_ON(!list_empty(&opp_table->opp_list))) { @@ -1625,47 +1412,28 @@ int dev_pm_opp_register_set_opp_helper(struct device *dev, opp_table->set_opp = set_opp; - mutex_unlock(&opp_table_lock); - return 0; + return opp_table; err: - _remove_opp_table(opp_table); -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); - return ret; + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper); /** * dev_pm_opp_register_put_opp_helper() - Releases resources blocked for * set_opp helper - * @dev: Device for which custom set_opp helper has to be cleared. + * @opp_table: OPP table returned from dev_pm_opp_register_set_opp_helper(). * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. + * Release resources blocked for platform specific set_opp helper. */ -void dev_pm_opp_register_put_opp_helper(struct device *dev) +void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table) { - struct opp_table *opp_table; - - mutex_lock(&opp_table_lock); - - /* Check for existing table for 'dev' first */ - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - dev_err(dev, "Failed to find opp_table: %ld\n", - PTR_ERR(opp_table)); - goto unlock; - } - if (!opp_table->set_opp) { - dev_err(dev, "%s: Doesn't have custom set_opp helper set\n", - __func__); - goto unlock; + pr_err("%s: Doesn't have custom set_opp helper set\n", + __func__); + return; } /* Make sure there are no concurrent readers while updating opp_table */ @@ -1673,11 +1441,7 @@ void dev_pm_opp_register_put_opp_helper(struct device *dev) opp_table->set_opp = NULL; - /* Try freeing opp_table if this was the last blocking resource */ - _remove_opp_table(opp_table); - -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); } EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper); @@ -1691,12 +1455,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper); * The opp is made available by default and it can be controlled using * dev_pm_opp_enable/disable functions. * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * * Return: * 0 On success OR * Duplicate OPPs (both freq and volt are same) and opp->available @@ -1706,7 +1464,17 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper); */ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) { - return _opp_add_v1(dev, freq, u_volt, true); + struct opp_table *opp_table; + int ret; + + opp_table = dev_pm_opp_get_opp_table(dev); + if (!opp_table) + return -ENOMEM; + + ret = _opp_add_v1(opp_table, dev, freq, u_volt, true); + + dev_pm_opp_put_opp_table(opp_table); + return ret; } EXPORT_SYMBOL_GPL(dev_pm_opp_add); @@ -1716,41 +1484,30 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add); * @freq: OPP frequency to modify availability * @availability_req: availability status requested for this opp * - * Set the availability of an OPP with an RCU operation, opp_{enable,disable} - * share a common logic which is isolated here. + * Set the availability of an OPP, opp_{enable,disable} share a common logic + * which is isolated here. * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the * copy operation, returns 0 if no modification was done OR modification was * successful. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks to - * keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex locking or synchronize_rcu() blocking calls cannot be used. */ static int _opp_set_availability(struct device *dev, unsigned long freq, bool availability_req) { struct opp_table *opp_table; - struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); + struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV); int r = 0; - /* keep the node allocated */ - new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL); - if (!new_opp) - return -ENOMEM; - - mutex_lock(&opp_table_lock); - /* Find the opp_table */ opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { r = PTR_ERR(opp_table); dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); - goto unlock; + return r; } + mutex_lock(&opp_table->lock); + /* Do we have the frequency? */ list_for_each_entry(tmp_opp, &opp_table->opp_list, node) { if (tmp_opp->rate == freq) { @@ -1758,6 +1515,7 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, break; } } + if (IS_ERR(opp)) { r = PTR_ERR(opp); goto unlock; @@ -1766,29 +1524,20 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, /* Is update really needed? */ if (opp->available == availability_req) goto unlock; - /* copy the old data over */ - *new_opp = *opp; - /* plug in new node */ - new_opp->available = availability_req; - - list_replace_rcu(&opp->node, &new_opp->node); - mutex_unlock(&opp_table_lock); - call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); + opp->available = availability_req; /* Notify the change of the OPP availability */ if (availability_req) - srcu_notifier_call_chain(&opp_table->srcu_head, - OPP_EVENT_ENABLE, new_opp); + blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ENABLE, + opp); else - srcu_notifier_call_chain(&opp_table->srcu_head, - OPP_EVENT_DISABLE, new_opp); - - return 0; + blocking_notifier_call_chain(&opp_table->head, + OPP_EVENT_DISABLE, opp); unlock: - mutex_unlock(&opp_table_lock); - kfree(new_opp); + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); return r; } @@ -1801,12 +1550,6 @@ unlock: * corresponding error value. It is meant to be used for users an OPP available * after being temporarily made unavailable with dev_pm_opp_disable. * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function indirectly uses RCU and mutex locks to keep the - * integrity of the internal data structures. Callers should ensure that - * this function is *NOT* called under RCU protection or in contexts where - * mutex locking or synchronize_rcu() blocking calls cannot be used. - * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the * copy operation, returns 0 if no modification was done OR modification was * successful. @@ -1827,12 +1570,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_enable); * control by users to make this OPP not available until the circumstances are * right to make it available again (with a call to dev_pm_opp_enable). * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function indirectly uses RCU and mutex locks to keep the - * integrity of the internal data structures. Callers should ensure that - * this function is *NOT* called under RCU protection or in contexts where - * mutex locking or synchronize_rcu() blocking calls cannot be used. - * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the * copy operation, returns 0 if no modification was done OR modification was * successful. @@ -1844,41 +1581,78 @@ int dev_pm_opp_disable(struct device *dev, unsigned long freq) EXPORT_SYMBOL_GPL(dev_pm_opp_disable); /** - * dev_pm_opp_get_notifier() - find notifier_head of the device with opp - * @dev: device pointer used to lookup OPP table. + * dev_pm_opp_register_notifier() - Register OPP notifier for the device + * @dev: Device for which notifier needs to be registered + * @nb: Notifier block to be registered * - * Return: pointer to notifier head if found, otherwise -ENODEV or - * -EINVAL based on type of error casted as pointer. value must be checked - * with IS_ERR to determine valid pointer or error result. + * Return: 0 on success or a negative error value. + */ +int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb) +{ + struct opp_table *opp_table; + int ret; + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) + return PTR_ERR(opp_table); + + ret = blocking_notifier_chain_register(&opp_table->head, nb); + + dev_pm_opp_put_opp_table(opp_table); + + return ret; +} +EXPORT_SYMBOL(dev_pm_opp_register_notifier); + +/** + * dev_pm_opp_unregister_notifier() - Unregister OPP notifier for the device + * @dev: Device for which notifier needs to be unregistered + * @nb: Notifier block to be unregistered * - * Locking: This function must be called under rcu_read_lock(). opp_table is a - * RCU protected pointer. The reason for the same is that the opp pointer which - * is returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * Return: 0 on success or a negative error value. */ -struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) +int dev_pm_opp_unregister_notifier(struct device *dev, + struct notifier_block *nb) { - struct opp_table *opp_table = _find_opp_table(dev); + struct opp_table *opp_table; + int ret; + opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) - return ERR_CAST(opp_table); /* matching type */ + return PTR_ERR(opp_table); + + ret = blocking_notifier_chain_unregister(&opp_table->head, nb); - return &opp_table->srcu_head; + dev_pm_opp_put_opp_table(opp_table); + + return ret; } -EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); +EXPORT_SYMBOL(dev_pm_opp_unregister_notifier); /* * Free OPPs either created using static entries present in DT or even the * dynamically added entries based on remove_all param. */ -void _dev_pm_opp_remove_table(struct device *dev, bool remove_all) +void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev, + bool remove_all) { - struct opp_table *opp_table; struct dev_pm_opp *opp, *tmp; - /* Hold our table modification lock here */ - mutex_lock(&opp_table_lock); + /* Find if opp_table manages a single device */ + if (list_is_singular(&opp_table->dev_list)) { + /* Free static OPPs */ + list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) { + if (remove_all || !opp->dynamic) + dev_pm_opp_put(opp); + } + } else { + _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table); + } +} + +void _dev_pm_opp_find_and_remove_table(struct device *dev, bool remove_all) +{ + struct opp_table *opp_table; /* Check for existing table for 'dev' */ opp_table = _find_opp_table(dev); @@ -1890,22 +1664,12 @@ void _dev_pm_opp_remove_table(struct device *dev, bool remove_all) IS_ERR_OR_NULL(dev) ? "Invalid device" : dev_name(dev), error); - goto unlock; + return; } - /* Find if opp_table manages a single device */ - if (list_is_singular(&opp_table->dev_list)) { - /* Free static OPPs */ - list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) { - if (remove_all || !opp->dynamic) - _opp_remove(opp_table, opp, true); - } - } else { - _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table); - } + _dev_pm_opp_remove_table(opp_table, dev, remove_all); -unlock: - mutex_unlock(&opp_table_lock); + dev_pm_opp_put_opp_table(opp_table); } /** @@ -1914,15 +1678,9 @@ unlock: * * Free both OPPs created using static entries present in DT and the * dynamically added entries. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function indirectly uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ void dev_pm_opp_remove_table(struct device *dev) { - _dev_pm_opp_remove_table(dev, true); + _dev_pm_opp_find_and_remove_table(dev, true); } EXPORT_SYMBOL_GPL(dev_pm_opp_remove_table); |