3122 lines · 84.0 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; 	guard(mutex)(&opp_table->lock); 	list_for_each_entry(opp_dev, &opp_table->dev_list, node)		if (opp_dev->dev == dev)			return true; 	return false;} 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))			return dev_pm_opp_get_opp_table_ref(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){	if (IS_ERR_OR_NULL(dev)) {		pr_err("%s: Invalid parameters\n", __func__);		return ERR_PTR(-EINVAL);	} 	guard(mutex)(&opp_table_lock);	return _find_opp_table_unlocked(dev);} /* * 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,			      unsigned int __always_unused index){	return !WARN_ON(opp_table->clk_count > 1);} /* * Returns true if clock table is large enough to contain the clock index. */static bool assert_clk_index(struct opp_table *opp_table,			     unsigned int index){	return opp_table->clk_count > index;} /* * Returns true if bandwidth table is large enough to contain the bandwidth index. */static bool assert_bandwidth_index(struct opp_table *opp_table,				   unsigned int index){	return opp_table->path_count > index;} /** * dev_pm_opp_get_bw() - Gets the bandwidth corresponding to an opp * @opp:	opp for which bandwidth has to be returned for * @peak:	select peak or average bandwidth * @index:	bandwidth index * * Return: bandwidth in kBps, else return 0 */unsigned long dev_pm_opp_get_bw(struct dev_pm_opp *opp, bool peak, int index){	if (IS_ERR_OR_NULL(opp)) {		pr_err("%s: Invalid parameters\n", __func__);		return 0;	} 	if (index >= opp->opp_table->path_count)		return 0; 	if (!opp->bandwidth)		return 0; 	return peak ? opp->bandwidth[index].peak : opp->bandwidth[index].avg;}EXPORT_SYMBOL_GPL(dev_pm_opp_get_bw); /** * 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 U32_MAX;	} 	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 __free(put_opp_table) =		_find_opp_table(dev); 	if (IS_ERR(opp_table))		return 0; 	return opp_table->clock_latency_ns_max;}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 dev_pm_opp *opp;	struct regulator *reg;	unsigned long latency_ns = 0;	int ret, i, count;	struct {		unsigned long min;		unsigned long max;	} *uV; 	struct opp_table *opp_table __free(put_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)		return 0; 	count = opp_table->regulator_count; 	uV = kmalloc_objs(*uV, count);	if (!uV)		return 0; 	scoped_guard(mutex, &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;			}		}	} 	/*	 * 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); 	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){	unsigned long freq = 0; 	struct opp_table *opp_table __free(put_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); 	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; 	guard(mutex)(&opp_table->lock); 	list_for_each_entry(opp, &opp_table->opp_list, node) {		if (opp->available)			count++;	} 	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 __free(put_opp_table) =		_find_opp_table(dev); 	if (IS_ERR(opp_table)) {		dev_dbg(dev, "%s: OPP table not found (%pe)\n",			__func__, opp_table);		return PTR_ERR(opp_table);	} 	return _get_opp_count(opp_table);}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;} static unsigned long _read_opp_key(struct dev_pm_opp *opp, int index,				   struct dev_pm_opp_key *key){	key->bw = opp->bandwidth ? opp->bandwidth[index].peak : 0;	key->freq = opp->rates[index];	key->level = opp->level; 	return true;} /* 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;

Lines 1–500 of 3122.