2938 lines · 76.7 KB · C
RawDownload
// 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.