diff options
Diffstat (limited to 'drivers/opp/core.c')
-rw-r--r-- | drivers/opp/core.c | 178 |
1 files changed, 128 insertions, 50 deletions
diff --git a/drivers/opp/core.c b/drivers/opp/core.c index c3f3d9249cc5..e366218d6736 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -998,14 +998,15 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, old_opp = opp_table->current_opp; /* Return early if nothing to do */ - if (opp_table->enabled && old_opp == opp) { + if (old_opp == opp && opp_table->current_rate == freq && + opp_table->enabled) { dev_dbg(dev, "%s: OPPs are same, nothing to do\n", __func__); return 0; } dev_dbg(dev, "%s: switching OPP: Freq %lu -> %lu Hz, Level %u -> %u, Bw %u -> %u\n", - __func__, old_opp->rate, freq, old_opp->level, opp->level, - old_opp->bandwidth ? old_opp->bandwidth[0].peak : 0, + __func__, opp_table->current_rate, freq, old_opp->level, + opp->level, old_opp->bandwidth ? old_opp->bandwidth[0].peak : 0, opp->bandwidth ? opp->bandwidth[0].peak : 0); scaling_down = _opp_compare_key(old_opp, opp); @@ -1061,6 +1062,7 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, /* Make sure current_opp doesn't get freed */ dev_pm_opp_get(opp); opp_table->current_opp = opp; + opp_table->current_rate = freq; return ret; } @@ -1490,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; } @@ -1500,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) { @@ -1518,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; } @@ -1537,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); @@ -1853,6 +1857,35 @@ void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) } EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); +static void devm_pm_opp_supported_hw_release(void *data) +{ + dev_pm_opp_put_supported_hw(data); +} + +/** + * devm_pm_opp_set_supported_hw() - Set supported platforms + * @dev: Device for which supported-hw has to be set. + * @versions: Array of hierarchy of versions to match. + * @count: Number of elements in the array. + * + * This is a resource-managed variant of dev_pm_opp_set_supported_hw(). + * + * Return: 0 on success and errorno otherwise. + */ +int devm_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, + unsigned int count) +{ + struct opp_table *opp_table; + + opp_table = dev_pm_opp_set_supported_hw(dev, versions, count); + if (IS_ERR(opp_table)) + return PTR_ERR(opp_table); + + return devm_add_action_or_reset(dev, devm_pm_opp_supported_hw_release, + opp_table); +} +EXPORT_SYMBOL_GPL(devm_pm_opp_set_supported_hw); + /** * dev_pm_opp_set_prop_name() - Set prop-extn name * @dev: Device for which the prop-name has to be set. @@ -2043,6 +2076,36 @@ put_opp_table: } EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); +static void devm_pm_opp_regulators_release(void *data) +{ + dev_pm_opp_put_regulators(data); +} + +/** + * devm_pm_opp_set_regulators() - Set regulator names for the device + * @dev: Device for which regulator name is being set. + * @names: Array of pointers to the names of the regulator. + * @count: Number of regulators. + * + * This is a resource-managed variant of dev_pm_opp_set_regulators(). + * + * Return: 0 on success and errorno otherwise. + */ +int devm_pm_opp_set_regulators(struct device *dev, + const char * const names[], + unsigned int count) +{ + struct opp_table *opp_table; + + opp_table = dev_pm_opp_set_regulators(dev, names, count); + if (IS_ERR(opp_table)) + return PTR_ERR(opp_table); + + return devm_add_action_or_reset(dev, devm_pm_opp_regulators_release, + opp_table); +} +EXPORT_SYMBOL_GPL(devm_pm_opp_set_regulators); + /** * dev_pm_opp_set_clkname() - Set clk name for the device * @dev: Device for which clk name is being set. @@ -2115,6 +2178,33 @@ void dev_pm_opp_put_clkname(struct opp_table *opp_table) } EXPORT_SYMBOL_GPL(dev_pm_opp_put_clkname); +static void devm_pm_opp_clkname_release(void *data) +{ + dev_pm_opp_put_clkname(data); +} + +/** + * devm_pm_opp_set_clkname() - Set clk name for the device + * @dev: Device for which clk name is being set. + * @name: Clk name. + * + * This is a resource-managed variant of dev_pm_opp_set_clkname(). + * + * Return: 0 on success and errorno otherwise. + */ +int devm_pm_opp_set_clkname(struct device *dev, const char *name) +{ + struct opp_table *opp_table; + + opp_table = dev_pm_opp_set_clkname(dev, name); + if (IS_ERR(opp_table)) + return PTR_ERR(opp_table); + + return devm_add_action_or_reset(dev, devm_pm_opp_clkname_release, + opp_table); +} +EXPORT_SYMBOL_GPL(devm_pm_opp_set_clkname); + /** * dev_pm_opp_register_set_opp_helper() - Register custom set OPP helper * @dev: Device for which the helper is getting registered. @@ -2205,25 +2295,19 @@ static void devm_pm_opp_unregister_set_opp_helper(void *data) * * This is a resource-managed version of dev_pm_opp_register_set_opp_helper(). * - * Return: pointer to 'struct opp_table' on success and errorno otherwise. + * Return: 0 on success and errorno otherwise. */ -struct opp_table * -devm_pm_opp_register_set_opp_helper(struct device *dev, - int (*set_opp)(struct dev_pm_set_opp_data *data)) +int devm_pm_opp_register_set_opp_helper(struct device *dev, + int (*set_opp)(struct dev_pm_set_opp_data *data)) { struct opp_table *opp_table; - int err; opp_table = dev_pm_opp_register_set_opp_helper(dev, set_opp); if (IS_ERR(opp_table)) - return opp_table; - - err = devm_add_action_or_reset(dev, devm_pm_opp_unregister_set_opp_helper, - opp_table); - if (err) - return ERR_PTR(err); + return PTR_ERR(opp_table); - return opp_table; + return devm_add_action_or_reset(dev, devm_pm_opp_unregister_set_opp_helper, + opp_table); } EXPORT_SYMBOL_GPL(devm_pm_opp_register_set_opp_helper); @@ -2376,25 +2460,19 @@ static void devm_pm_opp_detach_genpd(void *data) * * This is a resource-managed version of dev_pm_opp_attach_genpd(). * - * Return: pointer to 'struct opp_table' on success and errorno otherwise. + * Return: 0 on success and errorno otherwise. */ -struct opp_table * -devm_pm_opp_attach_genpd(struct device *dev, const char **names, - struct device ***virt_devs) +int devm_pm_opp_attach_genpd(struct device *dev, const char **names, + struct device ***virt_devs) { struct opp_table *opp_table; - int err; opp_table = dev_pm_opp_attach_genpd(dev, names, virt_devs); if (IS_ERR(opp_table)) - return opp_table; - - err = devm_add_action_or_reset(dev, devm_pm_opp_detach_genpd, - opp_table); - if (err) - return ERR_PTR(err); + return PTR_ERR(opp_table); - return opp_table; + return devm_add_action_or_reset(dev, devm_pm_opp_detach_genpd, + opp_table); } EXPORT_SYMBOL_GPL(devm_pm_opp_attach_genpd); |