summaryrefslogtreecommitdiff
path: root/drivers/opp/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/opp/core.c')
-rw-r--r--drivers/opp/core.c48
1 files changed, 25 insertions, 23 deletions
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index c2689386a906..1556998425d5 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -1492,7 +1492,11 @@ static struct dev_pm_opp *_opp_get_next(struct opp_table *opp_table,
mutex_lock(&opp_table->lock);
list_for_each_entry(temp, &opp_table->opp_list, node) {
- if (dynamic == temp->dynamic) {
+ /*
+ * Refcount must be dropped only once for each OPP by OPP core,
+ * do that with help of "removed" flag.
+ */
+ if (!temp->removed && dynamic == temp->dynamic) {
opp = temp;
break;
}
@@ -1502,10 +1506,27 @@ static struct dev_pm_opp *_opp_get_next(struct opp_table *opp_table,
return opp;
}
-bool _opp_remove_all_static(struct opp_table *opp_table)
+/*
+ * Can't call dev_pm_opp_put() from under the lock as debugfs removal needs to
+ * happen lock less to avoid circular dependency issues. This routine must be
+ * called without the opp_table->lock held.
+ */
+static void _opp_remove_all(struct opp_table *opp_table, bool dynamic)
{
struct dev_pm_opp *opp;
+ while ((opp = _opp_get_next(opp_table, dynamic))) {
+ opp->removed = true;
+ dev_pm_opp_put(opp);
+
+ /* Drop the references taken by dev_pm_opp_add() */
+ if (dynamic)
+ dev_pm_opp_put_opp_table(opp_table);
+ }
+}
+
+bool _opp_remove_all_static(struct opp_table *opp_table)
+{
mutex_lock(&opp_table->lock);
if (!opp_table->parsed_static_opps) {
@@ -1520,13 +1541,7 @@ bool _opp_remove_all_static(struct opp_table *opp_table)
mutex_unlock(&opp_table->lock);
- /*
- * Can't remove the OPP from under the lock, debugfs removal needs to
- * happen lock less to avoid circular dependency issues.
- */
- while ((opp = _opp_get_next(opp_table, false)))
- dev_pm_opp_put(opp);
-
+ _opp_remove_all(opp_table, false);
return true;
}
@@ -1539,25 +1554,12 @@ bool _opp_remove_all_static(struct opp_table *opp_table)
void dev_pm_opp_remove_all_dynamic(struct device *dev)
{
struct opp_table *opp_table;
- struct dev_pm_opp *opp;
- int count = 0;
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
return;
- /*
- * Can't remove the OPP from under the lock, debugfs removal needs to
- * happen lock less to avoid circular dependency issues.
- */
- while ((opp = _opp_get_next(opp_table, true))) {
- dev_pm_opp_put(opp);
- count++;
- }
-
- /* Drop the references taken by dev_pm_opp_add() */
- while (count--)
- dev_pm_opp_put_opp_table(opp_table);
+ _opp_remove_all(opp_table, true);
/* Drop the reference taken by _find_opp_table() */
dev_pm_opp_put_opp_table(opp_table);