cmd: dm: allow for selecting uclass and device

The output from "dm tree" or "dm uclass" is a bit annoying
if the number of devices available on the system is huge.
(This is especially true on sandbox when I debug some DM code.)

With this patch, we can specify the uclass name or the device
name that we are interested in in order to limit the output.

For instance,

=> dm uclass usb
uclass 121: usb
0     usb@1 @ 0bcff8b0, seq 1

uclass 124: usb

=> dm tree usb:usb@1
 Class     Index  Probed  Driver                Name
-----------------------------------------------------------
 usb           0  [   ]   usb_sandbox           usb@1
 usb_hub       0  [   ]   usb_hub               `-- hub
 usb_emul      0  [   ]   usb_sandbox_hub           `-- hub-emul
 usb_emul      1  [   ]   usb_sandbox_flash             |-- flash-stick@0
 usb_emul      2  [   ]   usb_sandbox_flash             |-- flash-stick@1
 usb_emul      3  [   ]   usb_sandbox_flash             |-- flash-stick@2
 usb_emul      4  [   ]   usb_sandbox_keyb              `-- keyb@3

If you want forward-matching against a uclass or udevice name,
you can specify "-e" option.

=> dm uclass -e usb
uclass 15: usb_emul
0     hub-emul @ 0bcffb00, seq 0
1     flash-stick@0 @ 0bcffc30, seq 1
2     flash-stick@1 @ 0bcffdc0, seq 2
3     flash-stick@2 @ 0bcfff50, seq 3
4     keyb@3 @ 0bd000e0, seq 4

uclass 64: usb_mass_storage

uclass 121: usb
0     usb@1 @ 0bcff8b0, seq 1

uclass 122: usb_dev_generic

uclass 123: usb_hub
0     hub @ 0bcff9b0, seq 0

uclass 124: usb

=> dm tree -e usb
 Class     Index  Probed  Driver                Name
-----------------------------------------------------------
 usb           0  [   ]   usb_sandbox           usb@1
 usb_hub       0  [   ]   usb_hub               `-- hub
 usb_emul      0  [   ]   usb_sandbox_hub           `-- hub-emul
 usb_emul      1  [   ]   usb_sandbox_flash             |-- flash-stick@0
 usb_emul      2  [   ]   usb_sandbox_flash             |-- flash-stick@1
 usb_emul      3  [   ]   usb_sandbox_flash             |-- flash-stick@2
 usb_emul      4  [   ]   usb_sandbox_keyb              `-- keyb@3

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/core/dump.c b/drivers/core/dump.c
index 3e77832..4023b39 100644
--- a/drivers/core/dump.c
+++ b/drivers/core/dump.c
@@ -85,29 +85,65 @@
 	}
 }
 
-void dm_dump_tree(bool sort)
+static void dm_dump_tree_single(struct udevice *dev, bool sort)
 {
-	struct udevice *root;
+	int dev_count, uclasses;
+	struct udevice **devs = NULL;
 
-	root = dm_root();
-	if (root) {
-		int dev_count, uclasses;
-		struct udevice **devs = NULL;
+	dm_get_stats(&dev_count, &uclasses);
+
+	if (sort) {
+		devs = calloc(dev_count, sizeof(struct udevice *));
+		if (!devs) {
+			printf("(out of memory)\n");
+			return;
+		}
+	}
+	show_devices(dev, -1, 0, devs);
+	free(devs);
+}
+
+static void dm_dump_tree_recursive(struct udevice *dev, char *dev_name,
+				   bool extended, bool sort)
+{
+	struct udevice *child;
+	size_t len;
 
-		dm_get_stats(&dev_count, &uclasses);
+	len = strlen(dev_name);
 
-		printf(" Class     Index  Probed  Driver                Name\n");
-		printf("-----------------------------------------------------------\n");
-		if (sort) {
-			devs = calloc(dev_count, sizeof(struct udevice *));
-			if (!devs) {
-				printf("(out of memory)\n");
-				return;
+	device_foreach_child(child, dev) {
+		if (extended) {
+			if (!strncmp(child->name, dev_name, len)) {
+				dm_dump_tree_single(child, sort);
+				continue;
+			}
+		} else {
+			if (!strcmp(child->name, dev_name)) {
+				dm_dump_tree_single(child, sort);
+				continue;
 			}
 		}
-		show_devices(root, -1, 0, devs);
-		free(devs);
+		dm_dump_tree_recursive(child, dev_name, extended, sort);
+	}
+}
+
+void dm_dump_tree(char *dev_name, bool extended, bool sort)
+{
+	struct udevice *root;
+
+	printf(" Class     Index  Probed  Driver                Name\n");
+	printf("-----------------------------------------------------------\n");
+
+	root = dm_root();
+	if (!root)
+		return;
+
+	if (!dev_name || !strcmp(dev_name, "root")) {
+		dm_dump_tree_single(root, sort);
+		return;
 	}
+
+	dm_dump_tree_recursive(root, dev_name, extended, sort);
 }
 
 /**
@@ -127,26 +163,50 @@
 	puts("\n");
 }
 
-void dm_dump_uclass(void)
+static void dm_dump_uclass_single(enum uclass_id id)
 {
 	struct uclass *uc;
+	struct udevice *dev;
+	int i = 0, ret;
+
+	ret = uclass_get(id, &uc);
+	if (ret)
+		return;
+
+	printf("uclass %d: %s\n", id, uc->uc_drv->name);
+	uclass_foreach_dev(dev, uc) {
+		dm_display_line(dev, i);
+		i++;
+	}
+	puts("\n");
+}
+
+void dm_dump_uclass(char *uclass, bool extended)
+{
+	struct uclass *uc;
+	enum uclass_id id;
+	bool matching;
 	int ret;
-	int id;
 
-	for (id = 0; id < UCLASS_COUNT; id++) {
-		struct udevice *dev;
-		int i = 0;
+	matching = !!(uclass && strcmp(uclass, "root"));
 
+	for (id = 0; id < UCLASS_COUNT; id++) {
 		ret = uclass_get(id, &uc);
 		if (ret)
 			continue;
 
-		printf("uclass %d: %s\n", id, uc->uc_drv->name);
-		uclass_foreach_dev(dev, uc) {
-			dm_display_line(dev, i);
-			i++;
+		if (matching) {
+			if (extended) {
+				if (!strncmp(uc->uc_drv->name, uclass,
+					     strlen(uclass)))
+					dm_dump_uclass_single(id);
+			} else {
+				if (!strcmp(uc->uc_drv->name, uclass))
+					dm_dump_uclass_single(id);
+			}
+		} else {
+			dm_dump_uclass_single(id);
 		}
-		puts("\n");
 	}
 }