diff --git a/common/cmd_usb.c b/common/cmd_usb.c
index 6198d063d1d6004a2c95dc498595ded96da95bbf..0ade7759f08e68b6cdffb5f9466016df09cc84d6 100644
--- a/common/cmd_usb.c
+++ b/common/cmd_usb.c
@@ -22,7 +22,7 @@
 #ifdef CONFIG_USB_STORAGE
 static int usb_stor_curr_dev = -1; /* current device */
 #endif
-#ifdef CONFIG_USB_HOST_ETHER
+#if defined(CONFIG_USB_HOST_ETHER) && !defined(CONFIG_DM_ETH)
 static int __maybe_unused usb_ether_curr_dev = -1; /* current ethernet device */
 #endif
 
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c
index c72b7e47c488bf31cd837a8b6964f5375d6f59f8..07d230988329fce83c9e5b4852c9314dce7f9e53 100644
--- a/drivers/usb/eth/usb_ether.c
+++ b/drivers/usb/eth/usb_ether.c
@@ -6,11 +6,137 @@
 
 #include <common.h>
 #include <dm.h>
+#include <malloc.h>
 #include <usb.h>
 #include <dm/device-internal.h>
 
 #include "usb_ether.h"
 
+#ifdef CONFIG_DM_ETH
+
+#define USB_BULK_RECV_TIMEOUT 500
+
+int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize)
+{
+	struct usb_device *udev = dev_get_parentdata(dev);
+	struct usb_interface_descriptor *iface_desc;
+	bool ep_in_found = false, ep_out_found = false;
+	struct usb_interface *iface;
+	const int ifnum = 0; /* Always use interface 0 */
+	int ret, i;
+
+	iface = &udev->config.if_desc[ifnum];
+	iface_desc = &udev->config.if_desc[ifnum].desc;
+
+	/* Initialize the ueth_data structure with some useful info */
+	ueth->ifnum = ifnum;
+	ueth->subclass = iface_desc->bInterfaceSubClass;
+	ueth->protocol = iface_desc->bInterfaceProtocol;
+
+	/*
+	 * We are expecting a minimum of 3 endpoints - in, out (bulk), and int.
+	 * We will ignore any others.
+	 */
+	for (i = 0; i < iface_desc->bNumEndpoints; i++) {
+		int ep_addr = iface->ep_desc[i].bEndpointAddress;
+
+		/* is it an BULK endpoint? */
+		if ((iface->ep_desc[i].bmAttributes &
+		     USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+			if (ep_addr & USB_DIR_IN && !ep_in_found) {
+				ueth->ep_in = ep_addr &
+					USB_ENDPOINT_NUMBER_MASK;
+				ep_in_found = true;
+			} else if (!(ep_addr & USB_DIR_IN) && !ep_out_found) {
+				ueth->ep_out = ep_addr &
+					USB_ENDPOINT_NUMBER_MASK;
+				ep_out_found = true;
+			}
+		}
+
+		/* is it an interrupt endpoint? */
+		if ((iface->ep_desc[i].bmAttributes &
+		    USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+			ueth->ep_int = iface->ep_desc[i].bEndpointAddress &
+				USB_ENDPOINT_NUMBER_MASK;
+			ueth->irqinterval = iface->ep_desc[i].bInterval;
+		}
+	}
+	debug("Endpoints In %d Out %d Int %d\n", ueth->ep_in, ueth->ep_out,
+	      ueth->ep_int);
+
+	/* Do some basic sanity checks, and bail if we find a problem */
+	if (!ueth->ep_in || !ueth->ep_out || !ueth->ep_int) {
+		debug("%s: %s: Cannot find endpoints\n", __func__, dev->name);
+		return -ENXIO;
+	}
+
+	ueth->rxsize = rxsize;
+	ueth->rxbuf = memalign(rxsize, ARCH_DMA_MINALIGN);
+	if (!ueth->rxbuf)
+		return -ENOMEM;
+
+	ret = usb_set_interface(udev, iface_desc->bInterfaceNumber, ifnum);
+	if (ret) {
+		debug("%s: %s: Cannot set interface: %d\n", __func__, dev->name,
+		      ret);
+		return ret;
+	}
+	ueth->pusb_dev = udev;
+
+	return 0;
+}
+
+int usb_ether_deregister(struct ueth_data *ueth)
+{
+	return 0;
+}
+
+int usb_ether_receive(struct ueth_data *ueth, int rxsize)
+{
+	int actual_len;
+	int ret;
+
+	if (rxsize > ueth->rxsize)
+		return -EINVAL;
+	ret = usb_bulk_msg(ueth->pusb_dev,
+			   usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in),
+			   ueth->rxbuf, rxsize, &actual_len,
+			   USB_BULK_RECV_TIMEOUT);
+	debug("Rx: len = %u, actual = %u, err = %d\n", rxsize, actual_len, ret);
+	if (ret) {
+		printf("Rx: failed to receive: %d\n", ret);
+		return ret;
+	}
+	if (actual_len > rxsize) {
+		debug("Rx: received too many bytes %d\n", actual_len);
+		return -ENOSPC;
+	}
+	ueth->rxlen = actual_len;
+	ueth->rxptr = 0;
+
+	return actual_len ? 0 : -EAGAIN;
+}
+
+void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes)
+{
+	ueth->rxptr += num_bytes;
+	if (num_bytes < 0 || ueth->rxptr >= ueth->rxlen)
+		ueth->rxlen = 0;
+}
+
+int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp)
+{
+	if (!ueth->rxlen)
+		return 0;
+
+	*ptrp = &ueth->rxbuf[ueth->rxptr];
+
+	return ueth->rxlen - ueth->rxptr;
+}
+
+#else
+
 typedef void (*usb_eth_before_probe)(void);
 typedef int (*usb_eth_probe)(struct usb_device *dev, unsigned int ifnum,
 			struct ueth_data *ss);
