|
@@ -394,6 +394,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
|
|
|
* 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
|
|
|
+ * -EEXIST: Freq are same and volt are different OR
|
|
|
+ * Duplicate OPPs (both freq and volt are same) and !opp->available
|
|
|
+ * -ENOMEM: Memory allocation failure
|
|
|
*/
|
|
|
int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
|
|
{
|
|
@@ -443,15 +450,31 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
|
|
new_opp->u_volt = u_volt;
|
|
|
new_opp->available = true;
|
|
|
|
|
|
- /* Insert new OPP in order of increasing frequency */
|
|
|
+ /*
|
|
|
+ * Insert new OPP in order of increasing frequency
|
|
|
+ * and discard if already present
|
|
|
+ */
|
|
|
head = &dev_opp->opp_list;
|
|
|
list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) {
|
|
|
- if (new_opp->rate < opp->rate)
|
|
|
+ if (new_opp->rate <= opp->rate)
|
|
|
break;
|
|
|
else
|
|
|
head = &opp->node;
|
|
|
}
|
|
|
|
|
|
+ /* Duplicate OPPs ? */
|
|
|
+ if (new_opp->rate == opp->rate) {
|
|
|
+ int ret = opp->available && new_opp->u_volt == opp->u_volt ?
|
|
|
+ 0 : -EEXIST;
|
|
|
+
|
|
|
+ dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
|
|
|
+ __func__, opp->rate, opp->u_volt, opp->available,
|
|
|
+ new_opp->rate, new_opp->u_volt, new_opp->available);
|
|
|
+ mutex_unlock(&dev_opp_list_lock);
|
|
|
+ kfree(new_opp);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
list_add_rcu(&new_opp->node, head);
|
|
|
mutex_unlock(&dev_opp_list_lock);
|
|
|
|