aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/of/fdt.c
diff options
context:
space:
mode:
authorRob Herring <robh@kernel.org>2014-04-02 16:10:14 -0400
committerRob Herring <robh@kernel.org>2014-04-30 01:59:15 -0400
commite6a6928c3ea1d0195ed75a091e345696b916c09b (patch)
tree78d750ff16e29fa7aebafa3983e69026f498dfb2 /drivers/of/fdt.c
parent9d0c4dfedd96ee54fc075b16d02f82499c8cc3a6 (diff)
of/fdt: Convert FDT functions to use libfdt
The kernel FDT functions predate libfdt and are much more limited in functionality. Also, the kernel functions and libfdt functions are not compatible with each other because they have different definitions of node offsets. To avoid this incompatibility and in preparation to add more FDT parsing functions which will need libfdt, let's first convert the existing code to use libfdt. The FDT unflattening, top-level FDT scanning, and property retrieval functions are converted to use libfdt. The scanning code should be re-worked to be more efficient and understandable by using libfdt to find nodes directly by path or compatible strings. Signed-off-by: Rob Herring <robh@kernel.org> Tested-by: Michal Simek <michal.simek@xilinx.com> Tested-by: Grant Likely <grant.likely@linaro.org> Tested-by: Stephen Chivers <schivers@csc.com>
Diffstat (limited to 'drivers/of/fdt.c')
-rw-r--r--drivers/of/fdt.c209
1 files changed, 57 insertions, 152 deletions
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 1d1582bb81fb..8e820a2b106d 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -19,58 +19,11 @@
19#include <linux/string.h> 19#include <linux/string.h>
20#include <linux/errno.h> 20#include <linux/errno.h>
21#include <linux/slab.h> 21#include <linux/slab.h>
22#include <linux/libfdt.h>
22 23
23#include <asm/setup.h> /* for COMMAND_LINE_SIZE */ 24#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
24#include <asm/page.h> 25#include <asm/page.h>
25 26
26char *of_fdt_get_string(struct boot_param_header *blob, u32 offset)
27{
28 return ((char *)blob) +
29 be32_to_cpu(blob->off_dt_strings) + offset;
30}
31
32/**
33 * of_fdt_get_property - Given a node in the given flat blob, return
34 * the property ptr
35 */
36void *of_fdt_get_property(struct boot_param_header *blob,
37 unsigned long node, const char *name,
38 int *size)
39{
40 unsigned long p = node;
41
42 do {
43 u32 tag = be32_to_cpup((__be32 *)p);
44 u32 sz, noff;
45 const char *nstr;
46
47 p += 4;
48 if (tag == OF_DT_NOP)
49 continue;
50 if (tag != OF_DT_PROP)
51 return NULL;
52
53 sz = be32_to_cpup((__be32 *)p);
54 noff = be32_to_cpup((__be32 *)(p + 4));
55 p += 8;
56 if (be32_to_cpu(blob->version) < 0x10)
57 p = ALIGN(p, sz >= 8 ? 8 : 4);
58
59 nstr = of_fdt_get_string(blob, noff);
60 if (nstr == NULL) {
61 pr_warning("Can't find property index name !\n");
62 return NULL;
63 }
64 if (strcmp(name, nstr) == 0) {
65 if (size)
66 *size = sz;
67 return (void *)p;
68 }
69 p += sz;
70 p = ALIGN(p, 4);
71 } while (1);
72}
73
74/** 27/**
75 * of_fdt_is_compatible - Return true if given node from the given blob has 28 * of_fdt_is_compatible - Return true if given node from the given blob has
76 * compat in its compatible list 29 * compat in its compatible list
@@ -88,7 +41,7 @@ int of_fdt_is_compatible(struct boot_param_header *blob,
88 int cplen; 41 int cplen;
89 unsigned long l, score = 0; 42 unsigned long l, score = 0;
90 43
91 cp = of_fdt_get_property(blob, node, "compatible", &cplen); 44 cp = fdt_getprop(blob, node, "compatible", &cplen);
92 if (cp == NULL) 45 if (cp == NULL)
93 return 0; 46 return 0;
94 while (cplen > 0) { 47 while (cplen > 0) {
@@ -147,28 +100,27 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size,
147 */ 100 */
148static void * unflatten_dt_node(struct boot_param_header *blob, 101static void * unflatten_dt_node(struct boot_param_header *blob,
149 void *mem, 102 void *mem,
150 void **p, 103 int *poffset,
151 struct device_node *dad, 104 struct device_node *dad,
152 struct device_node ***allnextpp, 105 struct device_node ***allnextpp,
153 unsigned long fpsize) 106 unsigned long fpsize)
154{ 107{
108 const __be32 *p;
155 struct device_node *np; 109 struct device_node *np;
156 struct property *pp, **prev_pp = NULL; 110 struct property *pp, **prev_pp = NULL;
157 char *pathp; 111 const char *pathp;
158 u32 tag;
159 unsigned int l, allocl; 112 unsigned int l, allocl;
113 static int depth = 0;
114 int old_depth;
115 int offset;
160 int has_name = 0; 116 int has_name = 0;
161 int new_format = 0; 117 int new_format = 0;
162 118
163 tag = be32_to_cpup(*p); 119 pathp = fdt_get_name(blob, *poffset, &l);
164 if (tag != OF_DT_BEGIN_NODE) { 120 if (!pathp)
165 pr_err("Weird tag at start of node: %x\n", tag);
166 return mem; 121 return mem;
167 } 122
168 *p += 4; 123 allocl = l++;
169 pathp = *p;
170 l = allocl = strlen(pathp) + 1;
171 *p = PTR_ALIGN(*p + l, 4);
172 124
173 /* version 0x10 has a more compact unit name here instead of the full 125 /* version 0x10 has a more compact unit name here instead of the full
174 * path. we accumulate the full path size using "fpsize", we'll rebuild 126 * path. we accumulate the full path size using "fpsize", we'll rebuild
@@ -186,7 +138,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
186 fpsize = 1; 138 fpsize = 1;
187 allocl = 2; 139 allocl = 2;
188 l = 1; 140 l = 1;
189 *pathp = '\0'; 141 pathp = "";
190 } else { 142 } else {
191 /* account for '/' and path size minus terminal 0 143 /* account for '/' and path size minus terminal 0
192 * already in 'l' 144 * already in 'l'
@@ -233,32 +185,23 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
233 } 185 }
234 } 186 }
235 /* process properties */ 187 /* process properties */
236 while (1) { 188 for (offset = fdt_first_property_offset(blob, *poffset);
237 u32 sz, noff; 189 (offset >= 0);
238 char *pname; 190 (offset = fdt_next_property_offset(blob, offset))) {
239 191 const char *pname;
240 tag = be32_to_cpup(*p); 192 u32 sz;
241 if (tag == OF_DT_NOP) { 193
242 *p += 4; 194 if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
243 continue; 195 offset = -FDT_ERR_INTERNAL;
244 }
245 if (tag != OF_DT_PROP)
246 break; 196 break;
247 *p += 4; 197 }
248 sz = be32_to_cpup(*p); 198
249 noff = be32_to_cpup(*p + 4);
250 *p += 8;
251 if (be32_to_cpu(blob->version) < 0x10)
252 *p = PTR_ALIGN(*p, sz >= 8 ? 8 : 4);
253
254 pname = of_fdt_get_string(blob, noff);
255 if (pname == NULL) { 199 if (pname == NULL) {
256 pr_info("Can't find property name in list !\n"); 200 pr_info("Can't find property name in list !\n");
257 break; 201 break;
258 } 202 }
259 if (strcmp(pname, "name") == 0) 203 if (strcmp(pname, "name") == 0)
260 has_name = 1; 204 has_name = 1;
261 l = strlen(pname) + 1;
262 pp = unflatten_dt_alloc(&mem, sizeof(struct property), 205 pp = unflatten_dt_alloc(&mem, sizeof(struct property),
263 __alignof__(struct property)); 206 __alignof__(struct property));
264 if (allnextpp) { 207 if (allnextpp) {
@@ -270,26 +213,25 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
270 if ((strcmp(pname, "phandle") == 0) || 213 if ((strcmp(pname, "phandle") == 0) ||
271 (strcmp(pname, "linux,phandle") == 0)) { 214 (strcmp(pname, "linux,phandle") == 0)) {
272 if (np->phandle == 0) 215 if (np->phandle == 0)
273 np->phandle = be32_to_cpup((__be32*)*p); 216 np->phandle = be32_to_cpup(p);
274 } 217 }
275 /* And we process the "ibm,phandle" property 218 /* And we process the "ibm,phandle" property
276 * used in pSeries dynamic device tree 219 * used in pSeries dynamic device tree
277 * stuff */ 220 * stuff */
278 if (strcmp(pname, "ibm,phandle") == 0) 221 if (strcmp(pname, "ibm,phandle") == 0)
279 np->phandle = be32_to_cpup((__be32 *)*p); 222 np->phandle = be32_to_cpup(p);
280 pp->name = pname; 223 pp->name = (char *)pname;
281 pp->length = sz; 224 pp->length = sz;
282 pp->value = *p; 225 pp->value = (__be32 *)p;
283 *prev_pp = pp; 226 *prev_pp = pp;
284 prev_pp = &pp->next; 227 prev_pp = &pp->next;
285 } 228 }
286 *p = PTR_ALIGN((*p) + sz, 4);
287 } 229 }
288 /* with version 0x10 we may not have the name property, recreate 230 /* with version 0x10 we may not have the name property, recreate
289 * it here from the unit name if absent 231 * it here from the unit name if absent
290 */ 232 */
291 if (!has_name) { 233 if (!has_name) {
292 char *p1 = pathp, *ps = pathp, *pa = NULL; 234 const char *p1 = pathp, *ps = pathp, *pa = NULL;
293 int sz; 235 int sz;
294 236
295 while (*p1) { 237 while (*p1) {
@@ -326,19 +268,18 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
326 if (!np->type) 268 if (!np->type)
327 np->type = "<NULL>"; 269 np->type = "<NULL>";
328 } 270 }
329 while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) { 271
330 if (tag == OF_DT_NOP) 272 old_depth = depth;
331 *p += 4; 273 *poffset = fdt_next_node(blob, *poffset, &depth);
332 else 274 if (depth < 0)
333 mem = unflatten_dt_node(blob, mem, p, np, allnextpp, 275 depth = 0;
334 fpsize); 276 while (*poffset > 0 && depth > old_depth)
335 tag = be32_to_cpup(*p); 277 mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
336 } 278 fpsize);
337 if (tag != OF_DT_END_NODE) { 279
338 pr_err("Weird tag at end of node: %x\n", tag); 280 if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
339 return mem; 281 pr_err("unflatten: error %d processing FDT\n", *poffset);
340 } 282
341 *p += 4;
342 return mem; 283 return mem;
343} 284}
344 285
@@ -359,7 +300,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
359 void * (*dt_alloc)(u64 size, u64 align)) 300 void * (*dt_alloc)(u64 size, u64 align))
360{ 301{
361 unsigned long size; 302 unsigned long size;
362 void *start, *mem; 303 int start;
304 void *mem;
363 struct device_node **allnextp = mynodes; 305 struct device_node **allnextp = mynodes;
364 306
365 pr_debug(" -> unflatten_device_tree()\n"); 307 pr_debug(" -> unflatten_device_tree()\n");
@@ -380,7 +322,7 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
380 } 322 }
381 323
382 /* First pass, scan for size */ 324 /* First pass, scan for size */
383 start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct); 325 start = 0;
384 size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0); 326 size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
385 size = ALIGN(size, 4); 327 size = ALIGN(size, 4);
386 328
@@ -395,10 +337,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
395 pr_debug(" unflattening %p...\n", mem); 337 pr_debug(" unflattening %p...\n", mem);
396 338
397 /* Second pass, do actual unflattening */ 339 /* Second pass, do actual unflattening */
398 start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct); 340 start = 0;
399 unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); 341 unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
400 if (be32_to_cpup(start) != OF_DT_END)
401 pr_warning("Weird tag at end of tree: %08x\n", be32_to_cpup(start));
402 if (be32_to_cpup(mem + size) != 0xdeadbeef) 342 if (be32_to_cpup(mem + size) != 0xdeadbeef)
403 pr_warning("End of tree marker overwritten: %08x\n", 343 pr_warning("End of tree marker overwritten: %08x\n",
404 be32_to_cpup(mem + size)); 344 be32_to_cpup(mem + size));
@@ -574,47 +514,19 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
574 void *data), 514 void *data),
575 void *data) 515 void *data)
576{ 516{
577 unsigned long p = ((unsigned long)initial_boot_params) + 517 const void *blob = initial_boot_params;
578 be32_to_cpu(initial_boot_params->off_dt_struct); 518 const char *pathp;
579 int rc = 0; 519 int offset, rc = 0, depth = -1;
580 int depth = -1; 520
581 521 for (offset = fdt_next_node(blob, -1, &depth);
582 do { 522 offset >= 0 && depth >= 0 && !rc;
583 u32 tag = be32_to_cpup((__be32 *)p); 523 offset = fdt_next_node(blob, offset, &depth)) {
584 const char *pathp; 524
585 525 pathp = fdt_get_name(blob, offset, NULL);
586 p += 4;
587 if (tag == OF_DT_END_NODE) {
588 depth--;
589 continue;
590 }
591 if (tag == OF_DT_NOP)
592 continue;
593 if (tag == OF_DT_END)
594 break;
595 if (tag == OF_DT_PROP) {
596 u32 sz = be32_to_cpup((__be32 *)p);
597 p += 8;
598 if (be32_to_cpu(initial_boot_params->version) < 0x10)
599 p = ALIGN(p, sz >= 8 ? 8 : 4);
600 p += sz;
601 p = ALIGN(p, 4);
602 continue;
603 }
604 if (tag != OF_DT_BEGIN_NODE) {
605 pr_err("Invalid tag %x in flat device tree!\n", tag);
606 return -EINVAL;
607 }
608 depth++;
609 pathp = (char *)p;
610 p = ALIGN(p + strlen(pathp) + 1, 4);
611 if (*pathp == '/') 526 if (*pathp == '/')
612 pathp = kbasename(pathp); 527 pathp = kbasename(pathp);
613 rc = it(p, pathp, depth, data); 528 rc = it(offset, pathp, depth, data);
614 if (rc != 0) 529 }
615 break;
616 } while (1);
617
618 return rc; 530 return rc;
619} 531}
620 532
@@ -623,14 +535,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
623 */ 535 */
624unsigned long __init of_get_flat_dt_root(void) 536unsigned long __init of_get_flat_dt_root(void)
625{ 537{
626 unsigned long p = ((unsigned long)initial_boot_params) + 538 return 0;
627 be32_to_cpu(initial_boot_params->off_dt_struct);
628
629 while (be32_to_cpup((__be32 *)p) == OF_DT_NOP)
630 p += 4;
631 BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
632 p += 4;
633 return ALIGN(p + strlen((char *)p) + 1, 4);
634} 539}
635 540
636/** 541/**
@@ -642,7 +547,7 @@ unsigned long __init of_get_flat_dt_root(void)
642const void *__init of_get_flat_dt_prop(unsigned long node, const char *name, 547const void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
643 int *size) 548 int *size)
644{ 549{
645 return of_fdt_get_property(initial_boot_params, node, name, size); 550 return fdt_getprop(initial_boot_params, node, name, size);
646} 551}
647 552
648/** 553/**