Explorar el Código

Merge branch 'master' of git://git.denx.de/u-boot-usb

Tom Rini hace 6 años
padre
commit
15fd1b7903

+ 1 - 1
arch/arm/mach-rockchip/rk3188-board-spl.c

@@ -49,7 +49,7 @@ u32 spl_boot_device(void)
 		debug("node=%d\n", node);
 		goto fallback;
 	}
-	ret = device_get_global_by_of_offset(node, &dev);
+	ret = device_get_global_by_ofnode(offset_to_ofnode(node), &dev);
 	if (ret) {
 		debug("device at node %s/%d not found: %d\n", bootdev, node,
 		      ret);

+ 1 - 1
arch/arm/mach-rockchip/rk3288-board-spl.c

@@ -51,7 +51,7 @@ u32 spl_boot_device(void)
 		debug("node=%d\n", node);
 		goto fallback;
 	}
-	ret = device_get_global_by_of_offset(node, &dev);
+	ret = device_get_global_by_ofnode(offset_to_ofnode(node), &dev);
 	if (ret) {
 		debug("device at node %s/%d not found: %d\n", bootdev, node,
 		      ret);

+ 11 - 0
arch/sandbox/dts/test.dts

@@ -61,6 +61,17 @@
 		reg = <2 1>;
 	};
 
