// SPDX-License-Identifier: GPL-2.0-only/* * Generic OPP Interface * * Copyright (C) 2009-2010 Texas Instruments Incorporated. * Nishanth Menon * Romit Dasgupta * Kevin Hilman */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/clk.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/device.h>#include <linux/export.h>#include <linux/pm_domain.h>#include <linux/regulator/consumer.h> #include "opp.h" /* * The root of the list of all opp-tables. All opp_table structures branch off * from here, with each opp_table containing the list of opps it supports in * various states of availability. */LIST_HEAD(opp_tables); /* OPP tables with uninitialized required OPPs */LIST_HEAD(lazy_opp_tables); /* Lock to allow exclusive modification to the device and opp lists */DEFINE_MUTEX(opp_table_lock);/* Flag indicating that opp_tables list is being updated at the moment */static bool opp_tables_busy; static bool _find_opp_dev(const struct device *dev, struct opp_table *opp_table){ struct opp_device *opp_dev; bool found = false; mutex_lock(&opp_table->lock); list_for_each_entry(opp_dev, &opp_table->dev_list, node) if (opp_dev->dev == dev) { found = true; break; } mutex_unlock(&opp_table->lock); return found;} static struct opp_table *_find_opp_table_unlocked(struct device *dev){ struct opp_table *opp_table; list_for_each_entry(opp_table, &opp_tables, node) { if (_find_opp_dev(dev, opp_table)) { _get_opp_table_kref(opp_table); return opp_table; } } return ERR_PTR(-ENODEV);} /** * _find_opp_table() - find opp_table struct using device pointer * @dev: device pointer used to lookup OPP table * * Search OPP table for one containing matching device. * * Return: pointer to 'struct opp_table' if found, otherwise -ENODEV or * -EINVAL based on type of error. * * The callers must call dev_pm_opp_put_opp_table() after the table is used. */struct opp_table *_find_opp_table(struct device *dev){ struct opp_table *opp_table; if (IS_ERR_OR_NULL(dev)) { pr_err("%s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } mutex_lock(&opp_table_lock); opp_table = _find_opp_table_unlocked(dev); mutex_unlock(&opp_table_lock); return opp_table;} /** * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an opp * @opp: opp for which voltage has to be returned for * * Return: voltage in micro volt corresponding to the opp, else * return 0 * * This is useful only for devices with single power supply. */unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp){ if (IS_ERR_OR_NULL(opp)) { pr_err("%s: Invalid parameters\n", __func__); return 0; } return opp->supplies[0].u_volt;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); /** * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp * @opp: opp for which frequency has to be returned for * * Return: frequency in hertz corresponding to the opp, else * return 0 */unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp){ if (IS_ERR_OR_NULL(opp)) { pr_err("%s: Invalid parameters\n", __func__); return 0; } return opp->rate;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); /** * dev_pm_opp_get_level() - Gets the level corresponding to an available opp * @opp: opp for which level value has to be returned for * * Return: level read from device tree corresponding to the opp, else * return 0. */unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp){ if (IS_ERR_OR_NULL(opp) || !opp->available) { pr_err("%s: Invalid parameters\n", __func__); return 0; } return opp->level;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_level); /** * dev_pm_opp_get_required_pstate() - Gets the required performance state * corresponding to an available opp * @opp: opp for which performance state has to be returned for * @index: index of the required opp * * Return: performance state read from device tree corresponding to the * required opp, else return 0. */unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp, unsigned int index){ if (IS_ERR_OR_NULL(opp) || !opp->available || index >= opp->opp_table->required_opp_count) { pr_err("%s: Invalid parameters\n", __func__); return 0; } /* required-opps not fully initialized yet */ if (lazy_linking_pending(opp->opp_table)) return 0; return opp->required_opps[index]->pstate;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_required_pstate); /** * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not * @opp: opp for which turbo mode is being verified * * Turbo OPPs are not for normal use, and can be enabled (under certain * conditions) for short duration of times to finish high throughput work * quickly. Running on them for longer times may overheat the chip. * * Return: true if opp is turbo opp, else false. */bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp){ if (IS_ERR_OR_NULL(opp) || !opp->available) { pr_err("%s: Invalid parameters\n", __func__); return false; } return opp->turbo;}EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); /** * dev_pm_opp_get_max_clock_latency() - Get max clock latency in nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max clock latency in nanoseconds. */unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev){ struct opp_table *opp_table; unsigned long clock_latency_ns; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return 0; clock_latency_ns = opp_table->clock_latency_ns_max; dev_pm_opp_put_opp_table(opp_table); return clock_latency_ns;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); /** * dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max voltage latency in nanoseconds. */unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev){ struct opp_table *opp_table; struct dev_pm_opp *opp; struct regulator *reg; unsigned long latency_ns = 0; int ret, i, count; struct { unsigned long min; unsigned long max; } *uV; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return 0; /* Regulator may not be required for the device */ if (!opp_table->regulators) goto put_opp_table; count = opp_table->regulator_count; uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL); if (!uV) goto put_opp_table; mutex_lock(&opp_table->lock); for (i = 0; i < count; i++) { uV[i].min = ~0; uV[i].max = 0; list_for_each_entry(opp, &opp_table->opp_list, node) { if (!opp->available) continue; if (opp->supplies[i].u_volt_min < uV[i].min) uV[i].min = opp->supplies[i].u_volt_min; if (opp->supplies[i].u_volt_max > uV[i].max) uV[i].max = opp->supplies[i].u_volt_max; } } mutex_unlock(&opp_table->lock); /* * The caller needs to ensure that opp_table (and hence the regulator) * isn't freed, while we are executing this routine. */ for (i = 0; i < count; i++) { reg = opp_table->regulators[i]; ret = regulator_set_voltage_time(reg, uV[i].min, uV[i].max); if (ret > 0) latency_ns += ret * 1000; } kfree(uV);put_opp_table: dev_pm_opp_put_opp_table(opp_table); return latency_ns;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_volt_latency); /** * dev_pm_opp_get_max_transition_latency() - Get max transition latency in * nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max transition latency, in nanoseconds, to * switch from one OPP to other. */unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev){ return dev_pm_opp_get_max_volt_latency(dev) + dev_pm_opp_get_max_clock_latency(dev);}EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_transition_latency); /** * dev_pm_opp_get_suspend_opp_freq() - Get frequency of suspend opp in Hz * @dev: device for which we do this operation * * Return: This function returns the frequency of the OPP marked as suspend_opp * if one is available, else returns 0; */unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev){ struct opp_table *opp_table; unsigned long freq = 0; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return 0; if (opp_table->suspend_opp && opp_table->suspend_opp->available) freq = dev_pm_opp_get_freq(opp_table->suspend_opp); dev_pm_opp_put_opp_table(opp_table); return freq;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq); int _get_opp_count(struct opp_table *opp_table){ struct dev_pm_opp *opp; int count = 0; mutex_lock(&opp_table->lock); list_for_each_entry(opp, &opp_table->opp_list, node) { if (opp->available) count++; } mutex_unlock(&opp_table->lock); return count;} /** * dev_pm_opp_get_opp_count() - Get number of opps available in the opp table * @dev: device for which we do this operation * * Return: This function returns the number of available opps if there are any, * else returns 0 if none or the corresponding error value. */int dev_pm_opp_get_opp_count(struct device *dev){ struct opp_table *opp_table; int count; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { count = PTR_ERR(opp_table); dev_dbg(dev, "%s: OPP table not found (%d)\n", __func__, count); return count; } count = _get_opp_count(opp_table); dev_pm_opp_put_opp_table(opp_table); return count;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); /** * dev_pm_opp_find_freq_exact() - search for an exact frequency * @dev: device for which we do this operation * @freq: frequency to search for * @available: true/false - match for available opp * * 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 * * Note: available is a modifier for the search. if available=true, then the * match is for exact matching frequency and is available in the stored OPP * table. if false, the match is for exact frequency which is not available. * * This provides a mechanism to enable an opp which is not available currently * or the opposite as well. * * The callers are required to call dev_pm_opp_put() for the returned OPP after * use. */struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available){ 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 == available && temp_opp->rate == freq) { 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_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;Lines 1–500 of 2938. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Generic OPP Interface * * Copyright (C) 2009-2010 Texas Instruments Incorporated. * Nishanth Menon * Romit Dasgupta * Kevin Hilman */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/clk.h> #include <linux/errno.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/export.h> #include <linux/pm_domain.h> #include <linux/regulator/consumer.h> #include "opp.h" /* * The root of the list of all opp-tables. All opp_table structures branch off * from here, with each opp_table containing the list of opps it supports in * various states of availability. */ LIST_HEAD(opp_tables); /* OPP tables with uninitialized required OPPs */ LIST_HEAD(lazy_opp_tables); /* Lock to allow exclusive modification to the device and opp lists */ DEFINE_MUTEX(opp_table_lock); /* Flag indicating that opp_tables list is being updated at the moment */ static bool opp_tables_busy; static bool _find_opp_dev(const struct device *dev, struct opp_table *opp_table) { struct opp_device *opp_dev; bool found = false; mutex_lock(&opp_table->lock); list_for_each_entry(opp_dev, &opp_table->dev_list, node) if (opp_dev->dev == dev) { found = true; break; } mutex_unlock(&opp_table->lock); return found; } static struct opp_table *_find_opp_table_unlocked(struct device *dev) { struct opp_table *opp_table; list_for_each_entry(opp_table, &opp_tables, node) { if (_find_opp_dev(dev, opp_table)) { _get_opp_table_kref(opp_table); return opp_table; } } return ERR_PTR(-ENODEV); } /** * _find_opp_table() - find opp_table struct using device pointer * @dev: device pointer used to lookup OPP table * * Search OPP table for one containing matching device. * * Return: pointer to 'struct opp_table' if found, otherwise -ENODEV or * -EINVAL based on type of error. * * The callers must call dev_pm_opp_put_opp_table() after the table is used. */ struct opp_table *_find_opp_table(struct device *dev) { struct opp_table *opp_table; if (IS_ERR_OR_NULL(dev)) { pr_err("%s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } mutex_lock(&opp_table_lock); opp_table = _find_opp_table_unlocked(dev); mutex_unlock(&opp_table_lock); return opp_table; } /** * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an opp * @opp: opp for which voltage has to be returned for * * Return: voltage in micro volt corresponding to the opp, else * return 0 * * This is useful only for devices with single power supply. */ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) { if (IS_ERR_OR_NULL(opp)) { pr_err("%s: Invalid parameters\n", __func__); return 0; } return opp->supplies[0].u_volt; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); /** * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp * @opp: opp for which frequency has to be returned for * * Return: frequency in hertz corresponding to the opp, else * return 0 */ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) { if (IS_ERR_OR_NULL(opp)) { pr_err("%s: Invalid parameters\n", __func__); return 0; } return opp->rate; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); /** * dev_pm_opp_get_level() - Gets the level corresponding to an available opp * @opp: opp for which level value has to be returned for * * Return: level read from device tree corresponding to the opp, else * return 0. */ unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp) { if (IS_ERR_OR_NULL(opp) || !opp->available) { pr_err("%s: Invalid parameters\n", __func__); return 0; } return opp->level; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_level); /** * dev_pm_opp_get_required_pstate() - Gets the required performance state * corresponding to an available opp * @opp: opp for which performance state has to be returned for * @index: index of the required opp * * Return: performance state read from device tree corresponding to the * required opp, else return 0. */ unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp, unsigned int index) { if (IS_ERR_OR_NULL(opp) || !opp->available || index >= opp->opp_table->required_opp_count) { pr_err("%s: Invalid parameters\n", __func__); return 0; } /* required-opps not fully initialized yet */ if (lazy_linking_pending(opp->opp_table)) return 0; return opp->required_opps[index]->pstate; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_required_pstate); /** * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not * @opp: opp for which turbo mode is being verified * * Turbo OPPs are not for normal use, and can be enabled (under certain * conditions) for short duration of times to finish high throughput work * quickly. Running on them for longer times may overheat the chip. * * Return: true if opp is turbo opp, else false. */ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) { if (IS_ERR_OR_NULL(opp) || !opp->available) { pr_err("%s: Invalid parameters\n", __func__); return false; } return opp->turbo; } EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); /** * dev_pm_opp_get_max_clock_latency() - Get max clock latency in nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max clock latency in nanoseconds. */ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) { struct opp_table *opp_table; unsigned long clock_latency_ns; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return 0; clock_latency_ns = opp_table->clock_latency_ns_max; dev_pm_opp_put_opp_table(opp_table); return clock_latency_ns; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); /** * dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max voltage latency in nanoseconds. */ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) { struct opp_table *opp_table; struct dev_pm_opp *opp; struct regulator *reg; unsigned long latency_ns = 0; int ret, i, count; struct { unsigned long min; unsigned long max; } *uV; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return 0; /* Regulator may not be required for the device */ if (!opp_table->regulators) goto put_opp_table; count = opp_table->regulator_count; uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL); if (!uV) goto put_opp_table; mutex_lock(&opp_table->lock); for (i = 0; i < count; i++) { uV[i].min = ~0; uV[i].max = 0; list_for_each_entry(opp, &opp_table->opp_list, node) { if (!opp->available) continue; if (opp->supplies[i].u_volt_min < uV[i].min) uV[i].min = opp->supplies[i].u_volt_min; if (opp->supplies[i].u_volt_max > uV[i].max) uV[i].max = opp->supplies[i].u_volt_max; } } mutex_unlock(&opp_table->lock); /* * The caller needs to ensure that opp_table (and hence the regulator) * isn't freed, while we are executing this routine. */ for (i = 0; i < count; i++) { reg = opp_table->regulators[i]; ret = regulator_set_voltage_time(reg, uV[i].min, uV[i].max); if (ret > 0) latency_ns += ret * 1000; } kfree(uV); put_opp_table: dev_pm_opp_put_opp_table(opp_table); return latency_ns; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_volt_latency); /** * dev_pm_opp_get_max_transition_latency() - Get max transition latency in * nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max transition latency, in nanoseconds, to * switch from one OPP to other. */ unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev) { return dev_pm_opp_get_max_volt_latency(dev) + dev_pm_opp_get_max_clock_latency(dev); } EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_transition_latency); /** * dev_pm_opp_get_suspend_opp_freq() - Get frequency of suspend opp in Hz * @dev: device for which we do this operation * * Return: This function returns the frequency of the OPP marked as suspend_opp * if one is available, else returns 0; */ unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev) { struct opp_table *opp_table; unsigned long freq = 0; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return 0; if (opp_table->suspend_opp && opp_table->suspend_opp->available) freq = dev_pm_opp_get_freq(opp_table->suspend_opp); dev_pm_opp_put_opp_table(opp_table); return freq; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq); int _get_opp_count(struct opp_table *opp_table) { struct dev_pm_opp *opp; int count = 0; mutex_lock(&opp_table->lock); list_for_each_entry(opp, &opp_table->opp_list, node) { if (opp->available) count++; } mutex_unlock(&opp_table->lock); return count; } /** * dev_pm_opp_get_opp_count() - Get number of opps available in the opp table * @dev: device for which we do this operation * * Return: This function returns the number of available opps if there are any, * else returns 0 if none or the corresponding error value. */ int dev_pm_opp_get_opp_count(struct device *dev) { struct opp_table *opp_table; int count; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { count = PTR_ERR(opp_table); dev_dbg(dev, "%s: OPP table not found (%d)\n", __func__, count); return count; } count = _get_opp_count(opp_table); dev_pm_opp_put_opp_table(opp_table); return count; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); /** * dev_pm_opp_find_freq_exact() - search for an exact frequency * @dev: device for which we do this operation * @freq: frequency to search for * @available: true/false - match for available opp * * 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 * * Note: available is a modifier for the search. if available=true, then the * match is for exact matching frequency and is available in the stored OPP * table. if false, the match is for exact frequency which is not available. * * This provides a mechanism to enable an opp which is not available currently * or the opposite as well. * * The callers are required to call dev_pm_opp_put() for the returned OPP after * use. */ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) { 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 == available && temp_opp->rate == freq) { 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_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;