Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.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);