Merge branch 'opp/power' into cpufreq/qcom-hw
[linux-2.6-block.git] / drivers / opp / core.c
index e5507add8f04f75b2bbd88cdc5de0ea7e8d714b4..4e00301060cf4d4d5e15ba12c9624fdea430ea7e 100644 (file)
@@ -533,9 +533,8 @@ static int _set_opp_voltage(struct device *dev, struct regulator *reg,
        return ret;
 }
 
-static inline int
-_generic_set_opp_clk_only(struct device *dev, struct clk *clk,
-                         unsigned long old_freq, unsigned long freq)
+static inline int _generic_set_opp_clk_only(struct device *dev, struct clk *clk,
+                                           unsigned long freq)
 {
        int ret;
 
@@ -572,7 +571,7 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table,
        }
 
        /* Change frequency */
-       ret = _generic_set_opp_clk_only(dev, opp_table->clk, old_freq, freq);
+       ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq);
        if (ret)
                goto restore_voltage;
 
@@ -586,7 +585,7 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table,
        return 0;
 
 restore_freq:
-       if (_generic_set_opp_clk_only(dev, opp_table->clk, freq, old_freq))
+       if (_generic_set_opp_clk_only(dev, opp_table->clk, old_freq))
                dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
                        __func__, old_freq);
 restore_voltage:
@@ -759,7 +758,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
                                                 opp->supplies);
        } else {
                /* Only frequency scaling */
-               ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
+               ret = _generic_set_opp_clk_only(dev, clk, freq);
        }
 
        /* Scaling down? Configure required OPPs after frequency */
@@ -988,11 +987,9 @@ void _opp_free(struct dev_pm_opp *opp)
        kfree(opp);
 }
 
-static void _opp_kref_release(struct kref *kref)
+static void _opp_kref_release(struct dev_pm_opp *opp,
+                             struct opp_table *opp_table)
 {
-       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.
@@ -1002,7 +999,22 @@ static void _opp_kref_release(struct kref *kref)
        opp_debug_remove_one(opp);
        list_del(&opp->node);
        kfree(opp);
+}
 
+static void _opp_kref_release_unlocked(struct kref *kref)
+{
+       struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref);
+       struct opp_table *opp_table = opp->opp_table;
+
+       _opp_kref_release(opp, opp_table);
+}
+
+static void _opp_kref_release_locked(struct kref *kref)
+{
+       struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref);
+       struct opp_table *opp_table = opp->opp_table;
+
+       _opp_kref_release(opp, opp_table);
        mutex_unlock(&opp_table->lock);
 }
 
@@ -1013,10 +1025,16 @@ void dev_pm_opp_get(struct dev_pm_opp *opp)
 
 void dev_pm_opp_put(struct dev_pm_opp *opp)
 {
-       kref_put_mutex(&opp->kref, _opp_kref_release, &opp->opp_table->lock);
+       kref_put_mutex(&opp->kref, _opp_kref_release_locked,
+                      &opp->opp_table->lock);
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_put);
 
+static void dev_pm_opp_put_unlocked(struct dev_pm_opp *opp)
+{
+       kref_put(&opp->kref, _opp_kref_release_unlocked);
+}
+
 /**
  * dev_pm_opp_remove()  - Remove an OPP from OPP table
  * @dev:       device for which we do this operation
@@ -1060,6 +1078,40 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
 
+/**
+ * dev_pm_opp_remove_all_dynamic() - Remove all dynamically created OPPs
+ * @dev:       device for which we do this operation
+ *
+ * This function removes all dynamically created OPPs from the opp table.
+ */
+void dev_pm_opp_remove_all_dynamic(struct device *dev)
+{
+       struct opp_table *opp_table;
+       struct dev_pm_opp *opp, *temp;
+       int count = 0;
+
+       opp_table = _find_opp_table(dev);
+       if (IS_ERR(opp_table))
+               return;
+
+       mutex_lock(&opp_table->lock);
+       list_for_each_entry_safe(opp, temp, &opp_table->opp_list, node) {
+               if (opp->dynamic) {
+                       dev_pm_opp_put_unlocked(opp);
+                       count++;
+               }
+       }
+       mutex_unlock(&opp_table->lock);
+
+       /* Drop the references taken by dev_pm_opp_add() */
+       while (count--)
+               dev_pm_opp_put_opp_table(opp_table);
+
+       /* Drop the reference taken by _find_opp_table() */
+       dev_pm_opp_put_opp_table(opp_table);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_remove_all_dynamic);
+
 struct dev_pm_opp *_opp_allocate(struct opp_table *table)
 {
        struct dev_pm_opp *opp;