xref: /openbmc/linux/drivers/of/resolver.c (revision da56d04c)
17941b27bSPantelis Antoniou /*
27941b27bSPantelis Antoniou  * Functions for dealing with DT resolution
37941b27bSPantelis Antoniou  *
47941b27bSPantelis Antoniou  * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
57941b27bSPantelis Antoniou  * Copyright (C) 2012 Texas Instruments Inc.
67941b27bSPantelis Antoniou  *
77941b27bSPantelis Antoniou  * This program is free software; you can redistribute it and/or
87941b27bSPantelis Antoniou  * modify it under the terms of the GNU General Public License
97941b27bSPantelis Antoniou  * version 2 as published by the Free Software Foundation.
107941b27bSPantelis Antoniou  */
117941b27bSPantelis Antoniou 
127941b27bSPantelis Antoniou #include <linux/kernel.h>
137941b27bSPantelis Antoniou #include <linux/module.h>
147941b27bSPantelis Antoniou #include <linux/of.h>
157941b27bSPantelis Antoniou #include <linux/of_device.h>
167941b27bSPantelis Antoniou #include <linux/string.h>
177941b27bSPantelis Antoniou #include <linux/ctype.h>
187941b27bSPantelis Antoniou #include <linux/errno.h>
197941b27bSPantelis Antoniou #include <linux/string.h>
207941b27bSPantelis Antoniou #include <linux/slab.h>
217941b27bSPantelis Antoniou 
227941b27bSPantelis Antoniou /* illegal phandle value (set when unresolved) */
237941b27bSPantelis Antoniou #define OF_PHANDLE_ILLEGAL	0xdeadbeef
247941b27bSPantelis Antoniou 
257941b27bSPantelis Antoniou /**
267941b27bSPantelis Antoniou  * Find a node with the give full name by recursively following any of
277941b27bSPantelis Antoniou  * the child node links.
287941b27bSPantelis Antoniou  */
297941b27bSPantelis Antoniou static struct device_node *__of_find_node_by_full_name(struct device_node *node,
307941b27bSPantelis Antoniou 		const char *full_name)
317941b27bSPantelis Antoniou {
327941b27bSPantelis Antoniou 	struct device_node *child, *found;
337941b27bSPantelis Antoniou 
347941b27bSPantelis Antoniou 	if (node == NULL)
357941b27bSPantelis Antoniou 		return NULL;
367941b27bSPantelis Antoniou 
377941b27bSPantelis Antoniou 	/* check */
387941b27bSPantelis Antoniou 	if (of_node_cmp(node->full_name, full_name) == 0)
397941b27bSPantelis Antoniou 		return node;
407941b27bSPantelis Antoniou 
417941b27bSPantelis Antoniou 	for_each_child_of_node(node, child) {
427941b27bSPantelis Antoniou 		found = __of_find_node_by_full_name(child, full_name);
437941b27bSPantelis Antoniou 		if (found != NULL)
447941b27bSPantelis Antoniou 			return found;
457941b27bSPantelis Antoniou 	}
467941b27bSPantelis Antoniou 
477941b27bSPantelis Antoniou 	return NULL;
487941b27bSPantelis Antoniou }
497941b27bSPantelis Antoniou 
507941b27bSPantelis Antoniou /*
517941b27bSPantelis Antoniou  * Find live tree's maximum phandle value.
527941b27bSPantelis Antoniou  */
537941b27bSPantelis Antoniou static phandle of_get_tree_max_phandle(void)
547941b27bSPantelis Antoniou {
557941b27bSPantelis Antoniou 	struct device_node *node;
567941b27bSPantelis Antoniou 	phandle phandle;
577941b27bSPantelis Antoniou 	unsigned long flags;
587941b27bSPantelis Antoniou 
597941b27bSPantelis Antoniou 	/* now search recursively */
607941b27bSPantelis Antoniou 	raw_spin_lock_irqsave(&devtree_lock, flags);
617941b27bSPantelis Antoniou 	phandle = 0;
627941b27bSPantelis Antoniou 	for_each_of_allnodes(node) {
637941b27bSPantelis Antoniou 		if (node->phandle != OF_PHANDLE_ILLEGAL &&
647941b27bSPantelis Antoniou 				node->phandle > phandle)
657941b27bSPantelis Antoniou 			phandle = node->phandle;
667941b27bSPantelis Antoniou 	}
677941b27bSPantelis Antoniou 	raw_spin_unlock_irqrestore(&devtree_lock, flags);
687941b27bSPantelis Antoniou 
697941b27bSPantelis Antoniou 	return phandle;
707941b27bSPantelis Antoniou }
717941b27bSPantelis Antoniou 
727941b27bSPantelis Antoniou /*
737941b27bSPantelis Antoniou  * Adjust a subtree's phandle values by a given delta.
747941b27bSPantelis Antoniou  * Makes sure not to just adjust the device node's phandle value,
757941b27bSPantelis Antoniou  * but modify the phandle properties values as well.
767941b27bSPantelis Antoniou  */
777941b27bSPantelis Antoniou static void __of_adjust_tree_phandles(struct device_node *node,
787941b27bSPantelis Antoniou 		int phandle_delta)
797941b27bSPantelis Antoniou {
807941b27bSPantelis Antoniou 	struct device_node *child;
817941b27bSPantelis Antoniou 	struct property *prop;
827941b27bSPantelis Antoniou 	phandle phandle;
837941b27bSPantelis Antoniou 
847941b27bSPantelis Antoniou 	/* first adjust the node's phandle direct value */
857941b27bSPantelis Antoniou 	if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
867941b27bSPantelis Antoniou 		node->phandle += phandle_delta;
877941b27bSPantelis Antoniou 
887941b27bSPantelis Antoniou 	/* now adjust phandle & linux,phandle values */
897941b27bSPantelis Antoniou 	for_each_property_of_node(node, prop) {
907941b27bSPantelis Antoniou 
917941b27bSPantelis Antoniou 		/* only look for these two */
927941b27bSPantelis Antoniou 		if (of_prop_cmp(prop->name, "phandle") != 0 &&
937941b27bSPantelis Antoniou 		    of_prop_cmp(prop->name, "linux,phandle") != 0)
947941b27bSPantelis Antoniou 			continue;
957941b27bSPantelis Antoniou 
967941b27bSPantelis Antoniou 		/* must be big enough */
977941b27bSPantelis Antoniou 		if (prop->length < 4)
987941b27bSPantelis Antoniou 			continue;
997941b27bSPantelis Antoniou 
1007941b27bSPantelis Antoniou 		/* read phandle value */
1017941b27bSPantelis Antoniou 		phandle = be32_to_cpup(prop->value);
1027941b27bSPantelis Antoniou 		if (phandle == OF_PHANDLE_ILLEGAL)	/* unresolved */
1037941b27bSPantelis Antoniou 			continue;
1047941b27bSPantelis Antoniou 
1057941b27bSPantelis Antoniou 		/* adjust */
1067941b27bSPantelis Antoniou 		*(uint32_t *)prop->value = cpu_to_be32(node->phandle);
1077941b27bSPantelis Antoniou 	}
1087941b27bSPantelis Antoniou 
1097941b27bSPantelis Antoniou 	/* now do the children recursively */
1107941b27bSPantelis Antoniou 	for_each_child_of_node(node, child)
1117941b27bSPantelis Antoniou 		__of_adjust_tree_phandles(child, phandle_delta);
1127941b27bSPantelis Antoniou }
1137941b27bSPantelis Antoniou 
114da56d04cSPantelis Antoniou static int __of_adjust_phandle_ref(struct device_node *node,
115da56d04cSPantelis Antoniou 		struct property *rprop, int value)
1167941b27bSPantelis Antoniou {
1177941b27bSPantelis Antoniou 	phandle phandle;
1187941b27bSPantelis Antoniou 	struct device_node *refnode;
1197941b27bSPantelis Antoniou 	struct property *sprop;
1207941b27bSPantelis Antoniou 	char *propval, *propcur, *propend, *nodestr, *propstr, *s;
1217941b27bSPantelis Antoniou 	int offset, propcurlen;
1227941b27bSPantelis Antoniou 	int err = 0;
1237941b27bSPantelis Antoniou 
1247941b27bSPantelis Antoniou 	/* make a copy */
1257941b27bSPantelis Antoniou 	propval = kmalloc(rprop->length, GFP_KERNEL);
1267941b27bSPantelis Antoniou 	if (!propval) {
1277941b27bSPantelis Antoniou 		pr_err("%s: Could not copy value of '%s'\n",
1287941b27bSPantelis Antoniou 				__func__, rprop->name);
1297941b27bSPantelis Antoniou 		return -ENOMEM;
1307941b27bSPantelis Antoniou 	}
1317941b27bSPantelis Antoniou 	memcpy(propval, rprop->value, rprop->length);
1327941b27bSPantelis Antoniou 
1337941b27bSPantelis Antoniou 	propend = propval + rprop->length;
1347941b27bSPantelis Antoniou 	for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
1357941b27bSPantelis Antoniou 		propcurlen = strlen(propcur);
1367941b27bSPantelis Antoniou 
1377941b27bSPantelis Antoniou 		nodestr = propcur;
1387941b27bSPantelis Antoniou 		s = strchr(propcur, ':');
1397941b27bSPantelis Antoniou 		if (!s) {
1407941b27bSPantelis Antoniou 			pr_err("%s: Illegal symbol entry '%s' (1)\n",
1417941b27bSPantelis Antoniou 				__func__, propcur);
1427941b27bSPantelis Antoniou 			err = -EINVAL;
1437941b27bSPantelis Antoniou 			goto err_fail;
1447941b27bSPantelis Antoniou 		}
1457941b27bSPantelis Antoniou 		*s++ = '\0';
1467941b27bSPantelis Antoniou 
1477941b27bSPantelis Antoniou 		propstr = s;
1487941b27bSPantelis Antoniou 		s = strchr(s, ':');
1497941b27bSPantelis Antoniou 		if (!s) {
1507941b27bSPantelis Antoniou 			pr_err("%s: Illegal symbol entry '%s' (2)\n",
1517941b27bSPantelis Antoniou 				__func__, (char *)rprop->value);
1527941b27bSPantelis Antoniou 			err = -EINVAL;
1537941b27bSPantelis Antoniou 			goto err_fail;
1547941b27bSPantelis Antoniou 		}
1557941b27bSPantelis Antoniou 
1567941b27bSPantelis Antoniou 		*s++ = '\0';
1577941b27bSPantelis Antoniou 		err = kstrtoint(s, 10, &offset);
1587941b27bSPantelis Antoniou 		if (err != 0) {
1597941b27bSPantelis Antoniou 			pr_err("%s: Could get offset '%s'\n",
1607941b27bSPantelis Antoniou 				__func__, (char *)rprop->value);
1617941b27bSPantelis Antoniou 			goto err_fail;
1627941b27bSPantelis Antoniou 		}
1637941b27bSPantelis Antoniou 
1647941b27bSPantelis Antoniou 		/* look into the resolve node for the full path */
1657941b27bSPantelis Antoniou 		refnode = __of_find_node_by_full_name(node, nodestr);
1667941b27bSPantelis Antoniou 		if (!refnode) {
1677941b27bSPantelis Antoniou 			pr_warn("%s: Could not find refnode '%s'\n",
1687941b27bSPantelis Antoniou 				__func__, (char *)rprop->value);
1697941b27bSPantelis Antoniou 			continue;
1707941b27bSPantelis Antoniou 		}
1717941b27bSPantelis Antoniou 
1727941b27bSPantelis Antoniou 		/* now find the property */
1737941b27bSPantelis Antoniou 		for_each_property_of_node(refnode, sprop) {
1747941b27bSPantelis Antoniou 			if (of_prop_cmp(sprop->name, propstr) == 0)
1757941b27bSPantelis Antoniou 				break;
1767941b27bSPantelis Antoniou 		}
1777941b27bSPantelis Antoniou 
1787941b27bSPantelis Antoniou 		if (!sprop) {
1797941b27bSPantelis Antoniou 			pr_err("%s: Could not find property '%s'\n",
1807941b27bSPantelis Antoniou 				__func__, (char *)rprop->value);
1817941b27bSPantelis Antoniou 			err = -ENOENT;
1827941b27bSPantelis Antoniou 			goto err_fail;
1837941b27bSPantelis Antoniou 		}
1847941b27bSPantelis Antoniou 
185da56d04cSPantelis Antoniou 		phandle = value;
1867941b27bSPantelis Antoniou 		*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
1877941b27bSPantelis Antoniou 	}
1887941b27bSPantelis Antoniou 
1897941b27bSPantelis Antoniou err_fail:
1907941b27bSPantelis Antoniou 	kfree(propval);
1917941b27bSPantelis Antoniou 	return err;
1927941b27bSPantelis Antoniou }
1937941b27bSPantelis Antoniou 
194da56d04cSPantelis Antoniou /* compare nodes taking into account that 'name' strips out the @ part */
195da56d04cSPantelis Antoniou static int __of_node_name_cmp(const struct device_node *dn1,
196da56d04cSPantelis Antoniou 		const struct device_node *dn2)
197da56d04cSPantelis Antoniou {
198da56d04cSPantelis Antoniou 	const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
199da56d04cSPantelis Antoniou 	const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
200da56d04cSPantelis Antoniou 
201da56d04cSPantelis Antoniou 	return of_node_cmp(n1, n2);
202da56d04cSPantelis Antoniou }
203da56d04cSPantelis Antoniou 
2047941b27bSPantelis Antoniou /*
2057941b27bSPantelis Antoniou  * Adjust the local phandle references by the given phandle delta.
206da56d04cSPantelis Antoniou  * Assumes the existances of a __local_fixups__ node at the root.
207da56d04cSPantelis Antoniou  * Assumes that __of_verify_tree_phandle_references has been called.
208da56d04cSPantelis Antoniou  * Does not take any devtree locks so make sure you call this on a tree
209da56d04cSPantelis Antoniou  * which is at the detached state.
2107941b27bSPantelis Antoniou  */
2117941b27bSPantelis Antoniou static int __of_adjust_tree_phandle_references(struct device_node *node,
212da56d04cSPantelis Antoniou 		struct device_node *target, int phandle_delta)
2137941b27bSPantelis Antoniou {
214da56d04cSPantelis Antoniou 	struct device_node *child, *childtarget;
215da56d04cSPantelis Antoniou 	struct property *rprop, *sprop;
216da56d04cSPantelis Antoniou 	int err, i, count;
217da56d04cSPantelis Antoniou 	unsigned int off;
218da56d04cSPantelis Antoniou 	phandle phandle;
2197941b27bSPantelis Antoniou 
220da56d04cSPantelis Antoniou 	if (node == NULL)
2217941b27bSPantelis Antoniou 		return 0;
2227941b27bSPantelis Antoniou 
223da56d04cSPantelis Antoniou 	for_each_property_of_node(node, rprop) {
224da56d04cSPantelis Antoniou 
2257941b27bSPantelis Antoniou 		/* skip properties added automatically */
226da56d04cSPantelis Antoniou 		if (of_prop_cmp(rprop->name, "name") == 0 ||
227da56d04cSPantelis Antoniou 		    of_prop_cmp(rprop->name, "phandle") == 0 ||
228da56d04cSPantelis Antoniou 		    of_prop_cmp(rprop->name, "linux,phandle") == 0)
2297941b27bSPantelis Antoniou 			continue;
2307941b27bSPantelis Antoniou 
231da56d04cSPantelis Antoniou 		if ((rprop->length % 4) != 0 || rprop->length == 0) {
232da56d04cSPantelis Antoniou 			pr_err("%s: Illegal property (size) '%s' @%s\n",
233da56d04cSPantelis Antoniou 					__func__, rprop->name, node->full_name);
234da56d04cSPantelis Antoniou 			return -EINVAL;
235da56d04cSPantelis Antoniou 		}
236da56d04cSPantelis Antoniou 		count = rprop->length / sizeof(__be32);
237da56d04cSPantelis Antoniou 
238da56d04cSPantelis Antoniou 		/* now find the target property */
239da56d04cSPantelis Antoniou 		for_each_property_of_node(target, sprop) {
240da56d04cSPantelis Antoniou 			if (of_prop_cmp(sprop->name, rprop->name) == 0)
241da56d04cSPantelis Antoniou 				break;
242da56d04cSPantelis Antoniou 		}
243da56d04cSPantelis Antoniou 
244da56d04cSPantelis Antoniou 		if (sprop == NULL) {
245da56d04cSPantelis Antoniou 			pr_err("%s: Could not find target property '%s' @%s\n",
246da56d04cSPantelis Antoniou 					__func__, rprop->name, node->full_name);
247da56d04cSPantelis Antoniou 			return -EINVAL;
248da56d04cSPantelis Antoniou 		}
249da56d04cSPantelis Antoniou 
250da56d04cSPantelis Antoniou 		for (i = 0; i < count; i++) {
251da56d04cSPantelis Antoniou 			off = be32_to_cpu(((__be32 *)rprop->value)[i]);
252da56d04cSPantelis Antoniou 			/* make sure the offset doesn't overstep (even wrap) */
253da56d04cSPantelis Antoniou 			if (off >= sprop->length ||
254da56d04cSPantelis Antoniou 					(off + 4) > sprop->length) {
255da56d04cSPantelis Antoniou 				pr_err("%s: Illegal property '%s' @%s\n",
256da56d04cSPantelis Antoniou 						__func__, rprop->name,
257da56d04cSPantelis Antoniou 						node->full_name);
258da56d04cSPantelis Antoniou 				return -EINVAL;
259da56d04cSPantelis Antoniou 			}
260da56d04cSPantelis Antoniou 
261da56d04cSPantelis Antoniou 			if (phandle_delta) {
262da56d04cSPantelis Antoniou 				/* adjust */
263da56d04cSPantelis Antoniou 				phandle = be32_to_cpu(*(__be32 *)(sprop->value + off));
264da56d04cSPantelis Antoniou 				phandle += phandle_delta;
265da56d04cSPantelis Antoniou 				*(__be32 *)(sprop->value + off) = cpu_to_be32(phandle);
266da56d04cSPantelis Antoniou 			}
267da56d04cSPantelis Antoniou 		}
268da56d04cSPantelis Antoniou 	}
269da56d04cSPantelis Antoniou 
270da56d04cSPantelis Antoniou 	for_each_child_of_node(node, child) {
271da56d04cSPantelis Antoniou 
272da56d04cSPantelis Antoniou 		for_each_child_of_node(target, childtarget)
273da56d04cSPantelis Antoniou 			if (__of_node_name_cmp(child, childtarget) == 0)
274da56d04cSPantelis Antoniou 				break;
275da56d04cSPantelis Antoniou 
276da56d04cSPantelis Antoniou 		if (!childtarget) {
277da56d04cSPantelis Antoniou 			pr_err("%s: Could not find target child '%s' @%s\n",
278da56d04cSPantelis Antoniou 					__func__, child->name, node->full_name);
279da56d04cSPantelis Antoniou 			return -EINVAL;
280da56d04cSPantelis Antoniou 		}
281da56d04cSPantelis Antoniou 
282da56d04cSPantelis Antoniou 		err = __of_adjust_tree_phandle_references(child, childtarget,
283da56d04cSPantelis Antoniou 				phandle_delta);
284da56d04cSPantelis Antoniou 		if (err != 0)
2857941b27bSPantelis Antoniou 			return err;
2867941b27bSPantelis Antoniou 	}
2877941b27bSPantelis Antoniou 
2887941b27bSPantelis Antoniou 	return 0;
2897941b27bSPantelis Antoniou }
2907941b27bSPantelis Antoniou 
2917941b27bSPantelis Antoniou /**
2927941b27bSPantelis Antoniou  * of_resolve	- Resolve the given node against the live tree.
2937941b27bSPantelis Antoniou  *
2947941b27bSPantelis Antoniou  * @resolve:	Node to resolve
2957941b27bSPantelis Antoniou  *
2967941b27bSPantelis Antoniou  * Perform dynamic Device Tree resolution against the live tree
2977941b27bSPantelis Antoniou  * to the given node to resolve. This depends on the live tree
2987941b27bSPantelis Antoniou  * having a __symbols__ node, and the resolve node the __fixups__ &
2997941b27bSPantelis Antoniou  * __local_fixups__ nodes (if needed).
3007941b27bSPantelis Antoniou  * The result of the operation is a resolve node that it's contents
3017941b27bSPantelis Antoniou  * are fit to be inserted or operate upon the live tree.
3027941b27bSPantelis Antoniou  * Returns 0 on success or a negative error value on error.
3037941b27bSPantelis Antoniou  */
3047941b27bSPantelis Antoniou int of_resolve_phandles(struct device_node *resolve)
3057941b27bSPantelis Antoniou {
306da56d04cSPantelis Antoniou 	struct device_node *child, *childroot, *refnode;
3077941b27bSPantelis Antoniou 	struct device_node *root_sym, *resolve_sym, *resolve_fix;
3087941b27bSPantelis Antoniou 	struct property *rprop;
3097941b27bSPantelis Antoniou 	const char *refpath;
3107941b27bSPantelis Antoniou 	phandle phandle, phandle_delta;
3117941b27bSPantelis Antoniou 	int err;
3127941b27bSPantelis Antoniou 
3137941b27bSPantelis Antoniou 	/* the resolve node must exist, and be detached */
3147941b27bSPantelis Antoniou 	if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
3157941b27bSPantelis Antoniou 		return -EINVAL;
3167941b27bSPantelis Antoniou 
3177941b27bSPantelis Antoniou 	/* first we need to adjust the phandles */
3187941b27bSPantelis Antoniou 	phandle_delta = of_get_tree_max_phandle() + 1;
3197941b27bSPantelis Antoniou 	__of_adjust_tree_phandles(resolve, phandle_delta);
320da56d04cSPantelis Antoniou 
321da56d04cSPantelis Antoniou 	/* locate the local fixups */
322da56d04cSPantelis Antoniou 	childroot = NULL;
323da56d04cSPantelis Antoniou 	for_each_child_of_node(resolve, childroot)
324da56d04cSPantelis Antoniou 		if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
325da56d04cSPantelis Antoniou 			break;
326da56d04cSPantelis Antoniou 
327da56d04cSPantelis Antoniou 	if (childroot != NULL) {
328da56d04cSPantelis Antoniou 		/* resolve root is guaranteed to be the '/' */
329da56d04cSPantelis Antoniou 		err = __of_adjust_tree_phandle_references(childroot,
330da56d04cSPantelis Antoniou 				resolve, 0);
3317941b27bSPantelis Antoniou 		if (err != 0)
3327941b27bSPantelis Antoniou 			return err;
3337941b27bSPantelis Antoniou 
334da56d04cSPantelis Antoniou 		BUG_ON(__of_adjust_tree_phandle_references(childroot,
335da56d04cSPantelis Antoniou 				resolve, phandle_delta));
336da56d04cSPantelis Antoniou 	}
337da56d04cSPantelis Antoniou 
3387941b27bSPantelis Antoniou 	root_sym = NULL;
3397941b27bSPantelis Antoniou 	resolve_sym = NULL;
3407941b27bSPantelis Antoniou 	resolve_fix = NULL;
3417941b27bSPantelis Antoniou 
3427941b27bSPantelis Antoniou 	/* this may fail (if no fixups are required) */
3437941b27bSPantelis Antoniou 	root_sym = of_find_node_by_path("/__symbols__");
3447941b27bSPantelis Antoniou 
3457941b27bSPantelis Antoniou 	/* locate the symbols & fixups nodes on resolve */
3467941b27bSPantelis Antoniou 	for_each_child_of_node(resolve, child) {
3477941b27bSPantelis Antoniou 
3487941b27bSPantelis Antoniou 		if (!resolve_sym &&
3497941b27bSPantelis Antoniou 				of_node_cmp(child->name, "__symbols__") == 0)
3507941b27bSPantelis Antoniou 			resolve_sym = child;
3517941b27bSPantelis Antoniou 
3527941b27bSPantelis Antoniou 		if (!resolve_fix &&
3537941b27bSPantelis Antoniou 				of_node_cmp(child->name, "__fixups__") == 0)
3547941b27bSPantelis Antoniou 			resolve_fix = child;
3557941b27bSPantelis Antoniou 
3567941b27bSPantelis Antoniou 		/* both found, don't bother anymore */
3577941b27bSPantelis Antoniou 		if (resolve_sym && resolve_fix)
3587941b27bSPantelis Antoniou 			break;
3597941b27bSPantelis Antoniou 	}
3607941b27bSPantelis Antoniou 
3617941b27bSPantelis Antoniou 	/* we do allow for the case where no fixups are needed */
3627941b27bSPantelis Antoniou 	if (!resolve_fix) {
3637941b27bSPantelis Antoniou 		err = 0;	/* no error */
3647941b27bSPantelis Antoniou 		goto out;
3657941b27bSPantelis Antoniou 	}
3667941b27bSPantelis Antoniou 
3677941b27bSPantelis Antoniou 	/* we need to fixup, but no root symbols... */
3687941b27bSPantelis Antoniou 	if (!root_sym) {
3697941b27bSPantelis Antoniou 		err = -EINVAL;
3707941b27bSPantelis Antoniou 		goto out;
3717941b27bSPantelis Antoniou 	}
3727941b27bSPantelis Antoniou 
3737941b27bSPantelis Antoniou 	for_each_property_of_node(resolve_fix, rprop) {
3747941b27bSPantelis Antoniou 
3757941b27bSPantelis Antoniou 		/* skip properties added automatically */
3767941b27bSPantelis Antoniou 		if (of_prop_cmp(rprop->name, "name") == 0)
3777941b27bSPantelis Antoniou 			continue;
3787941b27bSPantelis Antoniou 
3797941b27bSPantelis Antoniou 		err = of_property_read_string(root_sym,
3807941b27bSPantelis Antoniou 				rprop->name, &refpath);
3817941b27bSPantelis Antoniou 		if (err != 0) {
3827941b27bSPantelis Antoniou 			pr_err("%s: Could not find symbol '%s'\n",
3837941b27bSPantelis Antoniou 					__func__, rprop->name);
3847941b27bSPantelis Antoniou 			goto out;
3857941b27bSPantelis Antoniou 		}
3867941b27bSPantelis Antoniou 
3877941b27bSPantelis Antoniou 		refnode = of_find_node_by_path(refpath);
3887941b27bSPantelis Antoniou 		if (!refnode) {
3897941b27bSPantelis Antoniou 			pr_err("%s: Could not find node by path '%s'\n",
3907941b27bSPantelis Antoniou 					__func__, refpath);
3917941b27bSPantelis Antoniou 			err = -ENOENT;
3927941b27bSPantelis Antoniou 			goto out;
3937941b27bSPantelis Antoniou 		}
3947941b27bSPantelis Antoniou 
3957941b27bSPantelis Antoniou 		phandle = refnode->phandle;
3967941b27bSPantelis Antoniou 		of_node_put(refnode);
3977941b27bSPantelis Antoniou 
3987941b27bSPantelis Antoniou 		pr_debug("%s: %s phandle is 0x%08x\n",
3997941b27bSPantelis Antoniou 				__func__, rprop->name, phandle);
4007941b27bSPantelis Antoniou 
401da56d04cSPantelis Antoniou 		err = __of_adjust_phandle_ref(resolve, rprop, phandle);
4027941b27bSPantelis Antoniou 		if (err)
4037941b27bSPantelis Antoniou 			break;
4047941b27bSPantelis Antoniou 	}
4057941b27bSPantelis Antoniou 
4067941b27bSPantelis Antoniou out:
4077941b27bSPantelis Antoniou 	/* NULL is handled by of_node_put as NOP */
4087941b27bSPantelis Antoniou 	of_node_put(root_sym);
4097941b27bSPantelis Antoniou 
4107941b27bSPantelis Antoniou 	return err;
4117941b27bSPantelis Antoniou }
4127941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles);
413