1598 lines · 39.7 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.h>#include <linux/pm_domain.h>#include <linux/slab.h>#include <linux/export.h>#include <linux/energy_model.h> #include "opp.h" /* OPP tables with uninitialized required OPPs, protected by opp_table_lock */static LIST_HEAD(lazy_opp_tables); /* * 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 __free(device_node) =		_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)				managed_table = dev_pm_opp_get_opp_table_ref(opp_table); 			break;		}	} 	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; 	guard(mutex)(&opp_table->lock); 	list_for_each_entry(opp, &opp_table->opp_list, node) {		if (opp->np == opp_np)			return dev_pm_opp_get(opp);	} 	return NULL;} static struct device_node *of_parse_required_opp(struct device_node *np,						 int index){	return of_parse_phandle(np, "required-opps", index);} /* 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 __free(device_node) =		of_get_parent(opp_np); 	if (!opp_table_np)		return ERR_PTR(-ENODEV); 	guard(mutex)(&opp_table_lock); 	list_for_each_entry(opp_table, &opp_tables, node) {		if (opp_table_np == opp_table->np)			return dev_pm_opp_get_opp_table_ref(opp_table);	} 	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; 	guard(mutex)(&opp_table_lock);	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;	bool lazy = false;	int count, i, size; 	/* Traversing the first OPP node is all we need */	struct device_node *np __free(device_node) =		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 <= 0)		return; 	size = sizeof(*required_opp_tables) + sizeof(*opp_table->required_devs);	required_opp_tables = kcalloc(count, size, GFP_KERNEL);	if (!required_opp_tables)		return; 	opp_table->required_opp_tables = required_opp_tables;	opp_table->required_devs = (void *)(required_opp_tables + count);	opp_table->required_opp_count = count; 	for (i = 0; i < count; i++) {		struct device_node *required_np __free(device_node) =			of_parse_required_opp(np, i); 		if (!required_np) {			_opp_table_free_required_tables(opp_table);			return;		} 		required_opp_tables[i] = _find_table_of_opp_np(required_np); 		if (IS_ERR(required_opp_tables[i]))			lazy = true;	} 	/* Let's do the linking later on */	if (lazy) {		/*		 * The OPP table is not held while allocating the table, take it		 * now to avoid corruption to the lazy_opp_tables list.		 */		guard(mutex)(&opp_table_lock);		list_add(&opp_table->lazy, &lazy_opp_tables);	}} void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,			int index){	struct device_node *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.	 */	struct device_node *np __free(device_node) = 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_property_present(np, "#power-domain-cells"))		opp_table->is_genpd = true; 	/* Get OPP table node */	opp_np = _opp_of_get_opp_desc_node(np, index);	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);} void _of_clear_opp_table(struct opp_table *opp_table){	_opp_table_free_required_tables(opp_table);	of_node_put(opp_table->np);} /* * Release all resources previously acquired with a call to * _of_opp_alloc_required_opps(). */static 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);} void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp){	_of_opp_free_required_opps(opp_table, opp);	of_node_put(opp->np);} static int _link_required_opps(struct dev_pm_opp *opp,			       struct opp_table *required_table, int index){	struct device_node *np __free(device_node) =		of_parse_required_opp(opp->np, index); 	if (unlikely(!np))		return -ENODEV; 	opp->required_opps[index] = _find_opp_of_np(required_table, 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;} /* 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 opp_table *required_table;	int i, ret, count = opp_table->required_opp_count; 	if (!count)		return 0; 	opp->required_opps = kzalloc_objs(*opp->required_opps, count);	if (!opp->required_opps)		return -ENOMEM; 	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; 		ret = _link_required_opps(opp, required_table, i);		if (ret)			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 dev_pm_opp *opp;	int ret; 	list_for_each_entry(opp, &opp_table->opp_list, node) {		ret = _link_required_opps(opp, new_table, index);		if (ret)			return ret;	} 	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 dev_pm_opp *opp;	int i, ret; 	guard(mutex)(&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 */		struct device_node *opp_np __free(device_node) =			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 */			struct device_node *required_np __free(device_node) =				of_parse_required_opp(opp_np, i);			struct device_node *required_table_np __free(device_node) =				of_get_parent(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] = dev_pm_opp_get_opp_table_ref(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;			}		} 		/* All required opp-tables found, remove from lazy list */		if (!lazy) {			list_del_init(&opp_table->lazy); 			list_for_each_entry(opp, &opp_table->opp_list, node)				_required_opps_available(opp, opp_table->required_opp_count);		}	}} static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table){	struct device_node *opp_np __free(device_node) = NULL;	struct property *prop; 	if (!opp_table) {		struct device_node *np __free(device_node) =			of_node_get(dev->of_node); 		if (!np)			return -ENODEV; 		opp_np = _opp_of_get_opp_desc_node(np, 0);	} 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 */	struct device_node *np __free(device_node) =		of_get_next_available_child(opp_np, NULL); 	if (!np) {		dev_err(dev, "OPP table empty\n");		return -EINVAL;	} 	prop = of_find_property(np, "opp-peak-kBps", NULL);	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 __free(device_node) = of_node_get(dev->of_node);	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; 	if (!np)		return 0; 	ret = 0; 	count = of_count_phandle_with_args(np, "interconnects",					   "#interconnect-cells");	if (count < 0)		return 0; 	/* two phandles when #interconnect-cells = <1> */	if (count % 2) {		dev_err(dev, "%s: Invalid interconnects values\n", __func__);		return -EINVAL;	} 	num_paths = count / 2;	paths = kzalloc_objs(*paths, num_paths);	if (!paths)		return -ENOMEM; 	for (i = 0; i < num_paths; i++) {		paths[i] = of_icc_get_by_index(dev, i);		if (IS_ERR(paths[i])) {			ret = dev_err_probe(dev, PTR_ERR(paths[i]), "%s: Unable to get path%d\n", __func__, i);			goto err;		}	} 	if (opp_table) {		opp_table->paths = paths;		opp_table->path_count = num_paths;		return 0;	} err:	while (i--)		icc_put(paths[i]); 	kfree(paths); 	return ret;}EXPORT_SYMBOL_GPL(dev_pm_opp_of_find_icc_paths); static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,

Lines 1–500 of 1598.