]>
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); | |
47 | static int fdt_print(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 | */ | |
78 | len = simple_strtoul(argv[3], NULL, 16); | |
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 | char *newval; /* value from the user (as a string) */ |
781e09ee | 188 | int nodeoffset; /* node offset from libfdt */ |
addd8ce8 GVB |
189 | static char data[SCRATCHPAD]; /* storage for the property */ |
190 | int len; /* new length of the property */ | |
191 | int ret; /* return value */ | |
781e09ee GVB |
192 | |
193 | /* | |
194 | * Parameters: Node path, property, value. | |
195 | */ | |
196 | if (argc < 5) { | |
197 | printf ("Usage:\n%s\n", cmdtp->usage); | |
198 | return 1; | |
199 | } | |
200 | ||
201 | pathp = argv[2]; | |
202 | prop = argv[3]; | |
203 | newval = argv[4]; | |
204 | ||
8d04f02f | 205 | nodeoffset = fdt_path_offset (fdt, pathp); |
25114033 | 206 | if (nodeoffset < 0) { |
781e09ee | 207 | /* |
25114033 | 208 | * Not found or something else bad happened. |
781e09ee | 209 | */ |
8d04f02f | 210 | printf ("libfdt fdt_path_offset() returned %s\n", |
06e19a07 | 211 | fdt_strerror(nodeoffset)); |
781e09ee | 212 | return 1; |
25114033 | 213 | } |
addd8ce8 GVB |
214 | ret = fdt_parse_prop(pathp, prop, newval, data, &len); |
215 | if (ret != 0) | |
216 | return ret; | |
25114033 GVB |
217 | |
218 | ret = fdt_setprop(fdt, nodeoffset, prop, data, len); | |
219 | if (ret < 0) { | |
220 | printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret)); | |
221 | return 1; | |
781e09ee GVB |
222 | } |
223 | ||
224 | /******************************************************************** | |
225 | * Print (recursive) / List (single level) | |
226 | ********************************************************************/ | |
25114033 | 227 | } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { |
781e09ee GVB |
228 | int depth = MAX_LEVEL; /* how deep to print */ |
229 | char *pathp; /* path */ | |
addd8ce8 GVB |
230 | char *prop; /* property */ |
231 | int ret; /* return value */ | |
f738b4a7 | 232 | static char root[2] = "/"; |
781e09ee GVB |
233 | |
234 | /* | |
235 | * list is an alias for print, but limited to 1 level | |
236 | */ | |
25114033 | 237 | if (argv[1][0] == 'l') { |
781e09ee GVB |
238 | depth = 1; |
239 | } | |
240 | ||
241 | /* | |
242 | * Get the starting path. The root node is an oddball, | |
243 | * the offset is zero and has no name. | |
244 | */ | |
f738b4a7 KG |
245 | if (argc == 2) |
246 | pathp = root; | |
247 | else | |
248 | pathp = argv[2]; | |
781e09ee GVB |
249 | if (argc > 3) |
250 | prop = argv[3]; | |
251 | else | |
252 | prop = NULL; | |
253 | ||
addd8ce8 GVB |
254 | ret = fdt_print(pathp, prop, depth); |
255 | if (ret != 0) | |
256 | return ret; | |
781e09ee GVB |
257 | |
258 | /******************************************************************** | |
259 | * Remove a property/node | |
260 | ********************************************************************/ | |
25114033 | 261 | } else if (argv[1][0] == 'r') { |
781e09ee GVB |
262 | int nodeoffset; /* node offset from libfdt */ |
263 | int err; | |
264 | ||
265 | /* | |
266 | * Get the path. The root node is an oddball, the offset | |
267 | * is zero and has no name. | |
268 | */ | |
8d04f02f | 269 | nodeoffset = fdt_path_offset (fdt, argv[2]); |
25114033 GVB |
270 | if (nodeoffset < 0) { |
271 | /* | |
272 | * Not found or something else bad happened. | |
273 | */ | |
8d04f02f | 274 | printf ("libfdt fdt_path_offset() returned %s\n", |
06e19a07 | 275 | fdt_strerror(nodeoffset)); |
25114033 | 276 | return 1; |
781e09ee GVB |
277 | } |
278 | /* | |
279 | * Do the delete. A fourth parameter means delete a property, | |
280 | * otherwise delete the node. | |
281 | */ | |
282 | if (argc > 3) { | |
283 | err = fdt_delprop(fdt, nodeoffset, argv[3]); | |
284 | if (err < 0) { | |
addd8ce8 GVB |
285 | printf("libfdt fdt_delprop(): %s\n", |
286 | fdt_strerror(err)); | |
781e09ee GVB |
287 | return err; |
288 | } | |
289 | } else { | |
290 | err = fdt_del_node(fdt, nodeoffset); | |
291 | if (err < 0) { | |
addd8ce8 GVB |
292 | printf("libfdt fdt_del_node(): %s\n", |
293 | fdt_strerror(err)); | |
781e09ee GVB |
294 | return err; |
295 | } | |
296 | } | |
99dffca3 | 297 | } |
fd61e55d | 298 | #ifdef CONFIG_OF_BOARD_SETUP |
99dffca3 KP |
299 | /* Call the board-specific fixup routine */ |
300 | else if (argv[1][0] == 'b') | |
fd61e55d GVB |
301 | ft_board_setup(fdt, gd->bd); |
302 | #endif | |
99dffca3 KP |
303 | /* Create a chosen node */ |
304 | else if (argv[1][0] == 'c') | |
64dbbd40 | 305 | fdt_chosen(fdt, 0, 0, 1); |
781e09ee | 306 | |
99dffca3 KP |
307 | #ifdef CONFIG_OF_HAS_UBOOT_ENV |
308 | /* Create a u-boot-env node */ | |
309 | else if (argv[1][0] == 'e') | |
781e09ee | 310 | fdt_env(fdt); |
99dffca3 KP |
311 | #endif |
312 | #ifdef CONFIG_OF_HAS_BD_T | |
313 | /* Create a bd_t node */ | |
314 | else if (argv[1][0] == 'b') | |
781e09ee | 315 | fdt_bd_t(fdt); |
99dffca3 KP |
316 | #endif |
317 | else { | |
318 | /* Unrecognized command */ | |
781e09ee GVB |
319 | printf ("Usage:\n%s\n", cmdtp->usage); |
320 | return 1; | |
321 | } | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
addd8ce8 | 326 | /****************************************************************************/ |
781e09ee GVB |
327 | |
328 | static int fdt_valid(void) | |
329 | { | |
64dbbd40 GVB |
330 | int err; |
331 | ||
781e09ee | 332 | if (fdt == NULL) { |
64dbbd40 | 333 | printf ("The address of the fdt is invalid (NULL).\n"); |
781e09ee GVB |
334 | return 0; |
335 | } | |
64dbbd40 GVB |
336 | |
337 | err = fdt_check_header(fdt); | |
338 | if (err == 0) | |
339 | return 1; /* valid */ | |
340 | ||
341 | if (err < 0) { | |
25114033 | 342 | printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); |
64dbbd40 GVB |
343 | /* |
344 | * Be more informative on bad version. | |
345 | */ | |
346 | if (err == -FDT_ERR_BADVERSION) { | |
347 | if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) { | |
348 | printf (" - too old, fdt $d < %d", | |
addd8ce8 GVB |
349 | fdt_version(fdt), |
350 | FDT_FIRST_SUPPORTED_VERSION); | |
64dbbd40 GVB |
351 | fdt = NULL; |
352 | } | |
353 | if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) { | |
354 | printf (" - too new, fdt $d > %d", | |
addd8ce8 GVB |
355 | fdt_version(fdt), |
356 | FDT_LAST_SUPPORTED_VERSION); | |
64dbbd40 GVB |
357 | fdt = NULL; |
358 | } | |
359 | return 0; | |
360 | } | |
361 | printf("\n"); | |
781e09ee GVB |
362 | return 0; |
363 | } | |
364 | return 1; | |
365 | } | |
366 | ||
addd8ce8 | 367 | /****************************************************************************/ |
781e09ee GVB |
368 | |
369 | /* | |
addd8ce8 GVB |
370 | * Parse the user's input, partially heuristic. Valid formats: |
371 | * <00> - hex byte | |
372 | * <0011> - hex half word (16 bits) | |
373 | * <00112233> - hex word (32 bits) | |
374 | * - hex double words (64 bits) are not supported, must use | |
375 | * a byte stream instead. | |
376 | * [00 11 22 .. nn] - byte stream | |
377 | * "string" - If the the value doesn't start with "<" or "[", it is | |
378 | * treated as a string. Note that the quotes are | |
379 | * stripped by the parser before we get the string. | |
380 | */ | |
381 | static int fdt_parse_prop(char *pathp, char *prop, char *newval, | |
382 | char *data, int *len) | |
383 | { | |
384 | char *cp; /* temporary char pointer */ | |
385 | unsigned long tmp; /* holds converted values */ | |
386 | ||
387 | if (*newval == '<') { | |
388 | /* | |
389 | * Bigger values than bytes. | |
390 | */ | |
391 | *len = 0; | |
392 | newval++; | |
393 | while ((*newval != '>') && (*newval != '\0')) { | |
394 | cp = newval; | |
395 | tmp = simple_strtoul(cp, &newval, 16); | |
396 | if ((newval - cp) <= 2) { | |
397 | *data = tmp & 0xFF; | |
398 | data += 1; | |
399 | *len += 1; | |
400 | } else if ((newval - cp) <= 4) { | |
401 | *(uint16_t *)data = __cpu_to_be16(tmp); | |
402 | data += 2; | |
403 | *len += 2; | |
404 | } else if ((newval - cp) <= 8) { | |
405 | *(uint32_t *)data = __cpu_to_be32(tmp); | |
406 | data += 4; | |
407 | *len += 4; | |
408 | } else { | |
409 | printf("Sorry, I could not convert \"%s\"\n", | |
410 | cp); | |
411 | return 1; | |
412 | } | |
413 | while (*newval == ' ') | |
414 | newval++; | |
415 | } | |
416 | if (*newval != '>') { | |
417 | printf("Unexpected character '%c'\n", *newval); | |
418 | return 1; | |
419 | } | |
420 | } else if (*newval == '[') { | |
421 | /* | |
422 | * Byte stream. Convert the values. | |
423 | */ | |
424 | *len = 0; | |
425 | newval++; | |
426 | while ((*newval != ']') && (*newval != '\0')) { | |
427 | tmp = simple_strtoul(newval, &newval, 16); | |
428 | *data++ = tmp & 0xFF; | |
fd61e55d | 429 | *len = *len + 1; |
addd8ce8 GVB |
430 | while (*newval == ' ') |
431 | newval++; | |
432 | } | |
433 | if (*newval != ']') { | |
434 | printf("Unexpected character '%c'\n", *newval); | |
435 | return 1; | |
436 | } | |
437 | } else { | |
438 | /* | |
439 | * Assume it is a string. Copy it into our data area for | |
440 | * convenience (including the terminating '\0'). | |
441 | */ | |
442 | *len = strlen(newval) + 1; | |
443 | strcpy(data, newval); | |
444 | } | |
445 | return 0; | |
446 | } | |
447 | ||
448 | /****************************************************************************/ | |
449 | ||
450 | /* | |
451 | * Heuristic to guess if this is a string or concatenated strings. | |
781e09ee GVB |
452 | */ |
453 | ||
454 | static int is_printable_string(const void *data, int len) | |
455 | { | |
456 | const char *s = data; | |
457 | ||
458 | /* zero length is not */ | |
459 | if (len == 0) | |
460 | return 0; | |
461 | ||
462 | /* must terminate with zero */ | |
463 | if (s[len - 1] != '\0') | |
464 | return 0; | |
465 | ||
466 | /* printable or a null byte (concatenated strings) */ | |
467 | while (((*s == '\0') || isprint(*s)) && (len > 0)) { | |
468 | /* | |
469 | * If we see a null, there are three possibilities: | |
470 | * 1) If len == 1, it is the end of the string, printable | |
471 | * 2) Next character also a null, not printable. | |
472 | * 3) Next character not a null, continue to check. | |
473 | */ | |
474 | if (s[0] == '\0') { | |
475 | if (len == 1) | |
476 | return 1; | |
477 | if (s[1] == '\0') | |
478 | return 0; | |
479 | } | |
480 | s++; | |
481 | len--; | |
482 | } | |
483 | ||
484 | /* Not the null termination, or not done yet: not printable */ | |
485 | if (*s != '\0' || (len != 0)) | |
486 | return 0; | |
487 | ||
488 | return 1; | |
489 | } | |
490 | ||
addd8ce8 GVB |
491 | |
492 | /* | |
493 | * Print the property in the best format, a heuristic guess. Print as | |
494 | * a string, concatenated strings, a byte, word, double word, or (if all | |
495 | * else fails) it is printed as a stream of bytes. | |
496 | */ | |
781e09ee GVB |
497 | static void print_data(const void *data, int len) |
498 | { | |
499 | int j; | |
500 | const u8 *s; | |
501 | ||
502 | /* no data, don't print */ | |
503 | if (len == 0) | |
504 | return; | |
505 | ||
506 | /* | |
507 | * It is a string, but it may have multiple strings (embedded '\0's). | |
508 | */ | |
509 | if (is_printable_string(data, len)) { | |
510 | puts("\""); | |
511 | j = 0; | |
512 | while (j < len) { | |
513 | if (j > 0) | |
514 | puts("\", \""); | |
515 | puts(data); | |
516 | j += strlen(data) + 1; | |
517 | data += strlen(data) + 1; | |
518 | } | |
519 | puts("\""); | |
520 | return; | |
521 | } | |
522 | ||
523 | switch (len) { | |
524 | case 1: /* byte */ | |
525 | printf("<%02x>", (*(u8 *) data) & 0xff); | |
526 | break; | |
527 | case 2: /* half-word */ | |
528 | printf("<%04x>", be16_to_cpu(*(u16 *) data) & 0xffff); | |
529 | break; | |
530 | case 4: /* word */ | |
531 | printf("<%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU); | |
532 | break; | |
533 | case 8: /* double-word */ | |
534 | #if __WORDSIZE == 64 | |
535 | printf("<%016llx>", be64_to_cpu(*(uint64_t *) data)); | |
536 | #else | |
537 | printf("<%08x ", be32_to_cpu(*(u32 *) data) & 0xffffffffU); | |
538 | data += 4; | |
539 | printf("%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU); | |
540 | #endif | |
541 | break; | |
542 | default: /* anything else... hexdump */ | |
543 | printf("["); | |
544 | for (j = 0, s = data; j < len; j++) | |
545 | printf("%02x%s", s[j], j < len - 1 ? " " : ""); | |
546 | printf("]"); | |
547 | ||
548 | break; | |
549 | } | |
550 | } | |
551 | ||
addd8ce8 GVB |
552 | /****************************************************************************/ |
553 | ||
554 | /* | |
555 | * Recursively print (a portion of) the fdt. The depth parameter | |
556 | * determines how deeply nested the fdt is printed. | |
557 | */ | |
558 | static int fdt_print(char *pathp, char *prop, int depth) | |
559 | { | |
560 | static int offstack[MAX_LEVEL]; | |
561 | static char tabs[MAX_LEVEL+1] = | |
562 | "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" | |
563 | "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; | |
564 | void *nodep; /* property node pointer */ | |
565 | int nodeoffset; /* node offset from libfdt */ | |
566 | int nextoffset; /* next node offset from libfdt */ | |
567 | uint32_t tag; /* tag */ | |
568 | int len; /* length of the property */ | |
569 | int level = 0; /* keep track of nesting level */ | |
8d04f02f | 570 | const struct fdt_property *prop1; |
addd8ce8 | 571 | |
8d04f02f | 572 | nodeoffset = fdt_path_offset (fdt, pathp); |
addd8ce8 GVB |
573 | if (nodeoffset < 0) { |
574 | /* | |
575 | * Not found or something else bad happened. | |
576 | */ | |
8d04f02f | 577 | printf ("libfdt fdt_path_offset() returned %s\n", |
06e19a07 | 578 | fdt_strerror(nodeoffset)); |
addd8ce8 GVB |
579 | return 1; |
580 | } | |
581 | /* | |
582 | * The user passed in a property as well as node path. | |
583 | * Print only the given property and then return. | |
584 | */ | |
585 | if (prop) { | |
586 | nodep = fdt_getprop (fdt, nodeoffset, prop, &len); | |
587 | if (len == 0) { | |
588 | /* no property value */ | |
589 | printf("%s %s\n", pathp, prop); | |
590 | return 0; | |
591 | } else if (len > 0) { | |
592 | printf("%s=", prop); | |
593 | print_data (nodep, len); | |
594 | printf("\n"); | |
595 | return 0; | |
596 | } else { | |
597 | printf ("libfdt fdt_getprop(): %s\n", | |
598 | fdt_strerror(len)); | |
599 | return 1; | |
600 | } | |
601 | } | |
602 | ||
603 | /* | |
604 | * The user passed in a node path and no property, | |
605 | * print the node and all subnodes. | |
606 | */ | |
607 | offstack[0] = nodeoffset; | |
608 | ||
609 | while(level >= 0) { | |
8d04f02f | 610 | tag = fdt_next_tag(fdt, nodeoffset, &nextoffset); |
addd8ce8 GVB |
611 | switch(tag) { |
612 | case FDT_BEGIN_NODE: | |
8d04f02f | 613 | pathp = fdt_offset_ptr(fdt, nodeoffset, 1); |
addd8ce8 GVB |
614 | if(level <= depth) |
615 | printf("%s%s {\n", | |
616 | &tabs[MAX_LEVEL - level], pathp); | |
617 | level++; | |
618 | offstack[level] = nodeoffset; | |
619 | if (level >= MAX_LEVEL) { | |
620 | printf("Aaaiii <splat> nested too deep. " | |
621 | "Aborting.\n"); | |
622 | return 1; | |
623 | } | |
624 | break; | |
625 | case FDT_END_NODE: | |
626 | level--; | |
627 | if(level <= depth) | |
628 | printf("%s};\n", &tabs[MAX_LEVEL - level]); | |
629 | if (level == 0) { | |
630 | level = -1; /* exit the loop */ | |
631 | } | |
632 | break; | |
633 | case FDT_PROP: | |
8d04f02f KG |
634 | prop1 = fdt_offset_ptr(fdt, nodeoffset, sizeof(*prop1)); |
635 | pathp = fdt_string(fdt, fdt32_to_cpu(prop1->nameoff)); | |
addd8ce8 GVB |
636 | nodep = fdt_getprop (fdt, offstack[level], pathp, &len); |
637 | if (len < 0) { | |
638 | printf ("libfdt fdt_getprop(): %s\n", | |
639 | fdt_strerror(len)); | |
640 | return 1; | |
641 | } else if (len == 0) { | |
642 | /* the property has no value */ | |
643 | if(level <= depth) | |
644 | printf("%s%s;\n", | |
645 | &tabs[MAX_LEVEL - level], | |
646 | pathp); | |
647 | } else { | |
648 | if(level <= depth) { | |
649 | printf("%s%s=", | |
650 | &tabs[MAX_LEVEL - level], | |
651 | pathp); | |
652 | print_data (nodep, len); | |
653 | printf(";\n"); | |
654 | } | |
655 | } | |
656 | break; | |
657 | case FDT_NOP: | |
658 | break; | |
659 | case FDT_END: | |
660 | return 1; | |
661 | default: | |
662 | if(level <= depth) | |
663 | printf("Unknown tag 0x%08X\n", tag); | |
664 | return 1; | |
665 | } | |
666 | nodeoffset = nextoffset; | |
667 | } | |
668 | return 0; | |
669 | } | |
670 | ||
781e09ee GVB |
671 | /********************************************************************/ |
672 | ||
781e09ee GVB |
673 | U_BOOT_CMD( |
674 | fdt, 5, 0, do_fdt, | |
675 | "fdt - flattened device tree utility commands\n", | |
676 | "addr <addr> [<length>] - Set the fdt location to <addr>\n" | |
fd61e55d GVB |
677 | #ifdef CONFIG_OF_BOARD_SETUP |
678 | "fdt boardsetup - Do board-specific set up\n" | |
679 | #endif | |
781e09ee GVB |
680 | "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr>\n" |
681 | "fdt print <path> [<prop>] - Recursive print starting at <path>\n" | |
682 | "fdt list <path> [<prop>] - Print one level starting at <path>\n" | |
683 | "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n" | |
684 | "fdt mknode <path> <node> - Create a new node after <path>\n" | |
685 | "fdt rm <path> [<prop>] - Delete the node or <property>\n" | |
fd61e55d | 686 | "fdt chosen - Add/update the /chosen branch in the tree\n" |
781e09ee | 687 | #ifdef CONFIG_OF_HAS_UBOOT_ENV |
fd61e55d | 688 | "fdt env - Add/replace the /u-boot-env branch in the tree\n" |
781e09ee GVB |
689 | #endif |
690 | #ifdef CONFIG_OF_HAS_BD_T | |
fd61e55d | 691 | "fdt bd_t - Add/replace the /bd_t branch in the tree\n" |
781e09ee GVB |
692 | #endif |
693 | "Hints:\n" | |
fd61e55d GVB |
694 | " If the property you are setting/printing has a '#' character or spaces,\n" |
695 | " you MUST escape it with a \\ character or quote it with \".\n" | |
781e09ee GVB |
696 | "Examples: fdt print / # print the whole tree\n" |
697 | " fdt print /cpus \"#address-cells\"\n" | |
698 | " fdt set /cpus \"#address-cells\" \"[00 00 00 01]\"\n" | |
699 | ); |