diff options
Diffstat (limited to 'drivers/opp/core.c')
-rw-r--r-- | drivers/opp/core.c | 85 |
1 files changed, 72 insertions, 13 deletions
diff --git a/drivers/opp/core.c b/drivers/opp/core.c index c094d5d20fd7..3b7ffd0234e9 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -401,6 +401,54 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); +/** + * dev_pm_opp_find_level_exact() - search for an exact level + * @dev: device for which we do this operation + * @level: level to search for + * + * Return: Searches for exact match in the opp table and returns pointer to the + * matching opp if found, else returns ERR_PTR in case of error and should + * be handled using IS_ERR. Error return values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, + unsigned int level) +{ + struct opp_table *opp_table; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + int r = PTR_ERR(opp_table); + + dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); + return ERR_PTR(r); + } + + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { + if (temp_opp->level == level) { + opp = temp_opp; + + /* Increment the reference count of OPP */ + dev_pm_opp_get(opp); + break; + } + } + + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_exact); + static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, unsigned long *freq) { @@ -940,6 +988,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index) BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head); INIT_LIST_HEAD(&opp_table->opp_list); kref_init(&opp_table->kref); + kref_init(&opp_table->list_kref); /* Secure the device table modification */ list_add(&opp_table->node, &opp_tables); @@ -1577,6 +1626,12 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, goto free_regulators; } + ret = regulator_enable(reg); + if (ret < 0) { + regulator_put(reg); + goto free_regulators; + } + opp_table->regulators[i] = reg; } @@ -1590,8 +1645,10 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, return opp_table; free_regulators: - while (i != 0) - regulator_put(opp_table->regulators[--i]); + while (i--) { + regulator_disable(opp_table->regulators[i]); + regulator_put(opp_table->regulators[i]); + } kfree(opp_table->regulators); opp_table->regulators = NULL; @@ -1617,8 +1674,10 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table) /* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); - for (i = opp_table->regulator_count - 1; i >= 0; i--) + for (i = opp_table->regulator_count - 1; i >= 0; i--) { + regulator_disable(opp_table->regulators[i]); regulator_put(opp_table->regulators[i]); + } _free_set_opp_data(opp_table); @@ -1771,6 +1830,7 @@ static void _opp_detach_genpd(struct opp_table *opp_table) * dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer * @dev: Consumer device for which the genpd is getting attached. * @names: Null terminated array of pointers containing names of genpd to attach. + * @virt_devs: Pointer to return the array of virtual devices. * * Multiple generic power domains for a device are supported with the help of * virtual genpd devices, which are created for each consumer device - genpd @@ -1784,12 +1844,16 @@ static void _opp_detach_genpd(struct opp_table *opp_table) * * This helper needs to be called once with a list of all genpd to attach. * Otherwise the original device structure will be used instead by the OPP core. + * + * The order of entries in the names array must match the order in which + * "required-opps" are added in DT. */ -struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names) +struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, + const char **names, struct device ***virt_devs) { struct opp_table *opp_table; struct device *virt_dev; - int index, ret = -EINVAL; + int index = 0, ret = -EINVAL; const char **name = names; opp_table = dev_pm_opp_get_opp_table(dev); @@ -1815,14 +1879,6 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names goto unlock; while (*name) { - index = of_property_match_string(dev->of_node, - "power-domain-names", *name); - if (index < 0) { - dev_err(dev, "Failed to find power domain: %s (%d)\n", - *name, index); - goto err; - } - if (index >= opp_table->required_opp_count) { dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n", *name, opp_table->required_opp_count, index); @@ -1843,9 +1899,12 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names } opp_table->genpd_virt_devs[index] = virt_dev; + index++; name++; } + if (virt_devs) + *virt_devs = opp_table->genpd_virt_devs; mutex_unlock(&opp_table->genpd_virt_dev_lock); return opp_table; |