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.c33
1 files changed, 32 insertions, 1 deletions
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 2a6976265580..b555121b878b 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -892,6 +892,33 @@ static void _opp_table_kref_release(struct kref *kref)
mutex_unlock(&opp_table_lock);
}
+void _opp_remove_all_static(struct opp_table *opp_table)
+{
+ struct dev_pm_opp *opp, *tmp;
+
+ list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
+ if (!opp->dynamic)
+ dev_pm_opp_put(opp);
+ }
+
+ opp_table->parsed_static_opps = false;
+}
+
+static void _opp_table_list_kref_release(struct kref *kref)
+{
+ struct opp_table *opp_table = container_of(kref, struct opp_table,
+ list_kref);
+
+ _opp_remove_all_static(opp_table);
+ mutex_unlock(&opp_table_lock);
+}
+
+void _put_opp_list_kref(struct opp_table *opp_table)
+{
+ kref_put_mutex(&opp_table->list_kref, _opp_table_list_kref_release,
+ &opp_table_lock);
+}
+
void dev_pm_opp_put_opp_table(struct opp_table *opp_table)
{
kref_put_mutex(&opp_table->kref, _opp_table_kref_release,
@@ -1746,8 +1773,11 @@ void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
/* Find if opp_table manages a single device */
if (list_is_singular(&opp_table->dev_list)) {
/* Free static OPPs */
+ _put_opp_list_kref(opp_table);
+
+ /* Free dynamic OPPs */
list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
- if (remove_all || !opp->dynamic)
+ if (remove_all)
dev_pm_opp_put(opp);
}
@@ -1758,6 +1788,7 @@ void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
if (opp_table->genpd_performance_state)
dev_pm_genpd_set_performance_state(dev, 0);
} else {
+ _put_opp_list_kref(opp_table);
_remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
}