3098 lines · 81.5 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/device.h>#include <linux/export.h>#include <linux/pm_domain.h>#include <linux/regulator/consumer.h>#include <linux/slab.h>#include <linux/xarray.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); /* 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; /* OPP ID allocator */static DEFINE_XARRAY_ALLOC1(opp_configs); 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;} /* * Returns true if multiple clocks aren't there, else returns false with WARN. * * We don't force clk_count == 1 here as there are users who don't have a clock * representation in the OPP table and manage the clock configuration themselves * in an platform specific way. */static bool assert_single_clk(struct opp_table *opp_table){	return !WARN_ON(opp_table->clk_count > 1);} /** * 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_supplies() - Gets the supply information corresponding to an opp * @opp:	opp for which voltage has to be returned for * @supplies:	Placeholder for copying the supply information. * * Return: negative error number on failure, 0 otherwise on success after * setting @supplies. * * This can be used for devices with any number of power supplies. The caller * must ensure the @supplies array must contain space for each regulator. */int dev_pm_opp_get_supplies(struct dev_pm_opp *opp,			    struct dev_pm_opp_supply *supplies){	if (IS_ERR_OR_NULL(opp) || !supplies) {		pr_err("%s: Invalid parameters\n", __func__);		return -EINVAL;	} 	memcpy(supplies, opp->supplies,	       sizeof(*supplies) * opp->opp_table->regulator_count);	return 0;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_supplies); /** * dev_pm_opp_get_power() - Gets the power corresponding to an opp * @opp:	opp for which power has to be returned for * * Return: power in micro watt corresponding to the opp, else * return 0 * * This is useful only for devices with single power supply. */unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp){	unsigned long opp_power = 0;	int i; 	if (IS_ERR_OR_NULL(opp)) {		pr_err("%s: Invalid parameters\n", __func__);		return 0;	}	for (i = 0; i < opp->opp_table->regulator_count; i++)		opp_power += opp->supplies[i].u_watt; 	return opp_power;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_power); /** * dev_pm_opp_get_freq_indexed() - Gets the frequency corresponding to an *				   available opp with specified index * @opp: opp for which frequency has to be returned for * @index: index of the frequency within the required opp * * Return: frequency in hertz corresponding to the opp with specified index, * else return 0 */unsigned long dev_pm_opp_get_freq_indexed(struct dev_pm_opp *opp, u32 index){	if (IS_ERR_OR_NULL(opp) || index >= opp->opp_table->clk_count) {		pr_err("%s: Invalid parameters\n", __func__);		return 0;	} 	return opp->rates[index];}EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq_indexed); /** * 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 U32_MAX. */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 U32_MAX. */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; 	/* The required OPP table must belong to a genpd */	if (unlikely(!opp->opp_table->required_opp_tables[index]->is_genpd)) {		pr_err("%s: Performance state is only valid for genpds.\n", __func__);		return 0;	} 	return opp->required_opps[index]->level;}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); /* Helpers to read keys */static unsigned long _read_freq(struct dev_pm_opp *opp, int index){	return opp->rates[index];} static unsigned long _read_level(struct dev_pm_opp *opp, int index){	return opp->level;} static unsigned long _read_bw(struct dev_pm_opp *opp, int index){	return opp->bandwidth[index].peak;} /* Generic comparison helpers */static bool _compare_exact(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,			   unsigned long opp_key, unsigned long key){	if (opp_key == key) {		*opp = temp_opp;		return true;	} 	return false;} static bool _compare_ceil(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,			  unsigned long opp_key, unsigned long key){	if (opp_key >= key) {		*opp = temp_opp;		return true;	} 	return false;} static bool _compare_floor(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,			   unsigned long opp_key, unsigned long key){	if (opp_key > key)		return true; 	*opp = temp_opp;	return false;} /* Generic key finding helpers */static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table,		unsigned long *key, int index, bool available,		unsigned long (*read)(struct dev_pm_opp *opp, int index),		bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,

Lines 1–500 of 3098.