diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2022-05-25 15:02:26 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2022-05-25 15:02:26 +0200 |
commit | e04314082c3197874f0cb3f4031219e811741a49 (patch) | |
tree | 1e0a1c01d3d577cb2d5d6df3698ab4240cdb2077 /drivers/opp | |
parent | 09583dfed2cb9723da31601cb7080490c2e2e2d7 (diff) | |
parent | 22079af7df5a5dfef1c4d160abfd43035211759e (diff) |
Merge tag 'opp-updates-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
Pull OPP (Operating Performance Points) updates for 5.19-rc1 from Viresh
Kumar:
- Minor update to dt-binding for Qcom's opp-v2-kryo-cpu (Yassine
Oudjana).
- Use list iterator only inside the list_for_each_entry loop (Xiaomeng
Tong, and Jakob Koschel).
- New APIs related to finding OPP based on interconnect bandwidth
(Krzysztof Kozlowski).
- Fix the missing of_node_put() in _bandwidth_supported() (Dan
Carpenter).
- Cleanups (Krzysztof Kozlowski, and Viresh Kumar).
* tag 'opp-updates-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
opp: Reorder definition of ceil/floor helpers
opp: Add apis to retrieve opps with interconnect bandwidth
dt-bindings: opp: opp-v2-kryo-cpu: Remove SMEM
opp: use list iterator only inside the loop
opp: replace usage of found with dedicated list iterator variable
PM: opp: simplify with dev_err_probe()
OPP: call of_node_put() on error path in _bandwidth_supported()
Diffstat (limited to 'drivers/opp')
-rw-r--r-- | drivers/opp/core.c | 339 | ||||
-rw-r--r-- | drivers/opp/debugfs.c | 10 | ||||
-rw-r--r-- | drivers/opp/of.c | 2 |
3 files changed, 235 insertions, 116 deletions
diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 740407252298..84063eaebb91 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -456,103 +456,6 @@ 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); - -/** - * dev_pm_opp_find_level_ceil() - search for an rounded up level - * @dev: device for which we do this operation - * @level: level to search for - * - * Return: Searches for rounded up 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_ceil(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->available && temp_opp->level >= *level) { - opp = temp_opp; - *level = opp->level; - - /* 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_ceil); - static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, unsigned long *freq) { @@ -729,6 +632,223 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil_by_volt); +/** + * 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); + +/** + * dev_pm_opp_find_level_ceil() - search for an rounded up level + * @dev: device for which we do this operation + * @level: level to search for + * + * Return: Searches for rounded up 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_ceil(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->available && temp_opp->level >= *level) { + opp = temp_opp; + *level = opp->level; + + /* 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_ceil); + +/** + * dev_pm_opp_find_bw_ceil() - Search for a rounded ceil bandwidth + * @dev: device for which we do this operation + * @freq: start bandwidth + * @index: which bandwidth to compare, in case of OPPs with several values + * + * Search for the matching floor *available* OPP from a starting bandwidth + * for a device. + * + * Return: matching *opp and refreshes *bw accordingly, 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_bw_ceil(struct device *dev, + unsigned int *bw, int index) +{ + struct opp_table *opp_table; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + if (!dev || !bw) { + dev_err(dev, "%s: Invalid argument bw=%p\n", __func__, bw); + return ERR_PTR(-EINVAL); + } + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) + return ERR_CAST(opp_table); + + if (index >= opp_table->path_count) + return ERR_PTR(-EINVAL); + + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { + if (temp_opp->available && temp_opp->bandwidth) { + if (temp_opp->bandwidth[index].peak >= *bw) { + opp = temp_opp; + *bw = opp->bandwidth[index].peak; + + /* 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_bw_ceil); + +/** + * dev_pm_opp_find_bw_floor() - Search for a rounded floor bandwidth + * @dev: device for which we do this operation + * @freq: start bandwidth + * @index: which bandwidth to compare, in case of OPPs with several values + * + * Search for the matching floor *available* OPP from a starting bandwidth + * for a device. + * + * Return: matching *opp and refreshes *bw accordingly, 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_bw_floor(struct device *dev, + unsigned int *bw, int index) +{ + struct opp_table *opp_table; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + if (!dev || !bw) { + dev_err(dev, "%s: Invalid argument bw=%p\n", __func__, bw); + return ERR_PTR(-EINVAL); + } + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) + return ERR_CAST(opp_table); + + if (index >= opp_table->path_count) + return ERR_PTR(-EINVAL); + + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { + if (temp_opp->available && temp_opp->bandwidth) { + /* go to the next node, before choosing prev */ + if (temp_opp->bandwidth[index].peak > *bw) + break; + opp = temp_opp; + } + } + + /* Increment the reference count of OPP */ + if (!IS_ERR(opp)) + dev_pm_opp_get(opp); + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); + + if (!IS_ERR(opp)) + *bw = opp->bandwidth[index].peak; + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_bw_floor); + static int _set_opp_voltage(struct device *dev, struct regulator *reg, struct dev_pm_opp_supply *supply) { @@ -1486,9 +1606,8 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put); */ void dev_pm_opp_remove(struct device *dev, unsigned long freq) { - struct dev_pm_opp *opp; + struct dev_pm_opp *opp = NULL, *iter; struct opp_table *opp_table; - bool found = false; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) @@ -1496,16 +1615,16 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) mutex_lock(&opp_table->lock); - list_for_each_entry(opp, &opp_table->opp_list, node) { - if (opp->rate == freq) { - found = true; + list_for_each_entry(iter, &opp_table->opp_list, node) { + if (iter->rate == freq) { + opp = iter; break; } } mutex_unlock(&opp_table->lock); - if (found) { + if (opp) { dev_pm_opp_put(opp); /* Drop the reference taken by dev_pm_opp_add() */ @@ -2019,10 +2138,9 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, for (i = 0; i < count; i++) { reg = regulator_get_optional(dev, names[i]); if (IS_ERR(reg)) { - ret = PTR_ERR(reg); - if (ret != -EPROBE_DEFER) - dev_err(dev, "%s: no regulator (%s) found: %d\n", - __func__, names[i], ret); + ret = dev_err_probe(dev, PTR_ERR(reg), + "%s: no regulator (%s) found\n", + __func__, names[i]); goto free_regulators; } @@ -2168,11 +2286,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) /* Find clk for the device */ opp_table->clk = clk_get(dev, name); if (IS_ERR(opp_table->clk)) { - ret = PTR_ERR(opp_table->clk); - if (ret != -EPROBE_DEFER) { - dev_err(dev, "%s: Couldn't find clock: %d\n", __func__, - ret); - } + ret = dev_err_probe(dev, PTR_ERR(opp_table->clk), + "%s: Couldn't find clock\n", __func__); goto err; } diff --git a/drivers/opp/debugfs.c b/drivers/opp/debugfs.c index 3fcc1f97f2d1..1b6e5c55c3ed 100644 --- a/drivers/opp/debugfs.c +++ b/drivers/opp/debugfs.c @@ -195,14 +195,18 @@ void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table) static void opp_migrate_dentry(struct opp_device *opp_dev, struct opp_table *opp_table) { - struct opp_device *new_dev; + struct opp_device *new_dev = NULL, *iter; const struct device *dev; struct dentry *dentry; /* Look for next opp-dev */ - list_for_each_entry(new_dev, &opp_table->dev_list, node) - if (new_dev != opp_dev) + list_for_each_entry(iter, &opp_table->dev_list, node) + if (iter != opp_dev) { + new_dev = iter; break; + } + + BUG_ON(!new_dev); /* new_dev is guaranteed to be valid here */ dev = new_dev->dev; diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 485ea980bde7..30394929d700 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -437,11 +437,11 @@ static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table) /* Checking only first OPP is sufficient */ np = of_get_next_available_child(opp_np, NULL); + of_node_put(opp_np); if (!np) { dev_err(dev, "OPP table empty\n"); return -EINVAL; } - of_node_put(opp_np); prop = of_find_property(np, "opp-peak-kBps", NULL); of_node_put(np); |