]>
Commit | Line | Data |
---|---|---|
7941b27b PA |
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 | ||
606ad42a RH |
12 | #define pr_fmt(fmt) "OF: resolver: " fmt |
13 | ||
7941b27b PA |
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 | */ | |
31 | static struct device_node *__of_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 == NULL) | |
37 | return NULL; | |
38 | ||
7941b27b | 39 | if (of_node_cmp(node->full_name, full_name) == 0) |
82f68756 | 40 | return of_node_get(node); |
7941b27b PA |
41 | |
42 | for_each_child_of_node(node, child) { | |
43 | found = __of_find_node_by_full_name(child, full_name); | |
82f68756 AKC |
44 | if (found != NULL) { |
45 | of_node_put(child); | |
7941b27b | 46 | return found; |
82f68756 | 47 | } |
7941b27b PA |
48 | } |
49 | ||
50 | return NULL; | |
51 | } | |
52 | ||
53 | /* | |
54 | * Find live tree's maximum phandle value. | |
55 | */ | |
56 | static phandle of_get_tree_max_phandle(void) | |
57 | { | |
58 | struct device_node *node; | |
59 | phandle phandle; | |
60 | unsigned long flags; | |
61 | ||
7941b27b PA |
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. | |
7941b27b PA |
76 | */ |
77 | static void __of_adjust_tree_phandles(struct device_node *node, | |
78 | int phandle_delta) | |
79 | { | |
80 | struct device_node *child; | |
81 | struct property *prop; | |
82 | phandle phandle; | |
83 | ||
7941b27b PA |
84 | if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL) |
85 | node->phandle += phandle_delta; | |
86 | ||
7941b27b PA |
87 | for_each_property_of_node(node, prop) { |
88 | ||
7941b27b PA |
89 | if (of_prop_cmp(prop->name, "phandle") != 0 && |
90 | of_prop_cmp(prop->name, "linux,phandle") != 0) | |
91 | continue; | |
92 | ||
7941b27b PA |
93 | if (prop->length < 4) |
94 | continue; | |
95 | ||
7941b27b | 96 | phandle = be32_to_cpup(prop->value); |
a67976ec | 97 | if (phandle == OF_PHANDLE_ILLEGAL) |
7941b27b PA |
98 | continue; |
99 | ||
7941b27b PA |
100 | *(uint32_t *)prop->value = cpu_to_be32(node->phandle); |
101 | } | |
102 | ||
7941b27b PA |
103 | for_each_child_of_node(node, child) |
104 | __of_adjust_tree_phandles(child, phandle_delta); | |
105 | } | |
106 | ||
da56d04c PA |
107 | static int __of_adjust_phandle_ref(struct device_node *node, |
108 | struct property *rprop, int value) | |
7941b27b PA |
109 | { |
110 | phandle phandle; | |
111 | struct device_node *refnode; | |
112 | struct property *sprop; | |
113 | char *propval, *propcur, *propend, *nodestr, *propstr, *s; | |
114 | int offset, propcurlen; | |
115 | int err = 0; | |
116 | ||
7941b27b | 117 | propval = kmalloc(rprop->length, GFP_KERNEL); |
96d1c8e8 | 118 | if (!propval) |
7941b27b | 119 | return -ENOMEM; |
7941b27b PA |
120 | memcpy(propval, rprop->value, rprop->length); |
121 | ||
122 | propend = propval + rprop->length; | |
123 | for (propcur = propval; propcur < propend; propcur += propcurlen + 1) { | |
124 | propcurlen = strlen(propcur); | |
125 | ||
126 | nodestr = propcur; | |
127 | s = strchr(propcur, ':'); | |
128 | if (!s) { | |
7941b27b PA |
129 | err = -EINVAL; |
130 | goto err_fail; | |
131 | } | |
132 | *s++ = '\0'; | |
133 | ||
134 | propstr = s; | |
135 | s = strchr(s, ':'); | |
136 | if (!s) { | |
7941b27b PA |
137 | err = -EINVAL; |
138 | goto err_fail; | |
139 | } | |
140 | ||
141 | *s++ = '\0'; | |
142 | err = kstrtoint(s, 10, &offset); | |
96d1c8e8 | 143 | if (err != 0) |
7941b27b | 144 | goto err_fail; |
7941b27b | 145 | |
7941b27b | 146 | refnode = __of_find_node_by_full_name(node, nodestr); |
96d1c8e8 | 147 | if (!refnode) |
7941b27b | 148 | continue; |
7941b27b | 149 | |
7941b27b PA |
150 | for_each_property_of_node(refnode, sprop) { |
151 | if (of_prop_cmp(sprop->name, propstr) == 0) | |
152 | break; | |
153 | } | |
82f68756 | 154 | of_node_put(refnode); |
7941b27b PA |
155 | |
156 | if (!sprop) { | |
7941b27b PA |
157 | err = -ENOENT; |
158 | goto err_fail; | |
159 | } | |
160 | ||
da56d04c | 161 | phandle = value; |
7941b27b PA |
162 | *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle); |
163 | } | |
164 | ||
165 | err_fail: | |
166 | kfree(propval); | |
167 | return err; | |
168 | } | |
169 | ||
da56d04c PA |
170 | /* compare nodes taking into account that 'name' strips out the @ part */ |
171 | static int __of_node_name_cmp(const struct device_node *dn1, | |
172 | const struct device_node *dn2) | |
173 | { | |
174 | const char *n1 = strrchr(dn1->full_name, '/') ? : "/"; | |
175 | const char *n2 = strrchr(dn2->full_name, '/') ? : "/"; | |
176 | ||
177 | return of_node_cmp(n1, n2); | |
178 | } | |
179 | ||
7941b27b PA |
180 | /* |
181 | * Adjust the local phandle references by the given phandle delta. | |
da56d04c PA |
182 | * Assumes the existances of a __local_fixups__ node at the root. |
183 | * Assumes that __of_verify_tree_phandle_references has been called. | |
184 | * Does not take any devtree locks so make sure you call this on a tree | |
185 | * which is at the detached state. | |
7941b27b PA |
186 | */ |
187 | static int __of_adjust_tree_phandle_references(struct device_node *node, | |
da56d04c | 188 | struct device_node *target, int phandle_delta) |
7941b27b | 189 | { |
da56d04c PA |
190 | struct device_node *child, *childtarget; |
191 | struct property *rprop, *sprop; | |
192 | int err, i, count; | |
193 | unsigned int off; | |
194 | phandle phandle; | |
7941b27b | 195 | |
da56d04c | 196 | if (node == NULL) |
7941b27b PA |
197 | return 0; |
198 | ||
da56d04c PA |
199 | for_each_property_of_node(node, rprop) { |
200 | ||
7941b27b | 201 | /* skip properties added automatically */ |
da56d04c PA |
202 | if (of_prop_cmp(rprop->name, "name") == 0 || |
203 | of_prop_cmp(rprop->name, "phandle") == 0 || | |
204 | of_prop_cmp(rprop->name, "linux,phandle") == 0) | |
7941b27b PA |
205 | continue; |
206 | ||
96d1c8e8 | 207 | if ((rprop->length % 4) != 0 || rprop->length == 0) |
da56d04c | 208 | return -EINVAL; |
da56d04c PA |
209 | count = rprop->length / sizeof(__be32); |
210 | ||
da56d04c PA |
211 | for_each_property_of_node(target, sprop) { |
212 | if (of_prop_cmp(sprop->name, rprop->name) == 0) | |
213 | break; | |
214 | } | |
215 | ||
96d1c8e8 | 216 | if (sprop == NULL) |
da56d04c | 217 | return -EINVAL; |
da56d04c PA |
218 | |
219 | for (i = 0; i < count; i++) { | |
220 | off = be32_to_cpu(((__be32 *)rprop->value)[i]); | |
96d1c8e8 | 221 | if (off >= sprop->length || (off + 4) > sprop->length) |
da56d04c | 222 | return -EINVAL; |
da56d04c PA |
223 | |
224 | if (phandle_delta) { | |
da56d04c PA |
225 | phandle = be32_to_cpu(*(__be32 *)(sprop->value + off)); |
226 | phandle += phandle_delta; | |
227 | *(__be32 *)(sprop->value + off) = cpu_to_be32(phandle); | |
228 | } | |
229 | } | |
230 | } | |
231 | ||
232 | for_each_child_of_node(node, child) { | |
233 | ||
234 | for_each_child_of_node(target, childtarget) | |
235 | if (__of_node_name_cmp(child, childtarget) == 0) | |
236 | break; | |
237 | ||
96d1c8e8 | 238 | if (!childtarget) |
da56d04c | 239 | return -EINVAL; |
da56d04c PA |
240 | |
241 | err = __of_adjust_tree_phandle_references(child, childtarget, | |
242 | phandle_delta); | |
243 | if (err != 0) | |
7941b27b PA |
244 | return err; |
245 | } | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | /** | |
251 | * of_resolve - Resolve the given node against the live tree. | |
252 | * | |
253 | * @resolve: Node to resolve | |
254 | * | |
255 | * Perform dynamic Device Tree resolution against the live tree | |
256 | * to the given node to resolve. This depends on the live tree | |
257 | * having a __symbols__ node, and the resolve node the __fixups__ & | |
258 | * __local_fixups__ nodes (if needed). | |
259 | * The result of the operation is a resolve node that it's contents | |
260 | * are fit to be inserted or operate upon the live tree. | |
261 | * Returns 0 on success or a negative error value on error. | |
262 | */ | |
263 | int of_resolve_phandles(struct device_node *resolve) | |
264 | { | |
da56d04c | 265 | struct device_node *child, *childroot, *refnode; |
7941b27b PA |
266 | struct device_node *root_sym, *resolve_sym, *resolve_fix; |
267 | struct property *rprop; | |
268 | const char *refpath; | |
269 | phandle phandle, phandle_delta; | |
270 | int err; | |
271 | ||
5de3bbc8 MS |
272 | if (!resolve) |
273 | pr_err("%s: null node\n", __func__); | |
274 | if (resolve && !of_node_check_flag(resolve, OF_DETACHED)) | |
275 | pr_err("%s: node %s not detached\n", __func__, | |
276 | resolve->full_name); | |
7941b27b PA |
277 | if (!resolve || !of_node_check_flag(resolve, OF_DETACHED)) |
278 | return -EINVAL; | |
279 | ||
7941b27b PA |
280 | phandle_delta = of_get_tree_max_phandle() + 1; |
281 | __of_adjust_tree_phandles(resolve, phandle_delta); | |
da56d04c | 282 | |
da56d04c PA |
283 | childroot = NULL; |
284 | for_each_child_of_node(resolve, childroot) | |
285 | if (of_node_cmp(childroot->name, "__local_fixups__") == 0) | |
286 | break; | |
287 | ||
288 | if (childroot != NULL) { | |
da56d04c PA |
289 | err = __of_adjust_tree_phandle_references(childroot, |
290 | resolve, 0); | |
291 | if (err != 0) | |
292 | return err; | |
293 | ||
294 | BUG_ON(__of_adjust_tree_phandle_references(childroot, | |
295 | resolve, phandle_delta)); | |
296 | } | |
7941b27b PA |
297 | |
298 | root_sym = NULL; | |
299 | resolve_sym = NULL; | |
300 | resolve_fix = NULL; | |
301 | ||
7941b27b PA |
302 | root_sym = of_find_node_by_path("/__symbols__"); |
303 | ||
7941b27b PA |
304 | for_each_child_of_node(resolve, child) { |
305 | ||
306 | if (!resolve_sym && | |
307 | of_node_cmp(child->name, "__symbols__") == 0) | |
308 | resolve_sym = child; | |
309 | ||
310 | if (!resolve_fix && | |
311 | of_node_cmp(child->name, "__fixups__") == 0) | |
312 | resolve_fix = child; | |
313 | ||
7941b27b PA |
314 | if (resolve_sym && resolve_fix) |
315 | break; | |
316 | } | |
317 | ||
7941b27b | 318 | if (!resolve_fix) { |
a67976ec | 319 | err = 0; |
7941b27b PA |
320 | goto out; |
321 | } | |
322 | ||
7941b27b | 323 | if (!root_sym) { |
5de3bbc8 | 324 | pr_err("%s: no symbols in root of device tree.\n", __func__); |
7941b27b PA |
325 | err = -EINVAL; |
326 | goto out; | |
327 | } | |
328 | ||
329 | for_each_property_of_node(resolve_fix, rprop) { | |
330 | ||
331 | /* skip properties added automatically */ | |
332 | if (of_prop_cmp(rprop->name, "name") == 0) | |
333 | continue; | |
334 | ||
335 | err = of_property_read_string(root_sym, | |
336 | rprop->name, &refpath); | |
96d1c8e8 | 337 | if (err != 0) |
7941b27b | 338 | goto out; |
7941b27b PA |
339 | |
340 | refnode = of_find_node_by_path(refpath); | |
341 | if (!refnode) { | |
7941b27b PA |
342 | err = -ENOENT; |
343 | goto out; | |
344 | } | |
345 | ||
346 | phandle = refnode->phandle; | |
347 | of_node_put(refnode); | |
348 | ||
da56d04c | 349 | err = __of_adjust_phandle_ref(resolve, rprop, phandle); |
7941b27b PA |
350 | if (err) |
351 | break; | |
352 | } | |
353 | ||
354 | out: | |
7941b27b PA |
355 | of_node_put(root_sym); |
356 | ||
357 | return err; | |
358 | } | |
359 | EXPORT_SYMBOL_GPL(of_resolve_phandles); |