| /* | 
 |  * Generic OPP OF helpers | 
 |  * | 
 |  * Copyright (C) 2009-2010 Texas Instruments Incorporated. | 
 |  *	Nishanth Menon | 
 |  *	Romit Dasgupta | 
 |  *	Kevin Hilman | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License version 2 as | 
 |  * published by the Free Software Foundation. | 
 |  */ | 
 |  | 
 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
 |  | 
 | #include <linux/cpu.h> | 
 | #include <linux/errno.h> | 
 | #include <linux/device.h> | 
 | #include <linux/of_device.h> | 
 | #include <linux/pm_domain.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/export.h> | 
 |  | 
 | #include "opp.h" | 
 |  | 
 | static struct opp_table *_managed_opp(const struct device_node *np) | 
 | { | 
 | 	struct opp_table *opp_table, *managed_table = NULL; | 
 |  | 
 | 	mutex_lock(&opp_table_lock); | 
 |  | 
 | 	list_for_each_entry(opp_table, &opp_tables, node) { | 
 | 		if (opp_table->np == np) { | 
 | 			/* | 
 | 			 * Multiple devices can point to the same OPP table and | 
 | 			 * so will have same node-pointer, np. | 
 | 			 * | 
 | 			 * But the OPPs will be considered as shared only if the | 
 | 			 * OPP table contains a "opp-shared" property. | 
 | 			 */ | 
 | 			if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) { | 
 | 				_get_opp_table_kref(opp_table); | 
 | 				managed_table = opp_table; | 
 | 			} | 
 |  | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	mutex_unlock(&opp_table_lock); | 
 |  | 
 | 	return managed_table; | 
 | } | 
 |  | 
 | void _of_init_opp_table(struct opp_table *opp_table, struct device *dev) | 
 | { | 
 | 	struct device_node *np; | 
 |  | 
 | 	/* | 
 | 	 * Only required for backward compatibility with v1 bindings, but isn't | 
 | 	 * harmful for other cases. And so we do it unconditionally. | 
 | 	 */ | 
 | 	np = of_node_get(dev->of_node); | 
 | 	if (np) { | 
 | 		u32 val; | 
 |  | 
 | 		if (!of_property_read_u32(np, "clock-latency", &val)) | 
 | 			opp_table->clock_latency_ns_max = val; | 
 | 		of_property_read_u32(np, "voltage-tolerance", | 
 | 				     &opp_table->voltage_tolerance_v1); | 
 | 		of_node_put(np); | 
 | 	} | 
 | } | 
 |  | 
 | static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table, | 
 | 			      struct device_node *np) | 
 | { | 
 | 	unsigned int count = opp_table->supported_hw_count; | 
 | 	u32 version; | 
 | 	int ret; | 
 |  | 
 | 	if (!opp_table->supported_hw) { | 
 | 		/* | 
 | 		 * In the case that no supported_hw has been set by the | 
 | 		 * platform but there is an opp-supported-hw value set for | 
 | 		 * an OPP then the OPP should not be enabled as there is | 
 | 		 * no way to see if the hardware supports it. | 
 | 		 */ | 
 | 		if (of_find_property(np, "opp-supported-hw", NULL)) | 
 | 			return false; | 
 | 		else | 
 | 			return true; | 
 | 	} | 
 |  | 
 | 	while (count--) { | 
 | 		ret = of_property_read_u32_index(np, "opp-supported-hw", count, | 
 | 						 &version); | 
 | 		if (ret) { | 
 | 			dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n", | 
 | 				 __func__, count, ret); | 
 | 			return false; | 
 | 		} | 
 |  | 
 | 		/* Both of these are bitwise masks of the versions */ | 
 | 		if (!(version & opp_table->supported_hw[count])) | 
 | 			return false; | 
 | 	} | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, | 
 | 			      struct opp_table *opp_table) | 
 | { | 
 | 	u32 *microvolt, *microamp = NULL; | 
 | 	int supplies, vcount, icount, ret, i, j; | 
 | 	struct property *prop = NULL; | 
 | 	char name[NAME_MAX]; | 
 |  | 
 | 	supplies = opp_table->regulator_count ? opp_table->regulator_count : 1; | 
 |  | 
 | 	/* Search for "opp-microvolt-<name>" */ | 
 | 	if (opp_table->prop_name) { | 
 | 		snprintf(name, sizeof(name), "opp-microvolt-%s", | 
 | 			 opp_table->prop_name); | 
 | 		prop = of_find_property(opp->np, name, NULL); | 
 | 	} | 
 |  | 
 | 	if (!prop) { | 
 | 		/* Search for "opp-microvolt" */ | 
 | 		sprintf(name, "opp-microvolt"); | 
 | 		prop = of_find_property(opp->np, name, NULL); | 
 |  | 
 | 		/* Missing property isn't a problem, but an invalid entry is */ | 
 | 		if (!prop) { | 
 | 			if (!opp_table->regulator_count) | 
 | 				return 0; | 
 |  | 
 | 			dev_err(dev, "%s: opp-microvolt missing although OPP managing regulators\n", | 
 | 				__func__); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	vcount = of_property_count_u32_elems(opp->np, name); | 
 | 	if (vcount < 0) { | 
 | 		dev_err(dev, "%s: Invalid %s property (%d)\n", | 
 | 			__func__, name, vcount); | 
 | 		return vcount; | 
 | 	} | 
 |  | 
 | 	/* There can be one or three elements per supply */ | 
 | 	if (vcount != supplies && vcount != supplies * 3) { | 
 | 		dev_err(dev, "%s: Invalid number of elements in %s property (%d) with supplies (%d)\n", | 
 | 			__func__, name, vcount, supplies); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	microvolt = kmalloc_array(vcount, sizeof(*microvolt), GFP_KERNEL); | 
 | 	if (!microvolt) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	ret = of_property_read_u32_array(opp->np, name, microvolt, vcount); | 
 | 	if (ret) { | 
 | 		dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret); | 
 | 		ret = -EINVAL; | 
 | 		goto free_microvolt; | 
 | 	} | 
 |  | 
 | 	/* Search for "opp-microamp-<name>" */ | 
 | 	prop = NULL; | 
 | 	if (opp_table->prop_name) { | 
 | 		snprintf(name, sizeof(name), "opp-microamp-%s", | 
 | 			 opp_table->prop_name); | 
 | 		prop = of_find_property(opp->np, name, NULL); | 
 | 	} | 
 |  | 
 | 	if (!prop) { | 
 | 		/* Search for "opp-microamp" */ | 
 | 		sprintf(name, "opp-microamp"); | 
 | 		prop = of_find_property(opp->np, name, NULL); | 
 | 	} | 
 |  | 
 | 	if (prop) { | 
 | 		icount = of_property_count_u32_elems(opp->np, name); | 
 | 		if (icount < 0) { | 
 | 			dev_err(dev, "%s: Invalid %s property (%d)\n", __func__, | 
 | 				name, icount); | 
 | 			ret = icount; | 
 | 			goto free_microvolt; | 
 | 		} | 
 |  | 
 | 		if (icount != supplies) { | 
 | 			dev_err(dev, "%s: Invalid number of elements in %s property (%d) with supplies (%d)\n", | 
 | 				__func__, name, icount, supplies); | 
 | 			ret = -EINVAL; | 
 | 			goto free_microvolt; | 
 | 		} | 
 |  | 
 | 		microamp = kmalloc_array(icount, sizeof(*microamp), GFP_KERNEL); | 
 | 		if (!microamp) { | 
 | 			ret = -EINVAL; | 
 | 			goto free_microvolt; | 
 | 		} | 
 |  | 
 | 		ret = of_property_read_u32_array(opp->np, name, microamp, | 
 | 						 icount); | 
 | 		if (ret) { | 
 | 			dev_err(dev, "%s: error parsing %s: %d\n", __func__, | 
 | 				name, ret); | 
 | 			ret = -EINVAL; | 
 | 			goto free_microamp; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	for (i = 0, j = 0; i < supplies; i++) { | 
 | 		opp->supplies[i].u_volt = microvolt[j++]; | 
 |  | 
 | 		if (vcount == supplies) { | 
 | 			opp->supplies[i].u_volt_min = opp->supplies[i].u_volt; | 
 | 			opp->supplies[i].u_volt_max = opp->supplies[i].u_volt; | 
 | 		} else { | 
 | 			opp->supplies[i].u_volt_min = microvolt[j++]; | 
 | 			opp->supplies[i].u_volt_max = microvolt[j++]; | 
 | 		} | 
 |  | 
 | 		if (microamp) | 
 | 			opp->supplies[i].u_amp = microamp[i]; | 
 | 	} | 
 |  | 
 | free_microamp: | 
 | 	kfree(microamp); | 
 | free_microvolt: | 
 | 	kfree(microvolt); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | /** | 
 |  * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT | 
 |  *				  entries | 
 |  * @dev:	device pointer used to lookup OPP table. | 
 |  * | 
 |  * Free OPPs created using static entries present in DT. | 
 |  */ | 
 | void dev_pm_opp_of_remove_table(struct device *dev) | 
 | { | 
 | 	_dev_pm_opp_find_and_remove_table(dev, false); | 
 | } | 
 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); | 
 |  | 
 | /* Returns opp descriptor node for a device node, caller must | 
 |  * do of_node_put() */ | 
 | static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np, | 
 | 						     int index) | 
 | { | 
 | 	/* "operating-points-v2" can be an array for power domain providers */ | 
 | 	return of_parse_phandle(np, "operating-points-v2", index); | 
 | } | 
 |  | 
 | /* Returns opp descriptor node for a device, caller must do of_node_put() */ | 
 | struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev) | 
 | { | 
 | 	return _opp_of_get_opp_desc_node(dev->of_node, 0); | 
 | } | 
 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node); | 
 |  | 
 | /** | 
 |  * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) | 
 |  * @opp_table:	OPP table | 
 |  * @dev:	device for which we do this operation | 
 |  * @np:		device node | 
 |  * | 
 |  * This function adds an opp definition to the opp table and returns status. The | 
 |  * opp can be controlled using dev_pm_opp_enable/disable functions and may be | 
 |  * removed by dev_pm_opp_remove. | 
 |  * | 
 |  * Return: | 
 |  * 0		On success OR | 
 |  *		Duplicate OPPs (both freq and volt are same) and opp->available | 
 |  * -EEXIST	Freq are same and volt are different OR | 
 |  *		Duplicate OPPs (both freq and volt are same) and !opp->available | 
 |  * -ENOMEM	Memory allocation failure | 
 |  * -EINVAL	Failed parsing the OPP node | 
 |  */ | 
 | static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev, | 
 | 			      struct device_node *np) | 
 | { | 
 | 	struct dev_pm_opp *new_opp; | 
 | 	u64 rate = 0; | 
 | 	u32 val; | 
 | 	int ret; | 
 | 	bool rate_not_available = false; | 
 |  | 
 | 	new_opp = _opp_allocate(opp_table); | 
 | 	if (!new_opp) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	ret = of_property_read_u64(np, "opp-hz", &rate); | 
 | 	if (ret < 0) { | 
 | 		/* "opp-hz" is optional for devices like power domains. */ | 
 | 		if (!of_find_property(dev->of_node, "#power-domain-cells", | 
 | 				      NULL)) { | 
 | 			dev_err(dev, "%s: opp-hz not found\n", __func__); | 
 | 			goto free_opp; | 
 | 		} | 
 |  | 
 | 		rate_not_available = true; | 
 | 	} else { | 
 | 		/* | 
 | 		 * Rate is defined as an unsigned long in clk API, and so | 
 | 		 * casting explicitly to its type. Must be fixed once rate is 64 | 
 | 		 * bit guaranteed in clk API. | 
 | 		 */ | 
 | 		new_opp->rate = (unsigned long)rate; | 
 | 	} | 
 |  | 
 | 	/* Check if the OPP supports hardware's hierarchy of versions or not */ | 
 | 	if (!_opp_is_supported(dev, opp_table, np)) { | 
 | 		dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); | 
 | 		goto free_opp; | 
 | 	} | 
 |  | 
 | 	new_opp->turbo = of_property_read_bool(np, "turbo-mode"); | 
 |  | 
 | 	new_opp->np = np; | 
 | 	new_opp->dynamic = false; | 
 | 	new_opp->available = true; | 
 |  | 
 | 	if (!of_property_read_u32(np, "clock-latency-ns", &val)) | 
 | 		new_opp->clock_latency_ns = val; | 
 |  | 
 | 	new_opp->pstate = of_genpd_opp_to_performance_state(dev, np); | 
 |  | 
 | 	ret = opp_parse_supplies(new_opp, dev, opp_table); | 
 | 	if (ret) | 
 | 		goto free_opp; | 
 |  | 
 | 	ret = _opp_add(dev, new_opp, opp_table, rate_not_available); | 
 | 	if (ret) { | 
 | 		/* Don't return error for duplicate OPPs */ | 
 | 		if (ret == -EBUSY) | 
 | 			ret = 0; | 
 | 		goto free_opp; | 
 | 	} | 
 |  | 
 | 	/* OPP to select on device suspend */ | 
 | 	if (of_property_read_bool(np, "opp-suspend")) { | 
 | 		if (opp_table->suspend_opp) { | 
 | 			dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", | 
 | 				 __func__, opp_table->suspend_opp->rate, | 
 | 				 new_opp->rate); | 
 | 		} else { | 
 | 			new_opp->suspend = true; | 
 | 			opp_table->suspend_opp = new_opp; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max) | 
 | 		opp_table->clock_latency_ns_max = new_opp->clock_latency_ns; | 
 |  | 
 | 	pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", | 
 | 		 __func__, new_opp->turbo, new_opp->rate, | 
 | 		 new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min, | 
 | 		 new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns); | 
 |  | 
 | 	/* | 
 | 	 * Notify the changes in the availability of the operable | 
 | 	 * frequency/voltage list. | 
 | 	 */ | 
 | 	blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp); | 
 | 	return 0; | 
 |  | 
 | free_opp: | 
 | 	_opp_free(new_opp); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* Initializes OPP tables based on new bindings */ | 
 | static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) | 
 | { | 
 | 	struct device_node *np; | 
 | 	struct opp_table *opp_table; | 
 | 	int ret = 0, count = 0, pstate_count = 0; | 
 | 	struct dev_pm_opp *opp; | 
 |  | 
 | 	opp_table = _managed_opp(opp_np); | 
 | 	if (opp_table) { | 
 | 		/* OPPs are already managed */ | 
 | 		if (!_add_opp_dev(dev, opp_table)) | 
 | 			ret = -ENOMEM; | 
 | 		goto put_opp_table; | 
 | 	} | 
 |  | 
 | 	opp_table = dev_pm_opp_get_opp_table(dev); | 
 | 	if (!opp_table) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	/* We have opp-table node now, iterate over it and add OPPs */ | 
 | 	for_each_available_child_of_node(opp_np, np) { | 
 | 		count++; | 
 |  | 
 | 		ret = _opp_add_static_v2(opp_table, dev, np); | 
 | 		if (ret) { | 
 | 			dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, | 
 | 				ret); | 
 | 			_dev_pm_opp_remove_table(opp_table, dev, false); | 
 | 			of_node_put(np); | 
 | 			goto put_opp_table; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* There should be one of more OPP defined */ | 
 | 	if (WARN_ON(!count)) { | 
 | 		ret = -ENOENT; | 
 | 		goto put_opp_table; | 
 | 	} | 
 |  | 
 | 	list_for_each_entry(opp, &opp_table->opp_list, node) | 
 | 		pstate_count += !!opp->pstate; | 
 |  | 
 | 	/* Either all or none of the nodes shall have performance state set */ | 
 | 	if (pstate_count && pstate_count != count) { | 
 | 		dev_err(dev, "Not all nodes have performance state set (%d: %d)\n", | 
 | 			count, pstate_count); | 
 | 		ret = -ENOENT; | 
 | 		_dev_pm_opp_remove_table(opp_table, dev, false); | 
 | 		goto put_opp_table; | 
 | 	} | 
 |  | 
 | 	if (pstate_count) | 
 | 		opp_table->genpd_performance_state = true; | 
 |  | 
 | 	opp_table->np = opp_np; | 
 | 	if (of_property_read_bool(opp_np, "opp-shared")) | 
 | 		opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED; | 
 | 	else | 
 | 		opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE; | 
 |  | 
 | put_opp_table: | 
 | 	dev_pm_opp_put_opp_table(opp_table); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* Initializes OPP tables based on old-deprecated bindings */ | 
 | static int _of_add_opp_table_v1(struct device *dev) | 
 | { | 
 | 	struct opp_table *opp_table; | 
 | 	const struct property *prop; | 
 | 	const __be32 *val; | 
 | 	int nr, ret = 0; | 
 |  | 
 | 	prop = of_find_property(dev->of_node, "operating-points", NULL); | 
 | 	if (!prop) | 
 | 		return -ENODEV; | 
 | 	if (!prop->value) | 
 | 		return -ENODATA; | 
 |  | 
 | 	/* | 
 | 	 * Each OPP is a set of tuples consisting of frequency and | 
 | 	 * voltage like <freq-kHz vol-uV>. | 
 | 	 */ | 
 | 	nr = prop->length / sizeof(u32); | 
 | 	if (nr % 2) { | 
 | 		dev_err(dev, "%s: Invalid OPP table\n", __func__); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	opp_table = dev_pm_opp_get_opp_table(dev); | 
 | 	if (!opp_table) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	val = prop->value; | 
 | 	while (nr) { | 
 | 		unsigned long freq = be32_to_cpup(val++) * 1000; | 
 | 		unsigned long volt = be32_to_cpup(val++); | 
 |  | 
 | 		ret = _opp_add_v1(opp_table, dev, freq, volt, false); | 
 | 		if (ret) { | 
 | 			dev_err(dev, "%s: Failed to add OPP %ld (%d)\n", | 
 | 				__func__, freq, ret); | 
 | 			_dev_pm_opp_remove_table(opp_table, dev, false); | 
 | 			break; | 
 | 		} | 
 | 		nr -= 2; | 
 | 	} | 
 |  | 
 | 	dev_pm_opp_put_opp_table(opp_table); | 
 | 	return ret; | 
 | } | 
 |  | 
 | /** | 
 |  * dev_pm_opp_of_add_table() - Initialize opp table from device tree | 
 |  * @dev:	device pointer used to lookup OPP table. | 
 |  * | 
 |  * Register the initial OPP table with the OPP library for given device. | 
 |  * | 
 |  * Return: | 
 |  * 0		On success OR | 
 |  *		Duplicate OPPs (both freq and volt are same) and opp->available | 
 |  * -EEXIST	Freq are same and volt are different OR | 
 |  *		Duplicate OPPs (both freq and volt are same) and !opp->available | 
 |  * -ENOMEM	Memory allocation failure | 
 |  * -ENODEV	when 'operating-points' property is not found or is invalid data | 
 |  *		in device node. | 
 |  * -ENODATA	when empty 'operating-points' property is found | 
 |  * -EINVAL	when invalid entries are found in opp-v2 table | 
 |  */ | 
 | int dev_pm_opp_of_add_table(struct device *dev) | 
 | { | 
 | 	struct device_node *opp_np; | 
 | 	int ret; | 
 |  | 
 | 	/* | 
 | 	 * OPPs have two version of bindings now. The older one is deprecated, | 
 | 	 * try for the new binding first. | 
 | 	 */ | 
 | 	opp_np = dev_pm_opp_of_get_opp_desc_node(dev); | 
 | 	if (!opp_np) { | 
 | 		/* | 
 | 		 * Try old-deprecated bindings for backward compatibility with | 
 | 		 * older dtbs. | 
 | 		 */ | 
 | 		return _of_add_opp_table_v1(dev); | 
 | 	} | 
 |  | 
 | 	ret = _of_add_opp_table_v2(dev, opp_np); | 
 | 	of_node_put(opp_np); | 
 |  | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); | 
 |  | 
 | /** | 
 |  * dev_pm_opp_of_add_table_indexed() - Initialize indexed opp table from device tree | 
 |  * @dev:	device pointer used to lookup OPP table. | 
 |  * @index:	Index number. | 
 |  * | 
 |  * Register the initial OPP table with the OPP library for given device only | 
 |  * using the "operating-points-v2" property. | 
 |  * | 
 |  * Return: | 
 |  * 0		On success OR | 
 |  *		Duplicate OPPs (both freq and volt are same) and opp->available | 
 |  * -EEXIST	Freq are same and volt are different OR | 
 |  *		Duplicate OPPs (both freq and volt are same) and !opp->available | 
 |  * -ENOMEM	Memory allocation failure | 
 |  * -ENODEV	when 'operating-points' property is not found or is invalid data | 
 |  *		in device node. | 
 |  * -ENODATA	when empty 'operating-points' property is found | 
 |  * -EINVAL	when invalid entries are found in opp-v2 table | 
 |  */ | 
 | int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) | 
 | { | 
 | 	struct device_node *opp_np; | 
 | 	int ret, count; | 
 |  | 
 | again: | 
 | 	opp_np = _opp_of_get_opp_desc_node(dev->of_node, index); | 
 | 	if (!opp_np) { | 
 | 		/* | 
 | 		 * If only one phandle is present, then the same OPP table | 
 | 		 * applies for all index requests. | 
 | 		 */ | 
 | 		count = of_count_phandle_with_args(dev->of_node, | 
 | 						   "operating-points-v2", NULL); | 
 | 		if (count == 1 && index) { | 
 | 			index = 0; | 
 | 			goto again; | 
 | 		} | 
 |  | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	ret = _of_add_opp_table_v2(dev, opp_np); | 
 | 	of_node_put(opp_np); | 
 |  | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed); | 
 |  | 
 | /* CPU device specific helpers */ | 
 |  | 
 | /** | 
 |  * dev_pm_opp_of_cpumask_remove_table() - Removes OPP table for @cpumask | 
 |  * @cpumask:	cpumask for which OPP table needs to be removed | 
 |  * | 
 |  * This removes the OPP tables for CPUs present in the @cpumask. | 
 |  * This should be used only to remove static entries created from DT. | 
 |  */ | 
 | void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask) | 
 | { | 
 | 	_dev_pm_opp_cpumask_remove_table(cpumask, true); | 
 | } | 
 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); | 
 |  | 
 | /** | 
 |  * dev_pm_opp_of_cpumask_add_table() - Adds OPP table for @cpumask | 
 |  * @cpumask:	cpumask for which OPP table needs to be added. | 
 |  * | 
 |  * This adds the OPP tables for CPUs present in the @cpumask. | 
 |  */ | 
 | int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask) | 
 | { | 
 | 	struct device *cpu_dev; | 
 | 	int cpu, ret = 0; | 
 |  | 
 | 	WARN_ON(cpumask_empty(cpumask)); | 
 |  | 
 | 	for_each_cpu(cpu, cpumask) { | 
 | 		cpu_dev = get_cpu_device(cpu); | 
 | 		if (!cpu_dev) { | 
 | 			pr_err("%s: failed to get cpu%d device\n", __func__, | 
 | 			       cpu); | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		ret = dev_pm_opp_of_add_table(cpu_dev); | 
 | 		if (ret) { | 
 | 			/* | 
 | 			 * OPP may get registered dynamically, don't print error | 
 | 			 * message here. | 
 | 			 */ | 
 | 			pr_debug("%s: couldn't find opp table for cpu:%d, %d\n", | 
 | 				 __func__, cpu, ret); | 
 |  | 
 | 			/* Free all other OPPs */ | 
 | 			dev_pm_opp_of_cpumask_remove_table(cpumask); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); | 
 |  | 
 | /* | 
 |  * Works only for OPP v2 bindings. | 
 |  * | 
 |  * Returns -ENOENT if operating-points-v2 bindings aren't supported. | 
 |  */ | 
 | /** | 
 |  * dev_pm_opp_of_get_sharing_cpus() - Get cpumask of CPUs sharing OPPs with | 
 |  *				      @cpu_dev using operating-points-v2 | 
 |  *				      bindings. | 
 |  * | 
 |  * @cpu_dev:	CPU device for which we do this operation | 
 |  * @cpumask:	cpumask to update with information of sharing CPUs | 
 |  * | 
 |  * This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev. | 
 |  * | 
 |  * Returns -ENOENT if operating-points-v2 isn't present for @cpu_dev. | 
 |  */ | 
 | int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, | 
 | 				   struct cpumask *cpumask) | 
 | { | 
 | 	struct device_node *np, *tmp_np, *cpu_np; | 
 | 	int cpu, ret = 0; | 
 |  | 
 | 	/* Get OPP descriptor node */ | 
 | 	np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); | 
 | 	if (!np) { | 
 | 		dev_dbg(cpu_dev, "%s: Couldn't find opp node.\n", __func__); | 
 | 		return -ENOENT; | 
 | 	} | 
 |  | 
 | 	cpumask_set_cpu(cpu_dev->id, cpumask); | 
 |  | 
 | 	/* OPPs are shared ? */ | 
 | 	if (!of_property_read_bool(np, "opp-shared")) | 
 | 		goto put_cpu_node; | 
 |  | 
 | 	for_each_possible_cpu(cpu) { | 
 | 		if (cpu == cpu_dev->id) | 
 | 			continue; | 
 |  | 
 | 		cpu_np = of_cpu_device_node_get(cpu); | 
 | 		if (!cpu_np) { | 
 | 			dev_err(cpu_dev, "%s: failed to get cpu%d node\n", | 
 | 				__func__, cpu); | 
 | 			ret = -ENOENT; | 
 | 			goto put_cpu_node; | 
 | 		} | 
 |  | 
 | 		/* Get OPP descriptor node */ | 
 | 		tmp_np = _opp_of_get_opp_desc_node(cpu_np, 0); | 
 | 		of_node_put(cpu_np); | 
 | 		if (!tmp_np) { | 
 | 			pr_err("%pOF: Couldn't find opp node\n", cpu_np); | 
 | 			ret = -ENOENT; | 
 | 			goto put_cpu_node; | 
 | 		} | 
 |  | 
 | 		/* CPUs are sharing opp node */ | 
 | 		if (np == tmp_np) | 
 | 			cpumask_set_cpu(cpu, cpumask); | 
 |  | 
 | 		of_node_put(tmp_np); | 
 | 	} | 
 |  | 
 | put_cpu_node: | 
 | 	of_node_put(np); | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); | 
 |  | 
 | /** | 
 |  * of_dev_pm_opp_find_required_opp() - Search for required OPP. | 
 |  * @dev: The device whose OPP node is referenced by the 'np' DT node. | 
 |  * @np: Node that contains the "required-opps" property. | 
 |  * | 
 |  * Returns the OPP of the device 'dev', whose phandle is present in the "np" | 
 |  * node. Although the "required-opps" property supports having multiple | 
 |  * phandles, this helper routine only parses the very first phandle in the list. | 
 |  * | 
 |  * Return: Matching opp, else returns ERR_PTR in case of error and should be | 
 |  * handled using IS_ERR. | 
 |  * | 
 |  * The callers are required to call dev_pm_opp_put() for the returned OPP after | 
 |  * use. | 
 |  */ | 
 | struct dev_pm_opp *of_dev_pm_opp_find_required_opp(struct device *dev, | 
 | 						   struct device_node *np) | 
 | { | 
 | 	struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ENODEV); | 
 | 	struct device_node *required_np; | 
 | 	struct opp_table *opp_table; | 
 |  | 
 | 	opp_table = _find_opp_table(dev); | 
 | 	if (IS_ERR(opp_table)) | 
 | 		return ERR_CAST(opp_table); | 
 |  | 
 | 	required_np = of_parse_phandle(np, "required-opps", 0); | 
 | 	if (unlikely(!required_np)) { | 
 | 		dev_err(dev, "Unable to parse required-opps\n"); | 
 | 		goto put_opp_table; | 
 | 	} | 
 |  | 
 | 	mutex_lock(&opp_table->lock); | 
 |  | 
 | 	list_for_each_entry(temp_opp, &opp_table->opp_list, node) { | 
 | 		if (temp_opp->available && temp_opp->np == required_np) { | 
 | 			opp = temp_opp; | 
 |  | 
 | 			/* Increment the reference count of OPP */ | 
 | 			dev_pm_opp_get(opp); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	mutex_unlock(&opp_table->lock); | 
 |  | 
 | 	of_node_put(required_np); | 
 | put_opp_table: | 
 | 	dev_pm_opp_put_opp_table(opp_table); | 
 |  | 
 | 	return opp; | 
 | } | 
 | EXPORT_SYMBOL_GPL(of_dev_pm_opp_find_required_opp); | 
 |  | 
 | /** | 
 |  * dev_pm_opp_get_of_node() - Gets the DT node corresponding to an opp | 
 |  * @opp:	opp for which DT node has to be returned for | 
 |  * | 
 |  * Return: DT node corresponding to the opp, else 0 on success. | 
 |  * | 
 |  * The caller needs to put the node with of_node_put() after using it. | 
 |  */ | 
 | struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) | 
 | { | 
 | 	if (IS_ERR_OR_NULL(opp)) { | 
 | 		pr_err("%s: Invalid parameters\n", __func__); | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	return of_node_get(opp->np); | 
 | } | 
 | EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node); | 
 |  | 
 | /** | 
 |  * of_dev_pm_opp_get_cpu_power() - Estimates the power of a CPU | 
 |  * @mW:		pointer to the power estimate in milli-watts | 
 |  * @KHz:	pointer to the OPP's frequency, in kilo-hertz | 
 |  * @cpu:	CPU for which power needs to be estimated | 
 |  * | 
 |  * Computes the power estimated by @CPU at the first OPP above @KHz (ceil), | 
 |  * and updates @KHz and @mW accordingly. | 
 |  * | 
 |  * The power is estimated as P = C * V^2 * f, with C the CPU's capacitance | 
 |  * (read from the 'dynamic-power-coefficient' devicetree binding) and V and f | 
 |  * respectively the voltage and frequency of the OPP. | 
 |  * | 
 |  * Return: -ENODEV if the CPU device cannot be found, -EINVAL if the power | 
 |  * calculation failed because of missing parameters, 0 otherwise. | 
 |  */ | 
 | int of_dev_pm_opp_get_cpu_power(unsigned long *mW, unsigned long *KHz, int cpu) | 
 | { | 
 | 	unsigned long mV, Hz, MHz; | 
 | 	struct device *cpu_dev; | 
 | 	struct dev_pm_opp *opp; | 
 | 	struct device_node *np; | 
 | 	u32 cap; | 
 | 	u64 tmp; | 
 | 	int ret; | 
 |  | 
 | 	cpu_dev = get_cpu_device(cpu); | 
 | 	if (!cpu_dev) | 
 | 		return -ENODEV; | 
 |  | 
 | 	np = of_node_get(cpu_dev->of_node); | 
 | 	if (!np) | 
 | 		return -EINVAL; | 
 |  | 
 | 	ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap); | 
 | 	of_node_put(np); | 
 | 	if (ret) | 
 | 		return -EINVAL; | 
 |  | 
 | 	Hz = *KHz * 1000; | 
 | 	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); | 
 | 	if (IS_ERR(opp)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	mV = dev_pm_opp_get_voltage(opp) / 1000; | 
 | 	dev_pm_opp_put(opp); | 
 | 	if (!mV) | 
 | 		return -EINVAL; | 
 |  | 
 | 	MHz = Hz / 1000000; | 
 | 	tmp = (u64)cap * mV * mV * MHz; | 
 | 	do_div(tmp, 1000000000); | 
 |  | 
 | 	*mW = (unsigned long)tmp; | 
 | 	*KHz = Hz / 1000; | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(of_dev_pm_opp_get_cpu_power); |