Merge branch 'master' of git://www.denx.de/git/u-boot-mpc85xx
[glsdk/glsdk-u-boot.git] / common / cmd_fdt.c
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  */
27 #include <common.h>
28 #include <command.h>
29 #include <linux/ctype.h>
30 #include <linux/types.h>
31 #include <asm/global_data.h>
32 #include <fdt.h>
33 #include <libfdt.h>
34 #include <fdt_support.h>
36 #define MAX_LEVEL       32              /* how deeply nested we will go */
37 #define SCRATCHPAD      1024            /* bytes of scratchpad memory */
39 /*
40  * Global data (for the gd->bd)
41  */
42 DECLARE_GLOBAL_DATA_PTR;
44 static int fdt_valid(void);
45 static int fdt_parse_prop(char *const*newval, int count, char *data, int *len);
46 static int fdt_print(const char *pathp, char *prop, int depth);
48 /*
49  * The working_fdt points to our working flattened device tree.
50  */
51 struct fdt_header *working_fdt;
53 void set_working_fdt_addr(void *addr)
54 {
55         char buf[17];
57         working_fdt = addr;
59         sprintf(buf, "%lx", (unsigned long)addr);
60         setenv("fdtaddr", buf);
61 }
63 /*
64  * Flattened Device Tree command, see the help for parameter definitions.
65  */
66 int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
67 {
68         if (argc < 2)
69                 return CMD_RET_USAGE;
71         /*
72          * Set the address of the fdt
73          */
74         if (argv[1][0] == 'a') {
75                 unsigned long addr;
76                 /*
77                  * Set the address [and length] of the fdt.
78                  */
79                 if (argc == 2) {
80                         if (!fdt_valid()) {
81                                 return 1;
82                         }
83                         printf("The address of the fdt is %p\n", working_fdt);
84                         return 0;
85                 }
87                 addr = simple_strtoul(argv[2], NULL, 16);
88                 set_working_fdt_addr((void *)addr);
90                 if (!fdt_valid()) {
91                         return 1;
92                 }
94                 if (argc >= 4) {
95                         int  len;
96                         int  err;
97                         /*
98                          * Optional new length
99                          */
100                         len = simple_strtoul(argv[3], NULL, 16);
101                         if (len < fdt_totalsize(working_fdt)) {
102                                 printf ("New length %d < existing length %d, "
103                                         "ignoring.\n",
104                                         len, fdt_totalsize(working_fdt));
105                         } else {
106                                 /*
107                                  * Open in place with a new length.
108                                  */
109                                 err = fdt_open_into(working_fdt, working_fdt, len);
110                                 if (err != 0) {
111                                         printf ("libfdt fdt_open_into(): %s\n",
112                                                 fdt_strerror(err));
113                                 }
114                         }
115                 }
117                 return CMD_RET_SUCCESS;
118         }
120         if (!working_fdt) {
121                 puts(
122                         "No FDT memory address configured. Please configure\n"
123                         "the FDT address via \"fdt addr <address>\" command.\n"
124                         "Aborting!\n");
125                 return CMD_RET_FAILURE;
126         }
128         /*
129          * Move the working_fdt
130          */
131         if (strncmp(argv[1], "mo", 2) == 0) {
132                 struct fdt_header *newaddr;
133                 int  len;
134                 int  err;
136                 if (argc < 4)
137                         return CMD_RET_USAGE;
139                 /*
140                  * Set the address and length of the fdt.
141                  */
142                 working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
143                 if (!fdt_valid()) {
144                         return 1;
145                 }
147                 newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16);
149                 /*
150                  * If the user specifies a length, use that.  Otherwise use the
151                  * current length.
152                  */
153                 if (argc <= 4) {
154                         len = fdt_totalsize(working_fdt);
155                 } else {
156                         len = simple_strtoul(argv[4], NULL, 16);
157                         if (len < fdt_totalsize(working_fdt)) {
158                                 printf ("New length 0x%X < existing length "
159                                         "0x%X, aborting.\n",
160                                         len, fdt_totalsize(working_fdt));
161                                 return 1;
162                         }
163                 }
165                 /*
166                  * Copy to the new location.
167                  */
168                 err = fdt_open_into(working_fdt, newaddr, len);
169                 if (err != 0) {
170                         printf ("libfdt fdt_open_into(): %s\n",
171                                 fdt_strerror(err));
172                         return 1;
173                 }
174                 working_fdt = newaddr;
176         /*
177          * Make a new node
178          */
179         } else if (strncmp(argv[1], "mk", 2) == 0) {
180                 char *pathp;            /* path */
181                 char *nodep;            /* new node to add */
182                 int  nodeoffset;        /* node offset from libfdt */
183                 int  err;
185                 /*
186                  * Parameters: Node path, new node to be appended to the path.
187                  */
188                 if (argc < 4)
189                         return CMD_RET_USAGE;
191                 pathp = argv[2];
192                 nodep = argv[3];
194                 nodeoffset = fdt_path_offset (working_fdt, pathp);
195                 if (nodeoffset < 0) {
196                         /*
197                          * Not found or something else bad happened.
198                          */
199                         printf ("libfdt fdt_path_offset() returned %s\n",
200                                 fdt_strerror(nodeoffset));
201                         return 1;
202                 }
203                 err = fdt_add_subnode(working_fdt, nodeoffset, nodep);
204                 if (err < 0) {
205                         printf ("libfdt fdt_add_subnode(): %s\n",
206                                 fdt_strerror(err));
207                         return 1;
208                 }
210         /*
211          * Set the value of a property in the working_fdt.
212          */
213         } else if (argv[1][0] == 's') {
214                 char *pathp;            /* path */
215                 char *prop;             /* property */
216                 int  nodeoffset;        /* node offset from libfdt */
217                 static char data[SCRATCHPAD];   /* storage for the property */
218                 int  len;               /* new length of the property */
219                 int  ret;               /* return value */
221                 /*
222                  * Parameters: Node path, property, optional value.
223                  */
224                 if (argc < 4)
225                         return CMD_RET_USAGE;
227                 pathp  = argv[2];
228                 prop   = argv[3];
229                 if (argc == 4) {
230                         len = 0;
231                 } else {
232                         ret = fdt_parse_prop(&argv[4], argc - 4, data, &len);
233                         if (ret != 0)
234                                 return ret;
235                 }
237                 nodeoffset = fdt_path_offset (working_fdt, pathp);
238                 if (nodeoffset < 0) {
239                         /*
240                          * Not found or something else bad happened.
241                          */
242                         printf ("libfdt fdt_path_offset() returned %s\n",
243                                 fdt_strerror(nodeoffset));
244                         return 1;
245                 }
247                 ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len);
248                 if (ret < 0) {
249                         printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret));
250                         return 1;
251                 }
253         /*
254          * Print (recursive) / List (single level)
255          */
256         } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) {
257                 int depth = MAX_LEVEL;  /* how deep to print */
258                 char *pathp;            /* path */
259                 char *prop;             /* property */
260                 int  ret;               /* return value */
261                 static char root[2] = "/";
263                 /*
264                  * list is an alias for print, but limited to 1 level
265                  */
266                 if (argv[1][0] == 'l') {
267                         depth = 1;
268                 }
270                 /*
271                  * Get the starting path.  The root node is an oddball,
272                  * the offset is zero and has no name.
273                  */
274                 if (argc == 2)
275                         pathp = root;
276                 else
277                         pathp = argv[2];
278                 if (argc > 3)
279                         prop = argv[3];
280                 else
281                         prop = NULL;
283                 ret = fdt_print(pathp, prop, depth);
284                 if (ret != 0)
285                         return ret;
287         /*
288          * Remove a property/node
289          */
290         } else if (strncmp(argv[1], "rm", 2) == 0) {
291                 int  nodeoffset;        /* node offset from libfdt */
292                 int  err;
294                 /*
295                  * Get the path.  The root node is an oddball, the offset
296                  * is zero and has no name.
297                  */
298                 nodeoffset = fdt_path_offset (working_fdt, argv[2]);
299                 if (nodeoffset < 0) {
300                         /*
301                          * Not found or something else bad happened.
302                          */
303                         printf ("libfdt fdt_path_offset() returned %s\n",
304                                 fdt_strerror(nodeoffset));
305                         return 1;
306                 }
307                 /*
308                  * Do the delete.  A fourth parameter means delete a property,
309                  * otherwise delete the node.
310                  */
311                 if (argc > 3) {
312                         err = fdt_delprop(working_fdt, nodeoffset, argv[3]);
313                         if (err < 0) {
314                                 printf("libfdt fdt_delprop():  %s\n",
315                                         fdt_strerror(err));
316                                 return err;
317                         }
318                 } else {
319                         err = fdt_del_node(working_fdt, nodeoffset);
320                         if (err < 0) {
321                                 printf("libfdt fdt_del_node():  %s\n",
322                                         fdt_strerror(err));
323                                 return err;
324                         }
325                 }
327         /*
328          * Display header info
329          */
330         } else if (argv[1][0] == 'h') {
331                 u32 version = fdt_version(working_fdt);
332                 printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt));
333                 printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt),
334                        fdt_totalsize(working_fdt));
335                 printf("off_dt_struct:\t\t0x%x\n",
336                        fdt_off_dt_struct(working_fdt));
337                 printf("off_dt_strings:\t\t0x%x\n",
338                        fdt_off_dt_strings(working_fdt));
339                 printf("off_mem_rsvmap:\t\t0x%x\n",
340                        fdt_off_mem_rsvmap(working_fdt));
341                 printf("version:\t\t%d\n", version);
342                 printf("last_comp_version:\t%d\n",
343                        fdt_last_comp_version(working_fdt));
344                 if (version >= 2)
345                         printf("boot_cpuid_phys:\t0x%x\n",
346                                 fdt_boot_cpuid_phys(working_fdt));
347                 if (version >= 3)
348                         printf("size_dt_strings:\t0x%x\n",
349                                 fdt_size_dt_strings(working_fdt));
350                 if (version >= 17)
351                         printf("size_dt_struct:\t\t0x%x\n",
352                                 fdt_size_dt_struct(working_fdt));
353                 printf("number mem_rsv:\t\t0x%x\n",
354                        fdt_num_mem_rsv(working_fdt));
355                 printf("\n");
357         /*
358          * Set boot cpu id
359          */
360         } else if (strncmp(argv[1], "boo", 3) == 0) {
361                 unsigned long tmp = simple_strtoul(argv[2], NULL, 16);
362                 fdt_set_boot_cpuid_phys(working_fdt, tmp);
364         /*
365          * memory command
366          */
367         } else if (strncmp(argv[1], "me", 2) == 0) {
368                 uint64_t addr, size;
369                 int err;
370                 addr = simple_strtoull(argv[2], NULL, 16);
371                 size = simple_strtoull(argv[3], NULL, 16);
372                 err = fdt_fixup_memory(working_fdt, addr, size);
373                 if (err < 0)
374                         return err;
376         /*
377          * mem reserve commands
378          */
379         } else if (strncmp(argv[1], "rs", 2) == 0) {
380                 if (argv[2][0] == 'p') {
381                         uint64_t addr, size;
382                         int total = fdt_num_mem_rsv(working_fdt);
383                         int j, err;
384                         printf("index\t\t   start\t\t    size\n");
385                         printf("-------------------------------"
386                                 "-----------------\n");
387                         for (j = 0; j < total; j++) {
388                                 err = fdt_get_mem_rsv(working_fdt, j, &addr, &size);
389                                 if (err < 0) {
390                                         printf("libfdt fdt_get_mem_rsv():  %s\n",
391                                                         fdt_strerror(err));
392                                         return err;
393                                 }
394                                 printf("    %x\t%08x%08x\t%08x%08x\n", j,
395                                         (u32)(addr >> 32),
396                                         (u32)(addr & 0xffffffff),
397                                         (u32)(size >> 32),
398                                         (u32)(size & 0xffffffff));
399                         }
400                 } else if (argv[2][0] == 'a') {
401                         uint64_t addr, size;
402                         int err;
403                         addr = simple_strtoull(argv[3], NULL, 16);
404                         size = simple_strtoull(argv[4], NULL, 16);
405                         err = fdt_add_mem_rsv(working_fdt, addr, size);
407                         if (err < 0) {
408                                 printf("libfdt fdt_add_mem_rsv():  %s\n",
409                                         fdt_strerror(err));
410                                 return err;
411                         }
412                 } else if (argv[2][0] == 'd') {
413                         unsigned long idx = simple_strtoul(argv[3], NULL, 16);
414                         int err = fdt_del_mem_rsv(working_fdt, idx);
416                         if (err < 0) {
417                                 printf("libfdt fdt_del_mem_rsv():  %s\n",
418                                         fdt_strerror(err));
419                                 return err;
420                         }
421                 } else {
422                         /* Unrecognized command */
423                         return CMD_RET_USAGE;
424                 }
425         }
426 #ifdef CONFIG_OF_BOARD_SETUP
427         /* Call the board-specific fixup routine */
428         else if (strncmp(argv[1], "boa", 3) == 0)
429                 ft_board_setup(working_fdt, gd->bd);
430 #endif
431         /* Create a chosen node */
432         else if (argv[1][0] == 'c') {
433                 unsigned long initrd_start = 0, initrd_end = 0;
435                 if ((argc != 2) && (argc != 4))
436                         return CMD_RET_USAGE;
438                 if (argc == 4) {
439                         initrd_start = simple_strtoul(argv[2], NULL, 16);
440                         initrd_end = simple_strtoul(argv[3], NULL, 16);
441                 }
443                 fdt_chosen(working_fdt, 1);
444                 fdt_initrd(working_fdt, initrd_start, initrd_end, 1);
445         }
446         /* resize the fdt */
447         else if (strncmp(argv[1], "re", 2) == 0) {
448                 fdt_resize(working_fdt);
449         }
450         else {
451                 /* Unrecognized command */
452                 return CMD_RET_USAGE;
453         }
455         return 0;
458 /****************************************************************************/
460 static int fdt_valid(void)
462         int  err;
464         if (working_fdt == NULL) {
465                 printf ("The address of the fdt is invalid (NULL).\n");
466                 return 0;
467         }
469         err = fdt_check_header(working_fdt);
470         if (err == 0)
471                 return 1;       /* valid */
473         if (err < 0) {
474                 printf("libfdt fdt_check_header(): %s", fdt_strerror(err));
475                 /*
476                  * Be more informative on bad version.
477                  */
478                 if (err == -FDT_ERR_BADVERSION) {
479                         if (fdt_version(working_fdt) <
480                             FDT_FIRST_SUPPORTED_VERSION) {
481                                 printf (" - too old, fdt %d < %d",
482                                         fdt_version(working_fdt),
483                                         FDT_FIRST_SUPPORTED_VERSION);
484                                 working_fdt = NULL;
485                         }
486                         if (fdt_last_comp_version(working_fdt) >
487                             FDT_LAST_SUPPORTED_VERSION) {
488                                 printf (" - too new, fdt %d > %d",
489                                         fdt_version(working_fdt),
490                                         FDT_LAST_SUPPORTED_VERSION);
491                                 working_fdt = NULL;
492                         }
493                         return 0;
494                 }
495                 printf("\n");
496                 return 0;
497         }
498         return 1;
501 /****************************************************************************/
503 /*
504  * Parse the user's input, partially heuristic.  Valid formats:
505  * <0x00112233 4 05>    - an array of cells.  Numbers follow standard
506  *                      C conventions.
507  * [00 11 22 .. nn] - byte stream
508  * "string"     - If the the value doesn't start with "<" or "[", it is
509  *                      treated as a string.  Note that the quotes are
510  *                      stripped by the parser before we get the string.
511  * newval: An array of strings containing the new property as specified
512  *      on the command line
513  * count: The number of strings in the array
514  * data: A bytestream to be placed in the property
515  * len: The length of the resulting bytestream
516  */
517 static int fdt_parse_prop(char * const *newval, int count, char *data, int *len)
519         char *cp;               /* temporary char pointer */
520         char *newp;             /* temporary newval char pointer */
521         unsigned long tmp;      /* holds converted values */
522         int stridx = 0;
524         *len = 0;
525         newp = newval[0];
527         /* An array of cells */
528         if (*newp == '<') {
529                 newp++;
530                 while ((*newp != '>') && (stridx < count)) {
531                         /*
532                          * Keep searching until we find that last ">"
533                          * That way users don't have to escape the spaces
534                          */
535                         if (*newp == '\0') {
536                                 newp = newval[++stridx];
537                                 continue;
538                         }
540                         cp = newp;
541                         tmp = simple_strtoul(cp, &newp, 0);
542                         *(uint32_t *)data = __cpu_to_be32(tmp);
543                         data  += 4;
544                         *len += 4;
546                         /* If the ptr didn't advance, something went wrong */
547                         if ((newp - cp) <= 0) {
548                                 printf("Sorry, I could not convert \"%s\"\n",
549                                         cp);
550                                 return 1;
551                         }
553                         while (*newp == ' ')
554                                 newp++;
555                 }
557                 if (*newp != '>') {
558                         printf("Unexpected character '%c'\n", *newp);
559                         return 1;
560                 }
561         } else if (*newp == '[') {
562                 /*
563                  * Byte stream.  Convert the values.
564                  */
565                 newp++;
566                 while ((stridx < count) && (*newp != ']')) {
567                         while (*newp == ' ')
568                                 newp++;
569                         if (*newp == '\0') {
570                                 newp = newval[++stridx];
571                                 continue;
572                         }
573                         if (!isxdigit(*newp))
574                                 break;
575                         tmp = simple_strtoul(newp, &newp, 16);
576                         *data++ = tmp & 0xFF;
577                         *len    = *len + 1;
578                 }
579                 if (*newp != ']') {
580                         printf("Unexpected character '%c'\n", *newp);
581                         return 1;
582                 }
583         } else {
584                 /*
585                  * Assume it is one or more strings.  Copy it into our
586                  * data area for convenience (including the
587                  * terminating '\0's).
588                  */
589                 while (stridx < count) {
590                         size_t length = strlen(newp) + 1;
591                         strcpy(data, newp);
592                         data += length;
593                         *len += length;
594                         newp = newval[++stridx];
595                 }
596         }
597         return 0;
600 /****************************************************************************/
602 /*
603  * Heuristic to guess if this is a string or concatenated strings.
604  */
606 static int is_printable_string(const void *data, int len)
608         const char *s = data;
610         /* zero length is not */
611         if (len == 0)
612                 return 0;
614         /* must terminate with zero */
615         if (s[len - 1] != '\0')
616                 return 0;
618         /* printable or a null byte (concatenated strings) */
619         while (((*s == '\0') || isprint(*s)) && (len > 0)) {
620                 /*
621                  * If we see a null, there are three possibilities:
622                  * 1) If len == 1, it is the end of the string, printable
623                  * 2) Next character also a null, not printable.
624                  * 3) Next character not a null, continue to check.
625                  */
626                 if (s[0] == '\0') {
627                         if (len == 1)
628                                 return 1;
629                         if (s[1] == '\0')
630                                 return 0;
631                 }
632                 s++;
633                 len--;
634         }
636         /* Not the null termination, or not done yet: not printable */
637         if (*s != '\0' || (len != 0))
638                 return 0;
640         return 1;
644 /*
645  * Print the property in the best format, a heuristic guess.  Print as
646  * a string, concatenated strings, a byte, word, double word, or (if all
647  * else fails) it is printed as a stream of bytes.
648  */
649 static void print_data(const void *data, int len)
651         int j;
653         /* no data, don't print */
654         if (len == 0)
655                 return;
657         /*
658          * It is a string, but it may have multiple strings (embedded '\0's).
659          */
660         if (is_printable_string(data, len)) {
661                 puts("\"");
662                 j = 0;
663                 while (j < len) {
664                         if (j > 0)
665                                 puts("\", \"");
666                         puts(data);
667                         j    += strlen(data) + 1;
668                         data += strlen(data) + 1;
669                 }
670                 puts("\"");
671                 return;
672         }
674         if ((len %4) == 0) {
675                 const u32 *p;
677                 printf("<");
678                 for (j = 0, p = data; j < len/4; j ++)
679                         printf("0x%x%s", fdt32_to_cpu(p[j]), j < (len/4 - 1) ? " " : "");
680                 printf(">");
681         } else { /* anything else... hexdump */
682                 const u8 *s;
684                 printf("[");
685                 for (j = 0, s = data; j < len; j++)
686                         printf("%02x%s", s[j], j < len - 1 ? " " : "");
687                 printf("]");
688         }
691 /****************************************************************************/
693 /*
694  * Recursively print (a portion of) the working_fdt.  The depth parameter
695  * determines how deeply nested the fdt is printed.
696  */
697 static int fdt_print(const char *pathp, char *prop, int depth)
699         static char tabs[MAX_LEVEL+1] =
700                 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
701                 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
702         const void *nodep;      /* property node pointer */
703         int  nodeoffset;        /* node offset from libfdt */
704         int  nextoffset;        /* next node offset from libfdt */
705         uint32_t tag;           /* tag */
706         int  len;               /* length of the property */
707         int  level = 0;         /* keep track of nesting level */
708         const struct fdt_property *fdt_prop;
710         nodeoffset = fdt_path_offset (working_fdt, pathp);
711         if (nodeoffset < 0) {
712                 /*
713                  * Not found or something else bad happened.
714                  */
715                 printf ("libfdt fdt_path_offset() returned %s\n",
716                         fdt_strerror(nodeoffset));
717                 return 1;
718         }
719         /*
720          * The user passed in a property as well as node path.
721          * Print only the given property and then return.
722          */
723         if (prop) {
724                 nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len);
725                 if (len == 0) {
726                         /* no property value */
727                         printf("%s %s\n", pathp, prop);
728                         return 0;
729                 } else if (len > 0) {
730                         printf("%s = ", prop);
731                         print_data (nodep, len);
732                         printf("\n");
733                         return 0;
734                 } else {
735                         printf ("libfdt fdt_getprop(): %s\n",
736                                 fdt_strerror(len));
737                         return 1;
738                 }
739         }
741         /*
742          * The user passed in a node path and no property,
743          * print the node and all subnodes.
744          */
745         while(level >= 0) {
746                 tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset);
747                 switch(tag) {
748                 case FDT_BEGIN_NODE:
749                         pathp = fdt_get_name(working_fdt, nodeoffset, NULL);
750                         if (level <= depth) {
751                                 if (pathp == NULL)
752                                         pathp = "/* NULL pointer error */";
753                                 if (*pathp == '\0')
754                                         pathp = "/";    /* root is nameless */
755                                 printf("%s%s {\n",
756                                         &tabs[MAX_LEVEL - level], pathp);
757                         }
758                         level++;
759                         if (level >= MAX_LEVEL) {
760                                 printf("Nested too deep, aborting.\n");
761                                 return 1;
762                         }
763                         break;
764                 case FDT_END_NODE:
765                         level--;
766                         if (level <= depth)
767                                 printf("%s};\n", &tabs[MAX_LEVEL - level]);
768                         if (level == 0) {
769                                 level = -1;             /* exit the loop */
770                         }
771                         break;
772                 case FDT_PROP:
773                         fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset,
774                                         sizeof(*fdt_prop));
775                         pathp    = fdt_string(working_fdt,
776                                         fdt32_to_cpu(fdt_prop->nameoff));
777                         len      = fdt32_to_cpu(fdt_prop->len);
778                         nodep    = fdt_prop->data;
779                         if (len < 0) {
780                                 printf ("libfdt fdt_getprop(): %s\n",
781                                         fdt_strerror(len));
782                                 return 1;
783                         } else if (len == 0) {
784                                 /* the property has no value */
785                                 if (level <= depth)
786                                         printf("%s%s;\n",
787                                                 &tabs[MAX_LEVEL - level],
788                                                 pathp);
789                         } else {
790                                 if (level <= depth) {
791                                         printf("%s%s = ",
792                                                 &tabs[MAX_LEVEL - level],
793                                                 pathp);
794                                         print_data (nodep, len);
795                                         printf(";\n");
796                                 }
797                         }
798                         break;
799                 case FDT_NOP:
800                         printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]);
801                         break;
802                 case FDT_END:
803                         return 1;
804                 default:
805                         if (level <= depth)
806                                 printf("Unknown tag 0x%08X\n", tag);
807                         return 1;
808                 }
809                 nodeoffset = nextoffset;
810         }
811         return 0;
814 /********************************************************************/
816 U_BOOT_CMD(
817         fdt,    255,    0,      do_fdt,
818         "flattened device tree utility commands",
819             "addr   <addr> [<length>]        - Set the fdt location to <addr>\n"
820 #ifdef CONFIG_OF_BOARD_SETUP
821         "fdt boardsetup                      - Do board-specific set up\n"
822 #endif
823         "fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
824         "fdt resize                          - Resize fdt to size + padding to 4k addr\n"
825         "fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
826         "fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
827         "fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n"
828         "fdt mknode <path> <node>            - Create a new node after <path>\n"
829         "fdt rm     <path> [<prop>]          - Delete the node or <property>\n"
830         "fdt header                          - Display header info\n"
831         "fdt bootcpu <id>                    - Set boot cpuid\n"
832         "fdt memory <addr> <size>            - Add/Update memory node\n"
833         "fdt rsvmem print                    - Show current mem reserves\n"
834         "fdt rsvmem add <addr> <size>        - Add a mem reserve\n"
835         "fdt rsvmem delete <index>           - Delete a mem reserves\n"
836         "fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree\n"
837         "                                        <start>/<end> - initrd start/end addr\n"
838         "NOTE: Dereference aliases by omiting the leading '/', "
839                 "e.g. fdt print ethernet0."
840 );