]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/base/power/opp/core.c
Merge branches 'pm-cpuidle', 'pm-cpufreq' and 'pm-sleep'
[karo-tx-linux.git] / drivers / base / power / opp / core.c
index 35ff06283738036e8388ea19a502b9434874ab99..dae61720b31402be9f666063de5ad7c6361f147a 100644 (file)
@@ -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);