Linux Audio

Check our new training course

Loading...
v4.10.11
  1/*
  2 * Functions for dealing with DT resolution
  3 *
  4 * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
  5 * Copyright (C) 2012 Texas Instruments Inc.
  6 *
  7 * This program is free software; you can redistribute it and/or
  8 * modify it under the terms of the GNU General Public License
  9 * version 2 as published by the Free Software Foundation.
 10 */
 11
 12#define pr_fmt(fmt)	"OF: resolver: " fmt
 13
 14#include <linux/kernel.h>
 15#include <linux/module.h>
 16#include <linux/of.h>
 17#include <linux/of_device.h>
 18#include <linux/string.h>
 19#include <linux/ctype.h>
 20#include <linux/errno.h>
 21#include <linux/string.h>
 22#include <linux/slab.h>
 23
 24/* illegal phandle value (set when unresolved) */
 25#define OF_PHANDLE_ILLEGAL	0xdeadbeef
 26
 27/**
 28 * Find a node with the give full name by recursively following any of
 29 * the child node links.
 30 */
 31static struct device_node *find_node_by_full_name(struct device_node *node,
 32		const char *full_name)
 33{
 34	struct device_node *child, *found;
 35
 36	if (!node)
 37		return NULL;
 38
 39	if (!of_node_cmp(node->full_name, full_name))
 
 40		return of_node_get(node);
 41
 42	for_each_child_of_node(node, child) {
 43		found = find_node_by_full_name(child, full_name);
 44		if (found != NULL) {
 45			of_node_put(child);
 46			return found;
 47		}
 48	}
 49
 50	return NULL;
 51}
 52
 53static phandle live_tree_max_phandle(void)
 
 
 
 54{
 55	struct device_node *node;
 56	phandle phandle;
 57	unsigned long flags;
 58
 
 59	raw_spin_lock_irqsave(&devtree_lock, flags);
 60	phandle = 0;
 61	for_each_of_allnodes(node) {
 62		if (node->phandle != OF_PHANDLE_ILLEGAL &&
 63				node->phandle > phandle)
 64			phandle = node->phandle;
 65	}
 66	raw_spin_unlock_irqrestore(&devtree_lock, flags);
 67
 68	return phandle;
 69}
 70
 71static void adjust_overlay_phandles(struct device_node *overlay,
 
 
 
 
 
 72		int phandle_delta)
 73{
 74	struct device_node *child;
 75	struct property *prop;
 76	phandle phandle;
 77
 78	/* adjust node's phandle in node */
 79	if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL)
 80		overlay->phandle += phandle_delta;
 81
 82	/* copy adjusted phandle into *phandle properties */
 83	for_each_property_of_node(overlay, prop) {
 84
 85		if (of_prop_cmp(prop->name, "phandle") &&
 86		    of_prop_cmp(prop->name, "linux,phandle"))
 
 87			continue;
 88
 
 89		if (prop->length < 4)
 90			continue;
 91
 
 92		phandle = be32_to_cpup(prop->value);
 93		if (phandle == OF_PHANDLE_ILLEGAL)
 94			continue;
 95
 96		*(uint32_t *)prop->value = cpu_to_be32(overlay->phandle);
 
 97	}
 98
 99	for_each_child_of_node(overlay, child)
100		adjust_overlay_phandles(child, phandle_delta);
 
