diff options
Diffstat (limited to 'drivers/opp/core.c')
| -rw-r--r-- | drivers/opp/core.c | 48 | 
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);  | 
