1504 lines · 36.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Generic OPP OF helpers * * Copyright (C) 2009-2010 Texas Instruments Incorporated. *	Nishanth Menon *	Romit Dasgupta *	Kevin Hilman */ #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 <linux/energy_model.h> #include "opp.h" /* * 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); struct opp_table *_managed_opp(struct device *dev, int index){	struct opp_table *opp_table, *managed_table = NULL;	struct device_node *np; 	np = _opp_of_get_opp_desc_node(dev->of_node, index);	if (!np)		return NULL; 	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;		}	} 	of_node_put(np); 	return managed_table;} /* The caller must call dev_pm_opp_put() after the OPP is used */static struct dev_pm_opp *_find_opp_of_np(struct opp_table *opp_table,					  struct device_node *opp_np){	struct dev_pm_opp *opp; 	mutex_lock(&opp_table->lock); 	list_for_each_entry(opp, &opp_table->opp_list, node) {		if (opp->np == opp_np) {			dev_pm_opp_get(opp);			mutex_unlock(&opp_table->lock);			return opp;		}	} 	mutex_unlock(&opp_table->lock); 	return NULL;} static struct device_node *of_parse_required_opp(struct device_node *np,						 int index){	struct device_node *required_np; 	required_np = of_parse_phandle(np, "required-opps", index);	if (unlikely(!required_np)) {		pr_err("%s: Unable to parse required-opps: %pOF, index: %d\n",		       __func__, np, index);	} 	return required_np;} /* The caller must call dev_pm_opp_put_opp_table() after the table is used */static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np){	struct opp_table *opp_table;	struct device_node *opp_table_np; 	opp_table_np = of_get_parent(opp_np);	if (!opp_table_np)		goto err; 	/* It is safe to put the node now as all we need now is its address */	of_node_put(opp_table_np); 	mutex_lock(&opp_table_lock);	list_for_each_entry(opp_table, &opp_tables, node) {		if (opp_table_np == opp_table->np) {			_get_opp_table_kref(opp_table);			mutex_unlock(&opp_table_lock);			return opp_table;		}	}	mutex_unlock(&opp_table_lock); err:	return ERR_PTR(-ENODEV);} /* Free resources previously acquired by _opp_table_alloc_required_tables() */static void _opp_table_free_required_tables(struct opp_table *opp_table){	struct opp_table **required_opp_tables = opp_table->required_opp_tables;	int i; 	if (!required_opp_tables)		return; 	for (i = 0; i < opp_table->required_opp_count; i++) {		if (IS_ERR_OR_NULL(required_opp_tables[i]))			continue; 		dev_pm_opp_put_opp_table(required_opp_tables[i]);	} 	kfree(required_opp_tables); 	opp_table->required_opp_count = 0;	opp_table->required_opp_tables = NULL;	list_del(&opp_table->lazy);} /* * Populate all devices and opp tables which are part of "required-opps" list. * Checking only the first OPP node should be enough. */static void _opp_table_alloc_required_tables(struct opp_table *opp_table,					     struct device *dev,					     struct device_node *opp_np){	struct opp_table **required_opp_tables;	struct device_node *required_np, *np;	bool lazy = false;	int count, i; 	/* Traversing the first OPP node is all we need */	np = of_get_next_available_child(opp_np, NULL);	if (!np) {		dev_warn(dev, "Empty OPP table\n"); 		return;	} 	count = of_count_phandle_with_args(np, "required-opps", NULL);	if (!count)		goto put_np; 	required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),				      GFP_KERNEL);	if (!required_opp_tables)		goto put_np; 	opp_table->required_opp_tables = required_opp_tables;	opp_table->required_opp_count = count; 	for (i = 0; i < count; i++) {		required_np = of_parse_required_opp(np, i);		if (!required_np)			goto free_required_tables; 		required_opp_tables[i] = _find_table_of_opp_np(required_np);		of_node_put(required_np); 		if (IS_ERR(required_opp_tables[i])) {			lazy = true;			continue;		} 		/*		 * We only support genpd's OPPs in the "required-opps" for now,		 * as we don't know how much about other cases. Error out if the		 * required OPP doesn't belong to a genpd.		 */		if (!required_opp_tables[i]->is_genpd) {			dev_err(dev, "required-opp doesn't belong to genpd: %pOF\n",				required_np);			goto free_required_tables;		}	} 	/* Let's do the linking later on */	if (lazy)		list_add(&opp_table->lazy, &lazy_opp_tables); 	goto put_np; free_required_tables:	_opp_table_free_required_tables(opp_table);put_np:	of_node_put(np);} void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,			int index){	struct device_node *np, *opp_np;	u32 val; 	/*	 * 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)		return; 	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); 	if (of_find_property(np, "#power-domain-cells", NULL))		opp_table->is_genpd = true; 	/* Get OPP table node */	opp_np = _opp_of_get_opp_desc_node(np, index);	of_node_put(np); 	if (!opp_np)		return; 	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; 	opp_table->np = opp_np; 	_opp_table_alloc_required_tables(opp_table, dev, opp_np);	of_node_put(opp_np);} void _of_clear_opp_table(struct opp_table *opp_table){	_opp_table_free_required_tables(opp_table);} /* * Release all resources previously acquired with a call to * _of_opp_alloc_required_opps(). */void _of_opp_free_required_opps(struct opp_table *opp_table,				struct dev_pm_opp *opp){	struct dev_pm_opp **required_opps = opp->required_opps;	int i; 	if (!required_opps)		return; 	for (i = 0; i < opp_table->required_opp_count; i++) {		if (!required_opps[i])			continue; 		/* Put the reference back */		dev_pm_opp_put(required_opps[i]);	} 	opp->required_opps = NULL;	kfree(required_opps);} /* Populate all required OPPs which are part of "required-opps" list */static int _of_opp_alloc_required_opps(struct opp_table *opp_table,				       struct dev_pm_opp *opp){	struct dev_pm_opp **required_opps;	struct opp_table *required_table;	struct device_node *np;	int i, ret, count = opp_table->required_opp_count; 	if (!count)		return 0; 	required_opps = kcalloc(count, sizeof(*required_opps), GFP_KERNEL);	if (!required_opps)		return -ENOMEM; 	opp->required_opps = required_opps; 	for (i = 0; i < count; i++) {		required_table = opp_table->required_opp_tables[i]; 		/* Required table not added yet, we will link later */		if (IS_ERR_OR_NULL(required_table))			continue; 		np = of_parse_required_opp(opp->np, i);		if (unlikely(!np)) {			ret = -ENODEV;			goto free_required_opps;		} 		required_opps[i] = _find_opp_of_np(required_table, np);		of_node_put(np); 		if (!required_opps[i]) {			pr_err("%s: Unable to find required OPP node: %pOF (%d)\n",			       __func__, opp->np, i);			ret = -ENODEV;			goto free_required_opps;		}	} 	return 0; free_required_opps:	_of_opp_free_required_opps(opp_table, opp); 	return ret;} /* Link required OPPs for an individual OPP */static int lazy_link_required_opps(struct opp_table *opp_table,				   struct opp_table *new_table, int index){	struct device_node *required_np;	struct dev_pm_opp *opp; 	list_for_each_entry(opp, &opp_table->opp_list, node) {		required_np = of_parse_required_opp(opp->np, index);		if (unlikely(!required_np))			return -ENODEV; 		opp->required_opps[index] = _find_opp_of_np(new_table, required_np);		of_node_put(required_np); 		if (!opp->required_opps[index]) {			pr_err("%s: Unable to find required OPP node: %pOF (%d)\n",			       __func__, opp->np, index);			return -ENODEV;		}	} 	return 0;} /* Link required OPPs for all OPPs of the newly added OPP table */static void lazy_link_required_opp_table(struct opp_table *new_table){	struct opp_table *opp_table, *temp, **required_opp_tables;	struct device_node *required_np, *opp_np, *required_table_np;	struct dev_pm_opp *opp;	int i, ret; 	/*	 * We only support genpd's OPPs in the "required-opps" for now,	 * as we don't know much about other cases.	 */	if (!new_table->is_genpd)		return; 	mutex_lock(&opp_table_lock); 	list_for_each_entry_safe(opp_table, temp, &lazy_opp_tables, lazy) {		bool lazy = false; 		/* opp_np can't be invalid here */		opp_np = of_get_next_available_child(opp_table->np, NULL); 		for (i = 0; i < opp_table->required_opp_count; i++) {			required_opp_tables = opp_table->required_opp_tables; 			/* Required opp-table is already parsed */			if (!IS_ERR(required_opp_tables[i]))				continue; 			/* required_np can't be invalid here */			required_np = of_parse_required_opp(opp_np, i);			required_table_np = of_get_parent(required_np); 			of_node_put(required_table_np);			of_node_put(required_np); 			/*			 * Newly added table isn't the required opp-table for			 * opp_table.			 */			if (required_table_np != new_table->np) {				lazy = true;				continue;			} 			required_opp_tables[i] = new_table;			_get_opp_table_kref(new_table); 			/* Link OPPs now */			ret = lazy_link_required_opps(opp_table, new_table, i);			if (ret) {				/* The OPPs will be marked unusable */				lazy = false;				break;			}		} 		of_node_put(opp_np); 		/* All required opp-tables found, remove from lazy list */		if (!lazy) {			list_del(&opp_table->lazy);			INIT_LIST_HEAD(&opp_table->lazy); 			list_for_each_entry(opp, &opp_table->opp_list, node)				_required_opps_available(opp, opp_table->required_opp_count);		}	} 	mutex_unlock(&opp_table_lock);} static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table){	struct device_node *np, *opp_np;	struct property *prop; 	if (!opp_table) {		np = of_node_get(dev->of_node);		if (!np)			return -ENODEV; 		opp_np = _opp_of_get_opp_desc_node(np, 0);		of_node_put(np);	} else {		opp_np = of_node_get(opp_table->np);	} 	/* Lets not fail in case we are parsing opp-v1 bindings */	if (!opp_np)		return 0; 	/* Checking only first OPP is sufficient */	np = of_get_next_available_child(opp_np, NULL);	if (!np) {		dev_err(dev, "OPP table empty\n");		return -EINVAL;	}	of_node_put(opp_np); 	prop = of_find_property(np, "opp-peak-kBps", NULL);	of_node_put(np); 	if (!prop || !prop->length)		return 0; 	return 1;} int dev_pm_opp_of_find_icc_paths(struct device *dev,				 struct opp_table *opp_table){	struct device_node *np;	int ret, i, count, num_paths;	struct icc_path **paths; 	ret = _bandwidth_supported(dev, opp_table);	if (ret == -EINVAL)		return 0; /* Empty OPP table is a valid corner-case, let's not fail */	else if (ret <= 0)		return ret; 	ret = 0; 	np = of_node_get(dev->of_node);	if (!np)

Lines 1–500 of 1504.