101}
102
103static int update_usages_of_a_phandle_reference(struct device_node *overlay,
104		struct property *prop_fixup, phandle phandle)
105{
 
106	struct device_node *refnode;
107	struct property *prop;
108	char *value, *cur, *end, *node_path, *prop_name, *s;
109	int offset, len;
110	int err = 0;
111
112	value = kmalloc(prop_fixup->length, GFP_KERNEL);
113	if (!value)
 
 
 
114		return -ENOMEM;
115	memcpy(value, prop_fixup->value, prop_fixup->length);
 
116
117	/* prop_fixup contains a list of tuples of path:property_name:offset */
118	end = value + prop_fixup->length;
119	for (cur = value; cur < end; cur += len + 1) {
120		len = strlen(cur);
121
122		node_path = cur;
123		s = strchr(cur, ':');
124		if (!s) {
 
 
125			err = -EINVAL;
126			goto err_fail;
127		}
128		*s++ = '\0';
129
130		prop_name = s;
131		s = strchr(s, ':');
132		if (!s) {
 
 
133			err = -EINVAL;
134			goto err_fail;
135		}
136		*s++ = '\0';
137
 
138		err = kstrtoint(s, 10, &offset);
139		if (err)
 
 
140			goto err_fail;
 
141
142		refnode = find_node_by_full_name(overlay, node_path);
143		if (!refnode)
 
 
 
144			continue;
 
145
146		for_each_property_of_node(refnode, prop) {
147			if (!of_prop_cmp(prop->name, prop_name))
 
148				break;
149		}
150		of_node_put(refnode);
151
152		if (!prop) {
 
 
153			err = -ENOENT;
154			goto err_fail;
155		}
156
157		*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
 
158	}
159
160err_fail:
161	kfree(value);
162	return err;
163}
164
165/* compare nodes taking into account that 'name' strips out the @ part */
166static int node_name_cmp(const struct device_node *dn1,
167		const struct device_node *dn2)
168{
169	const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
170	const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
171
172	return of_node_cmp(n1, n2);
173}
174
175/*
176 * Adjust the local phandle references by the given phandle delta.
177 *
178 * Subtree @local_fixups, which is overlay node __local_fixups__,
179 * mirrors the fragment node structure at the root of the overlay.
180 *
181 * For each property in the fragments that contains a phandle reference,
182 * @local_fixups has a property of the same name that contains a list
183 * of offsets of the phandle reference(s) within the respective property
184 * value(s).  The values at these offsets will be fixed up.
185 */
186static int adjust_local_phandle_references(struct device_node *local_fixups,
187		struct device_node *overlay, int phandle_delta)
188{
189	struct device_node *child, *overlay_child;
190	struct property *prop_fix, *prop;
191	int err, i, count;
192	unsigned int off;
193	phandle phandle;
194
195	if (!local_fixups)
196		return 0;
197
198	for_each_property_of_node(local_fixups, prop_fix) {
199
200		/* skip properties added automatically */
201		if (!of_prop_cmp(prop_fix->name, "name") ||
202		    !of_prop_cmp(prop_fix->name, "phandle") ||
203		    !of_prop_cmp(prop_fix->name, "linux,phandle"))
204			continue;
205
206		if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
 
 
207			return -EINVAL;
208		count = prop_fix->length / sizeof(__be32);
 
209
210		for_each_property_of_node(overlay, prop) {
211			if (!of_prop_cmp(prop->name, prop_fix->name))
 
212				break;
213		}
214
215		if (!prop)
 
 
216			return -EINVAL;
 
217
218		for (i = 0; i < count; i++) {
219			off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
220			if ((off + 4) > prop->length)
 
 
 
 
 
221				return -EINVAL;
 
222
223			phandle = be32_to_cpu(*(__be32 *)(prop->value + off));
224			phandle += phandle_delta;
225			*(__be32 *)(prop->value + off) = cpu_to_be32(phandle);
 
 
 
226		}
227	}
228
229	/*
230	 * These nested loops recurse down two subtrees in parallel, where the
231	 * node names in the two subtrees match.
232	 *
233	 * The roots of the subtrees are the overlay's __local_fixups__ node
234	 * and the overlay's root node.
235	 */
236	for_each_child_of_node(local_fixups, child) {
237
238		for_each_child_of_node(overlay, overlay_child)
239			if (!node_name_cmp(child, overlay_child))
240				break;
241
242		if (!overlay_child)
 
 
243			return -EINVAL;
 
244
245		err = adjust_local_phandle_references(child, overlay_child,
246				phandle_delta);
247		if (err)
248			return err;
249	}
250
251	return 0;
252}
253
254/**
255 * of_resolve_phandles - Relocate and resolve overlay against live tree
256 *
257 * @overlay:	Pointer to devicetree overlay to relocate and resolve
258 *
259 * Modify (relocate) values of local phandles in @overlay to a range that
260 * does not conflict with the live expanded devicetree.  Update references
261 * to the local phandles in @overlay.  Update (resolve) phandle references
262 * in @overlay that refer to the live expanded devicetree.
263 *
264 * Phandle values in the live tree are in the range of
265 * 1 .. live_tree_max_phandle().  The range of phandle values in the overlay
266 * also begin with at 1.  Adjust the phandle values in the overlay to begin
267 * at live_tree_max_phandle() + 1.  Update references to the phandles to
268 * the adjusted phandle values.
269 *
270 * The name of each property in the "__fixups__" node in the overlay matches
271 * the name of a symbol (a label) in the live tree.  The values of each
272 * property in the "__fixups__" node is a list of the property values in the
273 * overlay that need to be updated to contain the phandle reference
274 * corresponding to that symbol in the live tree.  Update the references in
275 * the overlay with the phandle values in the live tree.
276 *
277 * @overlay must be detached.
278 *
279 * Resolving and applying @overlay to the live expanded devicetree must be
280 * protected by a mechanism to ensure that multiple overlays are processed
281 * in a single threaded manner so that multiple overlays will not relocate
282 * phandles to overlapping ranges.  The mechanism to enforce this is not
283 * yet implemented.
284 *
285 * Return: %0 on success or a negative error value on error.
 
 
 
 
 
 
286 */
287int of_resolve_phandles(struct device_node *overlay)
288{
289	struct device_node *child, *local_fixups, *refnode;
290	struct device_node *tree_symbols, *overlay_fixups;
291	struct property *prop;
292	const char *refpath;
293	phandle phandle, phandle_delta;
294	int err;
295
296	tree_symbols = NULL;
 
 
 
 
 
 
 
 
 
 
 
 
297
298	if (!overlay) {
299		pr_err("null overlay\n");
300		err = -EINVAL;
301		goto out;
302	}
303	if (!of_node_check_flag(overlay, OF_DETACHED)) {
304		pr_err("overlay not detached\n");
305		err = -EINVAL;
306		goto out;
307	}
308
309	phandle_delta = live_tree_max_phandle() + 1;
310	adjust_overlay_phandles(overlay, phandle_delta);
 
311
312	for_each_child_of_node(overlay, local_fixups)
313		if (!of_node_cmp(local_fixups->name, "__local_fixups__"))
314			break;
315
316	err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
317	if (err)
318		goto out;
319
320	overlay_fixups = NULL;
 
 
321
322	for_each_child_of_node(overlay, child) {
323		if (!of_node_cmp(child->name, "__fixups__"))
324			overlay_fixups = child;
 
 
 
 
325	}
326
327	if (!overlay_fixups) {
328		err = 0;
 
329		goto out;
330	}
331
332	tree_symbols = of_find_node_by_path("/__symbols__");
333	if (!tree_symbols) {
334		pr_err("no symbols in root of device tree.\n");
335		err = -EINVAL;
336		goto out;
337	}
338
339	for_each_property_of_node(overlay_fixups, prop) {
340
341		/* skip properties added automatically */
342		if (!of_prop_cmp(prop->name, "name"))
343			continue;
344
345		err = of_property_read_string(tree_symbols,
346				prop->name, &refpath);
347		if (err)
 
 
348			goto out;
 
349
350		refnode = of_find_node_by_path(refpath);
351		if (!refnode) {
 
 
352			err = -ENOENT;
353			goto out;
354		}
355
356		phandle = refnode->phandle;
357		of_node_put(refnode);
358
359		err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
 
 
 
360		if (err)
361			break;
362	}
363
364out:
365	if (err)
366		pr_err("overlay phandle fixup failed: %d\n", err);
367	of_node_put(tree_symbols);
368
369	return err;
370}
371EXPORT_SYMBOL_GPL(of_resolve_phandles);
v4.6
  1/*
  2 * Functions for dealing with DT resolution
  3 *
  4 * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
  5 * Copyright (C) 2012 Texas Instruments Inc.
  6 *
  7 * This program is free software; you can redistribute it and/or
  8 * modify it under the terms of the GNU General Public License
  9 * version 2 as published by the Free Software Foundation.
 10 */
 11
 
 
 12#include <linux/kernel.h>
 13#include <linux/module.h>
 14#include <linux/of.h>
 15#include <linux/of_device.h>
 16#include <linux/string.h>
 17#include <linux/ctype.h>
 18#include <linux/errno.h>
 19#include <linux/string.h>
 20#include <linux/slab.h>
 21
 22/* illegal phandle value (set when unresolved) */
 23#define OF_PHANDLE_ILLEGAL	0xdeadbeef
 24
 25/**
 26 * Find a node with the give full name by recursively following any of
 27 * the child node links.
 28 */
 29static struct device_node *__of_find_node_by_full_name(struct device_node *node,
 30		const char *full_name)
 31{
 32	struct device_node *child, *found;
 33
 34	if (node == NULL)
 35		return NULL;
 36
 37	/* check */
 38	if (of_node_cmp(node->full_name, full_name) == 0)
 39		return of_node_get(node);
 40
 41	for_each_child_of_node(node, child) {
 42		found = __of_find_node_by_full_name(child, full_name);
 43		if (found != NULL) {
 44			of_node_put(child);
 45			return found;
 46		}
 47	}
 48
 49	return NULL;
 50}
 51
 52/*
 53 * Find live tree's maximum phandle value.
 54 */
 55static phandle of_get_tree_max_phandle(void)
 56{
 57	struct device_node *node;
 58	phandle phandle;
 59	unsigned long flags;
 60
 61	/* now search recursively */
 62	raw_spin_lock_irqsave(&devtree_lock, flags);
 63	phandle = 0;
 64	for_each_of_allnodes(node) {
 65		if (node->phandle != OF_PHANDLE_ILLEGAL &&
 66				node->phandle > phandle)
 67			phandle = node->phandle;
 68	}
 69	raw_spin_unlock_irqrestore(&devtree_lock, flags);
 70
 71	return phandle;
 72}
 73
 74/*
 75 * Adjust a subtree's phandle values by a given delta.
 76 * Makes sure not to just adjust the device node's phandle value,
 77 * but modify the phandle properties values as well.
 78 */
 79static void __of_adjust_tree_phandles(struct device_node *node,
 80		int phandle_delta)
 81{
 82	struct device_node *child;
 83	struct property *prop;
 84	phandle phandle;
 85
 86	/* first adjust the node's phandle direct value */
 87	if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
 88		node->phandle += phandle_delta;
 89
 90	/* now adjust phandle & linux,phandle values */
 91	for_each_property_of_node(node, prop) {
 92
 93		/* only look for these two */
 94		if (of_prop_cmp(prop->name, "phandle") != 0 &&
 95		    of_prop_cmp(prop->name, "linux,phandle") != 0)
 96			continue;
 97
 98		/* must be big enough */
 99		if (prop->length < 4)
100			continue;
101
102		/* read phandle value */
103		phandle = be32_to_cpup(prop->value);
104		if (phandle == OF_PHANDLE_ILLEGAL)	/* unresolved */
105			continue;
106
107		/* adjust */
108		*(uint32_t *)prop->value = cpu_to_be32(node->phandle);
109	}
110
111	/* now do the children recursively */
112	for_each_child_of_node(node, child)
113		__of_adjust_tree_phandles(child, phandle_delta);
114}
115
116static int __of_adjust_phandle_ref(struct device_node *node,
117		struct property *rprop, int value)
118{
119	phandle phandle;
120	struct device_node *refnode;
121	struct property *sprop;
122	char *propval, *propcur, *propend, *nodestr, *propstr, *s;
123	int offset, propcurlen;
124	int err = 0;
125
126	/* make a copy */
127	propval = kmalloc(rprop->length, GFP_KERNEL);
128	if (!propval) {
129		pr_err("%s: Could not copy value of '%s'\n",
130				__func__, rprop->name);
131		return -ENOMEM;
132	}
133	memcpy(propval, rprop->value, rprop->length);
134
135	propend = propval + rprop->length;
136	for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
137		propcurlen = strlen(propcur);
 
138
139		nodestr = propcur;
140		s = strchr(propcur, ':');
141		if (!s) {
142			pr_err("%s: Illegal symbol entry '%s' (1)\n",
143				__func__, propcur);
144			err = -EINVAL;
145			goto err_fail;
146		}
147		*s++ = '\0';
148
149		propstr = s;
150		s = strchr(s, ':');
151		if (!s) {
152			pr_err("%s: Illegal symbol entry '%s' (2)\n",
153				__func__, (char *)rprop->value);
154			err = -EINVAL;
155			goto err_fail;
156		}
 
157
158		*s++ = '\0';
159		err = kstrtoint(s, 10, &offset);
160		if (err != 0) {
161			pr_err("%s: Could get offset '%s'\n",
162				__func__, (char *)rprop->value);
163			goto err_fail;
164		}
165
166		/* look into the resolve node for the full path */
167		refnode = __of_find_node_by_full_name(node, nodestr);
168		if (!refnode) {
169			pr_warn("%s: Could not find refnode '%s'\n",
170				__func__, (char *)rprop->value);
171			continue;
172		}
173
174		/* now find the property */
175		for_each_property_of_node(refnode, sprop) {
176			if (of_prop_cmp(sprop->name, propstr) == 0)
177				break;
178		}
179		of_node_put(refnode);
180
181		if (!sprop) {
182			pr_err("%s: Could not find property '%s'\n",
183				__func__, (char *)rprop->value);
184			err = -ENOENT;
185			goto err_fail;
186		}
187
188		phandle = value;
189		*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
190	}
191
192err_fail:
193	kfree(propval);
194	return err;
195}
196
197/* compare nodes taking into account that 'name' strips out the @ part */
198static int __of_node_name_cmp(const struct device_node *dn1,
199		const struct device_node *dn2)
200{
201	const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
202	const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
203
204	return of_node_cmp(n1, n2);
205}
206
207/*
208 * Adjust the local phandle references by the given phandle delta.
209 * Assumes the existances of a __local_fixups__ node at the root.
210 * Assumes that __of_verify_tree_phandle_references has been called.
211 * Does not take any devtree locks so make sure you call this on a tree
212 * which is at the detached state.
 
 
 
 
213 */
214static int __of_adjust_tree_phandle_references(struct device_node *node,
215		struct device_node *target, int phandle_delta)
216{
217	struct device_node *child, *childtarget;
218	struct property *rprop, *sprop;
219	int err, i, count;
220	unsigned int off;
221	phandle phandle;
222
223	if (node == NULL)
224		return 0;
225
226	for_each_property_of_node(node, rprop) {
227
228		/* skip properties added automatically */
229		if (of_prop_cmp(rprop->name, "name") == 0 ||
230		    of_prop_cmp(rprop->name, "phandle") == 0 ||
231		    of_prop_cmp(rprop->name, "linux,phandle") == 0)
232			continue;
233
234		if ((rprop->length % 4) != 0 || rprop->length == 0) {
235			pr_err("%s: Illegal property (size) '%s' @%s\n",
236					__func__, rprop->name, node->full_name);
237			return -EINVAL;
238		}
239		count = rprop->length / sizeof(__be32);
240
241		/* now find the target property */
242		for_each_property_of_node(target, sprop) {
243			if (of_prop_cmp(sprop->name, rprop->name) == 0)
244				break;
245		}
246
247		if (sprop == NULL) {
248			pr_err("%s: Could not find target property '%s' @%s\n",
249					__func__, rprop->name, node->full_name);
250			return -EINVAL;
251		}
252
253		for (i = 0; i < count; i++) {
254			off = be32_to_cpu(((__be32 *)rprop->value)[i]);
255			/* make sure the offset doesn't overstep (even wrap) */
256			if (off >= sprop->length ||
257					(off + 4) > sprop->length) {
258				pr_err("%s: Illegal property '%s' @%s\n",
259						__func__, rprop->name,
260						node->full_name);
261				return -EINVAL;
262			}
263
264			if (phandle_delta) {
265				/* adjust */
266				phandle = be32_to_cpu(*(__be32 *)(sprop->value + off));
267				phandle += phandle_delta;
268				*(__be32 *)(sprop->value + off) = cpu_to_be32(phandle);
269			}
270		}
271	}
272
273	for_each_child_of_node(node, child) {
 
 
 
 
 
 
 
274
275		for_each_child_of_node(target, childtarget)
276			if (__of_node_name_cmp(child, childtarget) == 0)
277				break;
278
279		if (!childtarget) {
280			pr_err("%s: Could not find target child '%s' @%s\n",
281					__func__, child->name, node->full_name);
282			return -EINVAL;
283		}
284
285		err = __of_adjust_tree_phandle_references(child, childtarget,
286				phandle_delta);
287		if (err != 0)
288			return err;
289	}
290
291	return 0;
292}
293
294/**
295 * of_resolve	- Resolve the given node against the live tree.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296 *
297 * @resolve:	Node to resolve
 
 
 
 
298 *
299 * Perform dynamic Device Tree resolution against the live tree
300 * to the given node to resolve. This depends on the live tree
301 * having a __symbols__ node, and the resolve node the __fixups__ &
302 * __local_fixups__ nodes (if needed).
303 * The result of the operation is a resolve node that it's contents
304 * are fit to be inserted or operate upon the live tree.
305 * Returns 0 on success or a negative error value on error.
306 */
307int of_resolve_phandles(struct device_node *resolve)
308{
309	struct device_node *child, *childroot, *refnode;
310	struct device_node *root_sym, *resolve_sym, *resolve_fix;
311	struct property *rprop;
312	const char *refpath;
313	phandle phandle, phandle_delta;
314	int err;
315
316	/* the resolve node must exist, and be detached */
317	if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
318		return -EINVAL;
319
320	/* first we need to adjust the phandles */
321	phandle_delta = of_get_tree_max_phandle() + 1;
322	__of_adjust_tree_phandles(resolve, phandle_delta);
323
324	/* locate the local fixups */
325	childroot = NULL;
326	for_each_child_of_node(resolve, childroot)
327		if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
328			break;
329
330	if (childroot != NULL) {
331		/* resolve root is guaranteed to be the '/' */
332		err = __of_adjust_tree_phandle_references(childroot,
333				resolve, 0);
334		if (err != 0)
335			return err;
336
337		BUG_ON(__of_adjust_tree_phandle_references(childroot,
338				resolve, phandle_delta));
339	}
340
341	root_sym = NULL;
342	resolve_sym = NULL;
343	resolve_fix = NULL;
344
345	/* this may fail (if no fixups are required) */
346	root_sym = of_find_node_by_path("/__symbols__");
 
347
348	/* locate the symbols & fixups nodes on resolve */
349	for_each_child_of_node(resolve, child) {
 
350
351		if (!resolve_sym &&
352				of_node_cmp(child->name, "__symbols__") == 0)
353			resolve_sym = child;
354
355		if (!resolve_fix &&
356				of_node_cmp(child->name, "__fixups__") == 0)
357			resolve_fix = child;
358
359		/* both found, don't bother anymore */
360		if (resolve_sym && resolve_fix)
361			break;
362	}
363
364	/* we do allow for the case where no fixups are needed */
365	if (!resolve_fix) {
366		err = 0;	/* no error */
367		goto out;
368	}
369
370	/* we need to fixup, but no root symbols... */
371	if (!root_sym) {
 
372		err = -EINVAL;
373		goto out;
374	}
375
376	for_each_property_of_node(resolve_fix, rprop) {
377
378		/* skip properties added automatically */
379		if (of_prop_cmp(rprop->name, "name") == 0)
380			continue;
381
382		err = of_property_read_string(root_sym,
383				rprop->name, &refpath);
384		if (err != 0) {
385			pr_err("%s: Could not find symbol '%s'\n",
386					__func__, rprop->name);
387			goto out;
388		}
389
390		refnode = of_find_node_by_path(refpath);
391		if (!refnode) {
392			pr_err("%s: Could not find node by path '%s'\n",
393					__func__, refpath);
394			err = -ENOENT;
395			goto out;
396		}
397
398		phandle = refnode->phandle;
399		of_node_put(refnode);
400
401		pr_debug("%s: %s phandle is 0x%08x\n",
402				__func__, rprop->name, phandle);
403
404		err = __of_adjust_phandle_ref(resolve, rprop, phandle);
405		if (err)
406			break;
407	}
408
409out:
410	/* NULL is handled by of_node_put as NOP */
411	of_node_put(root_sym);
 
412
413	return err;
414}
415EXPORT_SYMBOL_GPL(of_resolve_phandles);