]>
Commit | Line | Data |
---|---|---|
781e09ee GVB |
1 | /* |
2 | * (C) Copyright 2007 | |
3 | * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com | |
4 | * Based on code written by: | |
5 | * Pantelis Antoniou <pantelis.antoniou@gmail.com> and | |
6 | * Matthew McClintock <msm@freescale.com> | |
7 | * | |
8 | * See file CREDITS for list of people who contributed to this | |
9 | * project. | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or | |
12 | * modify it under the terms of the GNU General Public License as | |
13 | * published by the Free Software Foundation; either version 2 of | |
14 | * the License, or (at your option) any later version. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with this program; if not, write to the Free Software | |
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
24 | * MA 02111-1307 USA | |
25 | */ | |
26 | ||
27 | #include <common.h> | |
28 | #include <command.h> | |
29 | #include <linux/ctype.h> | |
30 | #include <linux/types.h> | |
781e09ee GVB |
31 | #include <asm/global_data.h> |
32 | #include <fdt.h> | |
33 | #include <libfdt.h> | |
64dbbd40 | 34 | #include <fdt_support.h> |
781e09ee GVB |
35 | |
36 | #define MAX_LEVEL 32 /* how deeply nested we will go */ | |
fd61e55d | 37 | #define SCRATCHPAD 1024 /* bytes of scratchpad memory */ |
781e09ee GVB |
38 | |
39 | /* | |
40 | * Global data (for the gd->bd) | |
41 | */ | |
42 | DECLARE_GLOBAL_DATA_PTR; | |
43 | ||
781e09ee | 44 | static int fdt_valid(void); |
addd8ce8 GVB |
45 | static int fdt_parse_prop(char *pathp, char *prop, char *newval, |
46 | char *data, int *len); | |
dbaf07ce | 47 | static int fdt_print(const char *pathp, char *prop, int depth); |
781e09ee | 48 | |
781e09ee GVB |
49 | /* |
50 | * Flattened Device Tree command, see the help for parameter definitions. | |
51 | */ | |
52 | int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | |
53 | { | |
781e09ee GVB |
54 | if (argc < 2) { |
55 | printf ("Usage:\n%s\n", cmdtp->usage); | |
56 | return 1; | |
57 | } | |
58 | ||
781e09ee GVB |
59 | /******************************************************************** |
60 | * Set the address of the fdt | |
61 | ********************************************************************/ | |
25114033 | 62 | if (argv[1][0] == 'a') { |
781e09ee GVB |
63 | /* |
64 | * Set the address [and length] of the fdt. | |
65 | */ | |
66 | fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); | |
67 | ||
68 | if (!fdt_valid()) { | |
69 | return 1; | |
70 | } | |
71 | ||
72 | if (argc >= 4) { | |
73 | int len; | |
74 | int err; | |
75 | /* | |
76 | * Optional new length | |
77 | */ | |
91623528 | 78 | len = simple_strtoul(argv[3], NULL, 16); |
781e09ee | 79 | if (len < fdt_totalsize(fdt)) { |
addd8ce8 GVB |
80 | printf ("New length %d < existing length %d, " |
81 | "ignoring.\n", | |
781e09ee GVB |
82 | len, fdt_totalsize(fdt)); |
83 | } else { | |
84 | /* | |
85 | * Open in place with a new length. | |
86 | */ | |
87 | err = fdt_open_into(fdt, fdt, len); | |
88 | if (err != 0) { | |
addd8ce8 GVB |
89 | printf ("libfdt fdt_open_into(): %s\n", |
90 | fdt_strerror(err)); | |
781e09ee GVB |
91 | } |
92 | } | |
93 | } | |
94 | ||
95 | /******************************************************************** | |
96 | * Move the fdt | |
97 | ********************************************************************/ | |
25114033 | 98 | } else if ((argv[1][0] == 'm') && (argv[1][1] == 'o')) { |
781e09ee GVB |
99 | struct fdt_header *newaddr; |
100 | int len; | |
101 | int err; | |
102 | ||
6be07cc1 | 103 | if (argc < 4) { |
781e09ee GVB |
104 | printf ("Usage:\n%s\n", cmdtp->usage); |
105 | return 1; | |
106 | } | |
107 | ||
108 | /* | |
109 | * Set the address and length of the fdt. | |
110 | */ | |
111 | fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); | |
112 | if (!fdt_valid()) { | |
113 | return 1; | |
114 | } | |
115 | ||
addd8ce8 | 116 | newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16); |
6be07cc1 GVB |
117 | |
118 | /* | |
119 | * If the user specifies a length, use that. Otherwise use the | |
120 | * current length. | |
121 | */ | |
122 | if (argc <= 4) { | |
123 | len = fdt_totalsize(fdt); | |
124 | } else { | |
125 | len = simple_strtoul(argv[4], NULL, 16); | |
126 | if (len < fdt_totalsize(fdt)) { | |
addd8ce8 GVB |
127 | printf ("New length 0x%X < existing length " |
128 | "0x%X, aborting.\n", | |
6be07cc1 GVB |
129 | len, fdt_totalsize(fdt)); |
130 | return 1; | |
131 | } | |
781e09ee GVB |
132 | } |
133 | ||
134 | /* | |
135 | * Copy to the new location. | |
136 | */ | |
137 | err = fdt_open_into(fdt, newaddr, len); | |
138 | if (err != 0) { | |
addd8ce8 GVB |
139 | printf ("libfdt fdt_open_into(): %s\n", |
140 | fdt_strerror(err)); | |
781e09ee GVB |
141 | return 1; |
142 | } | |
143 | fdt = newaddr; | |
144 | ||
145 | /******************************************************************** | |
25114033 GVB |
146 | * Make a new node |
147 | ********************************************************************/ | |
148 | } else if ((argv[1][0] == 'm') && (argv[1][1] == 'k')) { | |
149 | char *pathp; /* path */ | |
150 | char *nodep; /* new node to add */ | |
151 | int nodeoffset; /* node offset from libfdt */ | |
152 | int err; | |
153 | ||
154 | /* | |
155 | * Parameters: Node path, new node to be appended to the path. | |
156 | */ | |
157 | if (argc < 4) { | |
158 | printf ("Usage:\n%s\n", cmdtp->usage); | |
159 | return 1; | |
160 | } | |
161 | ||
162 | pathp = argv[2]; | |
163 | nodep = argv[3]; | |
164 | ||
8d04f02f | 165 | nodeoffset = fdt_path_offset (fdt, pathp); |
25114033 GVB |
166 | if (nodeoffset < 0) { |
167 | /* | |
168 | * Not found or something else bad happened. | |
169 | */ | |
8d04f02f | 170 | printf ("libfdt fdt_path_offset() returned %s\n", |
06e19a07 | 171 | fdt_strerror(nodeoffset)); |
25114033 GVB |
172 | return 1; |
173 | } | |
174 | err = fdt_add_subnode(fdt, nodeoffset, nodep); | |
175 | if (err < 0) { | |
addd8ce8 GVB |
176 | printf ("libfdt fdt_add_subnode(): %s\n", |
177 | fdt_strerror(err)); | |
25114033 GVB |
178 | return 1; |
179 | } | |
180 | ||
181 | /******************************************************************** | |
182 | * Set the value of a property in the fdt. | |
781e09ee | 183 | ********************************************************************/ |
25114033 | 184 | } else if (argv[1][0] == 's') { |
781e09ee | 185 | char *pathp; /* path */ |
addd8ce8 | 186 | char *prop; /* property */ |
781e09ee | 187 | int nodeoffset; /* node offset from libfdt */ |
addd8ce8 GVB |
188 | static char data[SCRATCHPAD]; /* storage for the property */ |
189 | int len; /* new length of the property */ | |
190 | int ret; /* return value */ | |
781e09ee GVB |
191 | |
192 | /* | |
ea6d8be1 | 193 | * Parameters: Node path, property, optional value. |
781e09ee | 194 | */ |
ea6d8be1 | 195 | if (argc < 4) { |
781e09ee GVB |
196 | printf ("Usage:\n%s\n", cmdtp->usage); |
197 | return 1; | |
198 | } | |
199 | ||
200 | pathp = argv[2]; | |
201 | prop = argv[3]; | |
ea6d8be1 GVB |
202 | if (argc == 4) { |
203 | len = 0; | |
204 | } else { | |
205 | ret = fdt_parse_prop(pathp, prop, argv[4], data, &len); | |
206 | if (ret != 0) | |
207 | return ret; | |
208 | } | |
781e09ee | 209 | |
8d04f02f | 210 | nodeoffset = fdt_path_offset (fdt, pathp); |
25114033 | 211 | if (nodeoffset < 0) { |
781e09ee | 212 | /* |
25114033 | 213 | * Not found or something else bad happened. |
781e09ee | 214 | */ |
8d04f02f | 215 | printf ("libfdt fdt_path_offset() returned %s\n", |
06e19a07 | 216 | fdt_strerror(nodeoffset)); |
781e09ee | 217 | return 1; |
25114033 | 218 | } |
25114033 GVB |
219 | |
220 | ret = fdt_setprop(fdt, nodeoffset, prop, data, len); | |
221 | if (ret < 0) { | |
222 | printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret)); | |
223 | return 1; | |
781e09ee GVB |
224 | } |
225 | ||
226 | /******************************************************************** | |
227 | * Print (recursive) / List (single level) | |
228 | ********************************************************************/ | |
25114033 | 229 | } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { |
781e09ee GVB |
230 | int depth = MAX_LEVEL; /* how deep to print */ |
231 | char *pathp; /* path */ | |
addd8ce8 GVB |
232 | char *prop; /* property */ |
233 | int ret; /* return value */ | |
f738b4a7 | 234 | static char root[2] = "/"; |
781e09ee GVB |
235 | |
236 | /* | |
237 | * list is an alias for print, but limited to 1 level | |
238 | */ | |
25114033 | 239 | if (argv[1][0] == 'l') { |
781e09ee GVB |
240 | depth = 1; |
241 | } | |
242 | ||
243 | /* | |
244 | * Get the starting path. The root node is an oddball, | |
245 | * the offset is zero and has no name. | |
246 | */ | |
f738b4a7 KG |
247 | if (argc == 2) |
248 | pathp = root; | |
249 | else | |
250 | pathp = argv[2]; | |
781e09ee GVB |
251 | if (argc > 3) |
252 | prop = argv[3]; | |
253 | else | |
254 | prop = NULL; | |
255 | ||
addd8ce8 GVB |
256 | ret = fdt_print(pathp, prop, depth); |
257 | if (ret != 0) | |
258 | return ret; | |
781e09ee GVB |
259 | |
260 | /******************************************************************** | |
261 | * Remove a property/node | |
262 | ********************************************************************/ | |
25114033 | 263 | } else if (argv[1][0] == 'r') { |
781e09ee GVB |
264 | int nodeoffset; /* node offset from libfdt */ |
265 | int err; | |
266 | ||
267 | /* | |
268 | * Get the path. The root node is an oddball, the offset | |
269 | * is zero and has no name. | |
270 | */ | |
8d04f02f | 271 | nodeoffset = fdt_path_offset (fdt, argv[2]); |
25114033 GVB |
272 | if (nodeoffset < 0) { |
273 | /* | |
274 | * Not found or something else bad happened. | |
275 | */ | |
8d04f02f | 276 | printf ("libfdt fdt_path_offset() returned %s\n", |
06e19a07 | 277 | fdt_strerror(nodeoffset)); |
25114033 | 278 | return 1; |
781e09ee GVB |
279 | } |
280 | /* | |
281 | * Do the delete. A fourth parameter means delete a property, | |
282 | * otherwise delete the node. | |
283 | */ | |
284 | if (argc > 3) { | |
285 | err = fdt_delprop(fdt, nodeoffset, argv[3]); | |
286 | if (err < 0) { | |
addd8ce8 GVB |
287 | printf("libfdt fdt_delprop(): %s\n", |
288 | fdt_strerror(err)); | |
781e09ee GVB |
289 | return err; |
290 | } | |
291 | } else { | |
292 | err = fdt_del_node(fdt, nodeoffset); | |
293 | if (err < 0) { | |
addd8ce8 GVB |
294 | printf("libfdt fdt_del_node(): %s\n", |
295 | fdt_strerror(err)); | |
781e09ee GVB |
296 | return err; |
297 | } | |
298 | } | |
99dffca3 | 299 | } |
fd61e55d | 300 | #ifdef CONFIG_OF_BOARD_SETUP |
99dffca3 KP |
301 | /* Call the board-specific fixup routine */ |
302 | else if (argv[1][0] == 'b') | |
fd61e55d GVB |
303 | ft_board_setup(fdt, gd->bd); |
304 | #endif | |
99dffca3 KP |
305 | /* Create a chosen node */ |
306 | else if (argv[1][0] == 'c') | |
64dbbd40 | 307 | fdt_chosen(fdt, 0, 0, 1); |
781e09ee | 308 | |
99dffca3 KP |
309 | #ifdef CONFIG_OF_HAS_UBOOT_ENV |
310 | /* Create a u-boot-env node */ | |
311 | else if (argv[1][0] == 'e') | |
781e09ee | 312 | fdt_env(fdt); |
99dffca3 KP |
313 | #endif |
314 | #ifdef CONFIG_OF_HAS_BD_T | |
315 | /* Create a bd_t node */ | |
316 | else if (argv[1][0] == 'b') | |
781e09ee | 317 | fdt_bd_t(fdt); |
99dffca3 KP |
318 | #endif |
319 | else { | |
320 | /* Unrecognized command */ | |
781e09ee GVB |
321 | printf ("Usage:\n%s\n", cmdtp->usage); |
322 | return 1; | |
323 | } | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
addd8ce8 | 328 | /****************************************************************************/ |
781e09ee GVB |
329 | |
330 | static int fdt_valid(void) | |
331 | { | |
64dbbd40 GVB |
332 | int err; |
333 | ||
781e09ee | 334 | if (fdt == NULL) { |
64dbbd40 | 335 | printf ("The address of the fdt is invalid (NULL).\n"); |
781e09ee GVB |
336 | return 0; |
337 | } | |
64dbbd40 GVB |
338 | |
339 | err = fdt_check_header(fdt); | |
340 | if (err == 0) | |
341 | return 1; /* valid */ | |
342 | ||
343 | if (err < 0) { | |
25114033 | 344 | printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); |
64dbbd40 GVB |
345 | /* |
346 | * Be more informative on bad version. | |
347 | */ | |
348 | if (err == -FDT_ERR_BADVERSION) { | |
349 | if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) { | |
350 | printf (" - too old, fdt $d < %d", | |
addd8ce8 GVB |
351 | fdt_version(fdt), |
352 | FDT_FIRST_SUPPORTED_VERSION); | |
64dbbd40 GVB |
353 | fdt = NULL; |
354 | } | |
355 | if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) { | |
356 | printf (" - too new, fdt $d > %d", | |
addd8ce8 GVB |
357 | fdt_version(fdt), |
358 | FDT_LAST_SUPPORTED_VERSION); | |
64dbbd40 GVB |
359 | fdt = NULL; |
360 | } | |
361 | return 0; | |
362 | } | |
363 | printf("\n"); | |
781e09ee GVB |
364 | return 0; |
365 | } | |
366 | return 1; | |
367 | } | |
368 | ||
addd8ce8 | 369 | /****************************************************************************/ |
781e09ee GVB |
370 | |
371 | /* | |
addd8ce8 GVB |
372 | * Parse the user's input, partially heuristic. Valid formats: |
373 | * <00> - hex byte | |
374 | * <0011> - hex half word (16 bits) | |
375 | * <00112233> - hex word (32 bits) | |
376 | * - hex double words (64 bits) are not supported, must use | |
377 | * a byte stream instead. | |
378 | * [00 11 22 .. nn] - byte stream | |
379 | * "string" - If the the value doesn't start with "<" or "[", it is | |
380 | * treated as a string. Note that the quotes are | |
381 | * stripped by the parser before we get the string. | |
382 | */ | |
383 | static int fdt_parse_prop(char *pathp, char *prop, char *newval, | |
384 | char *data, int *len) | |
385 | { | |
386 | char *cp; /* temporary char pointer */ | |
387 | unsigned long tmp; /* holds converted values */ | |
388 | ||
389 | if (*newval == '<') { | |
390 | /* | |
391 | * Bigger values than bytes. | |
392 | */ | |
393 | *len = 0; | |
394 | newval++; | |
395 | while ((*newval != '>') && (*newval != '\0')) { | |
396 | cp = newval; | |
397 | tmp = simple_strtoul(cp, &newval, 16); | |
398 | if ((newval - cp) <= 2) { | |
399 | *data = tmp & 0xFF; | |
400 | data += 1; | |
401 | *len += 1; | |
402 | } else if ((newval - cp) <= 4) { | |
403 | *(uint16_t *)data = __cpu_to_be16(tmp); | |
404 | data += 2; | |
405 | *len += 2; | |
406 | } else if ((newval - cp) <= 8) { | |
407 | *(uint32_t *)data = __cpu_to_be32(tmp); | |
408 | data += 4; | |
409 | *len += 4; | |
410 | } else { | |
411 | printf("Sorry, I could not convert \"%s\"\n", | |
412 | cp); | |
413 | return 1; | |
414 | } | |
415 | while (*newval == ' ') | |
416 | newval++; | |
417 | } | |
418 | if (*newval != '>') { | |
419 | printf("Unexpected character '%c'\n", *newval); | |
420 | return 1; | |
421 | } | |
422 | } else if (*newval == '[') { | |
423 | /* | |
424 | * Byte stream. Convert the values. | |
425 | */ | |
426 | *len = 0; | |
427 | newval++; | |
428 | while ((*newval != ']') && (*newval != '\0')) { | |
429 | tmp = simple_strtoul(newval, &newval, 16); | |
430 | *data++ = tmp & 0xFF; | |
fd61e55d | 431 | *len = *len + 1; |
addd8ce8 GVB |
432 | while (*newval == ' ') |
433 | newval++; | |
434 | } | |
435 | if (*newval != ']') { | |
436 | printf("Unexpected character '%c'\n", *newval); | |
437 | return 1; | |
438 | } | |
439 | } else { | |
440 | /* | |
441 | * Assume it is a string. Copy it into our data area for | |
442 | * convenience (including the terminating '\0'). | |
443 | */ | |
444 | *len = strlen(newval) + 1; | |
445 | strcpy(data, newval); | |
446 | } | |
447 | return 0; | |
448 | } | |
449 | ||
450 | /****************************************************************************/ | |
451 | ||
452 | /* | |
453 | * Heuristic to guess if this is a string or concatenated strings. | |
781e09ee GVB |
454 | */ |
455 | ||
456 | static int is_printable_string(const void *data, int len) | |
457 | { | |
458 | const char *s = data; | |
459 | ||
460 | /* zero length is not */ | |
461 | if (len == 0) | |
462 | return 0; | |
463 | ||
464 | /* must terminate with zero */ | |
465 | if (s[len - 1] != '\0') | |
466 | return 0; | |
467 | ||
468 | /* printable or a null byte (concatenated strings) */ | |
469 | while (((*s == '\0') || isprint(*s)) && (len > 0)) { | |
470 | /* | |
471 | * If we see a null, there are three possibilities: | |
472 | * 1) If len == 1, it is the end of the string, printable | |
473 | * 2) Next character also a null, not printable. | |
474 | * 3) Next character not a null, continue to check. | |
475 | */ | |
476 | if (s[0] == '\0') { | |
477 | if (len == 1) | |
478 | return 1; | |
479 | if (s[1] == '\0') | |
480 | return 0; | |
481 | } | |
482 | s++; | |
483 | len--; | |
484 | } | |
485 | ||
486 | /* Not the null termination, or not done yet: not printable */ | |
487 | if (*s != '\0' || (len != 0)) | |
488 | return 0; | |
489 | ||
490 | return 1; | |
491 | } | |
492 | ||
addd8ce8 GVB |
493 | |
494 | /* | |
495 | * Print the property in the best format, a heuristic guess. Print as | |
496 | * a string, concatenated strings, a byte, word, double word, or (if all | |
497 | * else fails) it is printed as a stream of bytes. | |
498 | */ | |
781e09ee GVB |
499 | static void print_data(const void *data, int len) |
500 | { | |
501 | int j; | |
502 | const u8 *s; | |
503 | ||
504 | /* no data, don't print */ | |
505 | if (len == 0) | |
506 | return; | |
507 | ||
508 | /* | |
509 | * It is a string, but it may have multiple strings (embedded '\0's). | |
510 | */ | |
511 | if (is_printable_string(data, len)) { | |
512 | puts("\""); | |
513 | j = 0; | |
514 | while (j < len) { | |
515 | if (j > 0) | |
516 | puts("\", \""); | |
517 | puts(data); | |
518 | j += strlen(data) + 1; | |
519 | data += strlen(data) + 1; | |
520 | } | |
521 | puts("\""); | |
522 | return; | |
523 | } | |
524 | ||
525 | switch (len) { | |
526 | case 1: /* byte */ | |
91623528 | 527 | printf("<0x%02x>", (*(u8 *) data) & 0xff); |
781e09ee GVB |
528 | break; |
529 | case 2: /* half-word */ | |
91623528 | 530 | printf("<0x%04x>", be16_to_cpu(*(u16 *) data) & 0xffff); |
781e09ee GVB |
531 | break; |
532 | case 4: /* word */ | |
91623528 | 533 | printf("<0x%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU); |
781e09ee GVB |
534 | break; |
535 | case 8: /* double-word */ | |
536 | #if __WORDSIZE == 64 | |
91623528 | 537 | printf("<0x%016llx>", be64_to_cpu(*(uint64_t *) data)); |
781e09ee | 538 | #else |
91623528 | 539 | printf("<0x%08x ", be32_to_cpu(*(u32 *) data) & 0xffffffffU); |
781e09ee | 540 | data += 4; |
91623528 | 541 | printf("0x%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU); |
781e09ee GVB |
542 | #endif |
543 | break; | |
544 | default: /* anything else... hexdump */ | |
545 | printf("["); | |
546 | for (j = 0, s = data; j < len; j++) | |
547 | printf("%02x%s", s[j], j < len - 1 ? " " : ""); | |
548 | printf("]"); | |
549 | ||
550 | break; | |
551 | } | |
552 | } | |
553 | ||
addd8ce8 GVB |
554 | /****************************************************************************/ |
555 | ||
556 | /* | |
557 | * Recursively print (a portion of) the fdt. The depth parameter | |
558 | * determines how deeply nested the fdt is printed. | |
559 | */ | |
dbaf07ce | 560 | static int fdt_print(const char *pathp, char *prop, int depth) |
addd8ce8 | 561 | { |
addd8ce8 GVB |
562 | static char tabs[MAX_LEVEL+1] = |
563 | "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" | |
564 | "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; | |
dbaf07ce | 565 | const void *nodep; /* property node pointer */ |
addd8ce8 GVB |
566 | int nodeoffset; /* node offset from libfdt */ |
567 | int nextoffset; /* next node offset from libfdt */ | |
568 | uint32_t tag; /* tag */ | |
569 | int len; /* length of the property */ | |
570 | int level = 0; /* keep track of nesting level */ | |
91623528 | 571 | const struct fdt_property *fdt_prop; |
addd8ce8 | 572 | |
8d04f02f | 573 | nodeoffset = fdt_path_offset (fdt, pathp); |
addd8ce8 GVB |
574 | if (nodeoffset < 0) { |
575 | /* | |
576 | * Not found or something else bad happened. | |
577 | */ | |
8d04f02f | 578 | printf ("libfdt fdt_path_offset() returned %s\n", |
06e19a07 | 579 | fdt_strerror(nodeoffset)); |
addd8ce8 GVB |
580 | return 1; |
581 | } | |
582 | /* | |
583 | * The user passed in a property as well as node path. | |
584 | * Print only the given property and then return. | |
585 | */ | |
586 | if (prop) { | |
587 | nodep = fdt_getprop (fdt, nodeoffset, prop, &len); | |
588 | if (len == 0) { | |
589 | /* no property value */ | |
590 | printf("%s %s\n", pathp, prop); | |
591 | return 0; | |
592 | } else if (len > 0) { | |
28f384b1 | 593 | printf("%s = ", prop); |
addd8ce8 GVB |
594 | print_data (nodep, len); |
595 | printf("\n"); | |
596 | return 0; | |
597 | } else { | |
598 | printf ("libfdt fdt_getprop(): %s\n", | |
599 | fdt_strerror(len)); | |
600 | return 1; | |
601 | } | |
602 | } | |
603 | ||
604 | /* | |
605 | * The user passed in a node path and no property, | |
606 | * print the node and all subnodes. | |
607 | */ | |
addd8ce8 | 608 | while(level >= 0) { |
8d04f02f | 609 | tag = fdt_next_tag(fdt, nodeoffset, &nextoffset); |
addd8ce8 GVB |
610 | switch(tag) { |
611 | case FDT_BEGIN_NODE: | |
91623528 GVB |
612 | pathp = fdt_get_name(fdt, nodeoffset, NULL); |
613 | if (level <= depth) { | |
614 | if (pathp == NULL) | |
615 | pathp = "/* NULL pointer error */"; | |
616 | if (*pathp == '\0') | |
617 | pathp = "/"; /* root is nameless */ | |
addd8ce8 GVB |
618 | printf("%s%s {\n", |
619 | &tabs[MAX_LEVEL - level], pathp); | |
91623528 | 620 | } |
addd8ce8 | 621 | level++; |
addd8ce8 | 622 | if (level >= MAX_LEVEL) { |
91623528 | 623 | printf("Nested too deep, aborting.\n"); |
addd8ce8 GVB |
624 | return 1; |
625 | } | |
626 | break; | |
627 | case FDT_END_NODE: | |
628 | level--; | |
91623528 | 629 | if (level <= depth) |
addd8ce8 GVB |
630 | printf("%s};\n", &tabs[MAX_LEVEL - level]); |
631 | if (level == 0) { | |
632 | level = -1; /* exit the loop */ | |
633 | } | |
634 | break; | |
635 | case FDT_PROP: | |
91623528 GVB |
636 | fdt_prop = fdt_offset_ptr(fdt, nodeoffset, |
637 | sizeof(*fdt_prop)); | |
638 | pathp = fdt_string(fdt, | |
639 | fdt32_to_cpu(fdt_prop->nameoff)); | |
640 | len = fdt32_to_cpu(fdt_prop->len); | |
641 | nodep = fdt_prop->data; | |
addd8ce8 GVB |
642 | if (len < 0) { |
643 | printf ("libfdt fdt_getprop(): %s\n", | |
644 | fdt_strerror(len)); | |
645 | return 1; | |
646 | } else if (len == 0) { | |
647 | /* the property has no value */ | |
91623528 | 648 | if (level <= depth) |
addd8ce8 GVB |
649 | printf("%s%s;\n", |
650 | &tabs[MAX_LEVEL - level], | |
651 | pathp); | |
652 | } else { | |
91623528 | 653 | if (level <= depth) { |
28f384b1 | 654 | printf("%s%s = ", |
addd8ce8 GVB |
655 | &tabs[MAX_LEVEL - level], |
656 | pathp); | |
657 | print_data (nodep, len); | |
658 | printf(";\n"); | |
659 | } | |
660 | } | |
661 | break; | |
662 | case FDT_NOP: | |
91623528 | 663 | printf("/* NOP */\n", &tabs[MAX_LEVEL - level]); |
addd8ce8 GVB |
664 | break; |
665 | case FDT_END: | |
666 | return 1; | |
667 | default: | |
91623528 | 668 | if (level <= depth) |
addd8ce8 GVB |
669 | printf("Unknown tag 0x%08X\n", tag); |
670 | return 1; | |
671 | } | |
672 | nodeoffset = nextoffset; | |
673 | } | |
674 | return 0; | |
675 | } | |
676 | ||
781e09ee GVB |
677 | /********************************************************************/ |
678 | ||
781e09ee GVB |
679 | U_BOOT_CMD( |
680 | fdt, 5, 0, do_fdt, | |
681 | "fdt - flattened device tree utility commands\n", | |
682 | "addr <addr> [<length>] - Set the fdt location to <addr>\n" | |
fd61e55d GVB |
683 | #ifdef CONFIG_OF_BOARD_SETUP |
684 | "fdt boardsetup - Do board-specific set up\n" | |
685 | #endif | |
238cb7a4 | 686 | "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n" |
781e09ee GVB |
687 | "fdt print <path> [<prop>] - Recursive print starting at <path>\n" |
688 | "fdt list <path> [<prop>] - Print one level starting at <path>\n" | |
689 | "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n" | |
690 | "fdt mknode <path> <node> - Create a new node after <path>\n" | |
691 | "fdt rm <path> [<prop>] - Delete the node or <property>\n" | |
fd61e55d | 692 | "fdt chosen - Add/update the /chosen branch in the tree\n" |
781e09ee | 693 | #ifdef CONFIG_OF_HAS_UBOOT_ENV |
fd61e55d | 694 | "fdt env - Add/replace the /u-boot-env branch in the tree\n" |
781e09ee GVB |
695 | #endif |
696 | #ifdef CONFIG_OF_HAS_BD_T | |
fd61e55d | 697 | "fdt bd_t - Add/replace the /bd_t branch in the tree\n" |
781e09ee | 698 | #endif |
238cb7a4 GVB |
699 | "NOTE: If the path or property you are setting/printing has a '#' character\n" |
700 | " or spaces, you MUST escape it with a \\ character or quote it with \".\n" | |
781e09ee | 701 | ); |