aboutsummaryrefslogtreecommitdiff
path: root/drivers/base/power/opp/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/power/opp/core.c')
-rw-r--r--drivers/base/power/opp/core.c114
1 files changed, 63 insertions, 51 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index 9870ee54d708..a6efa818029a 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -40,6 +40,8 @@ do { \
"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)
{
@@ -94,21 +96,13 @@ 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();
+ rcu_read_lock();
tmp_opp = rcu_dereference(opp);
if (IS_ERR_OR_NULL(tmp_opp))
@@ -116,6 +110,7 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
else
v = tmp_opp->supplies[0].u_volt;
+ rcu_read_unlock();
return v;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage);
@@ -126,21 +121,13 @@ 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();
+ rcu_read_lock();
tmp_opp = rcu_dereference(opp);
if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available)
@@ -148,6 +135,7 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp)
else
f = tmp_opp->rate;
+ rcu_read_unlock();
return f;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
@@ -161,20 +149,13 @@ 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;
+ bool turbo;
- opp_rcu_lockdep_assert();
+ rcu_read_lock();
tmp_opp = rcu_dereference(opp);
if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) {
@@ -182,7 +163,10 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp)
return false;
}
- return tmp_opp->turbo;
+ turbo = tmp_opp->turbo;
+
+ rcu_read_unlock();
+ return turbo;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo);
@@ -410,11 +394,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,
@@ -423,13 +404,14 @@ 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();
+ rcu_read_lock();
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
int r = PTR_ERR(opp_table);
dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r);
+ rcu_read_unlock();
return ERR_PTR(r);
}
@@ -437,10 +419,15 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
if (temp_opp->available == available &&
temp_opp->rate == freq) {
opp = temp_opp;
+
+ /* Increment the reference count of OPP */
+ dev_pm_opp_get(opp);
break;
}
}
+ rcu_read_unlock();
+
return opp;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);
@@ -454,6 +441,9 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table,
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;
}
}
@@ -476,29 +466,33 @@ 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);
return ERR_PTR(-EINVAL);
}
+ rcu_read_lock();
+
opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table))
+ if (IS_ERR(opp_table)) {
+ rcu_read_unlock();
return ERR_CAST(opp_table);
+ }
+
+ opp = _find_freq_ceil(opp_table, freq);
- return _find_freq_ceil(opp_table, freq);
+ rcu_read_unlock();
+
+ return opp;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil);
@@ -517,11 +511,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)
@@ -529,16 +520,18 @@ 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);
}
+ rcu_read_lock();
+
opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table))
+ if (IS_ERR(opp_table)) {
+ rcu_read_unlock();
return ERR_CAST(opp_table);
+ }
list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available) {
@@ -549,6 +542,12 @@ 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);
+ rcu_read_unlock();
+
if (!IS_ERR(opp))
*freq = opp->rate;
@@ -736,6 +735,8 @@ 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);
+ if (!IS_ERR(old_opp))
+ dev_pm_opp_put(old_opp);
rcu_read_unlock();
return ret;
}
@@ -747,6 +748,9 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
/* Only frequency scaling */
if (!regulators) {
+ dev_pm_opp_put(opp);
+ if (!IS_ERR(old_opp))
+ dev_pm_opp_put(old_opp);
rcu_read_unlock();
return _generic_set_opp_clk_only(dev, clk, old_freq, freq);
}
@@ -772,6 +776,9 @@ 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);
+ dev_pm_opp_put(opp);
+ if (!IS_ERR(old_opp))
+ dev_pm_opp_put(old_opp);
rcu_read_unlock();
return set_opp(data);
@@ -967,6 +974,11 @@ static void _opp_kref_release(struct kref *kref)
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);