blob: 3d123b6127891932267ddb3132867fec7b42c7a2 [file] [log] [blame]
Pantelis Antoniou7941b272014-07-04 19:59:20 +03001/*
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
Rob Herring606ad422016-06-15 08:32:18 -050012#define pr_fmt(fmt) "OF: resolver: " fmt
13
Pantelis Antoniou7941b272014-07-04 19:59:20 +030014#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 *__of_find_node_by_full_name(struct device_node *node,
32 const char *full_name)
33{
34 struct device_node *child, *found;
35
Frank Rowand9f27ede2016-10-28 23:26:23 -070036 if (!node)
Pantelis Antoniou7941b272014-07-04 19:59:20 +030037 return NULL;
38
Frank Rowand9f27ede2016-10-28 23:26:23 -070039 if (!of_node_cmp(node->full_name, full_name))
Amitoj Kaur Chawla82f68752016-02-03 23:39:01 +053040 return of_node_get(node);
Pantelis Antoniou7941b272014-07-04 19:59:20 +030041
42 for_each_child_of_node(node, child) {
43 found = __of_find_node_by_full_name(child, full_name);
Amitoj Kaur Chawla82f68752016-02-03 23:39:01 +053044 if (found != NULL) {
45 of_node_put(child);
Pantelis Antoniou7941b272014-07-04 19:59:20 +030046 return found;
Amitoj Kaur Chawla82f68752016-02-03 23:39:01 +053047 }
Pantelis Antoniou7941b272014-07-04 19:59:20 +030048 }
49
50 return NULL;
51}
52
53/*
54 * Find live tree's maximum phandle value.
55 */
Frank Rowandf94823f2016-10-28 23:26:24 -070056static phandle live_tree_max_phandle(void)
Pantelis Antoniou7941b272014-07-04 19:59:20 +030057{
58 struct device_node *node;
59 phandle phandle;
60 unsigned long flags;
61
Pantelis Antoniou7941b272014-07-04 19:59:20 +030062 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.
Pantelis Antoniou7941b272014-07-04 19:59:20 +030076 */
Frank Rowandf94823f2016-10-28 23:26:24 -070077static void adjust_overlay_phandles(struct device_node *node,
Pantelis Antoniou7941b272014-07-04 19:59:20 +030078 int phandle_delta)
79{
80 struct device_node *child;
81 struct property *prop;
82 phandle phandle;
83
Pantelis Antoniou7941b272014-07-04 19:59:20 +030084 if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
85 node->phandle += phandle_delta;
86
Pantelis Antoniou7941b272014-07-04 19:59:20 +030087 for_each_property_of_node(node, prop) {
88
Frank Rowand9f27ede2016-10-28 23:26:23 -070089 if (of_prop_cmp(prop->name, "phandle") &&
90 of_prop_cmp(prop->name, "linux,phandle"))
Pantelis Antoniou7941b272014-07-04 19:59:20 +030091 continue;
92
Pantelis Antoniou7941b272014-07-04 19:59:20 +030093 if (prop->length < 4)
94 continue;
95
Pantelis Antoniou7941b272014-07-04 19:59:20 +030096 phandle = be32_to_cpup(prop->value);
Frank Rowanda67976e2016-10-28 23:26:21 -070097 if (phandle == OF_PHANDLE_ILLEGAL)
Pantelis Antoniou7941b272014-07-04 19:59:20 +030098 continue;
99
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300100 *(uint32_t *)prop->value = cpu_to_be32(node->phandle);
101 }
102
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300103 for_each_child_of_node(node, child)
Frank Rowandf94823f2016-10-28 23:26:24 -0700104 adjust_overlay_phandles(child, phandle_delta);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300105}
106
Frank Rowandf94823f2016-10-28 23:26:24 -0700107static int update_usages_of_a_phandle_reference(struct device_node *node,
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200108 struct property *rprop, int value)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300109{
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
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300117 propval = kmalloc(rprop->length, GFP_KERNEL);
Frank Rowand96d1c8e2016-10-28 23:26:22 -0700118 if (!propval)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300119 return -ENOMEM;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300120 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) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300129 err = -EINVAL;
130 goto err_fail;
131 }
132 *s++ = '\0';
133
134 propstr = s;
135 s = strchr(s, ':');
136 if (!s) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300137 err = -EINVAL;
138 goto err_fail;
139 }
140
141 *s++ = '\0';
142 err = kstrtoint(s, 10, &offset);
Frank Rowand9f27ede2016-10-28 23:26:23 -0700143 if (err)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300144 goto err_fail;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300145
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300146 refnode = __of_find_node_by_full_name(node, nodestr);
Frank Rowand96d1c8e2016-10-28 23:26:22 -0700147 if (!refnode)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300148 continue;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300149
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300150 for_each_property_of_node(refnode, sprop) {
Frank Rowand9f27ede2016-10-28 23:26:23 -0700151 if (!of_prop_cmp(sprop->name, propstr))
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300152 break;
153 }
Amitoj Kaur Chawla82f68752016-02-03 23:39:01 +0530154 of_node_put(refnode);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300155
156 if (!sprop) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300157 err = -ENOENT;
158 goto err_fail;
159 }
160
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200161 phandle = value;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300162 *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
163 }
164
165err_fail:
166 kfree(propval);
167 return err;
168}
169
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200170/* compare nodes taking into account that 'name' strips out the @ part */
171static 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
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300180/*
181 * Adjust the local phandle references by the given phandle delta.
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200182 * 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.
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300186 */
Frank Rowandf94823f2016-10-28 23:26:24 -0700187static int adjust_local_phandle_references(struct device_node *node,
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200188 struct device_node *target, int phandle_delta)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300189{
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200190 struct device_node *child, *childtarget;
191 struct property *rprop, *sprop;
192 int err, i, count;
193 unsigned int off;
194 phandle phandle;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300195
Frank Rowand9f27ede2016-10-28 23:26:23 -0700196 if (!node)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300197 return 0;
198
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200199 for_each_property_of_node(node, rprop) {
200
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300201 /* skip properties added automatically */
Frank Rowand9f27ede2016-10-28 23:26:23 -0700202 if (!of_prop_cmp(rprop->name, "name") ||
203 !of_prop_cmp(rprop->name, "phandle") ||
204 !of_prop_cmp(rprop->name, "linux,phandle"))
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300205 continue;
206
Frank Rowand96d1c8e2016-10-28 23:26:22 -0700207 if ((rprop->length % 4) != 0 || rprop->length == 0)
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200208 return -EINVAL;
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200209 count = rprop->length / sizeof(__be32);
210
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200211 for_each_property_of_node(target, sprop) {
Frank Rowand9f27ede2016-10-28 23:26:23 -0700212 if (!of_prop_cmp(sprop->name, rprop->name))
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200213 break;
214 }
215
Frank Rowand9f27ede2016-10-28 23:26:23 -0700216 if (!sprop)
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200217 return -EINVAL;
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200218
219 for (i = 0; i < count; i++) {
220 off = be32_to_cpu(((__be32 *)rprop->value)[i]);
Frank Rowand96d1c8e2016-10-28 23:26:22 -0700221 if (off >= sprop->length || (off + 4) > sprop->length)
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200222 return -EINVAL;
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200223
224 if (phandle_delta) {
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200225 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)
Frank Rowand9f27ede2016-10-28 23:26:23 -0700235 if (!__of_node_name_cmp(child, childtarget))
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200236 break;
237
Frank Rowand96d1c8e2016-10-28 23:26:22 -0700238 if (!childtarget)
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200239 return -EINVAL;
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200240
Frank Rowandf94823f2016-10-28 23:26:24 -0700241 err = adjust_local_phandle_references(child, childtarget,
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200242 phandle_delta);
Frank Rowand9f27ede2016-10-28 23:26:23 -0700243 if (err)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300244 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 */
263int of_resolve_phandles(struct device_node *resolve)
264{
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200265 struct device_node *child, *childroot, *refnode;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300266 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
Michal Suchanek5de3bbc2016-06-26 22:11:58 +0200272 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);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300277 if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
278 return -EINVAL;
279
Frank Rowandf94823f2016-10-28 23:26:24 -0700280 phandle_delta = live_tree_max_phandle() + 1;
281 adjust_overlay_phandles(resolve, phandle_delta);
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200282
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200283 childroot = NULL;
284 for_each_child_of_node(resolve, childroot)
Frank Rowand9f27ede2016-10-28 23:26:23 -0700285 if (!of_node_cmp(childroot->name, "__local_fixups__"))
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200286 break;
287
288 if (childroot != NULL) {
Frank Rowandf94823f2016-10-28 23:26:24 -0700289 err = adjust_local_phandle_references(childroot,
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200290 resolve, 0);
Frank Rowand9f27ede2016-10-28 23:26:23 -0700291 if (err)
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200292 return err;
293
Frank Rowandf94823f2016-10-28 23:26:24 -0700294 BUG_ON(adjust_local_phandle_references(childroot,
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200295 resolve, phandle_delta));
296 }
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300297
298 root_sym = NULL;
299 resolve_sym = NULL;
300 resolve_fix = NULL;
301
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300302 root_sym = of_find_node_by_path("/__symbols__");
303
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300304 for_each_child_of_node(resolve, child) {
305
Frank Rowand9f27ede2016-10-28 23:26:23 -0700306 if (!resolve_sym && !of_node_cmp(child->name, "__symbols__"))
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300307 resolve_sym = child;
308
Frank Rowand9f27ede2016-10-28 23:26:23 -0700309 if (!resolve_fix && !of_node_cmp(child->name, "__fixups__"))
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300310 resolve_fix = child;
311
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300312 if (resolve_sym && resolve_fix)
313 break;
314 }
315
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300316 if (!resolve_fix) {
Frank Rowanda67976e2016-10-28 23:26:21 -0700317 err = 0;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300318 goto out;
319 }
320
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300321 if (!root_sym) {
Michal Suchanek5de3bbc2016-06-26 22:11:58 +0200322 pr_err("%s: no symbols in root of device tree.\n", __func__);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300323 err = -EINVAL;
324 goto out;
325 }
326
327 for_each_property_of_node(resolve_fix, rprop) {
328
329 /* skip properties added automatically */
Frank Rowand9f27ede2016-10-28 23:26:23 -0700330 if (!of_prop_cmp(rprop->name, "name"))
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300331 continue;
332
333 err = of_property_read_string(root_sym,
334 rprop->name, &refpath);
Frank Rowand9f27ede2016-10-28 23:26:23 -0700335 if (err)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300336 goto out;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300337
338 refnode = of_find_node_by_path(refpath);
339 if (!refnode) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300340 err = -ENOENT;
341 goto out;
342 }
343
344 phandle = refnode->phandle;
345 of_node_put(refnode);
346
Frank Rowandf94823f2016-10-28 23:26:24 -0700347 err = update_usages_of_a_phandle_reference(resolve, rprop, phandle);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300348 if (err)
349 break;
350 }
351
352out:
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300353 of_node_put(root_sym);
354
355 return err;
356}
357EXPORT_SYMBOL_GPL(of_resolve_phandles);