Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64b60e09 authored by Anton Vorontsov's avatar Anton Vorontsov Committed by Benjamin Herrenschmidt
Browse files

of: Add new helper of_parse_phandles_with_args()



The helper is factored out of of_get_gpio(). Will be used by the QE
pin multiplexing functions (they need to parse the gpios = <> too).

Signed-off-by: default avatarAnton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 302905a3
Loading
Loading
Loading
Loading
+109 −0
Original line number Diff line number Diff line
@@ -458,3 +458,112 @@ int of_modalias_node(struct device_node *node, char *modalias, int len)
}
EXPORT_SYMBOL_GPL(of_modalias_node);

/**
 * of_parse_phandles_with_args - Find a node pointed by phandle in a list
 * @np:		pointer to a device tree node containing a list
 * @list_name:	property name that contains a list
 * @cells_name:	property name that specifies phandles' arguments count
 * @index:	index of a phandle to parse out
 * @out_node:	pointer to device_node struct pointer (will be filled)
 * @out_args:	pointer to arguments pointer (will be filled)
 *
 * This function is useful to parse lists of phandles and their arguments.
 * Returns 0 on success and fills out_node and out_args, on error returns
 * appropriate errno value.
 *
 * Example:
 *
 * phandle1: node1 {
 * 	#list-cells = <2>;
 * }
 *
 * phandle2: node2 {
 * 	#list-cells = <1>;
 * }
 *
 * node3 {
 * 	list = <&phandle1 1 2 &phandle2 3>;
 * }
 *
 * To get a device_node of the `node2' node you may call this:
 * of_parse_phandles_with_args(node3, "list", "#list-cells", 2, &node2, &args);
 */
int of_parse_phandles_with_args(struct device_node *np, const char *list_name,
				const char *cells_name, int index,
				struct device_node **out_node,
				const void **out_args)
{
	int ret = -EINVAL;
	const u32 *list;
	const u32 *list_end;
	int size;
	int cur_index = 0;
	struct device_node *node = NULL;
	const void *args;

	list = of_get_property(np, list_name, &size);
	if (!list) {
		ret = -ENOENT;
		goto err0;
	}
	list_end = list + size / sizeof(*list);

	while (list < list_end) {
		const u32 *cells;
		const phandle *phandle;

		phandle = list;
		args = list + 1;

		/* one cell hole in the list = <>; */
		if (!*phandle) {
			list++;
			goto next;
		}

		node = of_find_node_by_phandle(*phandle);
		if (!node) {
			pr_debug("%s: could not find phandle\n",
				 np->full_name);
			goto err0;
		}

		cells = of_get_property(node, cells_name, &size);
		if (!cells || size != sizeof(*cells)) {
			pr_debug("%s: could not get %s for %s\n",
				 np->full_name, cells_name, node->full_name);
			goto err1;
		}

		/* Next phandle is at offset of one phandle cell + #cells */
		list += 1 + *cells;
		if (list > list_end) {
			pr_debug("%s: insufficient arguments length\n",
				 np->full_name);
			goto err1;
		}
next:
		if (cur_index == index)
			break;

		of_node_put(node);
		node = NULL;
		cur_index++;
	}

	if (!node) {
		ret = -ENOENT;
		goto err0;
	}

	*out_node = node;
	*out_args = args;

	return 0;
err1:
	of_node_put(node);
err0:
	pr_debug("%s failed with status %d\n", __func__, ret);
	return ret;
}
EXPORT_SYMBOL(of_parse_phandles_with_args);
+19 −62
Original line number Diff line number Diff line
@@ -28,42 +28,17 @@
 */
int of_get_gpio(struct device_node *np, int index)
{
	int ret = -EINVAL;
	int ret;
	struct device_node *gc;
	struct of_gpio_chip *of_gc = NULL;
	int size;
	const u32 *gpios;
	u32 nr_cells;
	int i;
	const void *gpio_spec;
	const u32 *gpio_cells;
	int gpio_index = 0;

	gpios = of_get_property(np, "gpios", &size);
	if (!gpios) {
		ret = -ENOENT;
		goto err0;
	}
	nr_cells = size / sizeof(u32);

	for (i = 0; i < nr_cells; gpio_index++) {
		const phandle *gpio_phandle;

		gpio_phandle = gpios + i;
		gpio_spec = gpio_phandle + 1;

		/* one cell hole in the gpios = <>; */
		if (!*gpio_phandle) {
			if (gpio_index == index)
				return -ENOENT;
			i++;
			continue;
		}

		gc = of_find_node_by_phandle(*gpio_phandle);
		if (!gc) {
			pr_debug("%s: could not find phandle for gpios\n",
				 np->full_name);
	ret = of_parse_phandles_with_args(np, "gpios", "#gpio-cells", index,
					  &gc, &gpio_spec);
	if (ret) {
		pr_debug("%s: can't parse gpios property\n", __func__);
		goto err0;
	}

@@ -71,6 +46,7 @@ int of_get_gpio(struct device_node *np, int index)
	if (!of_gc) {
		pr_debug("%s: gpio controller %s isn't registered\n",
			 np->full_name, gc->full_name);
		ret = -ENODEV;
		goto err1;
	}

@@ -79,29 +55,10 @@ int of_get_gpio(struct device_node *np, int index)
			*gpio_cells != of_gc->gpio_cells) {
		pr_debug("%s: wrong #gpio-cells for %s\n",
			 np->full_name, gc->full_name);
		ret = -EINVAL;
		goto err1;
	}

		/* Next phandle is at phandle cells + #gpio-cells */
		i += sizeof(*gpio_phandle) / sizeof(u32) + *gpio_cells;
		if (i >= nr_cells + 1) {
			pr_debug("%s: insufficient gpio-spec length\n",
				 np->full_name);
			goto err1;
		}

		if (gpio_index == index)
			break;

		of_gc = NULL;
		of_node_put(gc);
	}

	if (!of_gc) {
		ret = -ENOENT;
		goto err0;
	}

	ret = of_gc->xlate(of_gc, np, gpio_spec);
	if (ret < 0)
		goto err1;
+3 −0
Original line number Diff line number Diff line
@@ -71,5 +71,8 @@ extern int of_n_size_cells(struct device_node *np);
extern const struct of_device_id *of_match_node(
	const struct of_device_id *matches, const struct device_node *node);
extern int of_modalias_node(struct device_node *node, char *modalias, int len);
extern int of_parse_phandles_with_args(struct device_node *np,
	const char *list_name, const char *cells_name, int index,
	struct device_node **out_node, const void **out_args);

#endif /* _LINUX_OF_H */