blob: 76c09cb57eae3f9174b1ec1e9c9ccba5203cee90 [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 */
Frank Rowandfad556b2016-10-28 23:26:25 -070031static struct device_node *find_node_by_full_name(struct device_node *node,
Pantelis Antoniou7941b272014-07-04 19:59:20 +030032 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) {
Frank Rowandfad556b2016-10-28 23:26:25 -070043 found = 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 Rowand25e16872016-10-28 23:26:26 -070077static void adjust_overlay_phandles(struct device_node *overlay,
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
Frank Rowand25e16872016-10-28 23:26:26 -070084 if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL)
85 overlay->phandle += phandle_delta;
Pantelis Antoniou7941b272014-07-04 19:59:20 +030086
Frank Rowand25e16872016-10-28 23:26:26 -070087 for_each_property_of_node(overlay, prop) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +030088
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
Frank Rowand25e16872016-10-28 23:26:26 -0700100 *(uint32_t *)prop->value = cpu_to_be32(overlay->phandle);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300101 }
102
Frank Rowand25e16872016-10-28 23:26:26 -0700103 for_each_child_of_node(overlay, 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 Rowand25e16872016-10-28 23:26:26 -0700107static int update_usages_of_a_phandle_reference(struct device_node *overlay,
108 struct property *prop_fixup, phandle phandle)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300109{
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300110 struct device_node *refnode;
Frank Rowand25e16872016-10-28 23:26:26 -0700111 struct property *prop;
112 char *value, *cur, *end, *node_path, *prop_name, *s;
113 int offset, len;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300114 int err = 0;
115
Frank Rowand25e16872016-10-28 23:26:26 -0700116 value = kmalloc(prop_fixup->length, GFP_KERNEL);
117 if (!value)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300118 return -ENOMEM;
Frank Rowand25e16872016-10-28 23:26:26 -0700119 memcpy(value, prop_fixup->value, prop_fixup->length);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300120
Frank Rowand25e16872016-10-28 23:26:26 -0700121 end = value + prop_fixup->length;
122 for (cur = value; cur < end; cur += len + 1) {
123 len = strlen(cur);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300124
Frank Rowand25e16872016-10-28 23:26:26 -0700125 node_path = cur;
126 s = strchr(cur, ':');
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300127 if (!s) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300128 err = -EINVAL;
129 goto err_fail;
130 }
131 *s++ = '\0';
132
Frank Rowand25e16872016-10-28 23:26:26 -0700133 prop_name = s;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300134 s = strchr(s, ':');
135 if (!s) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300136 err = -EINVAL;
137 goto err_fail;
138 }
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300139 *s++ = '\0';
Frank Rowand624ab2a2016-10-28 23:26:27 -0700140
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300141 err = kstrtoint(s, 10, &offset);
Frank Rowand9f27ede2016-10-28 23:26:23 -0700142 if (err)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300143 goto err_fail;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300144
Frank Rowand25e16872016-10-28 23:26:26 -0700145 refnode = find_node_by_full_name(overlay, node_path);
Frank Rowand96d1c8e2016-10-28 23:26:22 -0700146 if (!refnode)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300147 continue;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300148
Frank Rowand25e16872016-10-28 23:26:26 -0700149 for_each_property_of_node(refnode, prop) {
150 if (!of_prop_cmp(prop->name, prop_name))
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300151 break;
152 }
Amitoj Kaur Chawla82f68752016-02-03 23:39:01 +0530153 of_node_put(refnode);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300154
Frank Rowand25e16872016-10-28 23:26:26 -0700155 if (!prop) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300156 err = -ENOENT;
157 goto err_fail;
158 }
159
Frank Rowand25e16872016-10-28 23:26:26 -0700160 *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300161 }
162
163err_fail:
Frank Rowand25e16872016-10-28 23:26:26 -0700164 kfree(value);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300165 return err;
166}
167
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200168/* compare nodes taking into account that 'name' strips out the @ part */
Frank Rowandfad556b2016-10-28 23:26:25 -0700169static int node_name_cmp(const struct device_node *dn1,
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200170 const struct device_node *dn2)
171{
172 const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
173 const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
174
175 return of_node_cmp(n1, n2);
176}
177
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300178/*
179 * Adjust the local phandle references by the given phandle delta.
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200180 * Assumes the existances of a __local_fixups__ node at the root.
181 * Assumes that __of_verify_tree_phandle_references has been called.
182 * Does not take any devtree locks so make sure you call this on a tree
183 * which is at the detached state.
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300184 */
Frank Rowand25e16872016-10-28 23:26:26 -0700185static int adjust_local_phandle_references(struct device_node *local_fixups,
186 struct device_node *overlay, int phandle_delta)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300187{
Frank Rowand25e16872016-10-28 23:26:26 -0700188 struct device_node *child, *overlay_child;
189 struct property *prop_fix, *prop;
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200190 int err, i, count;
191 unsigned int off;
192 phandle phandle;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300193
Frank Rowand25e16872016-10-28 23:26:26 -0700194 if (!local_fixups)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300195 return 0;
196
Frank Rowand25e16872016-10-28 23:26:26 -0700197 for_each_property_of_node(local_fixups, prop_fix) {
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200198
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300199 /* skip properties added automatically */
Frank Rowand25e16872016-10-28 23:26:26 -0700200 if (!of_prop_cmp(prop_fix->name, "name") ||
201 !of_prop_cmp(prop_fix->name, "phandle") ||
202 !of_prop_cmp(prop_fix->name, "linux,phandle"))
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300203 continue;
204
Frank Rowand25e16872016-10-28 23:26:26 -0700205 if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200206 return -EINVAL;
Frank Rowand25e16872016-10-28 23:26:26 -0700207 count = prop_fix->length / sizeof(__be32);
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200208
Frank Rowand25e16872016-10-28 23:26:26 -0700209 for_each_property_of_node(overlay, prop) {
210 if (!of_prop_cmp(prop->name, prop_fix->name))
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200211 break;
212 }
213
Frank Rowand25e16872016-10-28 23:26:26 -0700214 if (!prop)
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200215 return -EINVAL;
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200216
217 for (i = 0; i < count; i++) {
Frank Rowand25e16872016-10-28 23:26:26 -0700218 off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
Frank Rowandea8229b2016-10-28 23:26:28 -0700219 if ((off + 4) > prop->length)
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200220 return -EINVAL;
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200221
Frank Rowand624ab2a2016-10-28 23:26:27 -0700222 phandle = be32_to_cpu(*(__be32 *)(prop->value + off));
223 phandle += phandle_delta;
224 *(__be32 *)(prop->value + off) = cpu_to_be32(phandle);
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200225 }
226 }
227
Frank Rowand25e16872016-10-28 23:26:26 -0700228 for_each_child_of_node(local_fixups, child) {
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200229
Frank Rowand25e16872016-10-28 23:26:26 -0700230 for_each_child_of_node(overlay, overlay_child)
231 if (!node_name_cmp(child, overlay_child))
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200232 break;
233
Frank Rowand25e16872016-10-28 23:26:26 -0700234 if (!overlay_child)
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200235 return -EINVAL;
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200236
Frank Rowand25e16872016-10-28 23:26:26 -0700237 err = adjust_local_phandle_references(child, overlay_child,
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200238 phandle_delta);
Frank Rowand9f27ede2016-10-28 23:26:23 -0700239 if (err)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300240 return err;
241 }
242
243 return 0;
244}
245
246/**
247 * of_resolve - Resolve the given node against the live tree.
248 *
249 * @resolve: Node to resolve
250 *
251 * Perform dynamic Device Tree resolution against the live tree
252 * to the given node to resolve. This depends on the live tree
253 * having a __symbols__ node, and the resolve node the __fixups__ &
254 * __local_fixups__ nodes (if needed).
255 * The result of the operation is a resolve node that it's contents
256 * are fit to be inserted or operate upon the live tree.
257 * Returns 0 on success or a negative error value on error.
258 */
Frank Rowand25e16872016-10-28 23:26:26 -0700259int of_resolve_phandles(struct device_node *overlay)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300260{
Frank Rowand25e16872016-10-28 23:26:26 -0700261 struct device_node *child, *local_fixups, *refnode;
262 struct device_node *tree_symbols, *overlay_symbols, *overlay_fixups;
263 struct property *prop;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300264 const char *refpath;
265 phandle phandle, phandle_delta;
266 int err;
267
Frank Rowand624ab2a2016-10-28 23:26:27 -0700268 if (!overlay) {
269 pr_err("null overlay\n");
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300270 return -EINVAL;
Frank Rowand624ab2a2016-10-28 23:26:27 -0700271 }
272 if (!of_node_check_flag(overlay, OF_DETACHED)) {
273 pr_err("overlay not detached\n");
274 return -EINVAL;
275 }
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300276
Frank Rowandf94823f2016-10-28 23:26:24 -0700277 phandle_delta = live_tree_max_phandle() + 1;
Frank Rowand25e16872016-10-28 23:26:26 -0700278 adjust_overlay_phandles(overlay, phandle_delta);
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200279
Frank Rowand25e16872016-10-28 23:26:26 -0700280 for_each_child_of_node(overlay, local_fixups)
281 if (!of_node_cmp(local_fixups->name, "__local_fixups__"))
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200282 break;
283
Frank Rowand624ab2a2016-10-28 23:26:27 -0700284 err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
285 if (err)
286 return err;
Pantelis Antoniouda56d042014-10-28 22:33:49 +0200287
Frank Rowand25e16872016-10-28 23:26:26 -0700288 overlay_symbols = NULL;
289 overlay_fixups = NULL;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300290
Frank Rowand25e16872016-10-28 23:26:26 -0700291 tree_symbols = of_find_node_by_path("/__symbols__");
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300292
Frank Rowand25e16872016-10-28 23:26:26 -0700293 for_each_child_of_node(overlay, child) {
Frank Rowand624ab2a2016-10-28 23:26:27 -0700294 if (!of_node_cmp(child->name, "__symbols__"))
Frank Rowand25e16872016-10-28 23:26:26 -0700295 overlay_symbols = child;
Frank Rowand624ab2a2016-10-28 23:26:27 -0700296 if (!of_node_cmp(child->name, "__fixups__"))
Frank Rowand25e16872016-10-28 23:26:26 -0700297 overlay_fixups = child;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300298 }
299
Frank Rowand25e16872016-10-28 23:26:26 -0700300 if (!overlay_fixups) {
Frank Rowanda67976e2016-10-28 23:26:21 -0700301 err = 0;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300302 goto out;
303 }
304
Frank Rowand25e16872016-10-28 23:26:26 -0700305 if (!tree_symbols) {
Frank Rowand624ab2a2016-10-28 23:26:27 -0700306 pr_err("no symbols in root of device tree.\n");
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300307 err = -EINVAL;
308 goto out;
309 }
310
Frank Rowand25e16872016-10-28 23:26:26 -0700311 for_each_property_of_node(overlay_fixups, prop) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300312
313 /* skip properties added automatically */
Frank Rowand25e16872016-10-28 23:26:26 -0700314 if (!of_prop_cmp(prop->name, "name"))
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300315 continue;
316
Frank Rowand25e16872016-10-28 23:26:26 -0700317 err = of_property_read_string(tree_symbols,
318 prop->name, &refpath);
Frank Rowand9f27ede2016-10-28 23:26:23 -0700319 if (err)
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300320 goto out;
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300321
322 refnode = of_find_node_by_path(refpath);
323 if (!refnode) {
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300324 err = -ENOENT;
325 goto out;
326 }
327
328 phandle = refnode->phandle;
329 of_node_put(refnode);
330
Frank Rowand25e16872016-10-28 23:26:26 -0700331 err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300332 if (err)
333 break;
334 }
335
336out:
Frank Rowand25e16872016-10-28 23:26:26 -0700337 of_node_put(tree_symbols);
Pantelis Antoniou7941b272014-07-04 19:59:20 +0300338
339 return err;
340}
341EXPORT_SYMBOL_GPL(of_resolve_phandles);