fdt: Add get commands to fdt

Add commands to access data in the fdt.  This allows data from a dtb
or itb to be accessed from the shell scripts.

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
diff --git a/common/cmd_fdt.c b/common/cmd_fdt.c
index 699441b5..2997452 100644
--- a/common/cmd_fdt.c
+++ b/common/cmd_fdt.c
@@ -47,6 +47,7 @@
 static int fdt_valid(void);
 static int fdt_parse_prop(char *const*newval, int count, char *data, int *len);
 static int fdt_print(const char *pathp, char *prop, int depth);
+static int is_printable_string(const void *data, int len);
 
 /*
  * The working_fdt points to our working flattened device tree.
@@ -64,6 +65,34 @@
 }
 
 /*
+ * Get a value from the fdt and format it to be set in the environment
+ */
+static int fdt_value_setenv(const void *nodep, int len, const char *var)
+{
+	if (is_printable_string(nodep, len))
+		setenv(var, (void *)nodep);
+	else if (len == 4) {
+		char buf[11];
+
+		sprintf(buf, "0x%08X", *(uint32_t *)nodep);
+		setenv(var, buf);
+	} else if (len%4 == 0 && len <= 20) {
+		/* Needed to print things like sha1 hashes. */
+		char buf[41];
+		int i;
+
+		for (i = 0; i < len; i += sizeof(unsigned int))
+			sprintf(buf + (i * 2), "%08x",
+				*(unsigned int *)(nodep + i));
+		setenv(var, buf);
+	} else {
+		printf("error: unprintable value\n");
+		return 1;
+	}
+	return 0;
+}
+
+/*
  * Flattened Device Tree command, see the help for parameter definitions.
  */
 int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
@@ -253,6 +282,117 @@
 			return 1;
 		}
 
+	/********************************************************************
+	 * Get the value of a property in the working_fdt.
+	 ********************************************************************/
+	} else if (argv[1][0] == 'g') {
+		char *subcmd;		/* sub-command */
+		char *pathp;		/* path */
+		char *prop;		/* property */
+		char *var;		/* variable to store result */
+		int  nodeoffset;	/* node offset from libfdt */
+		const void *nodep;	/* property node pointer */
+		int  len = 0;		/* new length of the property */
+
+		/*
+		 * Parameters: Node path, property, optional value.
+		 */
+		if (argc < 5)
+			return CMD_RET_USAGE;
+
+		subcmd = argv[2];
+
+		if (argc < 6 && subcmd[0] != 's')
+			return CMD_RET_USAGE;
+
+		var    = argv[3];
+		pathp  = argv[4];
+		prop   = argv[5];
+
+		nodeoffset = fdt_path_offset(working_fdt, pathp);
+		if (nodeoffset < 0) {
+			/*
+			 * Not found or something else bad happened.
+			 */
+			printf("libfdt fdt_path_offset() returned %s\n",
+				fdt_strerror(nodeoffset));
+			return 1;
+		}
+
+		if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) {
+			int reqIndex = -1;
+			int startDepth = fdt_node_depth(
+				working_fdt, nodeoffset);
+			int curDepth = startDepth;
+			int curIndex = -1;
+			int nextNodeOffset = fdt_next_node(
+				working_fdt, nodeoffset, &curDepth);
+
+			if (subcmd[0] == 'n')
+				reqIndex = simple_strtoul(argv[5], NULL, 16);
+
+			while (curDepth > startDepth) {
+				if (curDepth == startDepth + 1)
+					curIndex++;
+				if (subcmd[0] == 'n' && curIndex == reqIndex) {
+					const char *nodeName = fdt_get_name(
+					    working_fdt, nextNodeOffset, NULL);
+
+					setenv(var, (char *)nodeName);
+					return 0;
+				}
+				nextNodeOffset = fdt_next_node(
+					working_fdt, nextNodeOffset, &curDepth);
+				if (nextNodeOffset < 0)
+					break;
+			}
+			if (subcmd[0] == 's') {
+				/* get the num nodes at this level */
+				char buf[11];
+
+				sprintf(buf, "%d", curIndex + 1);
+				setenv(var, buf);
+			} else {
+				/* node index not found */
+				printf("libfdt node not found\n");
+				return 1;
+			}
+		} else {
+			nodep = fdt_getprop(
+				working_fdt, nodeoffset, prop, &len);
+			if (len == 0) {
+				/* no property value */
+				setenv(var, "");
+				return 0;
+			} else if (len > 0) {
+				if (subcmd[0] == 'v') {
+					int ret;
+
+					ret = fdt_value_setenv(nodep, len, var);
+					if (ret != 0)
+						return ret;
+				} else if (subcmd[0] == 'a') {
+					/* Get address */
+					char buf[11];
+
+					sprintf(buf, "0x%08X", (uint32_t)nodep);
+					setenv(var, buf);
+				} else if (subcmd[0] == 's') {
+					/* Get size */
+					char buf[11];
+
+					sprintf(buf, "0x%08X", len);
+					setenv(var, buf);
+				} else
+					return CMD_RET_USAGE;
+				return 0;
+			} else {
+				printf("libfdt fdt_getprop(): %s\n",
+					fdt_strerror(len));
+				return 1;
+			}
+		}
+
 	/*
 	 * Print (recursive) / List (single level)
 	 */
@@ -836,6 +976,10 @@
 	"fdt resize                          - Resize fdt to size + padding to 4k addr\n"
 	"fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
 	"fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
+	"fdt get value <var> <path> <prop>   - Get <property> and store in <var>\n"
+	"fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>\n"
+	"fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>\n"
+	"fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>\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"