+	bind-test {
+		bind-test-child1 {
+			compatible = "sandbox,phy";
+			#phy-cells = <1>;
+		};
+
+		bind-test-child2 {
+			compatible = "simple-bus";
+		};
+	};
+
 	b-test {
 		reg = <3 1>;
 		compatible = "denx,u-boot-fdt-test";

+ 9 - 0
cmd/Kconfig

@@ -615,6 +615,15 @@ config CMD_ADC
 	  Shows ADC device info and permit printing one-shot analog converted
 	  data from a named Analog to Digital Converter.
 
+config CMD_BIND
+	bool "bind/unbind - Bind or unbind a device to/from a driver"
+	depends on DM
+	help
+	  Bind or unbind a device to/from a driver from the command line.
+	  This is useful in situations where a device may be handled by several
+	  drivers. For example, this can be used to bind a UDC to the usb ether
+	  gadget driver from the command line.
+
 config CMD_CLK
 	bool "clk - Show clock frequencies"
 	help

+ 1 - 0
cmd/Makefile

@@ -19,6 +19,7 @@ obj-$(CONFIG_SOURCE) += source.o
 obj-$(CONFIG_CMD_SOURCE) += source.o
 obj-$(CONFIG_CMD_BDI) += bdinfo.o
 obj-$(CONFIG_CMD_BEDBUG) += bedbug.o
+obj-$(CONFIG_CMD_BIND) += bind.o
 obj-$(CONFIG_CMD_BINOP) += binop.o
 obj-$(CONFIG_CMD_BLOCK_CACHE) += blkcache.o
 obj-$(CONFIG_CMD_BMP) += bmp.o

+ 255 - 0
cmd/bind.c

@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 JJ Hiblot <jjhiblot@ti.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+
+static int bind_by_class_index(const char *uclass, int index,
+			       const char *drv_name)
+{
+	static enum uclass_id uclass_id;
+	struct udevice *dev;
+	struct udevice *parent;
+	int ret;
+	struct driver *drv;
+
+	drv = lists_driver_lookup_name(drv_name);
+	if (!drv) {
+		printf("Cannot find driver '%s'\n", drv_name);
+		return -ENOENT;
+	}
+
+	uclass_id = uclass_get_by_name(uclass);
+	if (uclass_id == UCLASS_INVALID) {
+		printf("%s is not a valid uclass\n", uclass);
+		return -EINVAL;
+	}
+
+	ret = uclass_find_device(uclass_id, index, &parent);
+	if (!parent || ret) {
+		printf("Cannot find device %d of class %s\n", index, uclass);
+		return ret;
+	}
+
+	ret = device_bind_with_driver_data(parent, drv, drv->name, 0,
+					   ofnode_null(), &dev);
+	if (!dev || ret) {
+		printf("Unable to bind. err:%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int find_dev(const char *uclass, int index, struct udevice **devp)
+{
+	static enum uclass_id uclass_id;
+	int rc;
+
+	uclass_id = uclass_get_by_name(uclass);
+	if (uclass_id == UCLASS_INVALID) {
+		printf("%s is not a valid uclass\n", uclass);
+		return -EINVAL;
+	}
+
+	rc = uclass_find_device(uclass_id, index, devp);
+	if (!*devp || rc) {
+		printf("Cannot find device %d of class %s\n", index, uclass);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int unbind_by_class_index(const char *uclass, int index)
+{
+	int ret;
+	struct udevice *dev;
+
+	ret = find_dev(uclass, index, &dev);
+	if (ret)
+		return ret;
+
+	ret = device_remove(dev, DM_REMOVE_NORMAL);
+	if (ret) {
+		printf("Unable to remove. err:%d\n", ret);
+		return ret;
+	}
+
+	ret = device_unbind(dev);
+	if (ret) {
+		printf("Unable to unbind. err:%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int unbind_child_by_class_index(const char *uclass, int index,
+				       const char *drv_name)
+{
+	struct udevice *parent;
+	int ret;
+	struct driver *drv;
+
+	drv = lists_driver_lookup_name(drv_name);
+	if (!drv) {
+		printf("Cannot find driver '%s'\n", drv_name);
+		return -ENOENT;
+	}
+
+	ret = find_dev(uclass, index, &parent);
+	if (ret)
+		return ret;
+
+	ret = device_chld_remove(parent, drv, DM_REMOVE_NORMAL);
+	if (ret)
+		printf("Unable to remove all. err:%d\n", ret);
+
+	ret = device_chld_unbind(parent, drv);
+	if (ret)
+		printf("Unable to unbind all. err:%d\n", ret);
+
+	return ret;
+}
+
+static int bind_by_node_path(const char *path, const char *drv_name)
+{
+	struct udevice *dev;
+	struct udevice *parent = NULL;
+	int ret;
+	ofnode ofnode;
+	struct driver *drv;
+
+	drv = lists_driver_lookup_name(drv_name);
+	if (!drv) {
+		printf("%s is not a valid driver name\n", drv_name);
+		return -ENOENT;
+	}
+
+	ofnode = ofnode_path(path);
+	if (!ofnode_valid(ofnode)) {
+		printf("%s is not a valid node path\n", path);
+		return -EINVAL;
+	}
+
+	while (ofnode_valid(ofnode)) {
+		if (!device_find_global_by_ofnode(ofnode, &parent))
+			break;
+		ofnode = ofnode_get_parent(ofnode);
+	}
+
+	if (!parent) {
+		printf("Cannot find a parent device for node path %s\n", path);
+		return -ENODEV;
+	}
+
+	ofnode = ofnode_path(path);
+	ret = device_bind_with_driver_data(parent, drv, ofnode_get_name(ofnode),
+					   0, ofnode, &dev);
+	if (!dev || ret) {
+		printf("Unable to bind. err:%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int unbind_by_node_path(const char *path)
+{
+	struct udevice *dev;
+	int ret;
+	ofnode ofnode;
+
+	ofnode = ofnode_path(path);
+	if (!ofnode_valid(ofnode)) {
+		printf("%s is not a valid node path\n", path);
+		return -EINVAL;
+	}
+
+	ret = device_find_global_by_ofnode(ofnode, &dev);
+
+	if (!dev || ret) {
+		printf("Cannot find a device with path %s\n", path);
+		return -ENODEV;
+	}
+
+	ret = device_remove(dev, DM_REMOVE_NORMAL);
+	if (ret) {
+		printf("Unable to remove. err:%d\n", ret);
+		return ret;
+	}
+
+	ret = device_unbind(dev);
+	if (ret) {
+		printf("Unable to unbind. err:%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int do_bind_unbind(cmd_tbl_t *cmdtp, int flag, int argc,
+			  char * const argv[])
+{
+	int ret = 0;
+	bool bind;
+	bool by_node;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	bind = (argv[0][0] == 'b');
+	by_node = (argv[1][0] == '/');
+
+	if (by_node && bind) {
+		if (argc != 3)
+			return CMD_RET_USAGE;
+		ret = bind_by_node_path(argv[1], argv[2]);
+	} else if (by_node && !bind) {
+		if (argc != 2)
+			return CMD_RET_USAGE;
+		ret = unbind_by_node_path(argv[1]);
+	} else if (!by_node && bind) {
+		int index = (argc > 2) ? simple_strtoul(argv[2], NULL, 10) : 0;
+
+		if (argc != 4)
+			return CMD_RET_USAGE;
+		ret = bind_by_class_index(argv[1], index, argv[3]);
+	} else if (!by_node && !bind) {
+		int index = (argc > 2) ? simple_strtoul(argv[2], NULL, 10) : 0;
+
+		if (argc == 3)
+			ret = unbind_by_class_index(argv[1], index);
+		else if (argc == 4)
+			ret = unbind_child_by_class_index(argv[1], index,
+							  argv[3]);
+		else
+			return CMD_RET_USAGE;
+	}
+
+	if (ret)
+		return CMD_RET_FAILURE;
+	else
+		return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+	bind,	4,	0,	do_bind_unbind,
+	"Bind a device to a driver",
+	"<node path> <driver>\n"
+	"bind <class> <index> <driver>\n"
+);
+
+U_BOOT_CMD(
+	unbind,	4,	0,	do_bind_unbind,
+	"Unbind a device from a driver",
+	"<node path>\n"
+	"unbind <class> <index>\n"
+	"unbind <class> <index> <driver>\n"
+);

+ 12 - 1
cmd/fastboot.c

@@ -38,13 +38,18 @@ static int do_fastboot_usb(int argc, char *const argv[],
 #if CONFIG_IS_ENABLED(USB_FUNCTION_FASTBOOT)
 	int controller_index;
 	char *usb_controller;
+	char *endp;
 	int ret;
 
 	if (argc < 2)
 		return CMD_RET_USAGE;
 
 	usb_controller = argv[1];
-	controller_index = simple_strtoul(usb_controller, NULL, 0);
+	controller_index = simple_strtoul(usb_controller, &endp, 0);
+	if (*endp != '\0') {
+		pr_err("Error: Wrong USB controller index format\n");
+		return CMD_RET_FAILURE;
+	}
 
 	ret = board_usb_init(controller_index, USB_INIT_DEVICE);
 	if (ret) {
@@ -120,6 +125,12 @@ NXTARG:
 		;
 	}
 
+	/* Handle case when USB controller param is just '-' */
+	if (argc == 1) {
+		pr_err("Error: Incorrect USB controller index\n");
+		return CMD_RET_USAGE;
+	}
+
 	fastboot_init((void *)buf_addr, buf_size);
 
 	if (!strcmp(argv[1], "udp"))

+ 1 - 0
configs/sandbox_defconfig

@@ -34,6 +34,7 @@ CONFIG_CMD_MD5SUM=y
 CONFIG_CMD_MEMINFO=y
 CONFIG_CMD_MEMTEST=y
 CONFIG_CMD_MX_CYCLIC=y
+CONFIG_CMD_BIND=y
 CONFIG_CMD_DEMO=y
 CONFIG_CMD_GPIO=y
 CONFIG_CMD_GPT=y

+ 11 - 19
drivers/core/device-remove.c

@@ -17,16 +17,7 @@
 #include <dm/uclass-internal.h>
 #include <dm/util.h>
 
-/**
- * device_chld_unbind() - Unbind all device's children from the device
- *
- * On error, the function continues to unbind all children, and reports the
- * first error.
- *
- * @dev:	The device that is to be stripped of its children
- * @return 0 on success, -ve on error
- */
-static int device_chld_unbind(struct udevice *dev)
+int device_chld_unbind(struct udevice *dev, struct driver *drv)
 {
 	struct udevice *pos, *n;
 	int ret, saved_ret = 0;
@@ -34,6 +25,9 @@ static int device_chld_unbind(struct udevice *dev)
 	assert(dev);
 
 	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+		if (drv && (pos->driver != drv))
+			continue;
+
 		ret = device_unbind(pos);
 		if (ret && !saved_ret)
 			saved_ret = ret;
@@ -42,13 +36,8 @@ static int device_chld_unbind(struct udevice *dev)
 	return saved_ret;
 }
 
-/**
- * device_chld_remove() - Stop all device's children
- * @dev:	The device whose children are to be removed
- * @pre_os_remove: Flag, if this functions is called in the pre-OS stage
- * @return 0 on success, -ve on error
- */
-static int device_chld_remove(struct udevice *dev, uint flags)
+int device_chld_remove(struct udevice *dev, struct driver *drv,
+		       uint flags)
 {
 	struct udevice *pos, *n;
 	int ret;
@@ -56,6 +45,9 @@ static int device_chld_remove(struct udevice *dev, uint flags)
 	assert(dev);
 
 	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+		if (drv && (pos->driver != drv))
+			continue;
+
 		ret = device_remove(pos, flags);
 		if (ret)
 			return ret;
@@ -87,7 +79,7 @@ int device_unbind(struct udevice *dev)
 			return ret;
 	}
 
-	ret = device_chld_unbind(dev);
+	ret = device_chld_unbind(dev, NULL);
 	if (ret)
 		return ret;
 
@@ -178,7 +170,7 @@ int device_remove(struct udevice *dev, uint flags)
 	if (ret)
 		return ret;
 
-	ret = device_chld_remove(dev, flags);
+	ret = device_chld_remove(dev, NULL, flags);
 	if (ret)
 		goto err;
 

+ 13 - 6
drivers/core/device.c

@@ -594,16 +594,16 @@ int device_get_child_by_of_offset(struct udevice *parent, int node,
 	return device_get_device_tail(dev, ret, devp);
 }
 
-static struct udevice *_device_find_global_by_of_offset(struct udevice *parent,
-							int of_offset)
+static struct udevice *_device_find_global_by_ofnode(struct udevice *parent,
+						     ofnode ofnode)
 {
 	struct udevice *dev, *found;
 
-	if (dev_of_offset(parent) == of_offset)
+	if (ofnode_equal(dev_ofnode(parent), ofnode))
 		return parent;
 
 	list_for_each_entry(dev, &parent->child_head, sibling_node) {
-		found = _device_find_global_by_of_offset(dev, of_offset);
+		found = _device_find_global_by_ofnode(dev, ofnode);
 		if (found)
 			return found;
 	}
@@ -611,11 +611,18 @@ static struct udevice *_device_find_global_by_of_offset(struct udevice *parent,
 	return NULL;
 }
 
-int device_get_global_by_of_offset(int of_offset, struct udevice **devp)
+int device_find_global_by_ofnode(ofnode ofnode, struct udevice **devp)
+{
+	*devp = _device_find_global_by_ofnode(gd->dm_root, ofnode);
+
+	return *devp ? 0 : -ENOENT;
+}
+
+int device_get_global_by_ofnode(ofnode ofnode, struct udevice **devp)
 {
 	struct udevice *dev;
 
-	dev = _device_find_global_by_of_offset(gd->dm_root, of_offset);
+	dev = _device_find_global_by_ofnode(gd->dm_root, ofnode);
 	return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp);
 }
 

+ 10 - 6
drivers/core/dump.c

@@ -8,6 +8,7 @@
 #include <mapmem.h>
 #include <dm/root.h>
 #include <dm/util.h>
+#include <dm/uclass-internal.h>
 
 static void show_devices(struct udevice *dev, int depth, int last_flag)
 {
@@ -15,7 +16,8 @@ static void show_devices(struct udevice *dev, int depth, int last_flag)
 	struct udevice *child;
 
 	/* print the first 11 characters to not break the tree-format. */
-	printf(" %-10.10s [ %c ]   %-10.10s  ", dev->uclass->uc_drv->name,
+	printf(" %-10.10s  %d  [ %c ]   %-10.10s  ", dev->uclass->uc_drv->name,
+	       dev_get_uclass_index(dev, NULL),
 	       dev->flags & DM_FLAG_ACTIVATED ? '+' : ' ', dev->driver->name);
 
 	for (i = depth; i >= 0; i--) {
@@ -47,8 +49,8 @@ void dm_dump_all(void)
 
 	root = dm_root();
 	if (root) {
-		printf(" Class      Probed  Driver      Name\n");
-		printf("----------------------------------------\n");
+		printf(" Class    index  Probed  Driver      Name\n");
+		printf("-----------------------------------------\n");
 		show_devices(root, -1, 0);
 	}
 }
@@ -60,9 +62,9 @@ void dm_dump_all(void)
  *
  * @dev:	Device to display
  */
-static void dm_display_line(struct udevice *dev)
+static void dm_display_line(struct udevice *dev, int index)
 {
-	printf("- %c %s @ %08lx",
+	printf("%i %c %s @ %08lx", index,
 	       dev->flags & DM_FLAG_ACTIVATED ? '*' : ' ',
 	       dev->name, (ulong)map_to_sysmem(dev));
 	if (dev->seq != -1 || dev->req_seq != -1)
@@ -78,6 +80,7 @@ void dm_dump_uclass(void)
 
 	for (id = 0; id < UCLASS_COUNT; id++) {
 		struct udevice *dev;
+		int i = 0;
 
 		ret = uclass_get(id, &uc);
 		if (ret)
@@ -87,7 +90,8 @@ void dm_dump_uclass(void)
 		if (list_empty(&uc->dev_head))
 			continue;
 		list_for_each_entry(dev, &uc->dev_head, uclass_node) {
-			dm_display_line(dev);
+			dm_display_line(dev, i);
+			i++;
 		}
 		puts("\n");
 	}

+ 21 - 0
drivers/core/uclass.c

@@ -171,6 +171,27 @@ enum uclass_id uclass_get_by_name(const char *name)
 	return UCLASS_INVALID;
 }
 
+int dev_get_uclass_index(struct udevice *dev, struct uclass **ucp)
+{
+	struct udevice *iter;
+	struct uclass *uc = dev->uclass;
+	int i = 0;
+
+	if (list_empty(&uc->dev_head))
+		return -ENODEV;
+
+	list_for_each_entry(iter, &uc->dev_head, uclass_node) {
+		if (iter == dev) {
+			if (ucp)
+				*ucp = uc;
+			return i;
+		}
+		i++;
+	}
+
+	return -ENODEV;
+}
+
 int uclass_find_device(enum uclass_id id, int index, struct udevice **devp)
 {
 	struct uclass *uc;

+ 2 - 0
drivers/usb/gadget/gadget_chips.h

@@ -206,5 +206,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x21;
 	else if (gadget_is_fotg210(gadget))
 		return 0x22;
+	else if (gadget_is_dwc3(gadget))
+		return 0x23;
 	return -ENOENT;
 }

+ 116 - 480
drivers/usb/musb-new/omap2430.c

@@ -8,201 +8,19 @@
  *
  * This file is part of the Inventra Controller Driver for Linux.
  */
-#ifndef __UBOOT__
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/pm_runtime.h>
-#include <linux/err.h>
-#include <linux/usb/musb-omap.h>
-#else
 #include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <linux/usb/otg.h>
 #include <asm/omap_common.h>
 #include <asm/omap_musb.h>
 #include <twl4030.h>
 #include <twl6030.h>
 #include "linux-compat.h"
-#endif
-
 #include "musb_core.h"
 #include "omap2430.h"
-
-#ifndef __UBOOT__
-struct omap2430_glue {
-	struct device		*dev;
-	struct platform_device	*musb;
-	enum omap_musb_vbus_id_status status;
-	struct work_struct	omap_musb_mailbox_work;
-};
-#define glue_to_musb(g)		platform_get_drvdata(g->musb)
-
-struct omap2430_glue		*_glue;
-
-static struct timer_list musb_idle_timer;
-
-static void musb_do_idle(unsigned long _musb)
-{
-	struct musb	*musb = (void *)_musb;
-	unsigned long	flags;
-	u8	power;
-	u8	devctl;
-
-	spin_lock_irqsave(&musb->lock, flags);
-
-	switch (musb->xceiv->state) {
-	case OTG_STATE_A_WAIT_BCON:
-
-		devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
-		if (devctl & MUSB_DEVCTL_BDEVICE) {
-			musb->xceiv->state = OTG_STATE_B_IDLE;
-			MUSB_DEV_MODE(musb);
-		} else {
-			musb->xceiv->state = OTG_STATE_A_IDLE;
-			MUSB_HST_MODE(musb);
-		}
-		break;
-	case OTG_STATE_A_SUSPEND:
-		/* finish RESUME signaling? */
-		if (musb->port1_status & MUSB_PORT_STAT_RESUME) {
-			power = musb_readb(musb->mregs, MUSB_POWER);
-			power &= ~MUSB_POWER_RESUME;
-			dev_dbg(musb->controller, "root port resume stopped, power %02x\n", power);
-			musb_writeb(musb->mregs, MUSB_POWER, power);
-			musb->is_active = 1;
-			musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
-						| MUSB_PORT_STAT_RESUME);
-			musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
-			usb_hcd_poll_rh_status(musb_to_hcd(musb));
-			/* NOTE: it might really be A_WAIT_BCON ... */
-			musb->xceiv->state = OTG_STATE_A_HOST;
-		}
-		break;
-	case OTG_STATE_A_HOST:
-		devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
-		if (devctl &  MUSB_DEVCTL_BDEVICE)
-			musb->xceiv->state = OTG_STATE_B_IDLE;
-		else
-			musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
-	default:
-		break;
-	}
-	spin_unlock_irqrestore(&musb->lock, flags);
-}
-
-
-static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout)
-{
-	unsigned long		default_timeout = jiffies + msecs_to_jiffies(3);
-	static unsigned long	last_timer;
-
-	if (timeout == 0)
-		timeout = default_timeout;
-
-	/* Never idle if active, or when VBUS timeout is not set as host */
-	if (musb->is_active || ((musb->a_wait_bcon == 0)
-			&& (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
-		dev_dbg(musb->controller, "%s active, deleting timer\n",
-			otg_state_string(musb->xceiv->state));
-		del_timer(&musb_idle_timer);
-		last_timer = jiffies;
-		return;
-	}
-
-	if (time_after(last_timer, timeout)) {
-		if (!timer_pending(&musb_idle_timer))
-			last_timer = timeout;
-		else {
-			dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n");
-			return;
-		}
-	}
-	last_timer = timeout;
-
-	dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n",
-		otg_state_string(musb->xceiv->state),
-		(unsigned long)jiffies_to_msecs(timeout - jiffies));
-	mod_timer(&musb_idle_timer, timeout);
-}
-
-static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
-{
-	struct usb_otg	*otg = musb->xceiv->otg;
-	u8		devctl;
-	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-	int ret = 1;
-	/* HDRC controls CPEN, but beware current surges during device
-	 * connect.  They can trigger transient overcurrent conditions
-	 * that must be ignored.
-	 */
-
-	devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
-
-	if (is_on) {
-		if (musb->xceiv->state == OTG_STATE_A_IDLE) {
-			/* start the session */
-			devctl |= MUSB_DEVCTL_SESSION;
-			musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
-			/*
-			 * Wait for the musb to set as A device to enable the
-			 * VBUS
-			 */
-			while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
-
-				cpu_relax();
-
-				if (time_after(jiffies, timeout)) {
-					dev_err(musb->controller,
-					"configured as A device timeout");
-					ret = -EINVAL;
-					break;
-				}
-			}
-
-			if (ret && otg->set_vbus)
-				otg_set_vbus(otg, 1);
-		} else {
-			musb->is_active = 1;
-			otg->default_a = 1;
-			musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
-			devctl |= MUSB_DEVCTL_SESSION;
-			MUSB_HST_MODE(musb);
-		}
-	} else {
-		musb->is_active = 0;
-
-		/* NOTE:  we're skipping A_WAIT_VFALL -> A_IDLE and
-		 * jumping right to B_IDLE...
-		 */
-
-		otg->default_a = 0;
-		musb->xceiv->state = OTG_STATE_B_IDLE;
-		devctl &= ~MUSB_DEVCTL_SESSION;
-
-		MUSB_DEV_MODE(musb);
-	}
-	musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
-
-	dev_dbg(musb->controller, "VBUS %s, devctl %02x "
-		/* otg %3x conf %08x prcm %08x */ "\n",
-		otg_state_string(musb->xceiv->state),
-		musb_readb(musb->mregs, MUSB_DEVCTL));
-}
-
-static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode)
-{
-	u8	devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
-
-	devctl |= MUSB_DEVCTL_SESSION;
-	musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
-
-	return 0;
-}
-#endif
+#include "musb_uboot.h"
 
 static inline void omap2430_low_level_exit(struct musb *musb)
 {
@@ -223,100 +41,15 @@ static inline void omap2430_low_level_init(struct musb *musb)
 	musb_writel(musb->mregs, OTG_FORCESTDBY, l);
 }
 
-#ifndef __UBOOT__
-void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
-{
-	struct omap2430_glue	*glue = _glue;
-	struct musb		*musb = glue_to_musb(glue);
-
-	glue->status = status;
-	if (!musb) {
-		dev_err(glue->dev, "musb core is not yet ready\n");
-		return;
-	}
-
-	schedule_work(&glue->omap_musb_mailbox_work);
-}
-EXPORT_SYMBOL_GPL(omap_musb_mailbox);
-
-static void omap_musb_set_mailbox(struct omap2430_glue *glue)
-{
-	struct musb *musb = glue_to_musb(glue);
-	struct device *dev = musb->controller;
-	struct musb_hdrc_platform_data *pdata = dev->platform_data;
-	struct omap_musb_board_data *data = pdata->board_data;
-	struct usb_otg *otg = musb->xceiv->otg;
-
-	switch (glue->status) {
-	case OMAP_MUSB_ID_GROUND:
-		dev_dbg(dev, "ID GND\n");
-
-		otg->default_a = true;
-		musb->xceiv->state = OTG_STATE_A_IDLE;
-		musb->xceiv->last_event = USB_EVENT_ID;
-		if (!is_otg_enabled(musb) || musb->gadget_driver) {
-			pm_runtime_get_sync(dev);
-			usb_phy_init(musb->xceiv);
-			omap2430_musb_set_vbus(musb, 1);
-		}
-		break;
-
-	case OMAP_MUSB_VBUS_VALID:
-		dev_dbg(dev, "VBUS Connect\n");
-
-		otg->default_a = false;
-		musb->xceiv->state = OTG_STATE_B_IDLE;
-		musb->xceiv->last_event = USB_EVENT_VBUS;
-		if (musb->gadget_driver)
-			pm_runtime_get_sync(dev);
-		usb_phy_init(musb->xceiv);
-		break;
-
-	case OMAP_MUSB_ID_FLOAT:
-	case OMAP_MUSB_VBUS_OFF:
-		dev_dbg(dev, "VBUS Disconnect\n");
-
-		musb->xceiv->last_event = USB_EVENT_NONE;
-		if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
-			if (musb->gadget_driver) {
-				pm_runtime_mark_last_busy(dev);
-				pm_runtime_put_autosuspend(dev);
-			}
-
-		if (data->interface_type == MUSB_INTERFACE_UTMI) {
-			if (musb->xceiv->otg->set_vbus)
-				otg_set_vbus(musb->xceiv->otg, 0);
-		}
-		usb_phy_shutdown(musb->xceiv);
-		break;
-	default:
-		dev_dbg(dev, "ID float\n");
-	}
-}
-
-
-static void omap_musb_mailbox_work(struct work_struct *mailbox_work)
-{
-	struct omap2430_glue *glue = container_of(mailbox_work,
-				struct omap2430_glue, omap_musb_mailbox_work);
-	omap_musb_set_mailbox(glue);
-}
-#endif
 
 static int omap2430_musb_init(struct musb *musb)
 {
 	u32 l;
 	int status = 0;
 	unsigned long int start;
-#ifndef __UBOOT__
-	struct device *dev = musb->controller;
-	struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
-	struct musb_hdrc_platform_data *plat = dev->platform_data;
-	struct omap_musb_board_data *data = plat->board_data;
-#else
+
 	struct omap_musb_board_data *data =
 		(struct omap_musb_board_data *)musb->controller;
-#endif
 
 	/* Reset the controller */
 	musb_writel(musb->mregs, OTG_SYSCONFIG, SOFTRST);
@@ -334,24 +67,6 @@ static int omap2430_musb_init(struct musb *musb)
 		}
 	}
 
-#ifndef __UBOOT__
-	/* We require some kind of external transceiver, hooked
-	 * up through ULPI.  TWL4030-family PMICs include one,
-	 * which needs a driver, drivers aren't always needed.
-	 */
-	musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
-	if (IS_ERR_OR_NULL(musb->xceiv)) {
-		pr_err("HS USB OTG: no transceiver configured\n");
-		return -ENODEV;
-	}
-
-	status = pm_runtime_get_sync(dev);
-	if (status < 0) {
-		dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status);
-		goto err1;
-	}
-#endif
-
 	l = musb_readl(musb->mregs, OTG_INTERFSEL);
 
 	if (data->interface_type == MUSB_INTERFACE_UTMI) {
@@ -371,64 +86,14 @@ static int omap2430_musb_init(struct musb *musb)
 			musb_readl(musb->mregs, OTG_SYSSTATUS),
 			musb_readl(musb->mregs, OTG_INTERFSEL),
 			musb_readl(musb->mregs, OTG_SIMENABLE));
-
-#ifndef __UBOOT__
-	setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
-
-	if (glue->status != OMAP_MUSB_UNKNOWN)
-		omap_musb_set_mailbox(glue);
-
-	pm_runtime_put_noidle(musb->controller);
-#endif
 	return 0;
 
 err1:
 	return status;
 }
 
-#ifndef __UBOOT__
-static void omap2430_musb_enable(struct musb *musb)
-#else
 static int omap2430_musb_enable(struct musb *musb)
-#endif
 {
-#ifndef __UBOOT__
-	u8		devctl;
-	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-	struct device *dev = musb->controller;
-	struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
-	struct musb_hdrc_platform_data *pdata = dev->platform_data;
-	struct omap_musb_board_data *data = pdata->board_data;
-
-	switch (glue->status) {
-
-	case OMAP_MUSB_ID_GROUND:
-		usb_phy_init(musb->xceiv);
-		if (data->interface_type != MUSB_INTERFACE_UTMI)
-			break;
-		devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
-		/* start the session */
-		devctl |= MUSB_DEVCTL_SESSION;
-		musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
-		while (musb_readb(musb->mregs, MUSB_DEVCTL) &
-				MUSB_DEVCTL_BDEVICE) {
-			cpu_relax();
-
-			if (time_after(jiffies, timeout)) {
-				dev_err(dev, "configured as A device timeout");
-				break;
-			}
-		}
-		break;
-
-	case OMAP_MUSB_VBUS_VALID:
-		usb_phy_init(musb->xceiv);
-		break;
-
-	default:
-		break;
-	}
-#else
 #ifdef CONFIG_TWL4030_USB
 	if (twl4030_usb_ulpi_init()) {
 		serial_printf("ERROR: %s Could not initialize PHY\n",
@@ -447,18 +112,11 @@ static int omap2430_musb_enable(struct musb *musb)
 #endif
 
 	return 0;
-#endif
 }
 
 static void omap2430_musb_disable(struct musb *musb)
 {
-#ifndef __UBOOT__
-	struct device *dev = musb->controller;
-	struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
 
-	if (glue->status != OMAP_MUSB_UNKNOWN)
-		usb_phy_shutdown(musb->xceiv);
-#endif
 }
 
 static int omap2430_musb_exit(struct musb *musb)
@@ -470,174 +128,152 @@ static int omap2430_musb_exit(struct musb *musb)
 	return 0;
 }
 
-#ifndef __UBOOT__
-static const struct musb_platform_ops omap2430_ops = {
-#else
 const struct musb_platform_ops omap2430_ops = {
-#endif
 	.init		= omap2430_musb_init,
 	.exit		= omap2430_musb_exit,
-
-#ifndef __UBOOT__
-	.set_mode	= omap2430_musb_set_mode,
-	.try_idle	= omap2430_musb_try_idle,
-
-	.set_vbus	= omap2430_musb_set_vbus,
-#endif
-
 	.enable		= omap2430_musb_enable,
 	.disable	= omap2430_musb_disable,
 };
 
-#ifndef __UBOOT__
-static u64 omap2430_dmamask = DMA_BIT_MASK(32);
+#if defined(CONFIG_DM_USB)
+
+struct omap2430_musb_platdata {
+	void *base;
+	void *ctrl_mod_base;
+	struct musb_hdrc_platform_data plat;
+	struct musb_hdrc_config musb_config;
+	struct omap_musb_board_data otg_board_data;
+};
 
-static int __devinit omap2430_probe(struct platform_device *pdev)
+static int omap2430_musb_ofdata_to_platdata(struct udevice *dev)
 {
-	struct musb_hdrc_platform_data	*pdata = pdev->dev.platform_data;
-	struct platform_device		*musb;
-	struct omap2430_glue		*glue;
-	int				ret = -ENOMEM;
-
-	glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
-	if (!glue) {
-		dev_err(&pdev->dev, "failed to allocate glue context\n");
-		goto err0;
+	struct omap2430_musb_platdata *platdata = dev_get_platdata(dev);
+	const void *fdt = gd->fdt_blob;
+	int node = dev_of_offset(dev);
+
+	platdata->base = (void *)dev_read_addr_ptr(dev);
+
+	platdata->musb_config.multipoint = fdtdec_get_int(fdt, node,
+							  "multipoint",
+							  -1);
+	if (platdata->musb_config.multipoint < 0) {
+		pr_err("MUSB multipoint DT entry missing\n");
+		return -ENOENT;
 	}
 
-	musb = platform_device_alloc("musb-hdrc", -1);
-	if (!musb) {
-		dev_err(&pdev->dev, "failed to allocate musb device\n");
-		goto err0;
+	platdata->musb_config.dyn_fifo = 1;
+	platdata->musb_config.num_eps = fdtdec_get_int(fdt, node,
+						       "num-eps", -1);
+	if (platdata->musb_config.num_eps < 0) {
+		pr_err("MUSB num-eps DT entry missing\n");
+		return -ENOENT;
 	}
 
-	musb->dev.parent		= &pdev->dev;
-	musb->dev.dma_mask		= &omap2430_dmamask;
-	musb->dev.coherent_dma_mask	= omap2430_dmamask;
-
-	glue->dev			= &pdev->dev;
-	glue->musb			= musb;
-	glue->status			= OMAP_MUSB_UNKNOWN;
-
-	pdata->platform_ops		= &omap2430_ops;
-
-	platform_set_drvdata(pdev, glue);
-
-	/*
-	 * REVISIT if we ever have two instances of the wrapper, we will be
-	 * in big trouble
-	 */
-	_glue	= glue;
-
-	INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work);
-
-	ret = platform_device_add_resources(musb, pdev->resource,
-			pdev->num_resources);
-	if (ret) {
-		dev_err(&pdev->dev, "failed to add resources\n");
-		goto err1;
+	platdata->musb_config.ram_bits = fdtdec_get_int(fdt, node,
+							"ram-bits", -1);
+	if (platdata->musb_config.ram_bits < 0) {
+		pr_err("MUSB ram-bits DT entry missing\n");
+		return -ENOENT;
 	}
 
-	ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
-	if (ret) {
-		dev_err(&pdev->dev, "failed to add platform_data\n");
-		goto err1;
+	platdata->plat.power = fdtdec_get_int(fdt, node,
+								"power", -1);
+	if (platdata->plat.power < 0) {
+		pr_err("MUSB power DT entry missing\n");
+		return -ENOENT;
 	}
 
-	pm_runtime_enable(&pdev->dev);
-
-	ret = platform_device_add(musb);
-	if (ret) {
-		dev_err(&pdev->dev, "failed to register musb device\n");
-		goto err1;
+	platdata->otg_board_data.interface_type = fdtdec_get_int(fdt, node,
+									"interface-type", -1);
+	if (platdata->otg_board_data.interface_type < 0) {
+		pr_err("MUSB interface-type DT entry missing\n");
+		return -ENOENT;
 	}
 
-	return 0;
-
-err1:
-	platform_device_put(musb);
-
-err0:
-	return ret;
-}
-
-static int __devexit omap2430_remove(struct platform_device *pdev)
-{
-	struct omap2430_glue		*glue = platform_get_drvdata(pdev);
-
-	cancel_work_sync(&glue->omap_musb_mailbox_work);
-	platform_device_del(glue->musb);
-	platform_device_put(glue->musb);
-
+#if 0 /* In a perfect world, mode would be set to OTG, mode 3 from DT */
+	platdata->plat.mode = fdtdec_get_int(fdt, node,
+										"mode", -1);
+	if (platdata->plat.mode < 0) {
+		pr_err("MUSB mode DT entry missing\n");
+		return -ENOENT;
+	}
+#else /* MUSB_OTG, it doesn't work */
+#ifdef CONFIG_USB_MUSB_HOST /* Host seems to be the only option that works */
+	platdata->plat.mode = MUSB_HOST;
+#else /* For that matter, MUSB_PERIPHERAL doesn't either */
+	platdata->plat.mode = MUSB_PERIPHERAL;
+#endif
+#endif
+	platdata->otg_board_data.dev = dev;
+	platdata->plat.config = &platdata->musb_config;
+	platdata->plat.platform_ops = &omap2430_ops;
+	platdata->plat.board_data = &platdata->otg_board_data;
 	return 0;
 }
 
-#ifdef CONFIG_PM
-
-static int omap2430_runtime_suspend(struct device *dev)
+static int omap2430_musb_probe(struct udevice *dev)
 {
-	struct omap2430_glue		*glue = dev_get_drvdata(dev);
-	struct musb			*musb = glue_to_musb(glue);
-
-	if (musb) {
-		musb->context.otg_interfsel = musb_readl(musb->mregs,
-				OTG_INTERFSEL);
-
-		omap2430_low_level_exit(musb);
-		usb_phy_set_suspend(musb->xceiv, 1);
+#ifdef CONFIG_USB_MUSB_HOST
+	struct musb_host_data *host = dev_get_priv(dev);
+#endif
+	struct omap2430_musb_platdata *platdata = dev_get_platdata(dev);
+	struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
+	struct omap_musb_board_data *otg_board_data;
+	int ret;
+	void *base = dev_read_addr_ptr(dev);
+
+	priv->desc_before_addr = true;
+
+	otg_board_data = &platdata->otg_board_data;
+
+#ifdef CONFIG_USB_MUSB_HOST
+	host->host = musb_init_controller(&platdata->plat,
+					  (struct device *)otg_board_data,
+					  platdata->base);
+	if (!host->host) {
+		return -EIO;
 	}
 
-	return 0;
+	ret = musb_lowlevel_init(host);
+#else
+	ret = musb_register(&platdata->plat,
+			  (struct device *)otg_board_data,
+			  platdata->base);
+#endif
+	return ret;
 }
 
-static int omap2430_runtime_resume(struct device *dev)
+static int omap2430_musb_remove(struct udevice *dev)
 {
-	struct omap2430_glue		*glue = dev_get_drvdata(dev);
-	struct musb			*musb = glue_to_musb(glue);
+	struct musb_host_data *host = dev_get_priv(dev);
 
-	if (musb) {
-		omap2430_low_level_init(musb);
-		musb_writel(musb->mregs, OTG_INTERFSEL,
-				musb->context.otg_interfsel);
-
-		usb_phy_set_suspend(musb->xceiv, 0);
-	}
+	musb_stop(host->host);
 
 	return 0;
 }
 
-static struct dev_pm_ops omap2430_pm_ops = {
-	.runtime_suspend = omap2430_runtime_suspend,
-	.runtime_resume = omap2430_runtime_resume,
+static const struct udevice_id omap2430_musb_ids[] = {
+	{ .compatible = "ti,omap3-musb" },
+	{ .compatible = "ti,omap4-musb" },
+	{ }
 };
 
-#define DEV_PM_OPS	(&omap2430_pm_ops)
+U_BOOT_DRIVER(omap2430_musb) = {
+	.name	= "omap2430-musb",
+#ifdef CONFIG_USB_MUSB_HOST
+	.id		= UCLASS_USB,
 #else
-#define DEV_PM_OPS	NULL
+	.id		= UCLASS_USB_DEV_GENERIC,
 #endif
-
-static struct platform_driver omap2430_driver = {
-	.probe		= omap2430_probe,
-	.remove		= __devexit_p(omap2430_remove),
-	.driver		= {
-		.name	= "musb-omap2430",
-		.pm	= DEV_PM_OPS,
-	},
+	.of_match = omap2430_musb_ids,
+	.ofdata_to_platdata = omap2430_musb_ofdata_to_platdata,
+	.probe = omap2430_musb_probe,
+	.remove = omap2430_musb_remove,
+#ifdef CONFIG_USB_MUSB_HOST
+	.ops = &musb_usb_ops,
+#endif
+	.platdata_auto_alloc_size = sizeof(struct omap2430_musb_platdata),
+	.priv_auto_alloc_size = sizeof(struct musb_host_data),
 };
 
-MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer");
-MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
-MODULE_LICENSE("GPL v2");
-
-static int __init omap2430_init(void)
-{
-	return platform_driver_register(&omap2430_driver);
-}
-subsys_initcall(omap2430_init);
-
-static void __exit omap2430_exit(void)
-{
-	platform_driver_unregister(&omap2430_driver);
-}
-module_exit(omap2430_exit);
-#endif
+#endif /* CONFIG_DM_USB */

+ 38 - 0
include/dm/device-internal.h

@@ -130,6 +130,44 @@ void device_free(struct udevice *dev);
 static inline void device_free(struct udevice *dev) {}
 #endif
 
+/**
+ * device_chld_unbind() - Unbind all device's children from the device if bound
+ *			  to drv
+ *
+ * On error, the function continues to unbind all children, and reports the
+ * first error.
+ *
+ * @dev:	The device that is to be stripped of its children
+ * @drv:	The targeted driver
+ * @return 0 on success, -ve on error
+ */
+#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)
+int device_chld_unbind(struct udevice *dev, struct driver *drv);
+#else
+static inline int device_chld_unbind(struct udevice *dev, struct driver *drv)
+{
+	return 0;
+}
+#endif
+
+/**
+ * device_chld_remove() - Stop all device's children
+ * @dev:	The device whose children are to be removed
+ * @drv:	The targeted driver
+ * @flags:	Flag, if this functions is called in the pre-OS stage
+ * @return 0 on success, -ve on error
+ */
+#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)
+int device_chld_remove(struct udevice *dev, struct driver *drv,
+		       uint flags);
+#else
+static inline int device_chld_remove(struct udevice *dev, struct driver *drv,
+				     uint flags)
+{
+	return 0;
+}
+#endif
+
 /**
  * simple_bus_translate() - translate a bus address to a system address
  *

+ 19 - 4
include/dm/device.h

@@ -473,18 +473,33 @@ int device_get_child_by_of_offset(struct udevice *parent, int of_offset,
 				  struct udevice **devp);
 
 /**
- * device_get_global_by_of_offset() - Get a device based on FDT offset
+ * device_find_global_by_ofnode() - Get a device based on ofnode
  *
- * Locates a device by its device tree offset, searching globally throughout
+ * Locates a device by its device tree ofnode, searching globally throughout
+ * the all driver model devices.
+ *
+ * The device is NOT probed
+ *
+ * @node: Device tree ofnode to find
+ * @devp: Returns pointer to device if found, otherwise this is set to NULL
+ * @return 0 if OK, -ve on error
+ */
+
+int device_find_global_by_ofnode(ofnode node, struct udevice **devp);
+
+/**
+ * device_get_global_by_ofnode() - Get a device based on ofnode
+ *
+ * Locates a device by its device tree ofnode, searching globally throughout
  * the all driver model devices.
  *
  * The device is probed to activate it ready for use.
  *
- * @of_offset: Device tree offset to find
+ * @node: Device tree ofnode to find
  * @devp: Returns pointer to device if found, otherwise this is set to NULL
  * @return 0 if OK, -ve on error
  */
-int device_get_global_by_of_offset(int of_offset, struct udevice **devp);
+int device_get_global_by_ofnode(ofnode node, struct udevice **devp);
 
 /**
  * device_find_first_child() - Find the first child of a device

+ 11 - 0
include/dm/uclass-internal.h

@@ -23,6 +23,17 @@
  */
 int uclass_get_device_tail(struct udevice *dev, int ret, struct udevice **devp);
 
+/**
+ * dev_get_uclass_index() - Get uclass and index of device
+ * @dev:	- in - Device that we want the uclass/index of
+ * @ucp:	- out - A pointer to the uclass the device belongs to
+ *
+ * The device is not prepared for use - this is an internal function.
+ *
+ * @return the index of the device in the uclass list or -ENODEV if not found.
+ */
+int dev_get_uclass_index(struct udevice *dev, struct uclass **ucp);
+
 /**
  * uclass_find_device() - Return n-th child of uclass
  * @id:		Id number of the uclass

+ 2 - 1
net/eth-uclass.c

@@ -312,7 +312,8 @@ void eth_halt(void)
 
 	eth_get_ops(current)->stop(current);
 	priv = current->uclass_priv;
-	priv->state = ETH_STATE_PASSIVE;
+	if (priv)
+		priv->state = ETH_STATE_PASSIVE;
 }
 
 int eth_is_active(struct udevice *dev)

+ 178 - 0
test/py/tests/test_bind.py

@@ -0,0 +1,178 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+import os.path
+import pytest
+import re
+
+def in_tree(response, name, uclass, drv, depth, last_child):
+	lines = [x.strip() for x in response.splitlines()]
+	leaf = ' ' * 4 * depth;
+	if not last_child:
+		leaf = leaf + '\|'
+	else:
+		leaf = leaf + '`'
+	leaf = leaf + '-- ' + name
+	line = ' *{:10.10}  [0-9]*  \[ [ +] \]   {:10.10}  {}$'.format(uclass, drv,leaf)
+	prog = re.compile(line)
+	for l in lines:
+		if prog.match(l):
+			return True
+	return False
+
+
+@pytest.mark.buildconfigspec('cmd_bind')
+def test_bind_unbind_with_node(u_boot_console):
+
+	#bind /bind-test. Device should come up as well as its children
+	response = u_boot_console.run_command("bind  /bind-test generic_simple_bus")
+	assert response == ''
+	tree = u_boot_console.run_command("dm tree")
+	assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True)
+	assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, False)
+	assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True)
+
+	#Unbind child #1. No error expected and all devices should be there except for bind-test-child1
+	response = u_boot_console.run_command("unbind  /bind-test/bind-test-child1")
+	assert response == ''
+	tree = u_boot_console.run_command("dm tree")
+	assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True)
+	assert "bind-test-child1" not in tree
+	assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True)
+
+	#bind child #1. No error expected and all devices should be there
+	response = u_boot_console.run_command("bind  /bind-test/bind-test-child1 phy_sandbox")
+	assert response == ''
+	tree = u_boot_console.run_command("dm tree")
+	assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True)
+	assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, True)
+	assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, False)
+
+	#Unbind child #2. No error expected and all devices should be there except for bind-test-child2
+	response = u_boot_console.run_command("unbind  /bind-test/bind-test-child2")
+	assert response == ''
+	tree = u_boot_console.run_command("dm tree")
+	assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True)
+	assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, True)
+	assert "bind-test-child2" not in tree
+
+
+	#Bind child #2. No error expected and all devices should be there
+	response = u_boot_console.run_command("bind /bind-test/bind-test-child2 generic_simple_bus")
+	assert response == ''
+	tree = u_boot_console.run_command("dm tree")
+	assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True)
+	assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, False)
+	assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True)
+
+	#Unbind parent. No error expected. All devices should be removed and unbound
+	response = u_boot_console.run_command("unbind  /bind-test")
+	assert response == ''
+	tree = u_boot_console.run_command("dm tree")
+	assert "bind-test" not in tree
+	assert "bind-test-child1" not in tree
+	assert "bind-test-child2" not in tree
+
+	#try binding invalid node with valid driver
+	response = u_boot_console.run_command("bind  /not-a-valid-node generic_simple_bus")
+	assert response != ''
+	tree = u_boot_console.run_command("dm tree")
+	assert "not-a-valid-node" not in tree
+
+	#try binding valid node with invalid driver
+	response = u_boot_console.run_command("bind  /bind-test not_a_driver")
+	assert response != ''
+	tree = u_boot_console.run_command("dm tree")
+	assert "bind-test" not in tree
+
+	#bind /bind-test. Device should come up as well as its children
+	response = u_boot_console.run_command("bind  /bind-test generic_simple_bus")
+	assert response == ''
+	tree = u_boot_console.run_command("dm tree")
+	assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True)
+	assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, False)
+	assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True)
+
+	response = u_boot_console.run_command("unbind  /bind-test")
+	assert response == ''
+
+def get_next_line(tree, name):
+	treelines = [x.strip() for x in tree.splitlines() if x.strip()]
+	child_line = ""
+	for idx, line in enumerate(treelines):
+		if ("-- " + name) in line:
+			try:
+				child_line = treelines[idx+1]
+			except:
+				pass
+			break
+	return child_line
+
+@pytest.mark.buildconfigspec('cmd_bind')
+def test_bind_unbind_with_uclass(u_boot_console):
+	#bind /bind-test
+	response = u_boot_console.run_command("bind  /bind-test generic_simple_bus")
+	assert response == ''
+
+	#make sure bind-test-child2 is there and get its uclass/index pair
+	tree = u_boot_console.run_command("dm tree")
+	child2_line = [x.strip() for x in tree.splitlines() if "-- bind-test-child2" in x]
+	assert len(child2_line) == 1
+
+	child2_uclass = child2_line[0].split()[0]
+	child2_index = int(child2_line[0].split()[1])
+
+	#bind generic_simple_bus as a child of bind-test-child2
+	response = u_boot_console.run_command("bind  {} {} generic_simple_bus".format(child2_uclass, child2_index, "generic_simple_bus"))
+
+	#check that the child is there and its uclass/index pair is right
+	tree = u_boot_console.run_command("dm tree")
+
+	child_of_child2_line = get_next_line(tree, "bind-test-child2")
+	assert child_of_child2_line
+	child_of_child2_index = int(child_of_child2_line.split()[1])
+	assert in_tree(tree, "generic_simple_bus", "simple_bus", "generic_simple_bus", 2, True)
+	assert child_of_child2_index == child2_index + 1
+
+	#unbind the child and check it has been removed
+	response = u_boot_console.run_command("unbind  simple_bus {}".format(child_of_child2_index))
+	assert response == ''
+	tree = u_boot_console.run_command("dm tree")
+	assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True)
+	assert not in_tree(tree, "generic_simple_bus", "simple_bus", "generic_simple_bus", 2, True)
+	child_of_child2_line = get_next_line(tree, "bind-test-child2")
+	assert child_of_child2_line == ""
+
+	#bind generic_simple_bus as a child of bind-test-child2
+	response = u_boot_console.run_command("bind  {} {} generic_simple_bus".format(child2_uclass, child2_index, "generic_simple_bus"))
+
+	#check that the child is there and its uclass/index pair is right
+	tree = u_boot_console.run_command("dm tree")
+	treelines = [x.strip() for x in tree.splitlines() if x.strip()]
+
+	child_of_child2_line = get_next_line(tree, "bind-test-child2")
+	assert child_of_child2_line
+	child_of_child2_index = int(child_of_child2_line.split()[1])
+	assert in_tree(tree, "generic_simple_bus", "simple_bus", "generic_simple_bus", 2, True)
+	assert child_of_child2_index == child2_index + 1
+
+	#unbind the child and check it has been removed
+	response = u_boot_console.run_command("unbind  {} {} generic_simple_bus".format(child2_uclass, child2_index, "generic_simple_bus"))
+	assert response == ''
+
+	tree = u_boot_console.run_command("dm tree")
+	assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True)
+
+	child_of_child2_line = get_next_line(tree, "bind-test-child2")
+	assert child_of_child2_line == ""
+
+	#unbind the child again and check it doesn't change the tree
+	tree_old = u_boot_console.run_command("dm tree")
+	response = u_boot_console.run_command("unbind  {} {} generic_simple_bus".format(child2_uclass, child2_index, "generic_simple_bus"))
+	tree_new = u_boot_console.run_command("dm tree")
+
+	assert response == ''
+	assert tree_old == tree_new
+
+	response = u_boot_console.run_command("unbind  /bind-test")
+	assert response == ''