|
|
|
@ -37,46 +37,32 @@ |
|
|
|
|
#include <fdt_support.h> |
|
|
|
|
|
|
|
|
|
#define MAX_LEVEL 32 /* how deeply nested we will go */ |
|
|
|
|
#define SCRATCHPAD 1024 /* bytes of scratchpad memory */ |
|
|
|
|
#define SCRATCHPAD 1024 /* bytes of scratchpad memory */ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Global data (for the gd->bd) |
|
|
|
|
*/ |
|
|
|
|
DECLARE_GLOBAL_DATA_PTR; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Scratchpad memory. |
|
|
|
|
*/ |
|
|
|
|
static char data[SCRATCHPAD]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Function prototypes/declarations. |
|
|
|
|
*/ |
|
|
|
|
static int fdt_valid(void); |
|
|
|
|
static void print_data(const void *data, int len); |
|
|
|
|
|
|
|
|
|
static int fdt_parse_prop(char *pathp, char *prop, char *newval, |
|
|
|
|
char *data, int *len); |
|
|
|
|
static int fdt_print(char *pathp, char *prop, int depth); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Flattened Device Tree command, see the help for parameter definitions. |
|
|
|
|
*/ |
|
|
|
|
int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
{ |
|
|
|
|
char op; |
|
|
|
|
|
|
|
|
|
if (argc < 2) { |
|
|
|
|
printf ("Usage:\n%s\n", cmdtp->usage); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Figure out which subcommand was given |
|
|
|
|
*/ |
|
|
|
|
op = argv[1][0]; |
|
|
|
|
/********************************************************************
|
|
|
|
|
* Set the address of the fdt |
|
|
|
|
********************************************************************/ |
|
|
|
|
if (op == 'a') { |
|
|
|
|
if (argv[1][0] == 'a') { |
|
|
|
|
/*
|
|
|
|
|
* Set the address [and length] of the fdt. |
|
|
|
|
*/ |
|
|
|
@ -94,7 +80,8 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
*/ |
|
|
|
|
len = simple_strtoul(argv[3], NULL, 16); |
|
|
|
|
if (len < fdt_totalsize(fdt)) { |
|
|
|
|
printf ("New length %d < existing length %d, ignoring.\n", |
|
|
|
|
printf ("New length %d < existing length %d, " |
|
|
|
|
"ignoring.\n", |
|
|
|
|
len, fdt_totalsize(fdt)); |
|
|
|
|
} else { |
|
|
|
|
/*
|
|
|
|
@ -102,7 +89,8 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
*/ |
|
|
|
|
err = fdt_open_into(fdt, fdt, len); |
|
|
|
|
if (err != 0) { |
|
|
|
|
printf ("libfdt: %s\n", fdt_strerror(err)); |
|
|
|
|
printf ("libfdt fdt_open_into(): %s\n", |
|
|
|
|
fdt_strerror(err)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -110,12 +98,12 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
/********************************************************************
|
|
|
|
|
* Move the fdt |
|
|
|
|
********************************************************************/ |
|
|
|
|
} else if (op == 'm') { |
|
|
|
|
} else if ((argv[1][0] == 'm') && (argv[1][1] == 'o')) { |
|
|
|
|
struct fdt_header *newaddr; |
|
|
|
|
int len; |
|
|
|
|
int err; |
|
|
|
|
|
|
|
|
|
if (argc != 5) { |
|
|
|
|
if (argc < 4) { |
|
|
|
|
printf ("Usage:\n%s\n", cmdtp->usage); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
@ -128,12 +116,22 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
newaddr = (struct fdt_header *)simple_strtoul(argv[3], NULL, 16); |
|
|
|
|
len = simple_strtoul(argv[4], NULL, 16); |
|
|
|
|
if (len < fdt_totalsize(fdt)) { |
|
|
|
|
printf ("New length %d < existing length %d, aborting.\n", |
|
|
|
|
len, fdt_totalsize(fdt)); |
|
|
|
|
return 1; |
|
|
|
|
newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the user specifies a length, use that. Otherwise use the |
|
|
|
|
* current length. |
|
|
|
|
*/ |
|
|
|
|
if (argc <= 4) { |
|
|
|
|
len = fdt_totalsize(fdt); |
|
|
|
|
} else { |
|
|
|
|
len = simple_strtoul(argv[4], NULL, 16); |
|
|
|
|
if (len < fdt_totalsize(fdt)) { |
|
|
|
|
printf ("New length 0x%X < existing length " |
|
|
|
|
"0x%X, aborting.\n", |
|
|
|
|
len, fdt_totalsize(fdt)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -141,26 +139,59 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
*/ |
|
|
|
|
err = fdt_open_into(fdt, newaddr, len); |
|
|
|
|
if (err != 0) { |
|
|
|
|
printf ("libfdt: %s\n", fdt_strerror(err)); |
|
|
|
|
printf ("libfdt fdt_open_into(): %s\n", |
|
|
|
|
fdt_strerror(err)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
fdt = newaddr; |
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
|
* Set the value of a node in the fdt. |
|
|
|
|
* Make a new node |
|
|
|
|
********************************************************************/ |
|
|
|
|
} else if ((argv[1][0] == 'm') && (argv[1][1] == 'k')) { |
|
|
|
|
char *pathp; /* path */ |
|
|
|
|
char *nodep; /* new node to add */ |
|
|
|
|
int nodeoffset; /* node offset from libfdt */ |
|
|
|
|
int err; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Parameters: Node path, new node to be appended to the path. |
|
|
|
|
*/ |
|
|
|
|
if (argc < 4) { |
|
|
|
|
printf ("Usage:\n%s\n", cmdtp->usage); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pathp = argv[2]; |
|
|
|
|
nodep = argv[3]; |
|
|
|
|
|
|
|
|
|
nodeoffset = fdt_find_node_by_path (fdt, pathp); |
|
|
|
|
if (nodeoffset < 0) { |
|
|
|
|
/*
|
|
|
|
|
* Not found or something else bad happened. |
|
|
|
|
*/ |
|
|
|
|
printf ("libfdt fdt_find_node_by_path() returned %s\n", |
|
|
|
|
fdt_strerror(nodeoffset)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
err = fdt_add_subnode(fdt, nodeoffset, nodep); |
|
|
|
|
if (err < 0) { |
|
|
|
|
printf ("libfdt fdt_add_subnode(): %s\n", |
|
|
|
|
fdt_strerror(err)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
|
* Set the value of a property in the fdt. |
|
|
|
|
********************************************************************/ |
|
|
|
|
} else if (op == 's') { |
|
|
|
|
} else if (argv[1][0] == 's') { |
|
|
|
|
char *pathp; /* path */ |
|
|
|
|
char *prop; /* property */ |
|
|
|
|
struct fdt_property *nodep; /* node struct pointer */ |
|
|
|
|
char *prop; /* property */ |
|
|
|
|
char *newval; /* value from the user (as a string) */ |
|
|
|
|
char *vp; /* temporary value pointer */ |
|
|
|
|
char *cp; /* temporary char pointer */ |
|
|
|
|
int nodeoffset; /* node offset from libfdt */ |
|
|
|
|
int len; /* new length of the property */ |
|
|
|
|
int oldlen; /* original length of the property */ |
|
|
|
|
unsigned long tmp; /* holds converted values */ |
|
|
|
|
int ret; /* return value */ |
|
|
|
|
static char data[SCRATCHPAD]; /* storage for the property */ |
|
|
|
|
int len; /* new length of the property */ |
|
|
|
|
int ret; /* return value */ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Parameters: Node path, property, value. |
|
|
|
@ -174,121 +205,38 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
prop = argv[3]; |
|
|
|
|
newval = argv[4]; |
|
|
|
|
|
|
|
|
|
if (strcmp(pathp, "/") == 0) { |
|
|
|
|
nodeoffset = 0; |
|
|
|
|
} else { |
|
|
|
|
nodeoffset = fdt_path_offset (fdt, pathp); |
|
|
|
|
if (nodeoffset < 0) { |
|
|
|
|
/*
|
|
|
|
|
* Not found or something else bad happened. |
|
|
|
|
*/ |
|
|
|
|
printf ("libfdt: %s\n", fdt_strerror(nodeoffset)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
nodep = fdt_getprop (fdt, nodeoffset, prop, &oldlen); |
|
|
|
|
if (oldlen < 0) { |
|
|
|
|
printf ("libfdt %s\n", fdt_strerror(oldlen)); |
|
|
|
|
return 1; |
|
|
|
|
} else if (oldlen == 0) { |
|
|
|
|
nodeoffset = fdt_find_node_by_path (fdt, pathp); |
|
|
|
|
if (nodeoffset < 0) { |
|
|
|
|
/*
|
|
|
|
|
* The specified property has no value |
|
|
|
|
* Not found or something else bad happened. |
|
|
|
|
*/ |
|
|
|
|
printf("%s has no value, cannot set one (yet).\n", prop); |
|
|
|
|
printf ("libfdt fdt_find_node_by_path() returned %s\n", |
|
|
|
|
fdt_strerror(nodeoffset)); |
|
|
|
|
return 1; |
|
|
|
|
} else { |
|
|
|
|
/*
|
|
|
|
|
* Convert the new property |
|
|
|
|
*/ |
|
|
|
|
vp = data; |
|
|
|
|
if (*newval == '<') { |
|
|
|
|
/*
|
|
|
|
|
* Bigger values than bytes. |
|
|
|
|
*/ |
|
|
|
|
len = 0; |
|
|
|
|
newval++; |
|
|
|
|
while ((*newval != '>') && (*newval != '\0')) { |
|
|
|
|
cp = newval; |
|
|
|
|
tmp = simple_strtoul(cp, &newval, 16); |
|
|
|
|
if ((newval - cp) <= 2) { |
|
|
|
|
*vp = tmp & 0xFF; |
|
|
|
|
vp += 1; |
|
|
|
|
len += 1; |
|
|
|
|
} else if ((newval - cp) <= 4) { |
|
|
|
|
*(uint16_t *)vp = __cpu_to_be16(tmp); |
|
|
|
|
vp += 2; |
|
|
|
|
len += 2; |
|
|
|
|
} else if ((newval - cp) <= 8) { |
|
|
|
|
*(uint32_t *)vp = __cpu_to_be32(tmp); |
|
|
|
|
vp += 4; |
|
|
|
|
len += 4; |
|
|
|
|
} else { |
|
|
|
|
printf("Sorry, I could not convert \"%s\"\n", cp); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
while (*newval == ' ') |
|
|
|
|
newval++; |
|
|
|
|
} |
|
|
|
|
if (*newval != '>') { |
|
|
|
|
printf("Unexpected character '%c'\n", *newval); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} else if (*newval == '[') { |
|
|
|
|
/*
|
|
|
|
|
* Byte stream. Convert the values. |
|
|
|
|
*/ |
|
|
|
|
len = 0; |
|
|
|
|
newval++; |
|
|
|
|
while ((*newval != ']') && (*newval != '\0')) { |
|
|
|
|
tmp = simple_strtoul(newval, &newval, 16); |
|
|
|
|
*vp++ = tmp & 0xFF; |
|
|
|
|
len++; |
|
|
|
|
while (*newval == ' ') |
|
|
|
|
newval++; |
|
|
|
|
} |
|
|
|
|
if (*newval != ']') { |
|
|
|
|
printf("Unexpected character '%c'\n", *newval); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
/*
|
|
|
|
|
* Assume it is a string. Copy it into our data area for |
|
|
|
|
* convenience (including the terminating '\0'). |
|
|
|
|
*/ |
|
|
|
|
len = strlen(newval) + 1; |
|
|
|
|
strcpy(data, newval); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
ret = fdt_parse_prop(pathp, prop, newval, data, &len); |
|
|
|
|
if (ret != 0) |
|
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
ret = fdt_setprop(fdt, nodeoffset, prop, data, len); |
|
|
|
|
if (ret < 0) { |
|
|
|
|
printf ("libfdt %s\n", fdt_strerror(ret)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
ret = fdt_setprop(fdt, nodeoffset, prop, data, len); |
|
|
|
|
if (ret < 0) { |
|
|
|
|
printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
|
* Print (recursive) / List (single level) |
|
|
|
|
********************************************************************/ |
|
|
|
|
} else if ((op == 'p') || (op == 'l')) { |
|
|
|
|
/*
|
|
|
|
|
* Recursively print (a portion of) the fdt. |
|
|
|
|
*/ |
|
|
|
|
static int offstack[MAX_LEVEL]; |
|
|
|
|
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"; |
|
|
|
|
} else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { |
|
|
|
|
int depth = MAX_LEVEL; /* how deep to print */ |
|
|
|
|
char *pathp; /* path */ |
|
|
|
|
char *prop; /* property */ |
|
|
|
|
void *nodep; /* property node pointer */ |
|
|
|
|
int nodeoffset; /* node offset from libfdt */ |
|
|
|
|
int nextoffset; /* next node offset from libfdt */ |
|
|
|
|
uint32_t tag; /* tag */ |
|
|
|
|
int len; /* length of the property */ |
|
|
|
|
int level = 0; /* keep track of nesting level */ |
|
|
|
|
char *prop; /* property */ |
|
|
|
|
int ret; /* return value */ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* list is an alias for print, but limited to 1 level |
|
|
|
|
*/ |
|
|
|
|
if (op == 'l') { |
|
|
|
|
if (argv[1][0] == 'l') { |
|
|
|
|
depth = 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -302,99 +250,14 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
else |
|
|
|
|
prop = NULL; |
|
|
|
|
|
|
|
|
|
if (strcmp(pathp, "/") == 0) { |
|
|
|
|
nodeoffset = 0; |
|
|
|
|
printf("/"); |
|
|
|
|
} else { |
|
|
|
|
nodeoffset = fdt_path_offset (fdt, pathp); |
|
|
|
|
if (nodeoffset < 0) { |
|
|
|
|
/*
|
|
|
|
|
* Not found or something else bad happened. |
|
|
|
|
*/ |
|
|
|
|
printf ("libfdt %s\n", fdt_strerror(nodeoffset)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/*
|
|
|
|
|
* The user passed in a property as well as node path. Print only |
|
|
|
|
* the given property and then return. |
|
|
|
|
*/ |
|
|
|
|
if (prop) { |
|
|
|
|
nodep = fdt_getprop (fdt, nodeoffset, prop, &len); |
|
|
|
|
if (len == 0) { |
|
|
|
|
printf("%s %s\n", pathp, prop); /* no property value */ |
|
|
|
|
return 0; |
|
|
|
|
} else if (len > 0) { |
|
|
|
|
printf("%s=", prop); |
|
|
|
|
print_data (nodep, len); |
|
|
|
|
printf("\n"); |
|
|
|
|
return 0; |
|
|
|
|
} else { |
|
|
|
|
printf ("libfdt %s\n", fdt_strerror(len)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The user passed in a node path and no property, print the node |
|
|
|
|
* and all subnodes. |
|
|
|
|
*/ |
|
|
|
|
offstack[0] = nodeoffset; |
|
|
|
|
|
|
|
|
|
while(level >= 0) { |
|
|
|
|
tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, &pathp); |
|
|
|
|
switch(tag) { |
|
|
|
|
case FDT_BEGIN_NODE: |
|
|
|
|
if(level <= depth) |
|
|
|
|
printf("%s%s {\n", &tabs[MAX_LEVEL - level], pathp); |
|
|
|
|
level++; |
|
|
|
|
offstack[level] = nodeoffset; |
|
|
|
|
if (level >= MAX_LEVEL) { |
|
|
|
|
printf("Aaaiii <splat> nested too deep.\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case FDT_END_NODE: |
|
|
|
|
level--; |
|
|
|
|
if(level <= depth) |
|
|
|
|
printf("%s};\n", &tabs[MAX_LEVEL - level]); |
|
|
|
|
if (level == 0) { |
|
|
|
|
level = -1; /* exit the loop */ |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case FDT_PROP: |
|
|
|
|
nodep = fdt_getprop (fdt, offstack[level], pathp, &len); |
|
|
|
|
if (len < 0) { |
|
|
|
|
printf ("libfdt %s\n", fdt_strerror(len)); |
|
|
|
|
return 1; |
|
|
|
|
} else if (len == 0) { |
|
|
|
|
/* the property has no value */ |
|
|
|
|
if(level <= depth) |
|
|
|
|
printf("%s%s;\n", &tabs[MAX_LEVEL - level], pathp); |
|
|
|
|
} else { |
|
|
|
|
if(level <= depth) { |
|
|
|
|
printf("%s%s=", &tabs[MAX_LEVEL - level], pathp); |
|
|
|
|
print_data (nodep, len); |
|
|
|
|
printf(";\n"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case FDT_NOP: |
|
|
|
|
break; |
|
|
|
|
case FDT_END: |
|
|
|
|
return 1; |
|
|
|
|
default: |
|
|
|
|
if(level <= depth) |
|
|
|
|
printf("Unknown tag 0x%08X\n", tag); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
nodeoffset = nextoffset; |
|
|
|
|
} |
|
|
|
|
ret = fdt_print(pathp, prop, depth); |
|
|
|
|
if (ret != 0) |
|
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
|
* Remove a property/node |
|
|
|
|
********************************************************************/ |
|
|
|
|
} else if (op == 'r') { |
|
|
|
|
} else if (argv[1][0] == 'r') { |
|
|
|
|
int nodeoffset; /* node offset from libfdt */ |
|
|
|
|
int err; |
|
|
|
|
|
|
|
|
@ -402,17 +265,14 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
* Get the path. The root node is an oddball, the offset |
|
|
|
|
* is zero and has no name. |
|
|
|
|
*/ |
|
|
|
|
if (strcmp(argv[2], "/") == 0) { |
|
|
|
|
nodeoffset = 0; |
|
|
|
|
} else { |
|
|
|
|
nodeoffset = fdt_path_offset (fdt, argv[2]); |
|
|
|
|
if (nodeoffset < 0) { |
|
|
|
|
/*
|
|
|
|
|
* Not found or something else bad happened. |
|
|
|
|
*/ |
|
|
|
|
printf ("libfdt %s\n", fdt_strerror(nodeoffset)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
nodeoffset = fdt_find_node_by_path (fdt, argv[2]); |
|
|
|
|
if (nodeoffset < 0) { |
|
|
|
|
/*
|
|
|
|
|
* Not found or something else bad happened. |
|
|
|
|
*/ |
|
|
|
|
printf ("libfdt fdt_find_node_by_path() returned %s\n", |
|
|
|
|
fdt_strerror(nodeoffset)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
/*
|
|
|
|
|
* Do the delete. A fourth parameter means delete a property, |
|
|
|
@ -421,39 +281,40 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
if (argc > 3) { |
|
|
|
|
err = fdt_delprop(fdt, nodeoffset, argv[3]); |
|
|
|
|
if (err < 0) { |
|
|
|
|
printf("fdt_delprop libfdt: %s\n", fdt_strerror(err)); |
|
|
|
|
printf("libfdt fdt_delprop(): %s\n", |
|
|
|
|
fdt_strerror(err)); |
|
|
|
|
return err; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
err = fdt_del_node(fdt, nodeoffset); |
|
|
|
|
if (err < 0) { |
|
|
|
|
printf("fdt_del_node libfdt: %s\n", fdt_strerror(err)); |
|
|
|
|
printf("libfdt fdt_del_node(): %s\n", |
|
|
|
|
fdt_strerror(err)); |
|
|
|
|
return err; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
|
* Create a chosen node |
|
|
|
|
********************************************************************/ |
|
|
|
|
} else if (op == 'c') { |
|
|
|
|
} |
|
|
|
|
#ifdef CONFIG_OF_BOARD_SETUP |
|
|
|
|
/* Call the board-specific fixup routine */ |
|
|
|
|
else if (argv[1][0] == 'b') |
|
|
|
|
ft_board_setup(fdt, gd->bd); |
|
|
|
|
#endif |
|
|
|
|
/* Create a chosen node */ |
|
|
|
|
else if (argv[1][0] == 'c') |
|
|
|
|
fdt_chosen(fdt, 0, 0, 1); |
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
|
* Create a u-boot-env node |
|
|
|
|
********************************************************************/ |
|
|
|
|
} else if (op == 'e') { |
|
|
|
|
#ifdef CONFIG_OF_HAS_UBOOT_ENV |
|
|
|
|
/* Create a u-boot-env node */ |
|
|
|
|
else if (argv[1][0] == 'e') |
|
|
|
|
fdt_env(fdt); |
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
|
* Create a bd_t node |
|
|
|
|
********************************************************************/ |
|
|
|
|
} else if (op == 'b') { |
|
|
|
|
#endif |
|
|
|
|
#ifdef CONFIG_OF_HAS_BD_T |
|
|
|
|
/* Create a bd_t node */ |
|
|
|
|
else if (argv[1][0] == 'b') |
|
|
|
|
fdt_bd_t(fdt); |
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
|
* Unrecognized command |
|
|
|
|
********************************************************************/ |
|
|
|
|
} else { |
|
|
|
|
#endif |
|
|
|
|
else { |
|
|
|
|
/* Unrecognized command */ |
|
|
|
|
printf ("Usage:\n%s\n", cmdtp->usage); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
@ -461,7 +322,7 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/********************************************************************/ |
|
|
|
|
/****************************************************************************/ |
|
|
|
|
|
|
|
|
|
static int fdt_valid(void) |
|
|
|
|
{ |
|
|
|
@ -477,19 +338,21 @@ static int fdt_valid(void) |
|
|
|
|
return 1; /* valid */ |
|
|
|
|
|
|
|
|
|
if (err < 0) { |
|
|
|
|
printf("libfdt: %s", fdt_strerror(err)); |
|
|
|
|
printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); |
|
|
|
|
/*
|
|
|
|
|
* Be more informative on bad version. |
|
|
|
|
*/ |
|
|
|
|
if (err == -FDT_ERR_BADVERSION) { |
|
|
|
|
if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) { |
|
|
|
|
printf (" - too old, fdt $d < %d", |
|
|
|
|
fdt_version(fdt), FDT_FIRST_SUPPORTED_VERSION); |
|
|
|
|
fdt_version(fdt), |
|
|
|
|
FDT_FIRST_SUPPORTED_VERSION); |
|
|
|
|
fdt = NULL; |
|
|
|
|
} |
|
|
|
|
if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) { |
|
|
|
|
printf (" - too new, fdt $d > %d", |
|
|
|
|
fdt_version(fdt), FDT_LAST_SUPPORTED_VERSION); |
|
|
|
|
fdt_version(fdt), |
|
|
|
|
FDT_LAST_SUPPORTED_VERSION); |
|
|
|
|
fdt = NULL; |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
@ -500,13 +363,91 @@ static int fdt_valid(void) |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/********************************************************************/ |
|
|
|
|
/****************************************************************************/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* OF flat tree handling |
|
|
|
|
* Written by: Pantelis Antoniou <pantelis.antoniou@gmail.com> |
|
|
|
|
* Updated by: Matthew McClintock <msm@freescale.com> |
|
|
|
|
* Converted to libfdt by: Gerald Van Baren <vanbaren@cideas.com> |
|
|
|
|
* Parse the user's input, partially heuristic. Valid formats: |
|
|
|
|
* <00> - hex byte |
|
|
|
|
* <0011> - hex half word (16 bits) |
|
|
|
|
* <00112233> - hex word (32 bits) |
|
|
|
|
* - hex double words (64 bits) are not supported, must use |
|
|
|
|
* a byte stream instead. |
|
|
|
|
* [00 11 22 .. nn] - byte stream |
|
|
|
|
* "string" - If the the value doesn't start with "<" or "[", it is |
|
|
|
|
* treated as a string. Note that the quotes are |
|
|
|
|
* stripped by the parser before we get the string. |
|
|
|
|
*/ |
|
|
|
|
static int fdt_parse_prop(char *pathp, char *prop, char *newval, |
|
|
|
|
char *data, int *len) |
|
|
|
|
{ |
|
|
|
|
char *cp; /* temporary char pointer */ |
|
|
|
|
unsigned long tmp; /* holds converted values */ |
|
|
|
|
|
|
|
|
|
if (*newval == '<') { |
|
|
|
|
/*
|
|
|
|
|
* Bigger values than bytes. |
|
|
|
|
*/ |
|
|
|
|
*len = 0; |
|
|
|
|
newval++; |
|
|
|
|
while ((*newval != '>') && (*newval != '\0')) { |
|
|
|
|
cp = newval; |
|
|
|
|
tmp = simple_strtoul(cp, &newval, 16); |
|
|
|
|
if ((newval - cp) <= 2) { |
|
|
|
|
*data = tmp & 0xFF; |
|
|
|
|
data += 1; |
|
|
|
|
*len += 1; |
|
|
|
|
} else if ((newval - cp) <= 4) { |
|
|
|
|
*(uint16_t *)data = __cpu_to_be16(tmp); |
|
|
|
|
data += 2; |
|
|
|
|
*len += 2; |
|
|
|
|
} else if ((newval - cp) <= 8) { |
|
|
|
|
*(uint32_t *)data = __cpu_to_be32(tmp); |
|
|
|
|
data += 4; |
|
|
|
|
*len += 4; |
|
|
|
|
} else { |
|
|
|
|
printf("Sorry, I could not convert \"%s\"\n", |
|
|
|
|
cp); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
while (*newval == ' ') |
|
|
|
|
newval++; |
|
|
|
|
} |
|
|
|
|
if (*newval != '>') { |
|
|
|
|
printf("Unexpected character '%c'\n", *newval); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} else if (*newval == '[') { |
|
|
|
|
/*
|
|
|
|
|
* Byte stream. Convert the values. |
|
|
|
|
*/ |
|
|
|
|
*len = 0; |
|
|
|
|
newval++; |
|
|
|
|
while ((*newval != ']') && (*newval != '\0')) { |
|
|
|
|
tmp = simple_strtoul(newval, &newval, 16); |
|
|
|
|
*data++ = tmp & 0xFF; |
|
|
|
|
*len = *len + 1; |
|
|
|
|
while (*newval == ' ') |
|
|
|
|
newval++; |
|
|
|
|
} |
|
|
|
|
if (*newval != ']') { |
|
|
|
|
printf("Unexpected character '%c'\n", *newval); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
/*
|
|
|
|
|
* Assume it is a string. Copy it into our data area for |
|
|
|
|
* convenience (including the terminating '\0'). |
|
|
|
|
*/ |
|
|
|
|
*len = strlen(newval) + 1; |
|
|
|
|
strcpy(data, newval); |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/****************************************************************************/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Heuristic to guess if this is a string or concatenated strings. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static int is_printable_string(const void *data, int len) |
|
|
|
@ -546,6 +487,12 @@ static int is_printable_string(const void *data, int len) |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Print the property in the best format, a heuristic guess. Print as |
|
|
|
|
* a string, concatenated strings, a byte, word, double word, or (if all |
|
|
|
|
* else fails) it is printed as a stream of bytes. |
|
|
|
|
*/ |
|
|
|
|
static void print_data(const void *data, int len) |
|
|
|
|
{ |
|
|
|
|
int j; |
|
|
|
@ -601,32 +548,146 @@ static void print_data(const void *data, int len) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/****************************************************************************/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Recursively print (a portion of) the fdt. The depth parameter |
|
|
|
|
* determines how deeply nested the fdt is printed. |
|
|
|
|
*/ |
|
|
|
|
static int fdt_print(char *pathp, char *prop, int depth) |
|
|
|
|
{ |
|
|
|
|
static int offstack[MAX_LEVEL]; |
|
|
|
|
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"; |
|
|
|
|
void *nodep; /* property node pointer */ |
|
|
|
|
int nodeoffset; /* node offset from libfdt */ |
|
|
|
|
int nextoffset; /* next node offset from libfdt */ |
|
|
|
|
uint32_t tag; /* tag */ |
|
|
|
|
int len; /* length of the property */ |
|
|
|
|
int level = 0; /* keep track of nesting level */ |
|
|
|
|
|
|
|
|
|
nodeoffset = fdt_find_node_by_path (fdt, pathp); |
|
|
|
|
if (nodeoffset < 0) { |
|
|
|
|
/*
|
|
|
|
|
* Not found or something else bad happened. |
|
|
|
|
*/ |
|
|
|
|
printf ("libfdt fdt_find_node_by_path() returned %s\n", |
|
|
|
|
fdt_strerror(nodeoffset)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
/*
|
|
|
|
|
* The user passed in a property as well as node path. |
|
|
|
|
* Print only the given property and then return. |
|
|
|
|
*/ |
|
|
|
|
if (prop) { |
|
|
|
|
nodep = fdt_getprop (fdt, nodeoffset, prop, &len); |
|
|
|
|
if (len == 0) { |
|
|
|
|
/* no property value */ |
|
|
|
|
printf("%s %s\n", pathp, prop); |
|
|
|
|
return 0; |
|
|
|
|
} else if (len > 0) { |
|
|
|
|
printf("%s=", prop); |
|
|
|
|
print_data (nodep, len); |
|
|
|
|
printf("\n"); |
|
|
|
|
return 0; |
|
|
|
|
} else { |
|
|
|
|
printf ("libfdt fdt_getprop(): %s\n", |
|
|
|
|
fdt_strerror(len)); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The user passed in a node path and no property, |
|
|
|
|
* print the node and all subnodes. |
|
|
|
|
*/ |
|
|
|
|
offstack[0] = nodeoffset; |
|
|
|
|
|
|
|
|
|
while(level >= 0) { |
|
|
|
|
tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, &pathp); |
|
|
|
|
switch(tag) { |
|
|
|
|
case FDT_BEGIN_NODE: |
|
|
|
|
if(level <= depth) |
|
|
|
|
printf("%s%s {\n", |
|
|
|
|
&tabs[MAX_LEVEL - level], pathp); |
|
|
|
|
level++; |
|
|
|
|
offstack[level] = nodeoffset; |
|
|
|
|
if (level >= MAX_LEVEL) { |
|
|
|
|
printf("Aaaiii <splat> nested too deep. " |
|
|
|
|
"Aborting.\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case FDT_END_NODE: |
|
|
|
|
level--; |
|
|
|
|
if(level <= depth) |
|
|
|
|
printf("%s};\n", &tabs[MAX_LEVEL - level]); |
|
|
|
|
if (level == 0) { |
|
|
|
|
level = -1; /* exit the loop */ |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case FDT_PROP: |
|
|
|
|
nodep = fdt_getprop (fdt, offstack[level], pathp, &len); |
|
|
|
|
if (len < 0) { |
|
|
|
|
printf ("libfdt fdt_getprop(): %s\n", |
|
|
|
|
fdt_strerror(len)); |
|
|
|
|
return 1; |
|
|
|
|
} else if (len == 0) { |
|
|
|
|
/* the property has no value */ |
|
|
|
|
if(level <= depth) |
|
|
|
|
printf("%s%s;\n", |
|
|
|
|
&tabs[MAX_LEVEL - level], |
|
|
|
|
pathp); |
|
|
|
|
} else { |
|
|
|
|
if(level <= depth) { |
|
|
|
|
printf("%s%s=", |
|
|
|
|
&tabs[MAX_LEVEL - level], |
|
|
|
|
pathp); |
|
|
|
|
print_data (nodep, len); |
|
|
|
|
printf(";\n"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case FDT_NOP: |
|
|
|
|
break; |
|
|
|
|
case FDT_END: |
|
|
|
|
return 1; |
|
|
|
|
default: |
|
|
|
|
if(level <= depth) |
|
|
|
|
printf("Unknown tag 0x%08X\n", tag); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
nodeoffset = nextoffset; |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/********************************************************************/ |
|
|
|
|
|
|
|
|
|
U_BOOT_CMD( |
|
|
|
|
fdt, 5, 0, do_fdt, |
|
|
|
|
"fdt - flattened device tree utility commands\n", |
|
|
|
|
"addr <addr> [<length>] - Set the fdt location to <addr>\n" |
|
|
|
|
#ifdef CONFIG_OF_BOARD_SETUP |
|
|
|
|
"fdt boardsetup - Do board-specific set up\n" |
|
|
|
|
#endif |
|
|
|
|
"fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr>\n" |
|
|
|
|
"fdt print <path> [<prop>] - Recursive print starting at <path>\n" |
|
|
|
|
"fdt list <path> [<prop>] - Print one level starting at <path>\n" |
|
|
|
|
"fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n" |
|
|
|
|
"fdt mknode <path> <node> - Create a new node after <path>\n" |
|
|
|
|
"fdt rm <path> [<prop>] - Delete the node or <property>\n" |
|
|
|
|
"fdt chosen - Add/update the \"/chosen\" branch in the tree\n" |
|
|
|
|
"fdt chosen - Add/update the /chosen branch in the tree\n" |
|
|
|
|
#ifdef CONFIG_OF_HAS_UBOOT_ENV |
|
|
|
|
"fdt env - Add/replace the \"/u-boot-env\" branch in the tree\n" |
|
|
|
|
"fdt env - Add/replace the /u-boot-env branch in the tree\n" |
|
|
|
|
#endif |
|
|
|
|
#ifdef CONFIG_OF_HAS_BD_T |
|
|
|
|
"fdt bd_t - Add/replace the \"/bd_t\" branch in the tree\n" |
|
|
|
|
"fdt bd_t - Add/replace the /bd_t branch in the tree\n" |
|
|
|
|
#endif |
|
|
|
|
"Hints:\n" |
|
|
|
|
" * Set a larger length with the fdt addr command to add to the blob.\n" |
|
|
|
|
" * If the property you are setting/printing has a '#' character,\n" |
|
|
|
|
" you MUST escape it with a \\ character or quote it with \" or\n" |
|
|
|
|
" it will be ignored as a comment.\n" |
|
|
|
|
" * If the value has spaces in it, you MUST escape the spaces with\n" |
|
|
|
|
" \\ characters or quote it with \"\"\n" |
|
|
|
|
" If the property you are setting/printing has a '#' character or spaces,\n" |
|
|
|
|
" you MUST escape it with a \\ character or quote it with \".\n" |
|
|
|
|
"Examples: fdt print / # print the whole tree\n" |
|
|
|
|
" fdt print /cpus \"#address-cells\"\n" |
|
|
|
|
" fdt set /cpus \"#address-cells\" \"[00 00 00 01]\"\n" |
|
|
|
|