imx: nitrogen6x: Enable filesystem generic commands
[platform/kernel/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  */
26
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 <libfdt.h>
33 #include <fdt_support.h>
34 #include <asm/io.h>
35
36 #define MAX_LEVEL       32              /* how deeply nested we will go */
37 #define SCRATCHPAD      1024            /* bytes of scratchpad memory */
38 #ifndef CONFIG_CMD_FDT_MAX_DUMP
39 #define CONFIG_CMD_FDT_MAX_DUMP 64
40 #endif
41
42 /*
43  * Global data (for the gd->bd)
44  */
45 DECLARE_GLOBAL_DATA_PTR;
46
47 static int fdt_valid(struct fdt_header **blobp);
48 static int fdt_parse_prop(char *const*newval, int count, char *data, int *len);
49 static int fdt_print(const char *pathp, char *prop, int depth);
50 static int is_printable_string(const void *data, int len);
51
52 /*
53  * The working_fdt points to our working flattened device tree.
54  */
55 struct fdt_header *working_fdt;
56
57 void set_working_fdt_addr(void *addr)
58 {
59         void *buf;
60
61         buf = map_sysmem((ulong)addr, 0);
62         working_fdt = buf;
63         setenv_addr("fdtaddr", addr);
64 }
65
66 /*
67  * Get a value from the fdt and format it to be set in the environment
68  */
69 static int fdt_value_setenv(const void *nodep, int len, const char *var)
70 {
71         if (is_printable_string(nodep, len))
72                 setenv(var, (void *)nodep);
73         else if (len == 4) {
74                 char buf[11];
75
76                 sprintf(buf, "0x%08X", *(uint32_t *)nodep);
77                 setenv(var, buf);
78         } else if (len%4 == 0 && len <= 20) {
79                 /* Needed to print things like sha1 hashes. */
80                 char buf[41];
81                 int i;
82
83                 for (i = 0; i < len; i += sizeof(unsigned int))
84                         sprintf(buf + (i * 2), "%08x",
85                                 *(unsigned int *)(nodep + i));
86                 setenv(var, buf);
87         } else {
88                 printf("error: unprintable value\n");
89                 return 1;
90         }
91         return 0;
92 }
93
94 /*
95  * Flattened Device Tree command, see the help for parameter definitions.
96  */
97 static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
98 {
99         if (argc < 2)
100                 return CMD_RET_USAGE;
101
102         /*
103          * Set the address of the fdt
104          */
105         if (argv[1][0] == 'a') {
106                 unsigned long addr;
107                 int control = 0;
108                 struct fdt_header *blob;
109                 /*
110                  * Set the address [and length] of the fdt.
111                  */
112                 argc -= 2;
113                 argv += 2;
114 /* Temporary #ifdef - some archs don't have fdt_blob yet */
115 #ifdef CONFIG_OF_CONTROL
116                 if (argc && !strcmp(*argv, "-c")) {
117                         control = 1;
118                         argc--;
119                         argv++;
120                 }
121 #endif
122                 if (argc == 0) {
123                         if (control)
124                                 blob = (struct fdt_header *)gd->fdt_blob;
125                         else
126                                 blob = working_fdt;
127                         if (!blob || !fdt_valid(&blob))
128                                 return 1;
129                         printf("The address of the fdt is %#08lx\n",
130                                control ? (ulong)blob :
131                                         getenv_hex("fdtaddr", 0));
132                         return 0;
133                 }
134
135                 addr = simple_strtoul(argv[0], NULL, 16);
136                 blob = map_sysmem(addr, 0);
137                 if (!fdt_valid(&blob))
138                         return 1;
139                 if (control)
140                         gd->fdt_blob = blob;
141                 else
142                         set_working_fdt_addr(blob);
143
144                 if (argc >= 2) {
145                         int  len;
146                         int  err;
147                         /*
148                          * Optional new length
149                          */
150                         len = simple_strtoul(argv[1], NULL, 16);
151                         if (len < fdt_totalsize(blob)) {
152                                 printf ("New length %d < existing length %d, "
153                                         "ignoring.\n",
154                                         len, fdt_totalsize(blob));
155                         } else {
156                                 /*
157                                  * Open in place with a new length.
158                                  */
159                                 err = fdt_open_into(blob, blob, len);
160                                 if (err != 0) {
161                                         printf ("libfdt fdt_open_into(): %s\n",
162                                                 fdt_strerror(err));
163                                 }
164                         }
165                 }
166
167                 return CMD_RET_SUCCESS;
168         }
169
170         if (!working_fdt) {
171                 puts(
172                         "No FDT memory address configured. Please configure\n"
173                         "the FDT address via \"fdt addr <address>\" command.\n"
174                         "Aborting!\n");
175                 return CMD_RET_FAILURE;
176         }
177
178         /*
179          * Move the working_fdt
180          */
181         if (strncmp(argv[1], "mo", 2) == 0) {
182                 struct fdt_header *newaddr;
183                 int  len;
184                 int  err;
185
186                 if (argc < 4)
187                         return CMD_RET_USAGE;
188
189                 /*
190                  * Set the address and length of the fdt.
191                  */
192                 working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
193                 if (!fdt_valid(&working_fdt))
194                         return 1;
195
196                 newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16);
197
198                 /*
199                  * If the user specifies a length, use that.  Otherwise use the
200                  * current length.
201                  */
202                 if (argc <= 4) {
203                         len = fdt_totalsize(working_fdt);
204                 } else {
205                         len = simple_strtoul(argv[4], NULL, 16);
206                         if (len < fdt_totalsize(working_fdt)) {
207                                 printf ("New length 0x%X < existing length "
208                                         "0x%X, aborting.\n",
209                                         len, fdt_totalsize(working_fdt));
210                                 return 1;
211                         }
212                 }
213
214                 /*
215                  * Copy to the new location.
216                  */
217                 err = fdt_open_into(working_fdt, newaddr, len);
218                 if (err != 0) {
219                         printf ("libfdt fdt_open_into(): %s\n",
220                                 fdt_strerror(err));
221                         return 1;
222                 }
223                 working_fdt = newaddr;
224
225         /*
226          * Make a new node
227          */
228         } else if (strncmp(argv[1], "mk", 2) == 0) {
229                 char *pathp;            /* path */
230                 char *nodep;            /* new node to add */
231                 int  nodeoffset;        /* node offset from libfdt */
232                 int  err;
233
234                 /*
235                  * Parameters: Node path, new node to be appended to the path.
236                  */
237                 if (argc < 4)
238                         return CMD_RET_USAGE;
239
240                 pathp = argv[2];
241                 nodep = argv[3];
242
243                 nodeoffset = fdt_path_offset (working_fdt, pathp);
244                 if (nodeoffset < 0) {
245                         /*
246                          * Not found or something else bad happened.
247                          */
248                         printf ("libfdt fdt_path_offset() returned %s\n",
249                                 fdt_strerror(nodeoffset));
250                         return 1;
251                 }
252                 err = fdt_add_subnode(working_fdt, nodeoffset, nodep);
253                 if (err < 0) {
254                         printf ("libfdt fdt_add_subnode(): %s\n",
255                                 fdt_strerror(err));
256                         return 1;
257                 }
258
259         /*
260          * Set the value of a property in the working_fdt.
261          */
262         } else if (argv[1][0] == 's') {
263                 char *pathp;            /* path */
264                 char *prop;             /* property */
265                 int  nodeoffset;        /* node offset from libfdt */
266                 static char data[SCRATCHPAD];   /* storage for the property */
267                 int  len;               /* new length of the property */
268                 int  ret;               /* return value */
269
270                 /*
271                  * Parameters: Node path, property, optional value.
272                  */
273                 if (argc < 4)
274                         return CMD_RET_USAGE;
275
276                 pathp  = argv[2];
277                 prop   = argv[3];
278                 if (argc == 4) {
279                         len = 0;
280                 } else {
281                         ret = fdt_parse_prop(&argv[4], argc - 4, data, &len);
282                         if (ret != 0)
283                                 return ret;
284                 }
285
286                 nodeoffset = fdt_path_offset (working_fdt, pathp);
287                 if (nodeoffset < 0) {
288                         /*
289                          * Not found or something else bad happened.
290                          */
291                         printf ("libfdt fdt_path_offset() returned %s\n",
292                                 fdt_strerror(nodeoffset));
293                         return 1;
294                 }
295
296                 ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len);
297                 if (ret < 0) {
298                         printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret));
299                         return 1;
300                 }
301
302         /********************************************************************
303          * Get the value of a property in the working_fdt.
304          ********************************************************************/
305         } else if (argv[1][0] == 'g') {
306                 char *subcmd;           /* sub-command */
307                 char *pathp;            /* path */
308                 char *prop;             /* property */
309                 char *var;              /* variable to store result */
310                 int  nodeoffset;        /* node offset from libfdt */
311                 const void *nodep;      /* property node pointer */
312                 int  len = 0;           /* new length of the property */
313
314                 /*
315                  * Parameters: Node path, property, optional value.
316                  */
317                 if (argc < 5)
318                         return CMD_RET_USAGE;
319
320                 subcmd = argv[2];
321
322                 if (argc < 6 && subcmd[0] != 's')
323                         return CMD_RET_USAGE;
324
325                 var    = argv[3];
326                 pathp  = argv[4];
327                 prop   = argv[5];
328
329                 nodeoffset = fdt_path_offset(working_fdt, pathp);
330                 if (nodeoffset < 0) {
331                         /*
332                          * Not found or something else bad happened.
333                          */
334                         printf("libfdt fdt_path_offset() returned %s\n",
335                                 fdt_strerror(nodeoffset));
336                         return 1;
337                 }
338
339                 if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) {
340                         int reqIndex = -1;
341                         int startDepth = fdt_node_depth(
342                                 working_fdt, nodeoffset);
343                         int curDepth = startDepth;
344                         int curIndex = -1;
345                         int nextNodeOffset = fdt_next_node(
346                                 working_fdt, nodeoffset, &curDepth);
347
348                         if (subcmd[0] == 'n')
349                                 reqIndex = simple_strtoul(argv[5], NULL, 16);
350
351                         while (curDepth > startDepth) {
352                                 if (curDepth == startDepth + 1)
353                                         curIndex++;
354                                 if (subcmd[0] == 'n' && curIndex == reqIndex) {
355                                         const char *nodeName = fdt_get_name(
356                                             working_fdt, nextNodeOffset, NULL);
357
358                                         setenv(var, (char *)nodeName);
359                                         return 0;
360                                 }
361                                 nextNodeOffset = fdt_next_node(
362                                         working_fdt, nextNodeOffset, &curDepth);
363                                 if (nextNodeOffset < 0)
364                                         break;
365                         }
366                         if (subcmd[0] == 's') {
367                                 /* get the num nodes at this level */
368                                 setenv_ulong(var, curIndex + 1);
369                         } else {
370                                 /* node index not found */
371                                 printf("libfdt node not found\n");
372                                 return 1;
373                         }
374                 } else {
375                         nodep = fdt_getprop(
376                                 working_fdt, nodeoffset, prop, &len);
377                         if (len == 0) {
378                                 /* no property value */
379                                 setenv(var, "");
380                                 return 0;
381                         } else if (len > 0) {
382                                 if (subcmd[0] == 'v') {
383                                         int ret;
384
385                                         ret = fdt_value_setenv(nodep, len, var);
386                                         if (ret != 0)
387                                                 return ret;
388                                 } else if (subcmd[0] == 'a') {
389                                         /* Get address */
390                                         char buf[11];
391
392                                         sprintf(buf, "0x%p", nodep);
393                                         setenv(var, buf);
394                                 } else if (subcmd[0] == 's') {
395                                         /* Get size */
396                                         char buf[11];
397
398                                         sprintf(buf, "0x%08X", len);
399                                         setenv(var, buf);
400                                 } else
401                                         return CMD_RET_USAGE;
402                                 return 0;
403                         } else {
404                                 printf("libfdt fdt_getprop(): %s\n",
405                                         fdt_strerror(len));
406                                 return 1;
407                         }
408                 }
409
410         /*
411          * Print (recursive) / List (single level)
412          */
413         } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) {
414                 int depth = MAX_LEVEL;  /* how deep to print */
415                 char *pathp;            /* path */
416                 char *prop;             /* property */
417                 int  ret;               /* return value */
418                 static char root[2] = "/";
419
420                 /*
421                  * list is an alias for print, but limited to 1 level
422                  */
423                 if (argv[1][0] == 'l') {
424                         depth = 1;
425                 }
426
427                 /*
428                  * Get the starting path.  The root node is an oddball,
429                  * the offset is zero and has no name.
430                  */
431                 if (argc == 2)
432                         pathp = root;
433                 else
434                         pathp = argv[2];
435                 if (argc > 3)
436                         prop = argv[3];
437                 else
438                         prop = NULL;
439
440                 ret = fdt_print(pathp, prop, depth);
441                 if (ret != 0)
442                         return ret;
443
444         /*
445          * Remove a property/node
446          */
447         } else if (strncmp(argv[1], "rm", 2) == 0) {
448                 int  nodeoffset;        /* node offset from libfdt */
449                 int  err;
450
451                 /*
452                  * Get the path.  The root node is an oddball, the offset
453                  * is zero and has no name.
454                  */
455                 nodeoffset = fdt_path_offset (working_fdt, argv[2]);
456                 if (nodeoffset < 0) {
457                         /*
458                          * Not found or something else bad happened.
459                          */
460                         printf ("libfdt fdt_path_offset() returned %s\n",
461                                 fdt_strerror(nodeoffset));
462                         return 1;
463                 }
464                 /*
465                  * Do the delete.  A fourth parameter means delete a property,
466                  * otherwise delete the node.
467                  */
468                 if (argc > 3) {
469                         err = fdt_delprop(working_fdt, nodeoffset, argv[3]);
470                         if (err < 0) {
471                                 printf("libfdt fdt_delprop():  %s\n",
472                                         fdt_strerror(err));
473                                 return err;
474                         }
475                 } else {
476                         err = fdt_del_node(working_fdt, nodeoffset);
477                         if (err < 0) {
478                                 printf("libfdt fdt_del_node():  %s\n",
479                                         fdt_strerror(err));
480                                 return err;
481                         }
482                 }
483
484         /*
485          * Display header info
486          */
487         } else if (argv[1][0] == 'h') {
488                 u32 version = fdt_version(working_fdt);
489                 printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt));
490                 printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt),
491                        fdt_totalsize(working_fdt));
492                 printf("off_dt_struct:\t\t0x%x\n",
493                        fdt_off_dt_struct(working_fdt));
494                 printf("off_dt_strings:\t\t0x%x\n",
495                        fdt_off_dt_strings(working_fdt));
496                 printf("off_mem_rsvmap:\t\t0x%x\n",
497                        fdt_off_mem_rsvmap(working_fdt));
498                 printf("version:\t\t%d\n", version);
499                 printf("last_comp_version:\t%d\n",
500                        fdt_last_comp_version(working_fdt));
501                 if (version >= 2)
502                         printf("boot_cpuid_phys:\t0x%x\n",
503                                 fdt_boot_cpuid_phys(working_fdt));
504                 if (version >= 3)
505                         printf("size_dt_strings:\t0x%x\n",
506                                 fdt_size_dt_strings(working_fdt));
507                 if (version >= 17)
508                         printf("size_dt_struct:\t\t0x%x\n",
509                                 fdt_size_dt_struct(working_fdt));
510                 printf("number mem_rsv:\t\t0x%x\n",
511                        fdt_num_mem_rsv(working_fdt));
512                 printf("\n");
513
514         /*
515          * Set boot cpu id
516          */
517         } else if (strncmp(argv[1], "boo", 3) == 0) {
518                 unsigned long tmp = simple_strtoul(argv[2], NULL, 16);
519                 fdt_set_boot_cpuid_phys(working_fdt, tmp);
520
521         /*
522          * memory command
523          */
524         } else if (strncmp(argv[1], "me", 2) == 0) {
525                 uint64_t addr, size;
526                 int err;
527                 addr = simple_strtoull(argv[2], NULL, 16);
528                 size = simple_strtoull(argv[3], NULL, 16);
529                 err = fdt_fixup_memory(working_fdt, addr, size);
530                 if (err < 0)
531                         return err;
532
533         /*
534          * mem reserve commands
535          */
536         } else if (strncmp(argv[1], "rs", 2) == 0) {
537                 if (argv[2][0] == 'p') {
538                         uint64_t addr, size;
539                         int total = fdt_num_mem_rsv(working_fdt);
540                         int j, err;
541                         printf("index\t\t   start\t\t    size\n");
542                         printf("-------------------------------"
543                                 "-----------------\n");
544                         for (j = 0; j < total; j++) {
545                                 err = fdt_get_mem_rsv(working_fdt, j, &addr, &size);
546                                 if (err < 0) {
547                                         printf("libfdt fdt_get_mem_rsv():  %s\n",
548                                                         fdt_strerror(err));
549                                         return err;
550                                 }
551                                 printf("    %x\t%08x%08x\t%08x%08x\n", j,
552                                         (u32)(addr >> 32),
553                                         (u32)(addr & 0xffffffff),
554                                         (u32)(size >> 32),
555                                         (u32)(size & 0xffffffff));
556                         }
557                 } else if (argv[2][0] == 'a') {
558                         uint64_t addr, size;
559                         int err;
560                         addr = simple_strtoull(argv[3], NULL, 16);
561                         size = simple_strtoull(argv[4], NULL, 16);
562                         err = fdt_add_mem_rsv(working_fdt, addr, size);
563
564                         if (err < 0) {
565                                 printf("libfdt fdt_add_mem_rsv():  %s\n",
566                                         fdt_strerror(err));
567                                 return err;
568                         }
569                 } else if (argv[2][0] == 'd') {
570                         unsigned long idx = simple_strtoul(argv[3], NULL, 16);
571                         int err = fdt_del_mem_rsv(working_fdt, idx);
572
573                         if (err < 0) {
574                                 printf("libfdt fdt_del_mem_rsv():  %s\n",
575                                         fdt_strerror(err));
576                                 return err;
577                         }
578                 } else {
579                         /* Unrecognized command */
580                         return CMD_RET_USAGE;
581                 }
582         }
583 #ifdef CONFIG_OF_BOARD_SETUP
584         /* Call the board-specific fixup routine */
585         else if (strncmp(argv[1], "boa", 3) == 0)
586                 ft_board_setup(working_fdt, gd->bd);
587 #endif
588         /* Create a chosen node */
589         else if (argv[1][0] == 'c') {
590                 unsigned long initrd_start = 0, initrd_end = 0;
591
592                 if ((argc != 2) && (argc != 4))
593                         return CMD_RET_USAGE;
594
595                 if (argc == 4) {
596                         initrd_start = simple_strtoul(argv[2], NULL, 16);
597                         initrd_end = simple_strtoul(argv[3], NULL, 16);
598                 }
599
600                 fdt_chosen(working_fdt, 1);
601                 fdt_initrd(working_fdt, initrd_start, initrd_end, 1);
602         }
603         /* resize the fdt */
604         else if (strncmp(argv[1], "re", 2) == 0) {
605                 fdt_resize(working_fdt);
606         }
607         else {
608                 /* Unrecognized command */
609                 return CMD_RET_USAGE;
610         }
611
612         return 0;
613 }
614
615 /****************************************************************************/
616
617 /**
618  * fdt_valid() - Check if an FDT is valid. If not, change it to NULL
619  *
620  * @blobp: Pointer to FDT pointer
621  * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL)
622  */
623 static int fdt_valid(struct fdt_header **blobp)
624 {
625         const void *blob = *blobp;
626         int err;
627
628         if (blob == NULL) {
629                 printf ("The address of the fdt is invalid (NULL).\n");
630                 return 0;
631         }
632
633         err = fdt_check_header(blob);
634         if (err == 0)
635                 return 1;       /* valid */
636
637         if (err < 0) {
638                 printf("libfdt fdt_check_header(): %s", fdt_strerror(err));
639                 /*
640                  * Be more informative on bad version.
641                  */
642                 if (err == -FDT_ERR_BADVERSION) {
643                         if (fdt_version(blob) <
644                             FDT_FIRST_SUPPORTED_VERSION) {
645                                 printf (" - too old, fdt %d < %d",
646                                         fdt_version(blob),
647                                         FDT_FIRST_SUPPORTED_VERSION);
648                         }
649                         if (fdt_last_comp_version(blob) >
650                             FDT_LAST_SUPPORTED_VERSION) {
651                                 printf (" - too new, fdt %d > %d",
652                                         fdt_version(blob),
653                                         FDT_LAST_SUPPORTED_VERSION);
654                         }
655                 }
656                 printf("\n");
657                 *blobp = NULL;
658                 return 0;
659         }
660         return 1;
661 }
662
663 /****************************************************************************/
664
665 /*
666  * Parse the user's input, partially heuristic.  Valid formats:
667  * <0x00112233 4 05>    - an array of cells.  Numbers follow standard
668  *                      C conventions.
669  * [00 11 22 .. nn] - byte stream
670  * "string"     - If the the value doesn't start with "<" or "[", it is
671  *                      treated as a string.  Note that the quotes are
672  *                      stripped by the parser before we get the string.
673  * newval: An array of strings containing the new property as specified
674  *      on the command line
675  * count: The number of strings in the array
676  * data: A bytestream to be placed in the property
677  * len: The length of the resulting bytestream
678  */
679 static int fdt_parse_prop(char * const *newval, int count, char *data, int *len)
680 {
681         char *cp;               /* temporary char pointer */
682         char *newp;             /* temporary newval char pointer */
683         unsigned long tmp;      /* holds converted values */
684         int stridx = 0;
685
686         *len = 0;
687         newp = newval[0];
688
689         /* An array of cells */
690         if (*newp == '<') {
691                 newp++;
692                 while ((*newp != '>') && (stridx < count)) {
693                         /*
694                          * Keep searching until we find that last ">"
695                          * That way users don't have to escape the spaces
696                          */
697                         if (*newp == '\0') {
698                                 newp = newval[++stridx];
699                                 continue;
700                         }
701
702                         cp = newp;
703                         tmp = simple_strtoul(cp, &newp, 0);
704                         *(__be32 *)data = __cpu_to_be32(tmp);
705                         data  += 4;
706                         *len += 4;
707
708                         /* If the ptr didn't advance, something went wrong */
709                         if ((newp - cp) <= 0) {
710                                 printf("Sorry, I could not convert \"%s\"\n",
711                                         cp);
712                                 return 1;
713                         }
714
715                         while (*newp == ' ')
716                                 newp++;
717                 }
718
719                 if (*newp != '>') {
720                         printf("Unexpected character '%c'\n", *newp);
721                         return 1;
722                 }
723         } else if (*newp == '[') {
724                 /*
725                  * Byte stream.  Convert the values.
726                  */
727                 newp++;
728                 while ((stridx < count) && (*newp != ']')) {
729                         while (*newp == ' ')
730                                 newp++;
731                         if (*newp == '\0') {
732                                 newp = newval[++stridx];
733                                 continue;
734                         }
735                         if (!isxdigit(*newp))
736                                 break;
737                         tmp = simple_strtoul(newp, &newp, 16);
738                         *data++ = tmp & 0xFF;
739                         *len    = *len + 1;
740                 }
741                 if (*newp != ']') {
742                         printf("Unexpected character '%c'\n", *newp);
743                         return 1;
744                 }
745         } else {
746                 /*
747                  * Assume it is one or more strings.  Copy it into our
748                  * data area for convenience (including the
749                  * terminating '\0's).
750                  */
751                 while (stridx < count) {
752                         size_t length = strlen(newp) + 1;
753                         strcpy(data, newp);
754                         data += length;
755                         *len += length;
756                         newp = newval[++stridx];
757                 }
758         }
759         return 0;
760 }
761
762 /****************************************************************************/
763
764 /*
765  * Heuristic to guess if this is a string or concatenated strings.
766  */
767
768 static int is_printable_string(const void *data, int len)
769 {
770         const char *s = data;
771
772         /* zero length is not */
773         if (len == 0)
774                 return 0;
775
776         /* must terminate with zero or '\n' */
777         if (s[len - 1] != '\0' && s[len - 1] != '\n')
778                 return 0;
779
780         /* printable or a null byte (concatenated strings) */
781         while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) {
782                 /*
783                  * If we see a null, there are three possibilities:
784                  * 1) If len == 1, it is the end of the string, printable
785                  * 2) Next character also a null, not printable.
786                  * 3) Next character not a null, continue to check.
787                  */
788                 if (s[0] == '\0') {
789                         if (len == 1)
790                                 return 1;
791                         if (s[1] == '\0')
792                                 return 0;
793                 }
794                 s++;
795                 len--;
796         }
797
798         /* Not the null termination, or not done yet: not printable */
799         if (*s != '\0' || (len != 0))
800                 return 0;
801
802         return 1;
803 }
804
805
806 /*
807  * Print the property in the best format, a heuristic guess.  Print as
808  * a string, concatenated strings, a byte, word, double word, or (if all
809  * else fails) it is printed as a stream of bytes.
810  */
811 static void print_data(const void *data, int len)
812 {
813         int j;
814
815         /* no data, don't print */
816         if (len == 0)
817                 return;
818
819         /*
820          * It is a string, but it may have multiple strings (embedded '\0's).
821          */
822         if (is_printable_string(data, len)) {
823                 puts("\"");
824                 j = 0;
825                 while (j < len) {
826                         if (j > 0)
827                                 puts("\", \"");
828                         puts(data);
829                         j    += strlen(data) + 1;
830                         data += strlen(data) + 1;
831                 }
832                 puts("\"");
833                 return;
834         }
835
836         if ((len %4) == 0) {
837                 if (len > CONFIG_CMD_FDT_MAX_DUMP)
838                         printf("* 0x%p [0x%08x]", data, len);
839                 else {
840                         const __be32 *p;
841
842                         printf("<");
843                         for (j = 0, p = data; j < len/4; j++)
844                                 printf("0x%08x%s", fdt32_to_cpu(p[j]),
845                                         j < (len/4 - 1) ? " " : "");
846                         printf(">");
847                 }
848         } else { /* anything else... hexdump */
849                 if (len > CONFIG_CMD_FDT_MAX_DUMP)
850                         printf("* 0x%p [0x%08x]", data, len);
851                 else {
852                         const u8 *s;
853
854                         printf("[");
855                         for (j = 0, s = data; j < len; j++)
856                                 printf("%02x%s", s[j], j < len - 1 ? " " : "");
857                         printf("]");
858                 }
859         }
860 }
861
862 /****************************************************************************/
863
864 /*
865  * Recursively print (a portion of) the working_fdt.  The depth parameter
866  * determines how deeply nested the fdt is printed.
867  */
868 static int fdt_print(const char *pathp, char *prop, int depth)
869 {
870         static char tabs[MAX_LEVEL+1] =
871                 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
872                 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
873         const void *nodep;      /* property node pointer */
874         int  nodeoffset;        /* node offset from libfdt */
875         int  nextoffset;        /* next node offset from libfdt */
876         uint32_t tag;           /* tag */
877         int  len;               /* length of the property */
878         int  level = 0;         /* keep track of nesting level */
879         const struct fdt_property *fdt_prop;
880
881         nodeoffset = fdt_path_offset (working_fdt, pathp);
882         if (nodeoffset < 0) {
883                 /*
884                  * Not found or something else bad happened.
885                  */
886                 printf ("libfdt fdt_path_offset() returned %s\n",
887                         fdt_strerror(nodeoffset));
888                 return 1;
889         }
890         /*
891          * The user passed in a property as well as node path.
892          * Print only the given property and then return.
893          */
894         if (prop) {
895                 nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len);
896                 if (len == 0) {
897                         /* no property value */
898                         printf("%s %s\n", pathp, prop);
899                         return 0;
900                 } else if (len > 0) {
901                         printf("%s = ", prop);
902                         print_data (nodep, len);
903                         printf("\n");
904                         return 0;
905                 } else {
906                         printf ("libfdt fdt_getprop(): %s\n",
907                                 fdt_strerror(len));
908                         return 1;
909                 }
910         }
911
912         /*
913          * The user passed in a node path and no property,
914          * print the node and all subnodes.
915          */
916         while(level >= 0) {
917                 tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset);
918                 switch(tag) {
919                 case FDT_BEGIN_NODE:
920                         pathp = fdt_get_name(working_fdt, nodeoffset, NULL);
921                         if (level <= depth) {
922                                 if (pathp == NULL)
923                                         pathp = "/* NULL pointer error */";
924                                 if (*pathp == '\0')
925                                         pathp = "/";    /* root is nameless */
926                                 printf("%s%s {\n",
927                                         &tabs[MAX_LEVEL - level], pathp);
928                         }
929                         level++;
930                         if (level >= MAX_LEVEL) {
931                                 printf("Nested too deep, aborting.\n");
932                                 return 1;
933                         }
934                         break;
935                 case FDT_END_NODE:
936                         level--;
937                         if (level <= depth)
938                                 printf("%s};\n", &tabs[MAX_LEVEL - level]);
939                         if (level == 0) {
940                                 level = -1;             /* exit the loop */
941                         }
942                         break;
943                 case FDT_PROP:
944                         fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset,
945                                         sizeof(*fdt_prop));
946                         pathp    = fdt_string(working_fdt,
947                                         fdt32_to_cpu(fdt_prop->nameoff));
948                         len      = fdt32_to_cpu(fdt_prop->len);
949                         nodep    = fdt_prop->data;
950                         if (len < 0) {
951                                 printf ("libfdt fdt_getprop(): %s\n",
952                                         fdt_strerror(len));
953                                 return 1;
954                         } else if (len == 0) {
955                                 /* the property has no value */
956                                 if (level <= depth)
957                                         printf("%s%s;\n",
958                                                 &tabs[MAX_LEVEL - level],
959                                                 pathp);
960                         } else {
961                                 if (level <= depth) {
962                                         printf("%s%s = ",
963                                                 &tabs[MAX_LEVEL - level],
964                                                 pathp);
965                                         print_data (nodep, len);
966                                         printf(";\n");
967                                 }
968                         }
969                         break;
970                 case FDT_NOP:
971                         printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]);
972                         break;
973                 case FDT_END:
974                         return 1;
975                 default:
976                         if (level <= depth)
977                                 printf("Unknown tag 0x%08X\n", tag);
978                         return 1;
979                 }
980                 nodeoffset = nextoffset;
981         }
982         return 0;
983 }
984
985 /********************************************************************/
986 #ifdef CONFIG_SYS_LONGHELP
987 static char fdt_help_text[] =
988         "addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>\n"
989 #ifdef CONFIG_OF_BOARD_SETUP
990         "fdt boardsetup                      - Do board-specific set up\n"
991 #endif
992         "fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
993         "fdt resize                          - Resize fdt to size + padding to 4k addr\n"
994         "fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
995         "fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
996         "fdt get value <var> <path> <prop>   - Get <property> and store in <var>\n"
997         "fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>\n"
998         "fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>\n"
999         "fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>\n"
1000         "fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n"
1001         "fdt mknode <path> <node>            - Create a new node after <path>\n"
1002         "fdt rm     <path> [<prop>]          - Delete the node or <property>\n"
1003         "fdt header                          - Display header info\n"
1004         "fdt bootcpu <id>                    - Set boot cpuid\n"
1005         "fdt memory <addr> <size>            - Add/Update memory node\n"
1006         "fdt rsvmem print                    - Show current mem reserves\n"
1007         "fdt rsvmem add <addr> <size>        - Add a mem reserve\n"
1008         "fdt rsvmem delete <index>           - Delete a mem reserves\n"
1009         "fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree\n"
1010         "                                        <start>/<end> - initrd start/end addr\n"
1011         "NOTE: Dereference aliases by omiting the leading '/', "
1012                 "e.g. fdt print ethernet0.";
1013 #endif
1014
1015 U_BOOT_CMD(
1016         fdt,    255,    0,      do_fdt,
1017         "flattened device tree utility commands", fdt_help_text
1018 );