Merge with /home/stefan/git/u-boot/bamboo-nand
[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
32 #ifdef CONFIG_OF_LIBFDT
33
34 #include <asm/global_data.h>
35 #include <fdt.h>
36 #include <libfdt.h>
37 #include <fdt_support.h>
38
39 #define MAX_LEVEL       32              /* how deeply nested we will go */
40 #define SCRATCHPAD      1024    /* bytes of scratchpad memory */
41
42 /*
43  * Global data (for the gd->bd)
44  */
45 DECLARE_GLOBAL_DATA_PTR;
46
47 /*
48  * Scratchpad memory.
49  */
50 static char data[SCRATCHPAD];
51
52
53 /*
54  * Function prototypes/declarations.
55  */
56 static int fdt_valid(void);
57 static void print_data(const void *data, int len);
58
59
60 /*
61  * Flattened Device Tree command, see the help for parameter definitions.
62  */
63 int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
64 {
65         char            op;
66
67         if (argc < 2) {
68                 printf ("Usage:\n%s\n", cmdtp->usage);
69                 return 1;
70         }
71
72         /*
73          * Figure out which subcommand was given
74          */
75         op = argv[1][0];
76         /********************************************************************
77          * Set the address of the fdt
78          ********************************************************************/
79         if (op == 'a') {
80                 /*
81                  * Set the address [and length] of the fdt.
82                  */
83                 fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
84
85                 if (!fdt_valid()) {
86                         return 1;
87                 }
88
89                 if (argc >= 4) {
90                         int  len;
91                         int  err;
92                         /*
93                          * Optional new length
94                          */
95                         len =  simple_strtoul(argv[3], NULL, 16);
96                         if (len < fdt_totalsize(fdt)) {
97                                 printf ("New length %d < existing length %d, ignoring.\n",
98                                         len, fdt_totalsize(fdt));
99                         } else {
100                                 /*
101                                  * Open in place with a new length.
102                                  */
103                                 err = fdt_open_into(fdt, fdt, len);
104                                 if (err != 0) {
105                                         printf ("libfdt: %s\n", fdt_strerror(err));
106                                 }
107                         }
108                 }
109
110         /********************************************************************
111          * Move the fdt
112          ********************************************************************/
113         } else if (op == 'm') {
114                 struct fdt_header *newaddr;
115                 int  len;
116                 int  err;
117
118                 if (argc != 5) {
119                         printf ("Usage:\n%s\n", cmdtp->usage);
120                         return 1;
121                 }
122
123                 /*
124                  * Set the address and length of the fdt.
125                  */
126                 fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
127                 if (!fdt_valid()) {
128                         return 1;
129                 }
130
131                 newaddr = (struct fdt_header *)simple_strtoul(argv[3], NULL, 16);
132                 len     =  simple_strtoul(argv[4], NULL, 16);
133                 if (len < fdt_totalsize(fdt)) {
134                         printf ("New length %d < existing length %d, aborting.\n",
135                                 len, fdt_totalsize(fdt));
136                         return 1;
137                 }
138
139                 /*
140                  * Copy to the new location.
141                  */
142                 err = fdt_open_into(fdt, newaddr, len);
143                 if (err != 0) {
144                         printf ("libfdt: %s\n", fdt_strerror(err));
145                         return 1;
146                 }
147                 fdt = newaddr;
148
149         /********************************************************************
150          * Set the value of a node in the fdt.
151          ********************************************************************/
152         } else if (op == 's') {
153                 char *pathp;            /* path */
154                 char *prop;                     /* property */
155                 struct fdt_property *nodep;     /* node struct pointer */
156                 char *newval;           /* value from the user (as a string) */
157                 char *vp;                       /* temporary value pointer */
158                 char *cp;                       /* temporary char pointer */
159                 int  nodeoffset;        /* node offset from libfdt */
160                 int  len;                       /* new length of the property */
161                 int  oldlen;            /* original length of the property */
162                 unsigned long tmp;      /* holds converted values */
163                 int  ret;                       /* return value */
164
165                 /*
166                  * Parameters: Node path, property, value.
167                  */
168                 if (argc < 5) {
169                         printf ("Usage:\n%s\n", cmdtp->usage);
170                         return 1;
171                 }
172
173                 pathp  = argv[2];
174                 prop   = argv[3];
175                 newval = argv[4];
176
177                 if (strcmp(pathp, "/") == 0) {
178                         nodeoffset = 0;
179                 } else {
180                         nodeoffset = fdt_path_offset (fdt, pathp);
181                         if (nodeoffset < 0) {
182                                 /*
183                                  * Not found or something else bad happened.
184                                  */
185                                 printf ("libfdt: %s\n", fdt_strerror(nodeoffset));
186                                 return 1;
187                         }
188                 }
189                 nodep = fdt_getprop (fdt, nodeoffset, prop, &oldlen);
190                 if (oldlen < 0) {
191                         printf ("libfdt %s\n", fdt_strerror(oldlen));
192                         return 1;
193                 } else if (oldlen == 0) {
194                         /*
195                          * The specified property has no value
196                          */
197                         printf("%s has no value, cannot set one (yet).\n", prop);
198                         return 1;
199                 } else {
200                         /*
201                          * Convert the new property
202                          */
203                         vp = data;
204                         if (*newval == '<') {
205                                 /*
206                                  * Bigger values than bytes.
207                                  */
208                                 len = 0;
209                                 newval++;
210                                 while ((*newval != '>') && (*newval != '\0')) {
211                                         cp = newval;
212                                         tmp = simple_strtoul(cp, &newval, 16);
213                                         if ((newval - cp) <= 2) {
214                                                 *vp = tmp & 0xFF;
215                                                 vp  += 1;
216                                                 len += 1;
217                                         } else if ((newval - cp) <= 4) {
218                                                 *(uint16_t *)vp = __cpu_to_be16(tmp);
219                                                 vp  += 2;
220                                                 len += 2;
221                                         } else if ((newval - cp) <= 8) {
222                                                 *(uint32_t *)vp = __cpu_to_be32(tmp);
223                                                 vp  += 4;
224                                                 len += 4;
225                                         } else {
226                                                 printf("Sorry, I could not convert \"%s\"\n", cp);
227                                                 return 1;
228                                         }
229                                         while (*newval == ' ')
230                                                 newval++;
231                                 }
232                                 if (*newval != '>') {
233                                         printf("Unexpected character '%c'\n", *newval);
234                                         return 1;
235                                 }
236                         } else if (*newval == '[') {
237                                 /*
238                                  * Byte stream.  Convert the values.
239                                  */
240                                 len = 0;
241                                 newval++;
242                                 while ((*newval != ']') && (*newval != '\0')) {
243                                         tmp = simple_strtoul(newval, &newval, 16);
244                                         *vp++ = tmp & 0xFF;
245                                         len++;
246                                         while (*newval == ' ')
247                                                 newval++;
248                                 }
249                                 if (*newval != ']') {
250                                         printf("Unexpected character '%c'\n", *newval);
251                                         return 1;
252                                 }
253                         } else {
254                                 /*
255                                  * Assume it is a string.  Copy it into our data area for
256                                  * convenience (including the terminating '\0').
257                                  */
258                                 len = strlen(newval) + 1;
259                                 strcpy(data, newval);
260                         }
261
262                         ret = fdt_setprop(fdt, nodeoffset, prop, data, len);
263                         if (ret < 0) {
264                                 printf ("libfdt %s\n", fdt_strerror(ret));
265                                 return 1;
266                         }
267                 }
268
269         /********************************************************************
270          * Print (recursive) / List (single level)
271          ********************************************************************/
272         } else if ((op == 'p') || (op == 'l')) {
273                 /*
274                  * Recursively print (a portion of) the fdt.
275                  */
276                 static int offstack[MAX_LEVEL];
277                 static char tabs[MAX_LEVEL+1] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
278                 int depth = MAX_LEVEL;  /* how deep to print */
279                 char *pathp;            /* path */
280                 char *prop;                     /* property */
281                 void *nodep;            /* property node pointer */
282                 int  nodeoffset;        /* node offset from libfdt */
283                 int  nextoffset;        /* next node offset from libfdt */
284                 uint32_t tag;           /* tag */
285                 int  len;                       /* length of the property */
286                 int  level = 0;         /* keep track of nesting level */
287
288                 /*
289                  * list is an alias for print, but limited to 1 level
290                  */
291                 if (op == 'l') {
292                         depth = 1;
293                 }
294
295                 /*
296                  * Get the starting path.  The root node is an oddball,
297                  * the offset is zero and has no name.
298                  */
299                 pathp = argv[2];
300                 if (argc > 3)
301                         prop = argv[3];
302                 else
303                         prop = NULL;
304
305                 if (strcmp(pathp, "/") == 0) {
306                         nodeoffset = 0;
307                         printf("/");
308                 } else {
309                         nodeoffset = fdt_path_offset (fdt, pathp);
310                         if (nodeoffset < 0) {
311                                 /*
312                                  * Not found or something else bad happened.
313                                  */
314                                 printf ("libfdt %s\n", fdt_strerror(nodeoffset));
315                                 return 1;
316                         }
317                 }
318                 /*
319                  * The user passed in a property as well as node path.  Print only
320                  * the given property and then return.
321                  */
322                 if (prop) {
323                         nodep = fdt_getprop (fdt, nodeoffset, prop, &len);
324                         if (len == 0) {
325                                 printf("%s %s\n", pathp, prop); /* no property value */
326                                 return 0;
327                         } else if (len > 0) {
328                                 printf("%s=", prop);
329                                 print_data (nodep, len);
330                                 printf("\n");
331                                 return 0;
332                         } else {
333                                 printf ("libfdt %s\n", fdt_strerror(len));
334                                 return 1;
335                         }
336                 }
337
338                 /*
339                  * The user passed in a node path and no property, print the node
340                  * and all subnodes.
341                  */
342                 offstack[0] = nodeoffset;
343
344                 while(level >= 0) {
345                         tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, &pathp);
346                         switch(tag) {
347                         case FDT_BEGIN_NODE:
348                                 if(level <= depth)
349                                         printf("%s%s {\n", &tabs[MAX_LEVEL - level], pathp);
350                                 level++;
351                                 offstack[level] = nodeoffset;
352                                 if (level >= MAX_LEVEL) {
353                                         printf("Aaaiii <splat> nested too deep.\n");
354                                         return 1;
355                                 }
356                                 break;
357                         case FDT_END_NODE:
358                                 level--;
359                                 if(level <= depth)
360                                         printf("%s};\n", &tabs[MAX_LEVEL - level]);
361                                 if (level == 0) {
362                                         level = -1;             /* exit the loop */
363                                 }
364                                 break;
365                         case FDT_PROP:
366                                 nodep = fdt_getprop (fdt, offstack[level], pathp, &len);
367                                 if (len < 0) {
368                                         printf ("libfdt %s\n", fdt_strerror(len));
369                                         return 1;
370                                 } else if (len == 0) {
371                                         /* the property has no value */
372                                         if(level <= depth)
373                                                 printf("%s%s;\n", &tabs[MAX_LEVEL - level], pathp);
374                                 } else {
375                                         if(level <= depth) {
376                                                 printf("%s%s=", &tabs[MAX_LEVEL - level], pathp);
377                                                 print_data (nodep, len);
378                                                 printf(";\n");
379                                         }
380                                 }
381                                 break;
382                         case FDT_NOP:
383                                 break;
384                         case FDT_END:
385                                 return 1;
386                         default:
387                                 if(level <= depth)
388                                         printf("Unknown tag 0x%08X\n", tag);
389                                 return 1;
390                         }
391                         nodeoffset = nextoffset;
392                 }
393
394         /********************************************************************
395          * Remove a property/node
396          ********************************************************************/
397         } else if (op == 'r') {
398                 int  nodeoffset;        /* node offset from libfdt */
399                 int  err;
400
401                 /*
402                  * Get the path.  The root node is an oddball, the offset
403                  * is zero and has no name.
404                  */
405                 if (strcmp(argv[2], "/") == 0) {
406                         nodeoffset = 0;
407                 } else {
408                         nodeoffset = fdt_path_offset (fdt, argv[2]);
409                         if (nodeoffset < 0) {
410                                 /*
411                                  * Not found or something else bad happened.
412                                  */
413                                 printf ("libfdt %s\n", fdt_strerror(nodeoffset));
414                                 return 1;
415                         }
416                 }
417                 /*
418                  * Do the delete.  A fourth parameter means delete a property,
419                  * otherwise delete the node.
420                  */
421                 if (argc > 3) {
422                         err = fdt_delprop(fdt, nodeoffset, argv[3]);
423                         if (err < 0) {
424                                 printf("fdt_delprop libfdt: %s\n", fdt_strerror(err));
425                                 return err;
426                         }
427                 } else {
428                         err = fdt_del_node(fdt, nodeoffset);
429                         if (err < 0) {
430                                 printf("fdt_del_node libfdt: %s\n", fdt_strerror(err));
431                                 return err;
432                         }
433                 }
434
435         /********************************************************************
436          * Create a chosen node
437          ********************************************************************/
438         } else if (op == 'c') {
439                 fdt_chosen(fdt, 0, 0, 1);
440
441         /********************************************************************
442          * Create a u-boot-env node
443          ********************************************************************/
444         } else if (op == 'e') {
445                 fdt_env(fdt);
446
447         /********************************************************************
448          * Create a bd_t node
449          ********************************************************************/
450         } else if (op == 'b') {
451                 fdt_bd_t(fdt);
452
453         /********************************************************************
454          * Unrecognized command
455          ********************************************************************/
456         } else {
457                 printf ("Usage:\n%s\n", cmdtp->usage);
458                 return 1;
459         }
460
461         return 0;
462 }
463
464 /********************************************************************/
465
466 static int fdt_valid(void)
467 {
468         int  err;
469
470         if (fdt == NULL) {
471                 printf ("The address of the fdt is invalid (NULL).\n");
472                 return 0;
473         }
474
475         err = fdt_check_header(fdt);
476         if (err == 0)
477                 return 1;       /* valid */
478
479         if (err < 0) {
480                 printf("libfdt: %s", fdt_strerror(err));
481                 /*
482                  * Be more informative on bad version.
483                  */
484                 if (err == -FDT_ERR_BADVERSION) {
485                         if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) {
486                                 printf (" - too old, fdt $d < %d",
487                                         fdt_version(fdt), FDT_FIRST_SUPPORTED_VERSION);
488                                 fdt = NULL;
489                         }
490                         if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) {
491                                 printf (" - too new, fdt $d > %d",
492                                         fdt_version(fdt), FDT_LAST_SUPPORTED_VERSION);
493                                 fdt = NULL;
494                         }
495                         return 0;
496                 }
497                 printf("\n");
498                 return 0;
499         }
500         return 1;
501 }
502
503 /********************************************************************/
504
505 /*
506  * OF flat tree handling
507  * Written by: Pantelis Antoniou <pantelis.antoniou@gmail.com>
508  * Updated by: Matthew McClintock <msm@freescale.com>
509  * Converted to libfdt by: Gerald Van Baren <vanbaren@cideas.com>
510  */
511
512 static int is_printable_string(const void *data, int len)
513 {
514         const char *s = data;
515
516         /* zero length is not */
517         if (len == 0)
518                 return 0;
519
520         /* must terminate with zero */
521         if (s[len - 1] != '\0')
522                 return 0;
523
524         /* printable or a null byte (concatenated strings) */
525         while (((*s == '\0') || isprint(*s)) && (len > 0)) {
526                 /*
527                  * If we see a null, there are three possibilities:
528                  * 1) If len == 1, it is the end of the string, printable
529                  * 2) Next character also a null, not printable.
530                  * 3) Next character not a null, continue to check.
531                  */
532                 if (s[0] == '\0') {
533                         if (len == 1)
534                                 return 1;
535                         if (s[1] == '\0')
536                                 return 0;
537                 }
538                 s++;
539                 len--;
540         }
541
542         /* Not the null termination, or not done yet: not printable */
543         if (*s != '\0' || (len != 0))
544                 return 0;
545
546         return 1;
547 }
548
549 static void print_data(const void *data, int len)
550 {
551         int j;
552         const u8 *s;
553
554         /* no data, don't print */
555         if (len == 0)
556                 return;
557
558         /*
559          * It is a string, but it may have multiple strings (embedded '\0's).
560          */
561         if (is_printable_string(data, len)) {
562                 puts("\"");
563                 j = 0;
564                 while (j < len) {
565                         if (j > 0)
566                                 puts("\", \"");
567                         puts(data);
568                         j    += strlen(data) + 1;
569                         data += strlen(data) + 1;
570                 }
571                 puts("\"");
572                 return;
573         }
574
575         switch (len) {
576         case 1:  /* byte */
577                 printf("<%02x>", (*(u8 *) data) & 0xff);
578                 break;
579         case 2:  /* half-word */
580                 printf("<%04x>", be16_to_cpu(*(u16 *) data) & 0xffff);
581                 break;
582         case 4:  /* word */
583                 printf("<%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
584                 break;
585         case 8:  /* double-word */
586 #if __WORDSIZE == 64
587                 printf("<%016llx>", be64_to_cpu(*(uint64_t *) data));
588 #else
589                 printf("<%08x ", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
590                 data += 4;
591                 printf("%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
592 #endif
593                 break;
594         default:                /* anything else... hexdump */
595                 printf("[");
596                 for (j = 0, s = data; j < len; j++)
597                         printf("%02x%s", s[j], j < len - 1 ? " " : "");
598                 printf("]");
599
600                 break;
601         }
602 }
603
604 /********************************************************************/
605
606 U_BOOT_CMD(
607         fdt,    5,      0,      do_fdt,
608         "fdt     - flattened device tree utility commands\n",
609             "addr   <addr> [<length>]        - Set the fdt location to <addr>\n"
610         "fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr>\n"
611         "fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
612         "fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
613         "fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n"
614         "fdt mknode <path> <node>            - Create a new node after <path>\n"
615         "fdt rm     <path> [<prop>]          - Delete the node or <property>\n"
616         "fdt chosen - Add/update the \"/chosen\" branch in the tree\n"
617 #ifdef CONFIG_OF_HAS_UBOOT_ENV
618         "fdt env    - Add/replace the \"/u-boot-env\" branch in the tree\n"
619 #endif
620 #ifdef CONFIG_OF_HAS_BD_T
621         "fdt bd_t   - Add/replace the \"/bd_t\" branch in the tree\n"
622 #endif
623         "Hints:\n"
624         " * Set a larger length with the fdt addr command to add to the blob.\n"
625         " * If the property you are setting/printing has a '#' character,\n"
626         "     you MUST escape it with a \\ character or quote it with \" or\n"
627         "     it will be ignored as a comment.\n"
628         " * If the value has spaces in it, you MUST escape the spaces with\n"
629         "     \\ characters or quote it with \"\"\n"
630         "Examples: fdt print /               # print the whole tree\n"
631         "          fdt print /cpus \"#address-cells\"\n"
632         "          fdt set   /cpus \"#address-cells\" \"[00 00 00 01]\"\n"
633 );
634
635 #endif /* CONFIG_OF_LIBFDT */