@@ -197,3 +323,4 @@ int usb_host_eth_scan(int mode)
 		return 0;
 	return -1;
 }
+#endif
diff --git a/include/usb_ether.h b/include/usb_ether.h
index 23507e19e6024918e7617a4a4a98f87e69c774f9..c6d1416048a113c37d529d503bb255f47dffa197 100644
--- a/include/usb_ether.h
+++ b/include/usb_ether.h
@@ -19,25 +19,91 @@
 #define ETH_DATA_LEN	1500		/* Max. octets in payload	 */
 #define ETH_FRAME_LEN	PKTSIZE_ALIGN	/* Max. octets in frame sans FCS */
 
+/* TODO(sjg@chromium.org): Remove @pusb_dev when all boards use CONFIG_DM_ETH */
 struct ueth_data {
 	/* eth info */
-	struct eth_device eth_dev;		/* used with eth_register */
-	int phy_id;						/* mii phy id */
+#ifdef CONFIG_DM_ETH
+	uint8_t *rxbuf;
+	int rxsize;
+	int rxlen;			/* Total bytes available in rxbuf */
+	int rxptr;			/* Current position in rxbuf */
+#else
+	struct eth_device eth_dev;	/* used with eth_register */
+	/* driver private */
+	void *dev_priv;
+#endif
+	int phy_id;			/* mii phy id */
 
 	/* usb info */
 	struct usb_device *pusb_dev;	/* this usb_device */
-	unsigned char	ifnum;			/* interface number */
-	unsigned char	ep_in;			/* in endpoint */
-	unsigned char	ep_out;			/* out ....... */
-	unsigned char	ep_int;			/* interrupt . */
-	unsigned char	subclass;		/* as in overview */
-	unsigned char	protocol;		/* .............. */
+	unsigned char	ifnum;		/* interface number */
+	unsigned char	ep_in;		/* in endpoint */
+	unsigned char	ep_out;		/* out ....... */
+	unsigned char	ep_int;		/* interrupt . */
+	unsigned char	subclass;	/* as in overview */
+	unsigned char	protocol;	/* .............. */
 	unsigned char	irqinterval;	/* Intervall for IRQ Pipe */
-
-	/* driver private */
-	void *dev_priv;
 };
 
+#ifdef CONFIG_DM_ETH
+/**
+ * usb_ether_register() - register a new USB ethernet device
+ *
+ * This selects the correct USB interface and figures out the endpoints to use.
+ *
+ * @dev:	USB device
+ * @ss:		Place to put USB ethernet data
+ * @rxsize:	Maximum size to allocate for the receive buffer
+ * @return 0 if OK, -ve on error
+ */
+int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize);
+
+/**
+ * usb_ether_deregister() - deregister a USB ethernet device
+ *
+ * @ueth:	USB Ethernet device
+ * @return 0
+ */
+int usb_ether_deregister(struct ueth_data *ueth);
+
+/**
+ * usb_ether_receive() - recieve a packet from the bulk in endpoint
+ *
+ * The packet is stored in the internal buffer ready for processing.
+ *
+ * @ueth:	USB Ethernet device
+ * @rxsize:	Maximum size to receive
+ * @return 0 if a packet was received, -EAGAIN if not, -ENOSPC if @rxsize is
+ * larger than the size passed ot usb_ether_register(), other -ve on error
+ */
+int usb_ether_receive(struct ueth_data *ueth, int rxsize);
+
+/**
+ * usb_ether_get_rx_bytes() - obtain bytes from the internal packet buffer
+ *
+ * This should be called repeatedly to obtain packet data until it returns 0.
+ * After each packet is processed, call usb_ether_advance_rxbuf() to move to
+ * the next one.
+ *
+ * @ueth:	USB Ethernet device
+ * @ptrp:	Returns a pointer to the start of the next packet if there is
+ *		one available
+ * @return number of bytes available, or 0 if none
+ */
+int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp);
+
+/**
+ * usb_ether_advance_rxbuf() - Advance to the next packet in the internal buffer
+ *
+ * After processing the data returned by usb_ether_get_rx_bytes(), call this
+ * function to move to the next packet. You must specify the number of bytes
+ * you have processed in @num_bytes.
+ *
+ * @ueth:	USB Ethernet device
+ * @num_bytes:	Number of bytes to skip, or -1 to skip all bytes
+ */
+void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes);
+#else
 /*
  * Function definitions for each USB ethernet driver go here
  * (declaration is unconditional, compilation is conditional)
@@ -65,5 +131,6 @@ int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
 			struct ueth_data *ss);
 int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
 			struct eth_device *eth);
+#endif
 
 #endif /* __USB_ETHER_H__ */