summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Kconfig16
-rw-r--r--drivers/usb/emul/Kconfig8
-rw-r--r--drivers/usb/emul/Makefile10
-rw-r--r--drivers/usb/emul/sandbox_flash.c423
-rw-r--r--drivers/usb/emul/sandbox_hub.c303
-rw-r--r--drivers/usb/emul/usb-emul-uclass.c263
-rw-r--r--drivers/usb/eth/asix.c3
-rw-r--r--drivers/usb/eth/asix88179.c2
-rw-r--r--drivers/usb/eth/mcs7830.c2
-rw-r--r--drivers/usb/eth/smsc95xx.c5
-rw-r--r--drivers/usb/eth/usb_ether.c52
-rw-r--r--drivers/usb/gadget/ci_udc.c4
-rw-r--r--drivers/usb/gadget/ether.c13
-rw-r--r--drivers/usb/host/Makefile5
-rw-r--r--drivers/usb/host/ehci-exynos.c112
-rw-r--r--drivers/usb/host/ehci-faraday.c112
-rw-r--r--drivers/usb/host/ehci-hcd.c385
-rw-r--r--drivers/usb/host/ehci-mx5.c12
-rw-r--r--drivers/usb/host/ehci-tegra.c322
-rw-r--r--drivers/usb/host/ehci.h47
-rw-r--r--drivers/usb/host/usb-sandbox.c117
-rw-r--r--drivers/usb/host/usb-uclass.c645
-rw-r--r--drivers/usb/host/xhci-exynos5.c120
-rw-r--r--drivers/usb/host/xhci-mem.c24
-rw-r--r--drivers/usb/host/xhci-ring.c8
-rw-r--r--drivers/usb/host/xhci.c312
-rw-r--r--drivers/usb/host/xhci.h31
-rw-r--r--drivers/usb/musb-new/musb_uboot.c4
28 files changed, 3009 insertions, 351 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index b4a9442..637ef3d 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -35,8 +35,24 @@ config USB
if USB
+config DM_USB
+ bool "Enable driver model for USB"
+ depends on USB && DM
+ help
+ Enable driver model for USB. The USB interface is then implemented
+ by the USB uclass. Multiple USB controllers of different types
+ (XHCI, EHCI) can be attached and used. The 'usb' command works as
+ normal. OCHI is not supported at present.
+
+ Much of the code is shared but with this option enabled the USB
+ uclass takes care of device enumeration. USB devices can be
+ declared with the USB_DEVICE() macro and will be automatically
+ probed when found on the bus.
+
source "drivers/usb/host/Kconfig"
+source "drivers/usb/emul/Kconfig"
+
config USB_STORAGE
bool "USB Mass Storage support"
---help---
diff --git a/drivers/usb/emul/Kconfig b/drivers/usb/emul/Kconfig
new file mode 100644
index 0000000..ae1ab23
--- /dev/null
+++ b/drivers/usb/emul/Kconfig
@@ -0,0 +1,8 @@
+config USB_EMUL
+ bool "Support for USB device emulation"
+ depends on DM_USB && SANDBOX
+ help
+ Since sandbox does not have access to a real USB bus, it is possible
+ to use device emulators instead. This allows testing of the USB
+ stack on sandbox without needing a real device, or any host machine
+ USB resources.
diff --git a/drivers/usb/emul/Makefile b/drivers/usb/emul/Makefile
new file mode 100644
index 0000000..8fd83d5
--- /dev/null
+++ b/drivers/usb/emul/Makefile
@@ -0,0 +1,10 @@
+#
+# (C) Copyright 2015 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_USB_EMUL) += sandbox_flash.o
+obj-$(CONFIG_USB_EMUL) += sandbox_hub.o
+obj-$(CONFIG_USB_EMUL) += usb-emul-uclass.o
diff --git a/drivers/usb/emul/sandbox_flash.c b/drivers/usb/emul/sandbox_flash.c
new file mode 100644
index 0000000..6e0808d
--- /dev/null
+++ b/drivers/usb/emul/sandbox_flash.c
@@ -0,0 +1,423 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <os.h>
+#include <scsi.h>
+#include <usb.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * This driver emulates a flash stick using the UFI command specification and
+ * the BBB (bulk/bulk/bulk) protocol. It supports only a single logical unit
+ * number (LUN 0).
+ */
+
+enum {
+ SANDBOX_FLASH_EP_OUT = 1, /* endpoints */
+ SANDBOX_FLASH_EP_IN = 2,
+ SANDBOX_FLASH_BLOCK_LEN = 512,
+};
+
+enum cmd_phase {
+ PHASE_START,
+ PHASE_DATA,
+ PHASE_STATUS,
+};
+
+/**
+ * struct sandbox_flash_priv - private state for this driver
+ *
+ * @error: true if there is an error condition
+ * @alloc_len: Allocation length from the last incoming command
+ * @transfer_len: Transfer length from CBW header
+ * @read_len: Number of blocks of data left in the current read command
+ * @tag: Tag value from last command
+ * @fd: File descriptor of backing file
+ * @file_size: Size of file in bytes
+ * @status_buff: Data buffer for outgoing status
+ * @buff_used: Number of bytes ready to transfer back to host
+ * @buff: Data buffer for outgoing data
+ */
+struct sandbox_flash_priv {
+ bool error;
+ int alloc_len;
+ int transfer_len;
+ int read_len;
+ enum cmd_phase phase;
+ u32 tag;
+ int fd;
+ loff_t file_size;
+ struct umass_bbb_csw status;
+ int buff_used;
+ u8 buff[512];
+};
+
+struct sandbox_flash_plat {
+ const char *pathname;
+};
+
+struct scsi_inquiry_resp {
+ u8 type;
+ u8 flags;
+ u8 version;
+ u8 data_format;
+ u8 additional_len;
+ u8 spare[3];
+ char vendor[8];
+ char product[16];
+ char revision[4];
+};
+
+struct scsi_read_capacity_resp {
+ u32 last_block_addr;
+ u32 block_len;
+};
+
+struct __packed scsi_read10_req {
+ u8 cmd;
+ u8 lun_flags;
+ u32 lba;
+ u8 spare;
+ u16 transfer_len;
+ u8 spare2[3];
+};
+
+enum {
+ STRINGID_MANUFACTURER = 1,
+ STRINGID_PRODUCT,
+ STRINGID_SERIAL,
+
+ STRINGID_COUNT,
+};
+
+static struct usb_string flash_strings[] = {
+ {STRINGID_MANUFACTURER, "sandbox"},
+ {STRINGID_PRODUCT, "flash"},
+ {STRINGID_SERIAL, "2345"},
+ {},
+};
+
+static struct usb_device_descriptor flash_device_desc = {
+ .bLength = sizeof(flash_device_desc),
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+
+ .idVendor = __constant_cpu_to_le16(0x1234),
+ .idProduct = __constant_cpu_to_le16(0x5678),
+ .iManufacturer = STRINGID_MANUFACTURER,
+ .iProduct = STRINGID_PRODUCT,
+ .iSerialNumber = STRINGID_SERIAL,
+ .bNumConfigurations = 1,
+};
+
+static struct usb_config_descriptor flash_config0 = {
+ .bLength = sizeof(flash_config0),
+ .bDescriptorType = USB_DT_CONFIG,
+
+ /* wTotalLength is set up by usb-emul-uclass */
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 0,
+ .iConfiguration = 0,
+ .bmAttributes = 1 << 7,
+ .bMaxPower = 50,
+};
+
+static struct usb_interface_descriptor flash_interface0 = {
+ .bLength = sizeof(flash_interface0),
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = US_SC_UFI,
+ .bInterfaceProtocol = US_PR_BULK,
+ .iInterface = 0,
+};
+
+static struct usb_endpoint_descriptor flash_endpoint0_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = SANDBOX_FLASH_EP_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(1024),
+ .bInterval = 0,
+};
+
+static struct usb_endpoint_descriptor flash_endpoint1_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = SANDBOX_FLASH_EP_IN | USB_ENDPOINT_DIR_MASK,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(1024),
+ .bInterval = 0,
+};
+
+static void *flash_desc_list[] = {
+ &flash_device_desc,
+ &flash_config0,
+ &flash_interface0,
+ &flash_endpoint0_out,
+ &flash_endpoint1_in,
+ NULL,
+};
+
+static int sandbox_flash_control(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buff, int len,
+ struct devrequest *setup)
+{
+ struct sandbox_flash_priv *priv = dev_get_priv(dev);
+
+ if (pipe == usb_rcvctrlpipe(udev, 0)) {
+ switch (setup->request) {
+ case US_BBB_RESET:
+ priv->error = false;
+ return 0;
+ case US_BBB_GET_MAX_LUN:
+ *(char *)buff = '\0';
+ return 1;
+ default:
+ debug("request=%x\n", setup->request);
+ break;
+ }
+ }
+ debug("pipe=%lx\n", pipe);
+
+ return -EIO;
+}
+
+static void setup_fail_response(struct sandbox_flash_priv *priv)
+{
+ struct umass_bbb_csw *csw = &priv->status;
+
+ csw->dCSWSignature = CSWSIGNATURE;
+ csw->dCSWTag = priv->tag;
+ csw->dCSWDataResidue = 0;
+ csw->bCSWStatus = CSWSTATUS_FAILED;
+ priv->buff_used = 0;
+}
+
+/**
+ * setup_response() - set up a response to send back to the host
+ *
+ * @priv: Sandbox flash private data
+ * @resp: Response to send, or NULL if none
+ * @size: Size of response
+ */
+static void setup_response(struct sandbox_flash_priv *priv, void *resp,
+ int size)
+{
+ struct umass_bbb_csw *csw = &priv->status;
+
+ csw->dCSWSignature = CSWSIGNATURE;
+ csw->dCSWTag = priv->tag;
+ csw->dCSWDataResidue = 0;
+ csw->bCSWStatus = CSWSTATUS_GOOD;
+
+ assert(!resp || resp == priv->buff);
+ priv->buff_used = size;
+}
+
+static void handle_read(struct sandbox_flash_priv *priv, ulong lba,
+ ulong transfer_len)
+{
+ debug("%s: lba=%lx, transfer_len=%lx\n", __func__, lba, transfer_len);
+ if (priv->fd != -1) {
+ os_lseek(priv->fd, lba * SANDBOX_FLASH_BLOCK_LEN, OS_SEEK_SET);
+ priv->read_len = transfer_len;
+ setup_response(priv, priv->buff,
+ transfer_len * SANDBOX_FLASH_BLOCK_LEN);
+ } else {
+ setup_fail_response(priv);
+ }
+}
+
+static int handle_ufi_command(struct sandbox_flash_priv *priv, const void *buff,
+ int len)
+{
+ const struct SCSI_cmd_block *req = buff;
+
+ switch (*req->cmd) {
+ case SCSI_INQUIRY: {
+ struct scsi_inquiry_resp *resp = (void *)priv->buff;
+
+ priv->alloc_len = req->cmd[4];
+ memset(resp, '\0', sizeof(*resp));
+ resp->data_format = 1;
+ resp->additional_len = 0x1f;
+ strncpy(resp->vendor,
+ flash_strings[STRINGID_MANUFACTURER - 1].s,
+ sizeof(resp->vendor));
+ strncpy(resp->product, flash_strings[STRINGID_PRODUCT - 1].s,
+ sizeof(resp->product));
+ strncpy(resp->revision, "1.0", sizeof(resp->revision));
+ setup_response(priv, resp, sizeof(*resp));
+ break;
+ }
+ case SCSI_TST_U_RDY:
+ setup_response(priv, NULL, 0);
+ break;
+ case SCSI_RD_CAPAC: {
+ struct scsi_read_capacity_resp *resp = (void *)priv->buff;
+ uint blocks;
+
+ if (priv->file_size)
+ blocks = priv->file_size / SANDBOX_FLASH_BLOCK_LEN - 1;
+ else
+ blocks = 0;
+ resp->last_block_addr = cpu_to_be32(blocks);
+ resp->block_len = cpu_to_be32(SANDBOX_FLASH_BLOCK_LEN);
+ setup_response(priv, resp, sizeof(*resp));
+ break;
+ }
+ case SCSI_READ10: {
+ struct scsi_read10_req *req = (void *)buff;
+
+ handle_read(priv, be32_to_cpu(req->lba),
+ be16_to_cpu(req->transfer_len));
+ break;
+ }
+ default:
+ debug("Command not supported: %x\n", req->cmd[0]);
+ return -EPROTONOSUPPORT;
+ }
+
+ priv->phase = priv->transfer_len ? PHASE_DATA : PHASE_STATUS;
+ return 0;
+}
+
+static int sandbox_flash_bulk(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buff, int len)
+{
+ struct sandbox_flash_priv *priv = dev_get_priv(dev);
+ int ep = usb_pipeendpoint(pipe);
+ struct umass_bbb_cbw *cbw = buff;
+
+ debug("%s: dev=%s, pipe=%lx, ep=%x, len=%x, phase=%d\n", __func__,
+ dev->name, pipe, ep, len, priv->phase);
+ switch (ep) {
+ case SANDBOX_FLASH_EP_OUT:
+ switch (priv->phase) {
+ case PHASE_START:
+ priv->alloc_len = 0;
+ priv->read_len = 0;
+ if (priv->error || len != UMASS_BBB_CBW_SIZE ||
+ cbw->dCBWSignature != CBWSIGNATURE)
+ goto err;
+ if ((cbw->bCBWFlags & CBWFLAGS_SBZ) ||
+ cbw->bCBWLUN != 0)
+ goto err;
+ if (cbw->bCDBLength < 1 || cbw->bCDBLength >= 0x10)
+ goto err;
+ priv->transfer_len = cbw->dCBWDataTransferLength;
+ priv->tag = cbw->dCBWTag;
+ return handle_ufi_command(priv, cbw->CBWCDB,
+ cbw->bCDBLength);
+ case PHASE_DATA:
+ debug("data out\n");
+ break;
+ default:
+ break;
+ }
+ case SANDBOX_FLASH_EP_IN:
+ switch (priv->phase) {
+ case PHASE_DATA:
+ debug("data in, len=%x, alloc_len=%x, priv->read_len=%x\n",
+ len, priv->alloc_len, priv->read_len);
+ if (priv->read_len) {
+ ulong bytes_read;
+
+ bytes_read = os_read(priv->fd, buff, len);
+ if (bytes_read != len)
+ return -EIO;
+ priv->read_len -= len / SANDBOX_FLASH_BLOCK_LEN;
+ if (!priv->read_len)
+ priv->phase = PHASE_STATUS;
+ } else {
+ if (priv->alloc_len && len > priv->alloc_len)
+ len = priv->alloc_len;
+ memcpy(buff, priv->buff, len);
+ priv->phase = PHASE_STATUS;
+ }
+ return len;
+ case PHASE_STATUS:
+ debug("status in, len=%x\n", len);
+ if (len > sizeof(priv->status))
+ len = sizeof(priv->status);
+ memcpy(buff, &priv->status, len);
+ priv->phase = PHASE_START;
+ return len;
+ default:
+ break;
+ }
+ }
+err:
+ priv->error = true;
+ debug("%s: Detected transfer error\n", __func__);
+ return 0;
+}
+
+static int sandbox_flash_ofdata_to_platdata(struct udevice *dev)
+{
+ struct sandbox_flash_plat *plat = dev_get_platdata(dev);
+ const void *blob = gd->fdt_blob;
+
+ plat->pathname = fdt_getprop(blob, dev->of_offset, "sandbox,filepath",
+ NULL);
+
+ return 0;
+}
+
+static int sandbox_flash_bind(struct udevice *dev)
+{
+ return usb_emul_setup_device(dev, PACKET_SIZE_64, flash_strings,
+ flash_desc_list);
+}
+
+static int sandbox_flash_probe(struct udevice *dev)
+{
+ struct sandbox_flash_plat *plat = dev_get_platdata(dev);
+ struct sandbox_flash_priv *priv = dev_get_priv(dev);
+
+ priv->fd = os_open(plat->pathname, OS_O_RDONLY);
+ if (priv->fd != -1)
+ return os_get_filesize(plat->pathname, &priv->file_size);
+
+ return 0;
+}
+
+static const struct dm_usb_ops sandbox_usb_flash_ops = {
+ .control = sandbox_flash_control,
+ .bulk = sandbox_flash_bulk,
+};
+
+static const struct udevice_id sandbox_usb_flash_ids[] = {
+ { .compatible = "sandbox,usb-flash" },
+ { }
+};
+
+U_BOOT_DRIVER(usb_sandbox_flash) = {
+ .name = "usb_sandbox_flash",
+ .id = UCLASS_USB_EMUL,
+ .of_match = sandbox_usb_flash_ids,
+ .bind = sandbox_flash_bind,
+ .probe = sandbox_flash_probe,
+ .ofdata_to_platdata = sandbox_flash_ofdata_to_platdata,
+ .ops = &sandbox_usb_flash_ops,
+ .priv_auto_alloc_size = sizeof(struct sandbox_flash_priv),
+ .platdata_auto_alloc_size = sizeof(struct sandbox_flash_plat),
+};
diff --git a/drivers/usb/emul/sandbox_hub.c b/drivers/usb/emul/sandbox_hub.c
new file mode 100644
index 0000000..280c708
--- /dev/null
+++ b/drivers/usb/emul/sandbox_hub.c
@@ -0,0 +1,303 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <usb.h>
+#include <dm/device-internal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* We only support up to 8 */
+#define SANDBOX_NUM_PORTS 2
+
+struct sandbox_hub_platdata {
+ struct usb_dev_platdata plat;
+ int port; /* Port number (numbered from 0) */
+};
+
+enum {
+ STRING_MANUFACTURER = 1,
+ STRING_PRODUCT,
+ STRING_SERIAL,
+
+ STRING_count,
+};
+
+static struct usb_string hub_strings[] = {
+ {STRING_MANUFACTURER, "sandbox"},
+ {STRING_PRODUCT, "hub"},
+ {STRING_SERIAL, "2345"},
+};
+
+static struct usb_device_descriptor hub_device_desc = {
+ .bLength = sizeof(hub_device_desc),
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+
+ .bDeviceClass = USB_CLASS_HUB,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+
+ .idVendor = __constant_cpu_to_le16(0x1234),
+ .idProduct = __constant_cpu_to_le16(0x5678),
+ .iManufacturer = STRING_MANUFACTURER,
+ .iProduct = STRING_PRODUCT,
+ .iSerialNumber = STRING_SERIAL,
+ .bNumConfigurations = 1,
+};
+
+static struct usb_config_descriptor hub_config1 = {
+ .bLength = sizeof(hub_config1),
+ .bDescriptorType = USB_DT_CONFIG,
+
+ /* wTotalLength is set up by usb-emul-uclass */
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 0,
+ .iConfiguration = 0,
+ .bmAttributes = 1 << 7,
+ .bMaxPower = 50,
+};
+
+static struct usb_interface_descriptor hub_interface0 = {
+ .bLength = sizeof(hub_interface0),
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HUB,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = US_PR_CB,
+ .iInterface = 0,
+};
+
+static struct usb_endpoint_descriptor hub_endpoint0_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(1024),
+ .bInterval = 0,
+};
+
+static struct usb_hub_descriptor hub_desc = {
+ .bLength = sizeof(hub_desc),
+ .bDescriptorType = USB_DT_HUB,
+ .bNbrPorts = SANDBOX_NUM_PORTS,
+ .wHubCharacteristics = __constant_cpu_to_le16(1 << 0 | 1 << 3 |
+ 1 << 7),
+ .bPwrOn2PwrGood = 2,
+ .bHubContrCurrent = 5,
+ .DeviceRemovable = {0, 0xff}, /* all ports removeable */
+#if SANDBOX_NUM_PORTS > 8
+#error "This code sets up an incorrect mask"
+#endif
+};
+
+static void *hub_desc_list[] = {
+ &hub_device_desc,
+ &hub_config1,
+ &hub_interface0,
+ &hub_endpoint0_in,
+ &hub_desc,
+ NULL,
+};
+
+struct sandbox_hub_priv {
+ int status[SANDBOX_NUM_PORTS];
+ int change[SANDBOX_NUM_PORTS];
+};
+
+static struct udevice *hub_find_device(struct udevice *hub, int port)
+{
+ struct udevice *dev;
+
+ for (device_find_first_child(hub, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ struct sandbox_hub_platdata *plat;
+
+ plat = dev_get_parent_platdata(dev);
+ if (plat->port == port)
+ return dev;
+ }
+
+ return NULL;
+}
+
+static int clrset_post_state(struct udevice *hub, int port, int clear, int set)
+{
+ struct sandbox_hub_priv *priv = dev_get_priv(hub);
+ int *status = &priv->status[port];
+ int *change = &priv->change[port];
+ int ret = 0;
+
+ if ((clear | set) & USB_PORT_STAT_POWER) {
+ struct udevice *dev = hub_find_device(hub, port);
+
+ if (dev) {
+ if (set & USB_PORT_STAT_POWER) {
+ ret = device_probe(dev);
+ debug("%s: %s: power on, probed, ret=%d\n",
+ __func__, dev->name, ret);
+ if (!ret) {
+ set |= USB_PORT_STAT_CONNECTION |
+ USB_PORT_STAT_ENABLE;
+ }
+
+ } else if (clear & USB_PORT_STAT_POWER) {
+ debug("%s: %s: power off, removed, ret=%d\n",
+ __func__, dev->name, ret);
+ ret = device_remove(dev);
+ clear |= USB_PORT_STAT_CONNECTION;
+ }
+ }
+ }
+ *change |= *status & clear;
+ *change |= ~*status & set;
+ *change &= 0x1f;
+ *status = (*status & ~clear) | set;
+
+ return ret;
+}
+
+static int sandbox_hub_submit_control_msg(struct udevice *bus,
+ struct usb_device *udev,
+ unsigned long pipe,
+ void *buffer, int length,
+ struct devrequest *setup)
+{
+ struct sandbox_hub_priv *priv = dev_get_priv(bus);
+ int ret = 0;
+
+ if (pipe == usb_rcvctrlpipe(udev, 0)) {
+ switch (setup->requesttype) {
+ case USB_RT_HUB | USB_DIR_IN:
+ switch (setup->request) {
+ case USB_REQ_GET_STATUS: {
+ struct usb_hub_status *hubsts = buffer;
+
+ hubsts->wHubStatus = 0;
+ hubsts->wHubChange = 0;
+ udev->status = 0;
+ udev->act_len = sizeof(*hubsts);
+ return 0;
+ }
+ default:
+ debug("%s: rx ctl requesttype=%x, request=%x\n",
+ __func__, setup->requesttype,
+ setup->request);
+ break;
+ }
+ case USB_RT_PORT | USB_DIR_IN:
+ switch (setup->request) {
+ case USB_REQ_GET_STATUS: {
+ struct usb_port_status *portsts = buffer;
+ int port;
+
+ port = (setup->index & USB_HUB_PORT_MASK) - 1;
+ portsts->wPortStatus = priv->status[port];
+ portsts->wPortChange = priv->change[port];
+ udev->status = 0;
+ udev->act_len = sizeof(*portsts);
+ return 0;
+ }
+ }
+ default:
+ debug("%s: rx ctl requesttype=%x, request=%x\n",
+ __func__, setup->requesttype, setup->request);
+ break;
+ }
+ } else if (pipe == usb_sndctrlpipe(udev, 0)) {
+ switch (setup->requesttype) {
+ case USB_RT_PORT:
+ switch (setup->request) {
+ case USB_REQ_SET_FEATURE: {
+ int port;
+
+ port = (setup->index & USB_HUB_PORT_MASK) - 1;
+ debug("set feature port=%x, feature=%x\n",
+ port, setup->value);
+ if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
+ ret = clrset_post_state(bus, port, 0,
+ 1 << setup->value);
+ } else {
+ debug(" ** Invalid feature\n");
+ }
+ return ret;
+ }
+ case USB_REQ_CLEAR_FEATURE: {
+ int port;
+
+ port = (setup->index & USB_HUB_PORT_MASK) - 1;
+ debug("clear feature port=%x, feature=%x\n",
+ port, setup->value);
+ if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
+ ret = clrset_post_state(bus, port,
+ 1 << setup->value, 0);
+ } else {
+ priv->change[port] &= 1 <<
+ (setup->value - 16);
+ }
+ udev->status = 0;
+ return 0;
+ }
+ default:
+ debug("%s: tx ctl requesttype=%x, request=%x\n",
+ __func__, setup->requesttype,
+ setup->request);
+ break;
+ }
+ default:
+ debug("%s: tx ctl requesttype=%x, request=%x\n",
+ __func__, setup->requesttype, setup->request);
+ break;
+ }
+ }
+ debug("pipe=%lx\n", pipe);
+
+ return -EIO;
+}
+
+static int sandbox_hub_bind(struct udevice *dev)
+{
+ return usb_emul_setup_device(dev, PACKET_SIZE_64, hub_strings,
+ hub_desc_list);
+}
+
+static int sandbox_child_post_bind(struct udevice *dev)
+{
+ struct sandbox_hub_platdata *plat = dev_get_parent_platdata(dev);
+
+ plat->port = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
+
+ return 0;
+}
+
+static const struct dm_usb_ops sandbox_usb_hub_ops = {
+ .control = sandbox_hub_submit_control_msg,
+};
+
+static const struct udevice_id sandbox_usb_hub_ids[] = {
+ { .compatible = "sandbox,usb-hub" },
+ { }
+};
+
+U_BOOT_DRIVER(usb_sandbox_hub) = {
+ .name = "usb_sandbox_hub",
+ .id = UCLASS_USB_EMUL,
+ .of_match = sandbox_usb_hub_ids,
+ .bind = sandbox_hub_bind,
+ .ops = &sandbox_usb_hub_ops,
+ .priv_auto_alloc_size = sizeof(struct sandbox_hub_priv),
+ .per_child_platdata_auto_alloc_size =
+ sizeof(struct sandbox_hub_platdata),
+ .child_post_bind = sandbox_child_post_bind,
+};
diff --git a/drivers/usb/emul/usb-emul-uclass.c b/drivers/usb/emul/usb-emul-uclass.c
new file mode 100644
index 0000000..205f2c5
--- /dev/null
+++ b/drivers/usb/emul/usb-emul-uclass.c
@@ -0,0 +1,263 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <usb.h>
+#include <dm/root.h>
+#include <dm/device-internal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int copy_to_unicode(char *buff, int length, const char *str)
+{
+ int ptr;
+ int i;
+
+ if (length < 2)
+ return 0;
+ buff[1] = USB_DT_STRING;
+ for (ptr = 2, i = 0; ptr + 1 < length && *str; i++, ptr += 2) {
+ buff[ptr] = str[i];
+ buff[ptr + 1] = 0;
+ }
+ buff[0] = ptr;
+
+ return ptr;
+}
+
+static int usb_emul_get_string(struct usb_string *strings, int index,
+ char *buff, int length)
+{
+ if (index == 0) {
+ char *desc = buff;
+
+ desc[0] = 4;
+ desc[1] = USB_DT_STRING;
+ desc[2] = 0x09;
+ desc[3] = 0x14;
+ return 4;
+ } else if (strings) {
+ struct usb_string *ptr;
+
+ for (ptr = strings; ptr->s; ptr++) {
+ if (ptr->id == index)
+ return copy_to_unicode(buff, length, ptr->s);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static struct usb_generic_descriptor **find_descriptor(
+ struct usb_generic_descriptor **ptr, int type, int index)
+{
+ debug("%s: type=%x, index=%d\n", __func__, type, index);
+ for (; *ptr; ptr++) {
+ if ((*ptr)->bDescriptorType != type)
+ continue;
+ switch (type) {
+ case USB_DT_CONFIG: {
+ struct usb_config_descriptor *cdesc;
+
+ cdesc = (struct usb_config_descriptor *)*ptr;
+ if (cdesc && cdesc->bConfigurationValue == index)
+ return ptr;
+ break;
+ }
+ default:
+ return ptr;
+ }
+ }
+ debug("%s: config ptr=%p\n", __func__, *ptr);
+
+ return ptr;
+}
+
+static int usb_emul_get_descriptor(struct usb_dev_platdata *plat, int value,
+ void *buffer, int length)
+{
+ struct usb_generic_descriptor **ptr;
+ int type = value >> 8;
+ int index = value & 0xff;
+ int upto, todo;
+
+ debug("%s: type=%d, index=%d, plat=%p\n", __func__, type, index, plat);
+ if (type == USB_DT_STRING) {
+ return usb_emul_get_string(plat->strings, index, buffer,
+ length);
+ }
+
+ ptr = find_descriptor((struct usb_generic_descriptor **)plat->desc_list,
+ type, index);
+ if (!ptr) {
+ debug("%s: Could not find descriptor type %d, index %d\n",
+ __func__, type, index);
+ return -ENOENT;
+ }
+ for (upto = 0; *ptr && upto < length; ptr++, upto += todo) {
+ todo = min(length - upto, (int)(*ptr)->bLength);
+
+ memcpy(buffer + upto, *ptr, todo);
+ }
+
+ return upto ? upto : length ? -EIO : 0;
+}
+
+int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp)
+{
+ int devnum = usb_pipedevice(pipe);
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret;
+
+ *emulp = NULL;
+ ret = uclass_get(UCLASS_USB_EMUL, &uc);
+ if (ret)
+ return ret;
+ uclass_foreach_dev(dev, uc) {
+ struct usb_dev_platdata *udev = dev_get_parent_platdata(dev);
+
+ if (udev->devnum == devnum) {
+ debug("%s: Found emulator '%s', addr %d\n", __func__,
+ dev->name, udev->devnum);
+ *emulp = dev;
+ return 0;
+ }
+ }
+
+ debug("%s: No emulator found, addr %d\n", __func__, devnum);
+ return -ENOENT;
+}
+
+int usb_emul_control(struct udevice *emul, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ struct devrequest *setup)
+{
+ struct dm_usb_ops *ops = usb_get_emul_ops(emul);
+ struct usb_dev_platdata *plat;
+ int ret;
+
+ /* We permit getting the descriptor before we are probed */
+ plat = dev_get_parent_platdata(emul);
+ if (!ops->control)
+ return -ENOSYS;
+ debug("%s: dev=%s\n", __func__, emul->name);
+ if (pipe == usb_rcvctrlpipe(udev, 0)) {
+ switch (setup->request) {
+ case USB_REQ_GET_DESCRIPTOR: {
+ return usb_emul_get_descriptor(plat, setup->value,
+ buffer, length);
+ }
+ default:
+ ret = device_probe(emul);
+ if (ret)
+ return ret;
+ return ops->control(emul, udev, pipe, buffer, length,
+ setup);
+ }
+ } else if (pipe == usb_snddefctrl(udev)) {
+ switch (setup->request) {
+ case USB_REQ_SET_ADDRESS:
+ debug(" ** set address %s %d\n", emul->name,
+ setup->value);
+ plat->devnum = setup->value;
+ return 0;
+ default:
+ debug("requestsend =%x\n", setup->request);
+ break;
+ }
+ } else if (pipe == usb_sndctrlpipe(udev, 0)) {
+ switch (setup->request) {
+ case USB_REQ_SET_CONFIGURATION:
+ plat->configno = setup->value;
+ return 0;
+ default:
+ ret = device_probe(emul);
+ if (ret)
+ return ret;
+ return ops->control(emul, udev, pipe, buffer, length,
+ setup);
+ }
+ }
+ debug("pipe=%lx\n", pipe);
+
+ return -EIO;
+}
+
+int usb_emul_bulk(struct udevice *emul, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length)
+{
+ struct dm_usb_ops *ops = usb_get_emul_ops(emul);
+ int ret;
+
+ /* We permit getting the descriptor before we are probed */
+ if (!ops->bulk)
+ return -ENOSYS;
+ debug("%s: dev=%s\n", __func__, emul->name);
+ ret = device_probe(emul);
+ if (ret)
+ return ret;
+ return ops->bulk(emul, udev, pipe, buffer, length);
+}
+
+int usb_emul_setup_device(struct udevice *dev, int maxpacketsize,
+ struct usb_string *strings, void **desc_list)
+{
+ struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
+ struct usb_generic_descriptor **ptr;
+ struct usb_config_descriptor *cdesc;
+ int upto;
+
+ plat->strings = strings;
+ plat->desc_list = (struct usb_generic_descriptor **)desc_list;
+
+ /* Fill in wTotalLength for each configuration descriptor */
+ ptr = plat->desc_list;
+ for (cdesc = NULL, upto = 0; *ptr; upto += (*ptr)->bLength, ptr++) {
+ debug(" - upto=%d, type=%d\n", upto, (*ptr)->bDescriptorType);
+ if ((*ptr)->bDescriptorType == USB_DT_CONFIG) {
+ if (cdesc) {
+ cdesc->wTotalLength = upto;
+ debug("%s: config %d length %d\n", __func__,
+ cdesc->bConfigurationValue,
+ cdesc->bLength);
+ }
+ cdesc = (struct usb_config_descriptor *)*ptr;
+ upto = 0;
+ }
+ }
+ if (cdesc) {
+ cdesc->wTotalLength = upto;
+ debug("%s: config %d length %d\n", __func__,
+ cdesc->bConfigurationValue, cdesc->wTotalLength);
+ }
+
+ return 0;
+}
+
+int usb_emul_post_bind(struct udevice *dev)
+{
+ /* Scan the bus for devices */
+ return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
+void usb_emul_reset(struct udevice *dev)
+{
+ struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
+
+ plat->devnum = 0;
+ plat->configno = 0;
+}
+
+UCLASS_DRIVER(usb_emul) = {
+ .id = UCLASS_USB_EMUL,
+ .name = "usb_emul",
+ .post_bind = usb_emul_post_bind,
+ .per_child_auto_alloc_size = sizeof(struct usb_device),
+ .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata),
+};
diff --git a/drivers/usb/eth/asix.c b/drivers/usb/eth/asix.c
index 1cd179b..c8697ae 100644
--- a/drivers/usb/eth/asix.c
+++ b/drivers/usb/eth/asix.c
@@ -534,7 +534,8 @@ static int asix_recv(struct eth_device *eth)
}
/* Notify net stack */
- NetReceive(buf_ptr + sizeof(packet_len), packet_len);
+ net_process_received_packet(buf_ptr + sizeof(packet_len),
+ packet_len);
/* Adjust for next iteration. Packets are padded to 16-bits */
if (packet_len & 1)
diff --git a/drivers/usb/eth/asix88179.c b/drivers/usb/eth/asix88179.c
index 0ef85db..94dfe85 100644
--- a/drivers/usb/eth/asix88179.c
+++ b/drivers/usb/eth/asix88179.c
@@ -558,7 +558,7 @@ static int asix_recv(struct eth_device *eth)
frame_pos += 2;
- NetReceive(recv_buf + frame_pos, pkt_len);
+ net_process_received_packet(recv_buf + frame_pos, pkt_len);
pkt_hdr++;
frame_pos += ((pkt_len + 7) & 0xFFF8)-2;
diff --git a/drivers/usb/eth/mcs7830.c b/drivers/usb/eth/mcs7830.c
index 8e738d4..c1b7086 100644
--- a/drivers/usb/eth/mcs7830.c
+++ b/drivers/usb/eth/mcs7830.c
@@ -600,7 +600,7 @@ static int mcs7830_recv(struct eth_device *eth)
if (sts == STAT_RX_FRAME_CORRECT) {
debug("%s() got a frame, len=%d\n", __func__, gotlen);
- NetReceive(buf, gotlen);
+ net_process_received_packet(buf, gotlen);
return 0;
}
diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c
index 6bca34d..a7e50d6 100644
--- a/drivers/usb/eth/smsc95xx.c
+++ b/drivers/usb/eth/smsc95xx.c
@@ -355,7 +355,7 @@ static int smsc95xx_init_mac_address(struct eth_device *eth,
/* try reading mac address from EEPROM */
if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN,
eth->enetaddr) == 0) {
- if (is_valid_ether_addr(eth->enetaddr)) {
+ if (is_valid_ethaddr(eth->enetaddr)) {
/* eeprom values are valid so use them */
debug("MAC address read from EEPROM\n");
return 0;
@@ -760,7 +760,8 @@ static int smsc95xx_recv(struct eth_device *eth)
}
/* Notify net stack */
- NetReceive(buf_ptr + sizeof(packet_len), packet_len - 4);
+ net_process_received_packet(buf_ptr + sizeof(packet_len),
+ packet_len - 4);
/* Adjust for next iteration */
actual_len -= sizeof(packet_len) + packet_len;
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c
index 7cb96e3..c72b7e4 100644
--- a/drivers/usb/eth/usb_ether.c
+++ b/drivers/usb/eth/usb_ether.c
@@ -5,7 +5,9 @@
*/
#include <common.h>
+#include <dm.h>
#include <usb.h>
+#include <dm/device-internal.h>
#include "usb_ether.h"
@@ -118,8 +120,6 @@ static void probe_valid_drivers(struct usb_device *dev)
int usb_host_eth_scan(int mode)
{
int i, old_async;
- struct usb_device *dev;
-
if (mode == 1)
printf(" scanning usb for ethernet devices... ");
@@ -138,23 +138,59 @@ int usb_host_eth_scan(int mode)
}
usb_max_eth_dev = 0;
+#ifdef CONFIG_DM_USB
+ /*
+ * TODO: We should add USB_DEVICE() declarations to each USB ethernet
+ * driver and then most of this file can be removed.
+ */
+ struct udevice *bus;
+ struct uclass *uc;
+ int ret;
+
+ ret = uclass_get(UCLASS_USB, &uc);
+ if (ret)
+ return ret;
+ uclass_foreach_dev(bus, uc) {
+ for (i = 0; i < USB_MAX_DEVICE; i++) {
+ struct usb_device *dev;
+
+ dev = usb_get_dev_index(bus, i); /* get device */
+ debug("i=%d, %s\n", i, dev ? dev->dev->name : "(done)");
+ if (!dev)
+ break; /* no more devices available */
+
+ /*
+ * find valid usb_ether driver for this device,
+ * if any
+ */
+ probe_valid_drivers(dev);
+
+ /* check limit */
+ if (usb_max_eth_dev == USB_MAX_ETH_DEV)
+ break;
+ } /* for */
+ }
+#else
for (i = 0; i < USB_MAX_DEVICE; i++) {
+ struct usb_device *dev;
+
dev = usb_get_dev_index(i); /* get device */
debug("i=%d\n", i);
- if (dev == NULL)
+ if (!dev)
break; /* no more devices available */
/* find valid usb_ether driver for this device, if any */
probe_valid_drivers(dev);
/* check limit */
- if (usb_max_eth_dev == USB_MAX_ETH_DEV) {
- printf("max USB Ethernet Device reached: %d stopping\n",
- usb_max_eth_dev);
+ if (usb_max_eth_dev == USB_MAX_ETH_DEV)
break;
- }
} /* for */
-
+#endif
+ if (usb_max_eth_dev == USB_MAX_ETH_DEV) {
+ printf("max USB Ethernet Device reached: %d stopping\n",
+ usb_max_eth_dev);
+ }
usb_disable_asynch(old_async); /* restore asynch value */
printf("%d Ethernet Device(s) found\n", usb_max_eth_dev);
if (usb_max_eth_dev > 0)
diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c
index 3b7024c..22d288c 100644
--- a/drivers/usb/gadget/ci_udc.c
+++ b/drivers/usb/gadget/ci_udc.c
@@ -883,7 +883,11 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH)
return -EINVAL;
+#ifdef CONFIG_DM_USB
+ ret = usb_setup_ehci_gadget(&controller.ctrl);
+#else
ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl);
+#endif
if (ret)
return ret;
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 7e3b3ed..141ff8b 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -1522,7 +1522,7 @@ static int rx_submit(struct eth_dev *dev, struct usb_request *req,
* RNDIS headers involve variable numbers of LE32 values.
*/
- req->buf = (u8 *) NetRxPackets[0];
+ req->buf = (u8 *)net_rx_packets[0];
req->length = size;
req->complete = rx_complete;
@@ -1645,13 +1645,13 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
if (!eth_is_promisc (dev)) {
u8 *dest = skb->data;
- if (is_multicast_ether_addr(dest)) {
+ if (is_multicast_ethaddr(dest)) {
u16 type;
/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
* SET_ETHERNET_MULTICAST_FILTERS requests
*/
- if (is_broadcast_ether_addr(dest))
+ if (is_broadcast_ethaddr(dest))
type = USB_CDC_PACKET_TYPE_BROADCAST;
else
type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
@@ -1942,7 +1942,7 @@ static int is_eth_addr_valid(char *str)
}
/* Now check the contents. */
- return is_valid_ether_addr(ea);
+ return is_valid_ethaddr(ea);
}
return 0;
}
@@ -1971,7 +1971,7 @@ static int get_ether_addr(const char *str, u8 *dev_addr)
num |= (nibble(*str++));
dev_addr[i] = num;
}
- if (is_valid_ether_addr(dev_addr))
+ if (is_valid_ethaddr(dev_addr))
return 0;
}
return 1;
@@ -2446,7 +2446,8 @@ static int usb_eth_recv(struct eth_device *netdev)
if (packet_received) {
debug("%s: packet received\n", __func__);
if (dev->rx_req) {
- NetReceive(NetRxPackets[0], dev->rx_req->length);
+ net_process_received_packet(net_rx_packets[0],
+ dev->rx_req->length);
packet_received = 0;
rx_submit(dev, dev->rx_req, 0);
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index eb6f34b..7658f87 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -5,6 +5,11 @@
# SPDX-License-Identifier: GPL-2.0+
#
+ifdef CONFIG_DM_USB
+obj-$(CONFIG_CMD_USB) += usb-uclass.o
+obj-$(CONFIG_SANDBOX) += usb-sandbox.o
+endif
+
# ohci
obj-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o
obj-$(CONFIG_USB_ATMEL) += ohci-at91.o
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index f3c077d..86cf631 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -8,6 +8,7 @@
*/
#include <common.h>
+#include <dm.h>
#include <fdtdec.h>
#include <libfdt.h>
#include <malloc.h>
@@ -24,19 +25,73 @@
/* Declare global data pointer */
DECLARE_GLOBAL_DATA_PTR;
+#ifdef CONFIG_DM_USB
+struct exynos_ehci_platdata {
+ struct usb_platdata usb_plat;
+ fdt_addr_t hcd_base;
+ fdt_addr_t phy_base;
+ struct gpio_desc vbus_gpio;
+};
+#endif
+
/**
* Contains pointers to register base addresses
* for the usb controller.
*/
struct exynos_ehci {
+ struct ehci_ctrl ctrl;
struct exynos_usb_phy *usb;
struct ehci_hccr *hcd;
+#ifndef CONFIG_DM_USB
struct gpio_desc vbus_gpio;
+#endif
};
+#ifndef CONFIG_DM_USB
static struct exynos_ehci exynos;
+#endif
-#ifdef CONFIG_OF_CONTROL
+#ifdef CONFIG_DM_USB
+static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
+{
+ struct exynos_ehci_platdata *plat = dev_get_platdata(dev);
+ const void *blob = gd->fdt_blob;
+ unsigned int node;
+ int depth;
+
+ /*
+ * Get the base address for XHCI controller from the device node
+ */
+ plat->hcd_base = dev_get_addr(dev);
+ if (plat->hcd_base == FDT_ADDR_T_NONE) {
+ debug("Can't get the XHCI register base address\n");
+ return -ENXIO;
+ }
+
+ depth = 0;
+ node = fdtdec_next_compatible_subnode(blob, dev->of_offset,
+ COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth);
+ if (node <= 0) {
+ debug("XHCI: Can't get device node for usb3-phy controller\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Get the base address for usbphy from the device node
+ */
+ plat->phy_base = fdtdec_get_addr(blob, node, "reg");
+ if (plat->phy_base == FDT_ADDR_T_NONE) {
+ debug("Can't get the usbphy register address\n");
+ return -ENXIO;
+ }
+
+ /* Vbus gpio */
+ gpio_request_by_name(dev, "samsung,vbus-gpio", 0,
+ &plat->vbus_gpio, GPIOD_IS_OUT);
+
+ return 0;
+}
+#else
static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos)
{
fdt_addr_t addr;
@@ -215,6 +270,7 @@ static void reset_usb_phy(struct exynos_usb_phy *usb)
set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE);
}
+#ifndef CONFIG_DM_USB
/*
* EHCI-initialization
* Create the appropriate control structures to manage
@@ -268,3 +324,57 @@ int ehci_hcd_stop(int index)
return 0;
}
+#endif
+
+#ifdef CONFIG_DM_USB
+static int ehci_usb_probe(struct udevice *dev)
+{
+ struct exynos_ehci_platdata *plat = dev_get_platdata(dev);
+ struct exynos_ehci *ctx = dev_get_priv(dev);
+ struct ehci_hcor *hcor;
+
+ ctx->hcd = (struct ehci_hccr *)plat->hcd_base;
+ ctx->usb = (struct exynos_usb_phy *)plat->phy_base;
+ hcor = (struct ehci_hcor *)((uint32_t)ctx->hcd +
+ HC_LENGTH(ehci_readl(&ctx->hcd->cr_capbase)));
+
+ /* setup the Vbus gpio here */
+ if (dm_gpio_is_valid(&plat->vbus_gpio))
+ dm_gpio_set_value(&plat->vbus_gpio, 1);
+
+ setup_usb_phy(ctx->usb);
+
+ return ehci_register(dev, ctx->hcd, hcor, NULL, 0, USB_INIT_HOST);
+}
+
+static int ehci_usb_remove(struct udevice *dev)
+{
+ struct exynos_ehci *ctx = dev_get_priv(dev);
+ int ret;
+
+ ret = ehci_deregister(dev);
+ if (ret)
+ return ret;
+ reset_usb_phy(ctx->usb);
+
+ return 0;
+}
+
+static const struct udevice_id ehci_usb_ids[] = {
+ { .compatible = "samsung,exynos-ehci" },
+ { }
+};
+
+U_BOOT_DRIVER(usb_ehci) = {
+ .name = "ehci_exynos",
+ .id = UCLASS_USB,
+ .of_match = ehci_usb_ids,
+ .ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
+ .probe = ehci_usb_probe,
+ .remove = ehci_usb_remove,
+ .ops = &ehci_usb_ops,
+ .priv_auto_alloc_size = sizeof(struct exynos_ehci),
+ .platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata),
+ .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
+#endif
diff --git a/drivers/usb/host/ehci-faraday.c b/drivers/usb/host/ehci-faraday.c
index 3b761bc..821222c 100644
--- a/drivers/usb/host/ehci-faraday.c
+++ b/drivers/usb/host/ehci-faraday.c
@@ -29,6 +29,59 @@ static inline int ehci_is_fotg2xx(union ehci_faraday_regs *regs)
return !readl(&regs->usb.easstr);
}
+void faraday_ehci_set_usbmode(struct ehci_ctrl *ctrl)
+{
+ /* nothing needs to be done */
+}
+
+int faraday_ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg)
+{
+ int spd, ret = PORTSC_PSPD_HS;
+ union ehci_faraday_regs *regs;
+
+ ret = (void __iomem *)((ulong)ctrl->hcor - 0x10);
+ if (ehci_is_fotg2xx(regs))
+ spd = OTGCSR_SPD(readl(&regs->otg.otgcsr));
+ else
+ spd = BMCSR_SPD(readl(&regs->usb.bmcsr));
+
+ switch (spd) {
+ case 0: /* full speed */
+ ret = PORTSC_PSPD_FS;
+ break;
+ case 1: /* low speed */
+ ret = PORTSC_PSPD_LS;
+ break;
+ case 2: /* high speed */
+ ret = PORTSC_PSPD_HS;
+ break;
+ default:
+ printf("ehci-faraday: invalid device speed\n");
+ break;
+ }
+
+ return ret;
+}
+
+uint32_t *faraday_ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port)
+{
+ /* Faraday EHCI has one and only one portsc register */
+ if (port) {
+ /* Printing the message would cause a scan failure! */
+ debug("The request port(%d) is not configured\n", port);
+ return NULL;
+ }
+
+ /* Faraday EHCI PORTSC register offset is 0x20 from hcor */
+ return (uint32_t *)((uint8_t *)ctrl->hcor + 0x20);
+}
+
+static const struct ehci_ops faraday_ehci_ops = {
+ .set_usb_mode = faraday_ehci_set_usbmode,
+ .get_port_speed = faraday_ehci_get_port_speed,
+ .get_portsc_register = faraday_ehci_get_portsc_register,
+};
+
/*
* Create the appropriate control structures to manage
* a new EHCI host controller.
@@ -43,6 +96,7 @@ int ehci_hcd_init(int index, enum usb_init_type init,
if (index < 0 || index >= ARRAY_SIZE(base_list))
return -1;
+ ehci_set_controller_priv(index, NULL, &faraday_ehci_ops);
regs = (void __iomem *)base_list[index];
hccr = (struct ehci_hccr *)&regs->usb.hccr;
hcor = (struct ehci_hcor *)&regs->usb.hcor;
@@ -87,61 +141,3 @@ int ehci_hcd_stop(int index)
{
return 0;
}
-
-/*
- * This ehci_set_usbmode() overrides the weak function
- * in "ehci-hcd.c".
- */
-void ehci_set_usbmode(int index)
-{
- /* nothing needs to be done */
-}
-
-/*
- * This ehci_get_port_speed() overrides the weak function
- * in "ehci-hcd.c".
- */
-int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
-{
- int spd, ret = PORTSC_PSPD_HS;
- union ehci_faraday_regs *regs = (void __iomem *)((ulong)hcor - 0x10);
-
- if (ehci_is_fotg2xx(regs))
- spd = OTGCSR_SPD(readl(&regs->otg.otgcsr));
- else
- spd = BMCSR_SPD(readl(&regs->usb.bmcsr));
-
- switch (spd) {
- case 0: /* full speed */
- ret = PORTSC_PSPD_FS;
- break;
- case 1: /* low speed */
- ret = PORTSC_PSPD_LS;
- break;
- case 2: /* high speed */
- ret = PORTSC_PSPD_HS;
- break;
- default:
- printf("ehci-faraday: invalid device speed\n");
- break;
- }
-
- return ret;
-}
-
-/*
- * This ehci_get_portsc_register() overrides the weak function
- * in "ehci-hcd.c".
- */
-uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port)
-{
- /* Faraday EHCI has one and only one portsc register */
- if (port) {
- /* Printing the message would cause a scan failure! */
- debug("The request port(%d) is not configured\n", port);
- return NULL;
- }
-
- /* Faraday EHCI PORTSC register offset is 0x20 from hcor */
- return (uint32_t *)((uint8_t *)hcor + 0x20);
-}
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 86f1646..bd9861d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -21,6 +21,7 @@
* MA 02111-1307 USA
*/
#include <common.h>
+#include <dm.h>
#include <errno.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
@@ -42,7 +43,9 @@
*/
#define HCHALT_TIMEOUT (8 * 1000)
+#ifndef CONFIG_DM_USB
static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];
+#endif
#define ALIGN_END_ADDR(type, ptr, size) \
((unsigned long)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN))
@@ -119,17 +122,33 @@ static struct descriptor {
#define ehci_is_TDI() (0)
#endif
-__weak int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
+static struct ehci_ctrl *ehci_get_ctrl(struct usb_device *udev)
+{
+#ifdef CONFIG_DM_USB
+ struct udevice *dev;
+
+ /* Find the USB controller */
+ for (dev = udev->dev;
+ device_get_uclass_id(dev) != UCLASS_USB;
+ dev = dev->parent)
+ ;
+ return dev_get_priv(dev);
+#else
+ return udev->controller;
+#endif
+}
+
+static int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg)
{
return PORTSC_PSPD(reg);
}
-__weak void ehci_set_usbmode(int index)
+static void ehci_set_usbmode(struct ehci_ctrl *ctrl)
{
uint32_t tmp;
uint32_t *reg_ptr;
- reg_ptr = (uint32_t *)((u8 *)&ehcic[index].hcor->or_usbcmd + USBMODE);
+ reg_ptr = (uint32_t *)((u8 *)&ctrl->hcor->or_usbcmd + USBMODE);
tmp = ehci_readl(reg_ptr);
tmp |= USBMODE_CM_HC;
#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN)
@@ -138,11 +157,23 @@ __weak void ehci_set_usbmode(int index)
ehci_writel(reg_ptr, tmp);
}
-__weak void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
+static void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg,
+ uint32_t *reg)
{
mdelay(50);
}
+static uint32_t *ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port)
+{
+ if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
+ /* Printing the message would cause a scan failure! */
+ debug("The request port(%u) is not configured\n", port);
+ return NULL;
+ }
+
+ return (uint32_t *)&ctrl->hcor->or_portsc[port];
+}
+
static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
{
uint32_t result;
@@ -159,15 +190,15 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
return -1;
}
-static int ehci_reset(int index)
+static int ehci_reset(struct ehci_ctrl *ctrl)
{
uint32_t cmd;
int ret = 0;
- cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd);
+ cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
cmd = (cmd & ~CMD_RUN) | CMD_RESET;
- ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd);
- ret = handshake((uint32_t *)&ehcic[index].hcor->or_usbcmd,
+ ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+ ret = handshake((uint32_t *)&ctrl->hcor->or_usbcmd,
CMD_RESET, 0, 250 * 1000);
if (ret < 0) {
printf("EHCI fail to reset\n");
@@ -175,13 +206,13 @@ static int ehci_reset(int index)
}
if (ehci_is_TDI())
- ehci_set_usbmode(index);
+ ctrl->ops.set_usb_mode(ctrl);
#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH
- cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning);
+ cmd = ehci_readl(&ctrl->hcor->or_txfilltuning);
cmd &= ~TXFIFO_THRESH_MASK;
cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH);
- ehci_writel(&ehcic[index].hcor->or_txfilltuning, cmd);
+ ehci_writel(&ctrl->hcor->or_txfilltuning, cmd);
#endif
out:
return ret;
@@ -264,12 +295,13 @@ static inline u8 ehci_encode_speed(enum usb_device_speed speed)
return QH_FULL_SPEED;
}
-static void ehci_update_endpt2_dev_n_port(struct usb_device *dev,
+static void ehci_update_endpt2_dev_n_port(struct usb_device *udev,
struct QH *qh)
{
struct usb_device *ttdev;
+ int parent_devnum;
- if (dev->speed != USB_SPEED_LOW && dev->speed != USB_SPEED_FULL)
+ if (udev->speed != USB_SPEED_LOW && udev->speed != USB_SPEED_FULL)
return;
/*
@@ -277,14 +309,35 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *dev,
* the tt, so of the first upstream usb-2 hub, there may be usb-1 hubs
* in the tree before that one!
*/
- ttdev = dev;
+#ifdef CONFIG_DM_USB
+ struct udevice *parent;
+
+ for (ttdev = udev; ; ) {
+ struct udevice *dev = ttdev->dev;
+
+ if (dev->parent &&
+ device_get_uclass_id(dev->parent) == UCLASS_USB_HUB)
+ parent = dev->parent;
+ else
+ parent = NULL;
+ if (!parent)
+ return;
+ ttdev = dev_get_parentdata(parent);
+ if (!ttdev->speed != USB_SPEED_HIGH)
+ break;
+ }
+ parent_devnum = ttdev->devnum;
+#else
+ ttdev = udev;
while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH)
ttdev = ttdev->parent;
if (!ttdev->parent)
return;
+ parent_devnum = ttdev->parent->devnum;
+#endif
qh->qh_endpt2 |= cpu_to_hc32(QH_ENDPT2_PORTNUM(ttdev->portnr) |
- QH_ENDPT2_HUBADDR(ttdev->parent->devnum));
+ QH_ENDPT2_HUBADDR(parent_devnum));
}
static int
@@ -303,7 +356,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
uint32_t cmd;
int timeout;
int ret = 0;
- struct ehci_ctrl *ctrl = dev->controller;
+ struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
buffer, length, req);
@@ -649,20 +702,8 @@ fail:
return -1;
}
-__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port)
-{
- if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
- /* Printing the message would cause a scan failure! */
- debug("The request port(%u) is not configured\n", port);
- return NULL;
- }
-
- return (uint32_t *)&hcor->or_portsc[port];
-}
-
-int
-ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
- int length, struct devrequest *req)
+static int ehci_submit_root(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int length, struct devrequest *req)
{
uint8_t tmpbuf[4];
u16 typeReq;
@@ -671,7 +712,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
uint32_t reg;
uint32_t *status_reg;
int port = le16_to_cpu(req->index) & 0xff;
- struct ehci_ctrl *ctrl = dev->controller;
+ struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
srclen = 0;
@@ -686,7 +727,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8):
case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
- status_reg = ehci_get_portsc_register(ctrl->hcor, port - 1);
+ status_reg = ctrl->ops.get_portsc_register(ctrl, port - 1);
if (!status_reg)
return -1;
break;
@@ -781,7 +822,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
if (ehci_is_TDI()) {
- switch (ehci_get_port_speed(ctrl->hcor, reg)) {
+ switch (ctrl->ops.get_port_speed(ctrl, reg)) {
case PORTSC_PSPD_FS:
break;
case PORTSC_PSPD_LS:
@@ -843,7 +884,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
* usb 2.0 specification say 50 ms resets on
* root
*/
- ehci_powerup_fixup(status_reg, &reg);
+ ctrl->ops.powerup_fixup(ctrl, status_reg, &reg);
ehci_writel(status_reg, reg & ~EHCI_PS_PR);
/*
@@ -930,41 +971,59 @@ unknown:
return -1;
}
-int usb_lowlevel_stop(int index)
+const struct ehci_ops default_ehci_ops = {
+ .set_usb_mode = ehci_set_usbmode,
+ .get_port_speed = ehci_get_port_speed,
+ .powerup_fixup = ehci_powerup_fixup,
+ .get_portsc_register = ehci_get_portsc_register,
+};
+
+static void ehci_setup_ops(struct ehci_ctrl *ctrl, const struct ehci_ops *ops)
{
- ehci_shutdown(&ehcic[index]);
- return ehci_hcd_stop(index);
+ if (!ops) {
+ ctrl->ops = default_ehci_ops;
+ } else {
+ ctrl->ops = *ops;
+ if (!ctrl->ops.set_usb_mode)
+ ctrl->ops.set_usb_mode = ehci_set_usbmode;
+ if (!ctrl->ops.get_port_speed)
+ ctrl->ops.get_port_speed = ehci_get_port_speed;
+ if (!ctrl->ops.powerup_fixup)
+ ctrl->ops.powerup_fixup = ehci_powerup_fixup;
+ if (!ctrl->ops.get_portsc_register)
+ ctrl->ops.get_portsc_register =
+ ehci_get_portsc_register;
+ }
}
-int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
+#ifndef CONFIG_DM_USB
+void ehci_set_controller_priv(int index, void *priv, const struct ehci_ops *ops)
+{
+ struct ehci_ctrl *ctrl = &ehcic[index];
+
+ ctrl->priv = priv;
+ ehci_setup_ops(ctrl, ops);
+}
+
+void *ehci_get_controller_priv(int index)
+{
+ return ehcic[index].priv;
+}
+#endif
+
+static int ehci_common_init(struct ehci_ctrl *ctrl, uint tweaks)
{
- uint32_t reg;
- uint32_t cmd;
struct QH *qh_list;
struct QH *periodic;
+ uint32_t reg;
+ uint32_t cmd;
int i;
- int rc;
-
- rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor);
- if (rc)
- return rc;
- if (init == USB_INIT_DEVICE)
- goto done;
- /* EHCI spec section 4.1 */
- if (ehci_reset(index))
- return -1;
-
-#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
- rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor);
- if (rc)
- return rc;
-#endif
/* Set the high address word (aka segment) for 64-bit controller */
- if (ehci_readl(&ehcic[index].hccr->cr_hccparams) & 1)
- ehci_writel(&ehcic[index].hcor->or_ctrldssegment, 0);
+ if (ehci_readl(&ctrl->hccr->cr_hccparams) & 1)
+ ehci_writel(&ctrl->hcor->or_ctrldssegment, 0);
- qh_list = &ehcic[index].qh_list;
+ qh_list = &ctrl->qh_list;
/* Set head of reclaim list */
memset(qh_list, 0, sizeof(*qh_list));
@@ -980,14 +1039,14 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
ALIGN_END_ADDR(struct QH, qh_list, 1));
/* Set async. queue head pointer. */
- ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (unsigned long)qh_list);
+ ehci_writel(&ctrl->hcor->or_asynclistaddr, (unsigned long)qh_list);
/*
* Set up periodic list
* Step 1: Parent QH for all periodic transfers.
*/
- ehcic[index].periodic_schedules = 0;
- periodic = &ehcic[index].periodic_queue;
+ ctrl->periodic_schedules = 0;
+ periodic = &ctrl->periodic_queue;
memset(periodic, 0, sizeof(*periodic));
periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE);
periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
@@ -1005,25 +1064,25 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
* Split Transactions will be spread across microframes using
* S-mask and C-mask.
*/
- if (ehcic[index].periodic_list == NULL)
- ehcic[index].periodic_list = memalign(4096, 1024 * 4);
+ if (ctrl->periodic_list == NULL)
+ ctrl->periodic_list = memalign(4096, 1024 * 4);
- if (!ehcic[index].periodic_list)
+ if (!ctrl->periodic_list)
return -ENOMEM;
for (i = 0; i < 1024; i++) {
- ehcic[index].periodic_list[i] = cpu_to_hc32((unsigned long)periodic
+ ctrl->periodic_list[i] = cpu_to_hc32((unsigned long)periodic
| QH_LINK_TYPE_QH);
}
- flush_dcache_range((unsigned long)ehcic[index].periodic_list,
- ALIGN_END_ADDR(uint32_t, ehcic[index].periodic_list,
+ flush_dcache_range((unsigned long)ctrl->periodic_list,
+ ALIGN_END_ADDR(uint32_t, ctrl->periodic_list,
1024));
/* Set periodic list base address */
- ehci_writel(&ehcic[index].hcor->or_periodiclistbase,
- (unsigned long)ehcic[index].periodic_list);
+ ehci_writel(&ctrl->hcor->or_periodiclistbase,
+ (unsigned long)ctrl->periodic_list);
- reg = ehci_readl(&ehcic[index].hccr->cr_hcsparams);
+ reg = ehci_readl(&ctrl->hccr->cr_hcsparams);
descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
debug("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);
/* Port Indicators */
@@ -1036,37 +1095,81 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
| 0x01, &descriptor.hub.wHubCharacteristics);
/* Start the host controller. */
- cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd);
+ cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
/*
* Philips, Intel, and maybe others need CMD_RUN before the
* root hub will detect new devices (why?); NEC doesn't
*/
cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
cmd |= CMD_RUN;
- ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd);
+ ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
-#ifndef CONFIG_USB_EHCI_FARADAY
- /* take control over the ports */
- cmd = ehci_readl(&ehcic[index].hcor->or_configflag);
- cmd |= FLAG_CF;
- ehci_writel(&ehcic[index].hcor->or_configflag, cmd);
-#endif
+ if (!(tweaks & EHCI_TWEAK_NO_INIT_CF)) {
+ /* take control over the ports */
+ cmd = ehci_readl(&ctrl->hcor->or_configflag);
+ cmd |= FLAG_CF;
+ ehci_writel(&ctrl->hcor->or_configflag, cmd);
+ }
/* unblock posted write */
- cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd);
+ cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
mdelay(5);
- reg = HC_VERSION(ehci_readl(&ehcic[index].hccr->cr_capbase));
+ reg = HC_VERSION(ehci_readl(&ctrl->hccr->cr_capbase));
printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
- ehcic[index].rootdev = 0;
+ return 0;
+}
+
+#ifndef CONFIG_DM_USB
+int usb_lowlevel_stop(int index)
+{
+ ehci_shutdown(&ehcic[index]);
+ return ehci_hcd_stop(index);
+}
+
+int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
+{
+ struct ehci_ctrl *ctrl = &ehcic[index];
+ uint tweaks = 0;
+ int rc;
+
+ /**
+ * Set ops to default_ehci_ops, ehci_hcd_init should call
+ * ehci_set_controller_priv to change any of these function pointers.
+ */
+ ctrl->ops = default_ehci_ops;
+
+ rc = ehci_hcd_init(index, init, &ctrl->hccr, &ctrl->hcor);
+ if (rc)
+ return rc;
+ if (init == USB_INIT_DEVICE)
+ goto done;
+
+ /* EHCI spec section 4.1 */
+ if (ehci_reset(ctrl))
+ return -1;
+
+#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
+ rc = ehci_hcd_init(index, init, &ctrl->hccr, &ctrl->hcor);
+ if (rc)
+ return rc;
+#endif
+#ifdef CONFIG_USB_EHCI_FARADAY
+ tweaks |= EHCI_TWEAK_NO_INIT_CF;
+#endif
+ rc = ehci_common_init(ctrl, tweaks);
+ if (rc)
+ return rc;
+
+ ctrl->rootdev = 0;
done:
*controller = &ehcic[index];
return 0;
}
+#endif
-int
-submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
- int length)
+static int _ehci_submit_bulk_msg(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int length)
{
if (usb_pipetype(pipe) != PIPE_BULK) {
@@ -1076,11 +1179,11 @@ submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
return ehci_submit_async(dev, pipe, buffer, length, NULL);
}
-int
-submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
- int length, struct devrequest *setup)
+static int _ehci_submit_control_msg(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int length,
+ struct devrequest *setup)
{
- struct ehci_ctrl *ctrl = dev->controller;
+ struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
if (usb_pipetype(pipe) != PIPE_CONTROL) {
debug("non-control pipe (type=%lu)", usb_pipetype(pipe));
@@ -1150,7 +1253,7 @@ struct int_queue *
create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
int elementsize, void *buffer, int interval)
{
- struct ehci_ctrl *ctrl = dev->controller;
+ struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
struct int_queue *result = NULL;
int i;
@@ -1342,7 +1445,7 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
int
destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
{
- struct ehci_ctrl *ctrl = dev->controller;
+ struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
int result = -1;
unsigned long timeout;
@@ -1386,9 +1489,8 @@ out:
return result;
}
-int
-submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
- int length, int interval)
+static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int length, int interval)
{
void *backbuffer;
struct int_queue *queue;
@@ -1423,3 +1525,98 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
/* everything worked out fine */
return result;
}
+
+#ifndef CONFIG_DM_USB
+int submit_bulk_msg(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int length)
+{
+ return _ehci_submit_bulk_msg(dev, pipe, buffer, length);
+}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int length, struct devrequest *setup)
+{
+ return _ehci_submit_control_msg(dev, pipe, buffer, length, setup);
+}
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int length, int interval)
+{
+ return _ehci_submit_int_msg(dev, pipe, buffer, length, interval);
+}
+#endif
+
+#ifdef CONFIG_DM_USB
+static int ehci_submit_control_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ struct devrequest *setup)
+{
+ debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__,
+ dev->name, udev, udev->dev->name, udev->portnr);
+
+ return _ehci_submit_control_msg(udev, pipe, buffer, length, setup);
+}
+
+static int ehci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _ehci_submit_bulk_msg(udev, pipe, buffer, length);
+}
+
+static int ehci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ int interval)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _ehci_submit_int_msg(udev, pipe, buffer, length, interval);
+}
+
+int ehci_register(struct udevice *dev, struct ehci_hccr *hccr,
+ struct ehci_hcor *hcor, const struct ehci_ops *ops,
+ uint tweaks, enum usb_init_type init)
+{
+ struct ehci_ctrl *ctrl = dev_get_priv(dev);
+ int ret;
+
+ debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p, init=%d\n", __func__,
+ dev->name, ctrl, hccr, hcor, init);
+
+ ehci_setup_ops(ctrl, ops);
+ ctrl->hccr = hccr;
+ ctrl->hcor = hcor;
+ ctrl->priv = ctrl;
+
+ if (init == USB_INIT_DEVICE)
+ goto done;
+ ret = ehci_reset(ctrl);
+ if (ret)
+ goto err;
+
+ ret = ehci_common_init(ctrl, tweaks);
+ if (ret)
+ goto err;
+done:
+ return 0;
+err:
+ free(ctrl);
+ debug("%s: failed, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+int ehci_deregister(struct udevice *dev)
+{
+ struct ehci_ctrl *ctrl = dev_get_priv(dev);
+
+ ehci_shutdown(ctrl);
+
+ return 0;
+}
+
+struct dm_usb_ops ehci_usb_ops = {
+ .control = ehci_submit_control_msg,
+ .bulk = ehci_submit_bulk_msg,
+ .interrupt = ehci_submit_int_msg,
+};
+
+#endif
diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c
index 7566c61..d319962 100644
--- a/drivers/usb/host/ehci-mx5.c
+++ b/drivers/usb/host/ehci-mx5.c
@@ -218,11 +218,23 @@ void __weak board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
{
}
+__weak void mx5_ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg,
+ uint32_t *reg)
+{
+ mdelay(50);
+}
+
+static const struct ehci_ops mx5_ehci_ops = {
+ .powerup_fixup = mx5_ehci_powerup_fixup,
+};
+
int ehci_hcd_init(int index, enum usb_init_type init,
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
{
struct usb_ehci *ehci;
+ /* The only user for this is efikamx-usb */
+ ehci_set_controller_priv(index, NULL, &mx5_ehci_ops);
set_usboh3_clk();
enable_usboh3_clk(true);
set_usb_phy_clk();
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index c6bfbe3..27705d6 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -7,6 +7,7 @@
*/
#include <common.h>
+#include <dm.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm-generic/gpio.h>
@@ -20,6 +21,8 @@
#include "ehci.h"
+DECLARE_GLOBAL_DATA_PTR;
+
#define USB1_ADDR_MASK 0xFFFF0000
#define HOSTPC1_DEVLC 0x84
@@ -32,9 +35,11 @@
#endif
#endif
+#ifndef CONFIG_DM_USB
enum {
USB_PORTS_MAX = 3, /* Maximum ports we allow */
};
+#endif
/* Parameters we need for USB */
enum {
@@ -61,14 +66,26 @@ enum dr_mode {
DR_MODE_OTG, /* supports both */
};
+enum usb_ctlr_type {
+ USB_CTLR_T20,
+ USB_CTLR_T30,
+ USB_CTLR_T114,
+
+ USB_CTRL_COUNT,
+};
+
/* Information about a USB port */
struct fdt_usb {
+ struct ehci_ctrl ehci;
struct usb_ctlr *reg; /* address of registers in physical memory */
unsigned utmi:1; /* 1 if port has external tranceiver, else 0 */
unsigned ulpi:1; /* 1 if port has external ULPI transceiver */
unsigned enabled:1; /* 1 to enable, 0 to disable */
unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */
+#ifndef CONFIG_DM_USB
unsigned initialized:1; /* has this port already been initialized? */
+#endif
+ enum usb_ctlr_type type;
enum usb_init_type init_type;
enum dr_mode dr_mode; /* dual role mode */
enum periph_id periph_id;/* peripheral id */
@@ -76,10 +93,10 @@ struct fdt_usb {
struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */
};
+#ifndef CONFIG_DM_USB
static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */
static unsigned port_count; /* Number of available ports */
-/* Port that needs to clear CSC after Port Reset */
-static u32 port_addr_clear_csc;
+#endif
/*
* This table has USB timing parameters for each Oscillator frequency we
@@ -156,13 +173,14 @@ static const u8 utmip_elastic_limit = 16;
static const u8 utmip_hs_sync_start_delay = 9;
struct fdt_usb_controller {
+ /* TODO(sjg@chromium.org): Remove when we only use driver model */
int compat;
/* flag to determine whether controller supports hostpc register */
u32 has_hostpc:1;
const unsigned *pll_parameter;
};
-static struct fdt_usb_controller fdt_usb_controllers[] = {
+static struct fdt_usb_controller fdt_usb_controllers[USB_CTRL_COUNT] = {
{
.compat = COMPAT_NVIDIA_TEGRA20_USB,
.has_hostpc = 0,
@@ -180,40 +198,36 @@ static struct fdt_usb_controller fdt_usb_controllers[] = {
},
};
-static struct fdt_usb_controller *controller;
-
/*
* A known hardware issue where Connect Status Change bit of PORTSC register
* of USB1 controller will be set after Port Reset.
* We have to clear it in order for later device enumeration to proceed.
- * This ehci_powerup_fixup overrides the weak function ehci_powerup_fixup
- * in "ehci-hcd.c".
*/
-void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
+static void tegra_ehci_powerup_fixup(struct ehci_ctrl *ctrl,
+ uint32_t *status_reg, uint32_t *reg)
{
+ struct fdt_usb *config = ctrl->priv;
+ struct fdt_usb_controller *controller;
+
+ controller = &fdt_usb_controllers[config->type];
mdelay(50);
/* This is to avoid PORT_ENABLE bit to be cleared in "ehci-hcd.c". */
if (controller->has_hostpc)
*reg |= EHCI_PS_PE;
- if (((unsigned long)status_reg & TEGRA_USB_ADDR_MASK) != port_addr_clear_csc)
+ if (!config->has_legacy_mode)
return;
/* For EHCI_PS_CSC to be cleared in ehci_hcd.c */
if (ehci_readl(status_reg) & EHCI_PS_CSC)
*reg |= EHCI_PS_CSC;
}
-/*
- * This ehci_set_usbmode overrides the weak function ehci_set_usbmode
- * in "ehci-hcd.c".
- */
-void ehci_set_usbmode(int index)
+static void tegra_ehci_set_usbmode(struct ehci_ctrl *ctrl)
{
- struct fdt_usb *config;
+ struct fdt_usb *config = ctrl->priv;
struct usb_ctlr *usbctlr;
uint32_t tmp;
- config = &port[index];
usbctlr = config->reg;
tmp = ehci_readl(&usbctlr->usb_mode);
@@ -221,17 +235,17 @@ void ehci_set_usbmode(int index)
ehci_writel(&usbctlr->usb_mode, tmp);
}
-/*
- * This ehci_get_port_speed overrides the weak function ehci_get_port_speed
- * in "ehci-hcd.c".
- */
-int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
+static int tegra_ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg)
{
+ struct fdt_usb *config = ctrl->priv;
+ struct fdt_usb_controller *controller;
uint32_t tmp;
uint32_t *reg_ptr;
+ controller = &fdt_usb_controllers[config->type];
if (controller->has_hostpc) {
- reg_ptr = (uint32_t *)((u8 *)&hcor->or_usbcmd + HOSTPC1_DEVLC);
+ reg_ptr = (uint32_t *)((u8 *)&ctrl->hcor->or_usbcmd +
+ HOSTPC1_DEVLC);
tmp = ehci_readl(reg_ptr);
return HOSTPC1_PSPD(tmp);
} else
@@ -263,7 +277,8 @@ static void set_up_vbus(struct fdt_usb *config, enum usb_init_type init)
}
}
-void usbf_reset_controller(struct fdt_usb *config, struct usb_ctlr *usbctlr)
+static void usbf_reset_controller(struct fdt_usb *config,
+ struct usb_ctlr *usbctlr)
{
/* Reset the USB controller with 2us delay */
reset_periph(config->periph_id, 2);
@@ -283,7 +298,7 @@ void usbf_reset_controller(struct fdt_usb *config, struct usb_ctlr *usbctlr)
setbits_le32(&usbctlr->susp_ctrl, UTMIP_PHY_ENB);
}
-static const unsigned *get_pll_timing(void)
+static const unsigned *get_pll_timing(struct fdt_usb_controller *controller)
{
const unsigned *timing;
@@ -330,6 +345,7 @@ static void init_phy_mux(struct fdt_usb *config, uint pts,
static int init_utmi_usb_controller(struct fdt_usb *config,
enum usb_init_type init)
{
+ struct fdt_usb_controller *controller;
u32 b_sess_valid_mask, val;
int loop_count;
const unsigned *timing;
@@ -362,11 +378,14 @@ static int init_utmi_usb_controller(struct fdt_usb *config,
VBUS_SENSE_CTL_MASK,
VBUS_SENSE_CTL_A_SESS_VLD << VBUS_SENSE_CTL_SHIFT);
+ controller = &fdt_usb_controllers[config->type];
+ debug("controller=%p, type=%d\n", controller, config->type);
+
/*
* PLL Delay CONFIGURATION settings. The following parameters control
* the bring up of the plls.
*/
- timing = get_pll_timing();
+ timing = get_pll_timing(controller);
if (!controller->has_hostpc) {
val = readl(&usbctlr->utmip_misc_cfg1);
@@ -517,7 +536,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config,
udelay(1);
}
if (!loop_count)
- return -1;
+ return -ETIMEDOUT;
/* Disable ICUSB FS/LS transceiver */
clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
@@ -560,6 +579,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config,
int loop_count;
struct ulpi_viewport ulpi_vp;
struct usb_ctlr *usbctlr = config->reg;
+ int ret;
/* set up ULPI reference clock on pllp_out4 */
clock_enable(PERIPH_ID_DEV2_OUT);
@@ -605,9 +625,10 @@ static int init_ulpi_usb_controller(struct fdt_usb *config,
ulpi_vp.port_num = 0;
ulpi_vp.viewport_addr = (u32)&usbctlr->ulpi_viewport;
- if (ulpi_init(&ulpi_vp)) {
+ ret = ulpi_init(&ulpi_vp);
+ if (ret) {
printf("Tegra ULPI viewport init failed\n");
- return -1;
+ return ret;
}
ulpi_set_vbus(&ulpi_vp, 1, 1);
@@ -624,7 +645,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config,
udelay(1);
}
if (!loop_count)
- return -1;
+ return -ETIMEDOUT;
clrbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR);
return 0;
@@ -635,7 +656,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config,
{
printf("No code to set up ULPI controller, please enable"
"CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT");
- return -1;
+ return -ENOSYS;
}
#endif
@@ -662,7 +683,7 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config)
else {
debug("%s: Cannot decode dr_mode '%s'\n", __func__,
mode);
- return -FDT_ERR_NOTFOUND;
+ return -EINVAL;
}
} else {
config->dr_mode = DR_MODE_HOST;
@@ -674,12 +695,10 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config)
config->enabled = fdtdec_get_is_enabled(blob, node);
config->has_legacy_mode = fdtdec_get_bool(blob, node,
"nvidia,has-legacy-mode");
- if (config->has_legacy_mode)
- port_addr_clear_csc = (unsigned long)config->reg;
config->periph_id = clock_decode_periph_id(blob, node);
if (config->periph_id == PERIPH_ID_NONE) {
debug("%s: Missing/invalid peripheral ID\n", __func__);
- return -FDT_ERR_NOTFOUND;
+ return -EINVAL;
}
gpio_request_by_name_nodev(blob, node, "nvidia,vbus-gpio", 0,
&config->vbus_gpio, GPIOD_IS_OUT);
@@ -695,16 +714,101 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config)
return 0;
}
+int usb_common_init(struct fdt_usb *config, enum usb_init_type init)
+{
+ int ret = 0;
+
+ switch (init) {
+ case USB_INIT_HOST:
+ switch (config->dr_mode) {
+ case DR_MODE_HOST:
+ case DR_MODE_OTG:
+ break;
+ default:
+ printf("tegrausb: Invalid dr_mode %d for host mode\n",
+ config->dr_mode);
+ return -1;
+ }
+ break;
+ case USB_INIT_DEVICE:
+ if (config->periph_id != PERIPH_ID_USBD) {
+ printf("tegrausb: Device mode only supported on first USB controller\n");
+ return -1;
+ }
+ if (!config->utmi) {
+ printf("tegrausb: Device mode only supported with UTMI PHY\n");
+ return -1;
+ }
+ switch (config->dr_mode) {
+ case DR_MODE_DEVICE:
+ case DR_MODE_OTG:
+ break;
+ default:
+ printf("tegrausb: Invalid dr_mode %d for device mode\n",
+ config->dr_mode);
+ return -1;
+ }
+ break;
+ default:
+ printf("tegrausb: Unknown USB_INIT_* %d\n", init);
+ return -1;
+ }
+
+#ifndef CONFIG_DM_USB
+ /* skip init, if the port is already initialized */
+ if (config->initialized && config->init_type == init)
+ return 0;
+#endif
+
+ debug("%d, %d\n", config->utmi, config->ulpi);
+ if (config->utmi)
+ ret = init_utmi_usb_controller(config, init);
+ else if (config->ulpi)
+ ret = init_ulpi_usb_controller(config, init);
+ if (ret)
+ return ret;
+
+ set_up_vbus(config, init);
+
+ config->init_type = init;
+
+ return 0;
+}
+
+void usb_common_uninit(struct fdt_usb *priv)
+{
+ struct usb_ctlr *usbctlr;
+
+ usbctlr = priv->reg;
+
+ /* Stop controller */
+ writel(0, &usbctlr->usb_cmd);
+ udelay(1000);
+
+ /* Initiate controller reset */
+ writel(2, &usbctlr->usb_cmd);
+ udelay(1000);
+}
+
+static const struct ehci_ops tegra_ehci_ops = {
+ .set_usb_mode = tegra_ehci_set_usbmode,
+ .get_port_speed = tegra_ehci_get_port_speed,
+ .powerup_fixup = tegra_ehci_powerup_fixup,
+};
+
+#ifndef CONFIG_DM_USB
/*
* process_usb_nodes() - Process a list of USB nodes, adding them to our list
* of USB ports.
* @blob: fdt blob
* @node_list: list of nodes to process (any <=0 are ignored)
* @count: number of nodes to process
+ * @id: controller type (enum usb_ctlr_type)
*
* Return: 0 - ok, -1 - error
*/
-static int process_usb_nodes(const void *blob, int node_list[], int count)
+static int process_usb_nodes(const void *blob, int node_list[], int count,
+ enum usb_ctlr_type id)
{
struct fdt_usb config;
int node, i;
@@ -728,9 +832,11 @@ static int process_usb_nodes(const void *blob, int node_list[], int count)
return -1;
}
if (!clk_done) {
- config_clock(get_pll_timing());
+ config_clock(get_pll_timing(
+ &fdt_usb_controllers[id]));
clk_done = 1;
}
+ config.type = id;
config.initialized = 0;
/* add new USB port to the list of available ports */
@@ -747,20 +853,17 @@ int usb_process_devicetree(const void *blob)
int i;
for (i = 0; i < ARRAY_SIZE(fdt_usb_controllers); i++) {
- controller = &fdt_usb_controllers[i];
-
count = fdtdec_find_aliases_for_id(blob, "usb",
- controller->compat, node_list, USB_PORTS_MAX);
+ fdt_usb_controllers[i].compat, node_list,
+ USB_PORTS_MAX);
if (count) {
- err = process_usb_nodes(blob, node_list, count);
+ err = process_usb_nodes(blob, node_list, count, i);
if (err)
printf("%s: Error processing USB node!\n",
__func__);
return err;
}
}
- if (i == ARRAY_SIZE(fdt_usb_controllers))
- controller = NULL;
return err;
}
@@ -780,68 +883,22 @@ int ehci_hcd_init(int index, enum usb_init_type init,
{
struct fdt_usb *config;
struct usb_ctlr *usbctlr;
+ int ret;
if (index >= port_count)
return -1;
config = &port[index];
+ ehci_set_controller_priv(index, config, &tegra_ehci_ops);
- switch (init) {
- case USB_INIT_HOST:
- switch (config->dr_mode) {
- case DR_MODE_HOST:
- case DR_MODE_OTG:
- break;
- default:
- printf("tegrausb: Invalid dr_mode %d for host mode\n",
- config->dr_mode);
- return -1;
- }
- break;
- case USB_INIT_DEVICE:
- if (config->periph_id != PERIPH_ID_USBD) {
- printf("tegrausb: Device mode only supported on first USB controller\n");
- return -1;
- }
- if (!config->utmi) {
- printf("tegrausb: Device mode only supported with UTMI PHY\n");
- return -1;
- }
- switch (config->dr_mode) {
- case DR_MODE_DEVICE:
- case DR_MODE_OTG:
- break;
- default:
- printf("tegrausb: Invalid dr_mode %d for device mode\n",
- config->dr_mode);
- return -1;
- }
- break;
- default:
- printf("tegrausb: Unknown USB_INIT_* %d\n", init);
- return -1;
- }
-
- /* skip init, if the port is already initialized */
- if (config->initialized && config->init_type == init)
- goto success;
-
- if (config->utmi && init_utmi_usb_controller(config, init)) {
- printf("tegrausb: Cannot init port %d\n", index);
- return -1;
- }
-
- if (config->ulpi && init_ulpi_usb_controller(config, init)) {
+ ret = usb_common_init(config, init);
+ if (ret) {
printf("tegrausb: Cannot init port %d\n", index);
- return -1;
+ return ret;
}
- set_up_vbus(config, init);
-
config->initialized = 1;
- config->init_type = init;
-success:
usbctlr = config->reg;
*hccr = (struct ehci_hccr *)&usbctlr->cap_length;
*hcor = (struct ehci_hcor *)&usbctlr->usb_cmd;
@@ -854,19 +911,80 @@ success:
*/
int ehci_hcd_stop(int index)
{
- struct usb_ctlr *usbctlr;
+ usb_common_uninit(&port[index]);
- usbctlr = port[index].reg;
+ port[index].initialized = 0;
- /* Stop controller */
- writel(0, &usbctlr->usb_cmd);
- udelay(1000);
+ return 0;
+}
+#endif /* !CONFIG_DM_USB */
- /* Initiate controller reset */
- writel(2, &usbctlr->usb_cmd);
- udelay(1000);
+#ifdef CONFIG_DM_USB
+static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
+{
+ struct fdt_usb *priv = dev_get_priv(dev);
+ int ret;
- port[index].initialized = 0;
+ ret = fdt_decode_usb(gd->fdt_blob, dev->of_offset, priv);
+ if (ret)
+ return ret;
+
+ priv->type = dev_get_driver_data(dev);
+
+ return 0;
+}
+
+static int ehci_usb_probe(struct udevice *dev)
+{
+ struct usb_platdata *plat = dev_get_platdata(dev);
+ struct fdt_usb *priv = dev_get_priv(dev);
+ struct ehci_hccr *hccr;
+ struct ehci_hcor *hcor;
+ static bool clk_done;
+ int ret;
+
+ ret = usb_common_init(priv, plat->init_type);
+ if (ret)
+ return ret;
+ hccr = (struct ehci_hccr *)&priv->reg->cap_length;
+ hcor = (struct ehci_hcor *)&priv->reg->usb_cmd;
+ if (!clk_done) {
+ config_clock(get_pll_timing(&fdt_usb_controllers[priv->type]));
+ clk_done = true;
+ }
+
+ return ehci_register(dev, hccr, hcor, &tegra_ehci_ops, 0,
+ plat->init_type);
+}
+
+static int ehci_usb_remove(struct udevice *dev)
+{
+ int ret;
+
+ ret = ehci_deregister(dev);
+ if (ret)
+ return ret;
return 0;
}
+
+static const struct udevice_id ehci_usb_ids[] = {
+ { .compatible = "nvidia,tegra20-ehci", .data = USB_CTLR_T20 },
+ { .compatible = "nvidia,tegra30-ehci", .data = USB_CTLR_T30 },
+ { .compatible = "nvidia,tegra114-ehci", .data = USB_CTLR_T114 },
+ { }
+};
+
+U_BOOT_DRIVER(usb_ehci) = {
+ .name = "ehci_tegra",
+ .id = UCLASS_USB,
+ .of_match = ehci_usb_ids,
+ .ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
+ .probe = ehci_usb_probe,
+ .remove = ehci_usb_remove,
+ .ops = &ehci_usb_ops,
+ .platdata_auto_alloc_size = sizeof(struct usb_platdata),
+ .priv_auto_alloc_size = sizeof(struct fdt_usb),
+ .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
+#endif
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 79aecd4..774282d 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -238,6 +238,22 @@ struct QH {
};
};
+/* Tweak flags for EHCI, used to control operation */
+enum {
+ /* don't use or_configflag in init */
+ EHCI_TWEAK_NO_INIT_CF = 1 << 0,
+};
+
+struct ehci_ctrl;
+
+struct ehci_ops {
+ void (*set_usb_mode)(struct ehci_ctrl *ctrl);
+ int (*get_port_speed)(struct ehci_ctrl *ctrl, uint32_t reg);
+ void (*powerup_fixup)(struct ehci_ctrl *ctrl, uint32_t *status_reg,
+ uint32_t *reg);
+ uint32_t *(*get_portsc_register)(struct ehci_ctrl *ctrl, int port);
+};
+
struct ehci_ctrl {
struct ehci_hccr *hccr; /* R/O registers, not need for volatile */
struct ehci_hcor *hcor;
@@ -248,11 +264,42 @@ struct ehci_ctrl {
uint32_t *periodic_list;
int periodic_schedules;
int ntds;
+ struct ehci_ops ops;
+ void *priv; /* client's private data */
};
+/**
+ * ehci_set_controller_info() - Set up private data for the controller
+ *
+ * This function can be called in ehci_hcd_init() to tell the EHCI layer
+ * about the controller's private data pointer. Then in the above functions
+ * this can be accessed given the struct ehci_ctrl pointer. Also special
+ * EHCI operation methods can be provided if required
+ *
+ * @index: Controller number to set
+ * @priv: Controller pointer
+ * @ops: Controller operations, or NULL to use default
+ */
+void ehci_set_controller_priv(int index, void *priv,
+ const struct ehci_ops *ops);
+
+/**
+ * ehci_get_controller_priv() - Get controller private data
+ *
+ * @index Controller number to get
+ * @return controller pointer for this index
+ */
+void *ehci_get_controller_priv(int index);
+
/* Low level init functions */
int ehci_hcd_init(int index, enum usb_init_type init,
struct ehci_hccr **hccr, struct ehci_hcor **hcor);
int ehci_hcd_stop(int index);
+int ehci_register(struct udevice *dev, struct ehci_hccr *hccr,
+ struct ehci_hcor *hcor, const struct ehci_ops *ops,
+ uint tweaks, enum usb_init_type init);
+int ehci_deregister(struct udevice *dev);
+extern struct dm_usb_ops ehci_usb_ops;
+
#endif /* USB_EHCI_H */
diff --git a/drivers/usb/host/usb-sandbox.c b/drivers/usb/host/usb-sandbox.c
new file mode 100644
index 0000000..c5f9822
--- /dev/null
+++ b/drivers/usb/host/usb-sandbox.c
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <usb.h>
+#include <dm/root.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void usbmon_trace(struct udevice *bus, ulong pipe,
+ struct devrequest *setup, struct udevice *emul)
+{
+ static const char types[] = "ZICB";
+ int type;
+
+ type = (pipe & USB_PIPE_TYPE_MASK) >> USB_PIPE_TYPE_SHIFT;
+ debug("0 0 S %c%c:%d:%03ld:%ld", types[type],
+ pipe & USB_DIR_IN ? 'i' : 'o',
+ bus->seq,
+ (pipe & USB_PIPE_DEV_MASK) >> USB_PIPE_DEV_SHIFT,
+ (pipe & USB_PIPE_EP_MASK) >> USB_PIPE_EP_SHIFT);
+ if (setup) {
+ debug(" s %02x %02x %04x %04x %04x", setup->requesttype,
+ setup->request, setup->value, setup->index,
+ setup->length);
+ }
+ debug(" %s", emul ? emul->name : "(no emul found)");
+
+ debug("\n");
+}
+
+static int sandbox_submit_control(struct udevice *bus,
+ struct usb_device *udev,
+ unsigned long pipe,
+ void *buffer, int length,
+ struct devrequest *setup)
+{
+ struct udevice *emul;
+ int ret;
+
+ /* Just use child of dev as emulator? */
+ debug("%s: bus=%s\n", __func__, bus->name);
+ ret = usb_emul_find(bus, pipe, &emul);
+ usbmon_trace(bus, pipe, setup, emul);
+ if (ret)
+ return ret;
+ ret = usb_emul_control(emul, udev, pipe, buffer, length, setup);
+ if (ret < 0) {
+ debug("ret=%d\n", ret);
+ udev->status = ret;
+ udev->act_len = 0;
+ } else {
+ udev->status = 0;
+ udev->act_len = ret;
+ }
+
+ return ret;
+}
+
+static int sandbox_submit_bulk(struct udevice *bus, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length)
+{
+ struct udevice *emul;
+ int ret;
+
+ /* Just use child of dev as emulator? */
+ debug("%s: bus=%s\n", __func__, bus->name);
+ ret = usb_emul_find(bus, pipe, &emul);
+ usbmon_trace(bus, pipe, NULL, emul);
+ if (ret)
+ return ret;
+ ret = usb_emul_bulk(emul, udev, pipe, buffer, length);
+ if (ret < 0) {
+ debug("ret=%d\n", ret);
+ udev->status = ret;
+ udev->act_len = 0;
+ } else {
+ udev->status = 0;
+ udev->act_len = ret;
+ }
+
+ return ret;
+}
+
+static int sandbox_alloc_device(struct udevice *dev, struct usb_device *udev)
+{
+ return 0;
+}
+
+static int sandbox_usb_probe(struct udevice *dev)
+{
+ return 0;
+}
+
+static const struct dm_usb_ops sandbox_usb_ops = {
+ .control = sandbox_submit_control,
+ .bulk = sandbox_submit_bulk,
+ .alloc_device = sandbox_alloc_device,
+};
+
+static const struct udevice_id sandbox_usb_ids[] = {
+ { .compatible = "sandbox,usb" },
+ { }
+};
+
+U_BOOT_DRIVER(usb_sandbox) = {
+ .name = "usb_sandbox",
+ .id = UCLASS_USB,
+ .of_match = sandbox_usb_ids,
+ .probe = sandbox_usb_probe,
+ .ops = &sandbox_usb_ops,
+};
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
new file mode 100644
index 0000000..714bc0e
--- /dev/null
+++ b/drivers/usb/host/usb-uclass.c
@@ -0,0 +1,645 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * usb_match_device() modified from Linux kernel v4.0.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <usb.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/root.h>
+#include <dm/uclass-internal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+extern bool usb_started; /* flag for the started/stopped USB status */
+static bool asynch_allowed;
+
+int usb_disable_asynch(int disable)
+{
+ int old_value = asynch_allowed;
+
+ asynch_allowed = !disable;
+ return old_value;
+}
+
+int submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
+ int length, int interval)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->interrupt)
+ return -ENOSYS;
+
+ return ops->interrupt(bus, udev, pipe, buffer, length, interval);
+}
+
+int submit_control_msg(struct usb_device *udev, unsigned long pipe,
+ void *buffer, int length, struct devrequest *setup)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->control)
+ return -ENOSYS;
+
+ return ops->control(bus, udev, pipe, buffer, length, setup);
+}
+
+int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
+ int length)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->bulk)
+ return -ENOSYS;
+
+ return ops->bulk(bus, udev, pipe, buffer, length);
+}
+
+int usb_alloc_device(struct usb_device *udev)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ /* This is only requird by some controllers - current XHCI */
+ if (!ops->alloc_device)
+ return 0;
+
+ return ops->alloc_device(bus, udev);
+}
+
+int usb_stop(void)
+{
+ struct udevice *bus;
+ struct uclass *uc;
+ int err = 0, ret;
+
+ /* De-activate any devices that have been activated */
+ ret = uclass_get(UCLASS_USB, &uc);
+ if (ret)
+ return ret;
+ uclass_foreach_dev(bus, uc) {
+ ret = device_remove(bus);
+ if (ret && !err)
+ err = ret;
+ }
+
+#ifdef CONFIG_SANDBOX
+ struct udevice *dev;
+
+ /* Reset all enulation devices */
+ ret = uclass_get(UCLASS_USB_EMUL, &uc);
+ if (ret)
+ return ret;
+
+ uclass_foreach_dev(dev, uc)
+ usb_emul_reset(dev);
+#endif
+ usb_stor_reset();
+ usb_hub_reset();
+ usb_started = 0;
+
+ return err;
+}
+
+static int usb_scan_bus(struct udevice *bus, bool recurse)
+{
+ struct usb_bus_priv *priv;
+ struct udevice *dev;
+ int ret;
+
+ priv = dev_get_uclass_priv(bus);
+
+ assert(recurse); /* TODO: Support non-recusive */
+
+ ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev);
+ if (ret)
+ return ret;
+
+ return priv->next_addr;
+}
+
+int usb_init(void)
+{
+ int controllers_initialized = 0;
+ struct udevice *bus;
+ struct uclass *uc;
+ int count = 0;
+ int ret;
+
+ asynch_allowed = 1;
+ usb_hub_reset();
+
+ ret = uclass_get(UCLASS_USB, &uc);
+ if (ret)
+ return ret;
+
+ uclass_foreach_dev(bus, uc) {
+ /* init low_level USB */
+ count++;
+ printf("USB");
+ printf("%d: ", bus->seq);
+ ret = device_probe(bus);
+ if (ret == -ENODEV) { /* No such device. */
+ puts("Port not available.\n");
+ controllers_initialized++;
+ continue;
+ }
+
+ if (ret) { /* Other error. */
+ printf("probe failed, error %d\n", ret);
+ continue;
+ }
+ /*
+ * lowlevel init is OK, now scan the bus for devices
+ * i.e. search HUBs and configure them
+ */
+ controllers_initialized++;
+ printf("scanning bus %d for devices... ", bus->seq);
+ debug("\n");
+ ret = usb_scan_bus(bus, true);
+ if (ret < 0)
+ printf("failed, error %d\n", ret);
+ else if (!ret)
+ printf("No USB Device found\n");
+ else
+ printf("%d USB Device(s) found\n", ret);
+ usb_started = true;
+ }
+
+ debug("scan end\n");
+ /* if we were not able to find at least one working bus, bail out */
+ if (!count)
+ printf("No controllers found\n");
+ else if (controllers_initialized == 0)
+ printf("USB error: all controllers failed lowlevel init\n");
+
+ return usb_started ? 0 : -1;
+}
+
+int usb_reset_root_port(void)
+{
+ return -ENOSYS;
+}
+
+static struct usb_device *find_child_devnum(struct udevice *parent, int devnum)
+{
+ struct usb_device *udev;
+ struct udevice *dev;
+
+ if (!device_active(parent))
+ return NULL;
+ udev = dev_get_parentdata(parent);
+ if (udev->devnum == devnum)
+ return udev;
+
+ for (device_find_first_child(parent, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ udev = find_child_devnum(dev, devnum);
+ if (udev)
+ return udev;
+ }
+
+ return NULL;
+}
+
+struct usb_device *usb_get_dev_index(struct udevice *bus, int index)
+{
+ struct udevice *hub;
+ int devnum = index + 1; /* Addresses are allocated from 1 on USB */
+
+ device_find_first_child(bus, &hub);
+ if (device_get_uclass_id(hub) == UCLASS_USB_HUB)
+ return find_child_devnum(hub, devnum);
+
+ return NULL;
+}
+
+int usb_post_bind(struct udevice *dev)
+{
+ /* Scan the bus for devices */
+ return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
+int usb_port_reset(struct usb_device *parent, int portnr)
+{
+ unsigned short portstatus;
+ int ret;
+
+ debug("%s: start\n", __func__);
+
+ if (parent) {
+ /* reset the port for the second time */
+ assert(portnr > 0);
+ debug("%s: reset %d\n", __func__, portnr - 1);
+ ret = legacy_hub_port_reset(parent, portnr - 1, &portstatus);
+ if (ret < 0) {
+ printf("\n Couldn't reset port %i\n", portnr);
+ return ret;
+ }
+ } else {
+ debug("%s: reset root\n", __func__);
+ usb_reset_root_port();
+ }
+
+ return 0;
+}
+
+int usb_legacy_port_reset(struct usb_device *parent, int portnr)
+{
+ return usb_port_reset(parent, portnr);
+}
+
+int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp)
+{
+ struct usb_platdata *plat;
+ struct udevice *dev;
+ int ret;
+
+ /* Find the old device and remove it */
+ ret = uclass_find_device_by_seq(UCLASS_USB, 0, true, &dev);
+ if (ret)
+ return ret;
+ ret = device_remove(dev);
+ if (ret)
+ return ret;
+
+ plat = dev_get_platdata(dev);
+ plat->init_type = USB_INIT_DEVICE;
+ ret = device_probe(dev);
+ if (ret)
+ return ret;
+ *ctlrp = dev_get_priv(dev);
+
+ return 0;
+}
+
+/* returns 0 if no match, 1 if match */
+int usb_match_device(const struct usb_device_descriptor *desc,
+ const struct usb_device_id *id)
+{
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+ id->idVendor != le16_to_cpu(desc->idVendor))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
+ id->idProduct != le16_to_cpu(desc->idProduct))
+ return 0;
+
+ /* No need to test id->bcdDevice_lo != 0, since 0 is never
+ greater than any unsigned number. */
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
+ (id->bcdDevice_lo > le16_to_cpu(desc->bcdDevice)))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
+ (id->bcdDevice_hi < le16_to_cpu(desc->bcdDevice)))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+ (id->bDeviceClass != desc->bDeviceClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
+ (id->bDeviceSubClass != desc->bDeviceSubClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
+ (id->bDeviceProtocol != desc->bDeviceProtocol))
+ return 0;
+
+ return 1;
+}
+
+/* returns 0 if no match, 1 if match */
+int usb_match_one_id_intf(const struct usb_device_descriptor *desc,
+ const struct usb_interface_descriptor *int_desc,
+ const struct usb_device_id *id)
+{
+ /* The interface class, subclass, protocol and number should never be
+ * checked for a match if the device class is Vendor Specific,
+ * unless the match record specifies the Vendor ID. */
+ if (desc->bDeviceClass == USB_CLASS_VENDOR_SPEC &&
+ !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+ (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS |
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL |
+ USB_DEVICE_ID_MATCH_INT_NUMBER)))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
+ (id->bInterfaceClass != int_desc->bInterfaceClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
+ (id->bInterfaceSubClass != int_desc->bInterfaceSubClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
+ (id->bInterfaceProtocol != int_desc->bInterfaceProtocol))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
+ (id->bInterfaceNumber != int_desc->bInterfaceNumber))
+ return 0;
+
+ return 1;
+}
+
+/* returns 0 if no match, 1 if match */
+int usb_match_one_id(struct usb_device_descriptor *desc,
+ struct usb_interface_descriptor *int_desc,
+ const struct usb_device_id *id)
+{
+ if (!usb_match_device(desc, id))
+ return 0;
+
+ return usb_match_one_id_intf(desc, int_desc, id);
+}
+
+/**
+ * usb_find_and_bind_driver() - Find and bind the right USB driver
+ *
+ * This only looks at certain fields in the descriptor.
+ */
+static int usb_find_and_bind_driver(struct udevice *parent,
+ struct usb_device_descriptor *desc,
+ struct usb_interface_descriptor *iface,
+ int bus_seq, int devnum,
+ struct udevice **devp)
+{
+ struct usb_driver_entry *start, *entry;
+ int n_ents;
+ int ret;
+ char name[30], *str;
+
+ *devp = NULL;
+ debug("%s: Searching for driver\n", __func__);
+ start = ll_entry_start(struct usb_driver_entry, usb_driver_entry);
+ n_ents = ll_entry_count(struct usb_driver_entry, usb_driver_entry);
+ for (entry = start; entry != start + n_ents; entry++) {
+ const struct usb_device_id *id;
+ struct udevice *dev;
+ const struct driver *drv;
+ struct usb_dev_platdata *plat;
+
+ for (id = entry->match; id->match_flags; id++) {
+ if (!usb_match_one_id(desc, iface, id))
+ continue;
+
+ drv = entry->driver;
+ /*
+ * We could pass the descriptor to the driver as
+ * platdata (instead of NULL) and allow its bind()
+ * method to return -ENOENT if it doesn't support this
+ * device. That way we could continue the search to
+ * find another driver. For now this doesn't seem
+ * necesssary, so just bind the first match.
+ */
+ ret = device_bind(parent, drv, drv->name, NULL, -1,
+ &dev);
+ if (ret)
+ goto error;
+ debug("%s: Match found: %s\n", __func__, drv->name);
+ dev->driver_data = id->driver_info;
+ plat = dev_get_parent_platdata(dev);
+ plat->id = *id;
+ *devp = dev;
+ return 0;
+ }
+ }
+
+ /* Bind a generic driver so that the device can be used */
+ snprintf(name, sizeof(name), "generic_bus_%x_dev_%x", bus_seq, devnum);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ ret = device_bind_driver(parent, "usb_dev_generic_drv", str, devp);
+
+error:
+ debug("%s: No match found: %d\n", __func__, ret);
+ return ret;
+}
+
+/**
+ * usb_find_child() - Find an existing device which matches our needs
+ *
+ *
+ */
+static int usb_find_child(struct udevice *parent,
+ struct usb_device_descriptor *desc,
+ struct usb_interface_descriptor *iface,
+ struct udevice **devp)
+{
+ struct udevice *dev;
+
+ *devp = NULL;
+ for (device_find_first_child(parent, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
+
+ /* If this device is already in use, skip it */
+ if (device_active(dev))
+ continue;
+ debug(" %s: name='%s', plat=%d, desc=%d\n", __func__,
+ dev->name, plat->id.bDeviceClass, desc->bDeviceClass);
+ if (usb_match_one_id(desc, iface, &plat->id)) {
+ *devp = dev;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+int usb_scan_device(struct udevice *parent, int port,
+ enum usb_device_speed speed, struct udevice **devp)
+{
+ struct udevice *dev;
+ bool created = false;
+ struct usb_dev_platdata *plat;
+ struct usb_bus_priv *priv;
+ struct usb_device *parent_udev;
+ int ret;
+ ALLOC_CACHE_ALIGN_BUFFER(struct usb_device, udev, 1);
+ struct usb_interface_descriptor *iface = &udev->config.if_desc[0].desc;
+
+ *devp = NULL;
+ memset(udev, '\0', sizeof(*udev));
+ ret = usb_get_bus(parent, &udev->controller_dev);
+ if (ret)
+ return ret;
+ priv = dev_get_uclass_priv(udev->controller_dev);
+
+ /*
+ * Somewhat nasty, this. We create a local device and use the normal
+ * USB stack to read its descriptor. Then we know what type of device
+ * to create for real.
+ *
+ * udev->dev is set to the parent, since we don't have a real device
+ * yet. The USB stack should not access udev.dev anyway, except perhaps
+ * to find the controller, and the controller will either be @parent,
+ * or some parent of @parent.
+ *
+ * Another option might be to create the device as a generic USB
+ * device, then morph it into the correct one when we know what it
+ * should be. This means that a generic USB device would morph into
+ * a network controller, or a USB flash stick, for example. However,
+ * we don't support such morphing and it isn't clear that it would
+ * be easy to do.
+ *
+ * Yet another option is to split out the USB stack parts of udev
+ * into something like a 'struct urb' (as Linux does) which can exist
+ * independently of any device. This feels cleaner, but calls for quite
+ * a big change to the USB stack.
+ *
+ * For now, the approach is to set up an empty udev, read its
+ * descriptor and assign it an address, then bind a real device and
+ * stash the resulting information into the device's parent
+ * platform data. Then when we probe it, usb_child_pre_probe() is called
+ * and it will pull the information out of the stash.
+ */
+ udev->dev = parent;
+ udev->speed = speed;
+ udev->devnum = priv->next_addr + 1;
+ udev->portnr = port;
+ debug("Calling usb_setup_device(), portnr=%d\n", udev->portnr);
+ parent_udev = device_get_uclass_id(parent) == UCLASS_USB_HUB ?
+ dev_get_parentdata(parent) : NULL;
+ ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev, port);
+ debug("read_descriptor for '%s': ret=%d\n", parent->name, ret);
+ if (ret)
+ return ret;
+ ret = usb_find_child(parent, &udev->descriptor, iface, &dev);
+ debug("** usb_find_child returns %d\n", ret);
+ if (ret) {
+ if (ret != -ENOENT)
+ return ret;
+ ret = usb_find_and_bind_driver(parent, &udev->descriptor, iface,
+ udev->controller_dev->seq,
+ udev->devnum, &dev);
+ if (ret)
+ return ret;
+ created = true;
+ }
+ plat = dev_get_parent_platdata(dev);
+ debug("%s: Probing '%s', plat=%p\n", __func__, dev->name, plat);
+ plat->devnum = udev->devnum;
+ plat->speed = udev->speed;
+ plat->slot_id = udev->slot_id;
+ plat->portnr = port;
+ debug("** device '%s': stashing slot_id=%d\n", dev->name,
+ plat->slot_id);
+ priv->next_addr++;
+ ret = device_probe(dev);
+ if (ret) {
+ debug("%s: Device '%s' probe failed\n", __func__, dev->name);
+ priv->next_addr--;
+ if (created)
+ device_unbind(dev);
+ return ret;
+ }
+ *devp = dev;
+
+ return 0;
+}
+
+int usb_child_post_bind(struct udevice *dev)
+{
+ struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
+ const void *blob = gd->fdt_blob;
+ int val;
+
+ if (dev->of_offset == -1)
+ return 0;
+
+ /* We only support matching a few things */
+ val = fdtdec_get_int(blob, dev->of_offset, "usb,device-class", -1);
+ if (val != -1) {
+ plat->id.match_flags |= USB_DEVICE_ID_MATCH_DEV_CLASS;
+ plat->id.bDeviceClass = val;
+ }
+ val = fdtdec_get_int(blob, dev->of_offset, "usb,interface-class", -1);
+ if (val != -1) {
+ plat->id.match_flags |= USB_DEVICE_ID_MATCH_INT_CLASS;
+ plat->id.bInterfaceClass = val;
+ }
+
+ return 0;
+}
+
+int usb_get_bus(struct udevice *dev, struct udevice **busp)
+{
+ struct udevice *bus;
+
+ *busp = NULL;
+ for (bus = dev; bus && device_get_uclass_id(bus) != UCLASS_USB; )
+ bus = bus->parent;
+ if (!bus) {
+ /* By design this cannot happen */
+ assert(bus);
+ debug("USB HUB '%s' does not have a controller\n", dev->name);
+ return -EXDEV;
+ }
+ *busp = bus;
+
+ return 0;
+}
+
+int usb_child_pre_probe(struct udevice *dev)
+{
+ struct udevice *bus;
+ struct usb_device *udev = dev_get_parentdata(dev);
+ struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
+ int ret;
+
+ ret = usb_get_bus(dev, &bus);
+ if (ret)
+ return ret;
+ udev->controller_dev = bus;
+ udev->dev = dev;
+ udev->devnum = plat->devnum;
+ udev->slot_id = plat->slot_id;
+ udev->portnr = plat->portnr;
+ udev->speed = plat->speed;
+ debug("** device '%s': getting slot_id=%d\n", dev->name, plat->slot_id);
+
+ ret = usb_select_config(udev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+UCLASS_DRIVER(usb) = {
+ .id = UCLASS_USB,
+ .name = "usb",
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+ .post_bind = usb_post_bind,
+ .per_child_auto_alloc_size = sizeof(struct usb_device),
+ .per_device_auto_alloc_size = sizeof(struct usb_bus_priv),
+ .child_post_bind = usb_child_post_bind,
+ .child_pre_probe = usb_child_pre_probe,
+ .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata),
+};
+
+UCLASS_DRIVER(usb_dev_generic) = {
+ .id = UCLASS_USB_DEV_GENERIC,
+ .name = "usb_dev_generic",
+};
+
+U_BOOT_DRIVER(usb_dev_generic_drv) = {
+ .id = UCLASS_USB_DEV_GENERIC,
+ .name = "usb_dev_generic_drv",
+};
diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c
index 3f86fdc..23c7ecc 100644
--- a/drivers/usb/host/xhci-exynos5.c
+++ b/drivers/usb/host/xhci-exynos5.c
@@ -14,6 +14,7 @@
*/
#include <common.h>
+#include <dm.h>
#include <fdtdec.h>
#include <libfdt.h>
#include <malloc.h>
@@ -32,20 +33,76 @@
/* Declare global data pointer */
DECLARE_GLOBAL_DATA_PTR;
+#ifdef CONFIG_DM_USB
+struct exynos_xhci_platdata {
+ fdt_addr_t hcd_base;
+ fdt_addr_t phy_base;
+ struct gpio_desc vbus_gpio;
+};
+#endif
+
/**
* Contains pointers to register base addresses
* for the usb controller.
*/
struct exynos_xhci {
+#ifdef CONFIG_DM_USB
+ struct usb_platdata usb_plat;
+#endif
+ struct xhci_ctrl ctrl;
struct exynos_usb3_phy *usb3_phy;
struct xhci_hccr *hcd;
struct dwc3 *dwc3_reg;
+#ifndef CONFIG_DM_USB
struct gpio_desc vbus_gpio;
+#endif
};
+#ifndef CONFIG_DM_USB
static struct exynos_xhci exynos;
+#endif
-#ifdef CONFIG_OF_CONTROL
+#ifdef CONFIG_DM_USB
+static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
+{
+ struct exynos_xhci_platdata *plat = dev_get_platdata(dev);
+ const void *blob = gd->fdt_blob;
+ unsigned int node;
+ int depth;
+
+ /*
+ * Get the base address for XHCI controller from the device node
+ */
+ plat->hcd_base = fdtdec_get_addr(blob, dev->of_offset, "reg");
+ if (plat->hcd_base == FDT_ADDR_T_NONE) {
+ debug("Can't get the XHCI register base address\n");
+ return -ENXIO;
+ }
+
+ depth = 0;
+ node = fdtdec_next_compatible_subnode(blob, dev->of_offset,
+ COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth);
+ if (node <= 0) {
+ debug("XHCI: Can't get device node for usb3-phy controller\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Get the base address for usbphy from the device node
+ */
+ plat->phy_base = fdtdec_get_addr(blob, node, "reg");
+ if (plat->phy_base == FDT_ADDR_T_NONE) {
+ debug("Can't get the usbphy register address\n");
+ return -ENXIO;
+ }
+
+ /* Vbus gpio */
+ gpio_request_by_name(dev, "samsung,vbus-gpio", 0,
+ &plat->vbus_gpio, GPIOD_IS_OUT);
+
+ return 0;
+}
+#else
static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos)
{
fdt_addr_t addr;
@@ -283,6 +340,7 @@ static void exynos_xhci_core_exit(struct exynos_xhci *exynos)
exynos5_usb3_phy_exit(exynos->usb3_phy);
}
+#ifndef CONFIG_DM_USB
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
struct exynos_xhci *ctx = &exynos;
@@ -326,3 +384,63 @@ void xhci_hcd_stop(int index)
exynos_xhci_core_exit(ctx);
}
+#endif
+
+#ifdef CONFIG_DM_USB
+static int xhci_usb_probe(struct udevice *dev)
+{
+ struct exynos_xhci_platdata *plat = dev_get_platdata(dev);
+ struct exynos_xhci *ctx = dev_get_priv(dev);
+ struct xhci_hcor *hcor;
+ int ret;
+
+ ctx->hcd = (struct xhci_hccr *)plat->hcd_base;
+ ctx->usb3_phy = (struct exynos_usb3_phy *)plat->phy_base;
+ ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);
+ hcor = (struct xhci_hcor *)((uint32_t)ctx->hcd +
+ HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase)));
+
+ /* setup the Vbus gpio here */
+ if (dm_gpio_is_valid(&plat->vbus_gpio))
+ dm_gpio_set_value(&plat->vbus_gpio, 1);
+
+ ret = exynos_xhci_core_init(ctx);
+ if (ret) {
+ puts("XHCI: failed to initialize controller\n");
+ return -EINVAL;
+ }
+
+ return xhci_register(dev, ctx->hcd, hcor);
+}
+
+static int xhci_usb_remove(struct udevice *dev)
+{
+ struct exynos_xhci *ctx = dev_get_priv(dev);
+ int ret;
+
+ ret = xhci_deregister(dev);
+ if (ret)
+ return ret;
+ exynos_xhci_core_exit(ctx);
+
+ return 0;
+}
+
+static const struct udevice_id xhci_usb_ids[] = {
+ { .compatible = "samsung,exynos5250-xhci" },
+ { }
+};
+
+U_BOOT_DRIVER(usb_xhci) = {
+ .name = "xhci_exynos",
+ .id = UCLASS_USB,
+ .of_match = xhci_usb_ids,
+ .ofdata_to_platdata = xhci_usb_ofdata_to_platdata,
+ .probe = xhci_usb_probe,
+ .remove = xhci_usb_remove,
+ .ops = &xhci_usb_ops,
+ .platdata_auto_alloc_size = sizeof(struct exynos_xhci_platdata),
+ .priv_auto_alloc_size = sizeof(struct exynos_xhci),
+ .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
+#endif
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 10f11cd..3744452 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -15,6 +15,7 @@
*/
#include <common.h>
+#include <dm.h>
#include <asm/byteorder.h>
#include <usb.h>
#include <malloc.h>
@@ -352,12 +353,10 @@ static struct xhci_container_ctx
* @param udev pointer to USB deivce structure
* @return 0 on success else -1 on failure
*/
-int xhci_alloc_virt_device(struct usb_device *udev)
+int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id)
{
u64 byte_64 = 0;
- unsigned int slot_id = udev->slot_id;
struct xhci_virt_device *virt_dev;
- struct xhci_ctrl *ctrl = udev->controller;
/* Slot ID 0 is reserved */
if (ctrl->devs[slot_id]) {
@@ -627,17 +626,16 @@ void xhci_slot_copy(struct xhci_ctrl *ctrl, struct xhci_container_ctx *in_ctx,
* @param udev pointer to the Device Data Structure
* @return returns negative value on failure else 0 on success
*/
-void xhci_setup_addressable_virt_dev(struct usb_device *udev)
+void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, int slot_id,
+ int speed, int hop_portnr)
{
- struct usb_device *hop = udev;
struct xhci_virt_device *virt_dev;
struct xhci_ep_ctx *ep0_ctx;
struct xhci_slot_ctx *slot_ctx;
u32 port_num = 0;
u64 trb_64 = 0;
- struct xhci_ctrl *ctrl = udev->controller;
- virt_dev = ctrl->devs[udev->slot_id];
+ virt_dev = ctrl->devs[slot_id];
BUG_ON(!virt_dev);
@@ -648,7 +646,7 @@ void xhci_setup_addressable_virt_dev(struct usb_device *udev)
/* Only the control endpoint is valid - one endpoint context */
slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | 0);
- switch (udev->speed) {
+ switch (speed) {
case USB_SPEED_SUPER:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS);
break;
@@ -666,11 +664,7 @@ void xhci_setup_addressable_virt_dev(struct usb_device *udev)
BUG();
}
- /* Extract the root hub port number */
- if (hop->parent)
- while (hop->parent->parent)
- hop = hop->parent;
- port_num = hop->portnr;
+ port_num = hop_portnr;
debug("port_num = %d\n", port_num);
slot_ctx->dev_info2 |=
@@ -680,9 +674,9 @@ void xhci_setup_addressable_virt_dev(struct usb_device *udev)
/* Step 4 - ring already allocated */
/* Step 5 */
ep0_ctx->ep_info2 = cpu_to_le32(CTRL_EP << EP_TYPE_SHIFT);
- debug("SPEED = %d\n", udev->speed);
+ debug("SPEED = %d\n", speed);
- switch (udev->speed) {
+ switch (speed) {
case USB_SPEED_SUPER:
ep0_ctx->ep_info2 |= cpu_to_le32(((512 & MAX_PACKET_MASK) <<
MAX_PACKET_SHIFT));
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index f3759d4..5a1391f 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -353,7 +353,7 @@ static void giveback_first_trb(struct usb_device *udev, int ep_index,
int start_cycle,
struct xhci_generic_trb *start_trb)
{
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
/*
* Pass all the TRBs to the hardware at once and make sure this write
@@ -477,7 +477,7 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected)
*/
static void abort_td(struct usb_device *udev, int ep_index)
{
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring;
union xhci_trb *event;
u32 field;
@@ -554,7 +554,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
int start_cycle;
u32 field = 0;
u32 length_field = 0;
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
int slot_id = udev->slot_id;
int ep_index;
struct xhci_virt_device *virt_dev;
@@ -748,7 +748,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
u32 length_field;
u64 buf_64 = 0;
struct xhci_generic_trb *start_trb;
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
int slot_id = udev->slot_id;
int ep_index;
u32 trb_fields[4];
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index f8b5ce4..0b09643 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -21,6 +21,7 @@
*/
#include <common.h>
+#include <dm.h>
#include <asm/byteorder.h>
#include <usb.h>
#include <malloc.h>
@@ -108,7 +109,25 @@ static struct descriptor {
},
};
+#ifndef CONFIG_DM_USB
static struct xhci_ctrl xhcic[CONFIG_USB_MAX_CONTROLLER_COUNT];
+#endif
+
+struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev)
+{
+#ifdef CONFIG_DM_USB
+ struct udevice *dev;
+
+ /* Find the USB controller */
+ for (dev = udev->dev;
+ device_get_uclass_id(dev) != UCLASS_USB;
+ dev = dev->parent)
+ ;
+ return dev_get_priv(dev);
+#else
+ return udev->controller;
+#endif
+}
/**
* Waits for as per specified amount of time
@@ -250,7 +269,7 @@ static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change)
{
struct xhci_container_ctx *in_ctx;
struct xhci_virt_device *virt_dev;
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
union xhci_trb *event;
virt_dev = ctrl->devs[udev->slot_id];
@@ -298,7 +317,7 @@ static int xhci_set_configuration(struct usb_device *udev)
int ep_index;
unsigned int dir;
unsigned int ep_type;
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
int num_of_ep;
int ep_flag = 0;
u64 trb_64 = 0;
@@ -379,10 +398,10 @@ static int xhci_set_configuration(struct usb_device *udev)
* @param udev pointer to the Device Data Structure
* @return 0 if successful else error code on failure
*/
-static int xhci_address_device(struct usb_device *udev)
+static int xhci_address_device(struct usb_device *udev, int root_portnr)
{
int ret = 0;
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
struct xhci_slot_ctx *slot_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
struct xhci_virt_device *virt_dev;
@@ -395,8 +414,9 @@ static int xhci_address_device(struct usb_device *udev)
* This is the first Set Address since device plug-in
* so setting up the slot context.
*/
- debug("Setting up addressable devices\n");
- xhci_setup_addressable_virt_dev(udev);
+ debug("Setting up addressable devices %p\n", ctrl->dcbaa);
+ xhci_setup_addressable_virt_dev(ctrl, udev->slot_id, udev->speed,
+ root_portnr);
ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
@@ -461,10 +481,10 @@ static int xhci_address_device(struct usb_device *udev)
* @param udev pointer to the Device Data Structure
* @return Returns 0 on succes else return error code on failure
*/
-int usb_alloc_device(struct usb_device *udev)
+int _xhci_alloc_device(struct usb_device *udev)
{
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
union xhci_trb *event;
- struct xhci_ctrl *ctrl = udev->controller;
int ret;
/*
@@ -486,7 +506,7 @@ int usb_alloc_device(struct usb_device *udev)
xhci_acknowledge_event(ctrl);
- ret = xhci_alloc_virt_device(udev);
+ ret = xhci_alloc_virt_device(ctrl, udev->slot_id);
if (ret < 0) {
/*
* TODO: Unsuccessful Address Device command shall leave
@@ -499,6 +519,13 @@ int usb_alloc_device(struct usb_device *udev)
return 0;
}
+#ifndef CONFIG_DM_USB
+int usb_alloc_device(struct usb_device *udev)
+{
+ return _xhci_alloc_device(udev);
+}
+#endif
+
/*
* Full speed devices may have a max packet size greater than 8 bytes, but the
* USB core doesn't know that until it reads the first 8 bytes of the
@@ -510,7 +537,7 @@ int usb_alloc_device(struct usb_device *udev)
*/
int xhci_check_maxpacket(struct usb_device *udev)
{
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
unsigned int slot_id = udev->slot_id;
int ep_index = 0; /* control endpoint */
struct xhci_container_ctx *in_ctx;
@@ -640,7 +667,7 @@ static int xhci_submit_root(struct usb_device *udev, unsigned long pipe,
int len, srclen;
uint32_t reg;
volatile uint32_t *status_reg;
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
struct xhci_hcor *hcor = ctrl->hcor;
if ((req->requesttype & USB_RT_PORT) &&
@@ -677,7 +704,7 @@ static int xhci_submit_root(struct usb_device *udev, unsigned long pipe,
srclen = 4;
break;
case 1: /* Vendor String */
- srcptr = "\16\3u\0-\0b\0o\0o\0t\0";
+ srcptr = "\16\3U\0-\0B\0o\0o\0t\0";
srclen = 14;
break;
case 2: /* Product Name */
@@ -858,9 +885,8 @@ unknown:
* @param interval interval of the interrupt
* @return 0
*/
-int
-submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
- int length, int interval)
+static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe,
+ void *buffer, int length, int interval)
{
/*
* TODO: Not addressing any interrupt type transfer requests
@@ -878,9 +904,8 @@ submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
* @param length length of the buffer
* @return returns 0 if successful else -1 on failure
*/
-int
-submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
- int length)
+static int _xhci_submit_bulk_msg(struct usb_device *udev, unsigned long pipe,
+ void *buffer, int length)
{
if (usb_pipetype(pipe) != PIPE_BULK) {
printf("non-bulk pipe (type=%lu)", usb_pipetype(pipe));
@@ -898,13 +923,14 @@ submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
* @param buffer buffer to be read/written based on the request
* @param length length of the buffer
* @param setup Request type
+ * @param root_portnr Root port number that this device is on
* @return returns 0 if successful else -1 on failure
*/
-int
-submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
- int length, struct devrequest *setup)
+static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe,
+ void *buffer, int length,
+ struct devrequest *setup, int root_portnr)
{
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
int ret = 0;
if (usb_pipetype(pipe) != PIPE_CONTROL) {
@@ -916,7 +942,7 @@ submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
return xhci_submit_root(udev, pipe, buffer, setup);
if (setup->request == USB_REQ_SET_ADDRESS)
- return xhci_address_device(udev);
+ return xhci_address_device(udev, root_portnr);
if (setup->request == USB_REQ_SET_CONFIGURATION) {
ret = xhci_set_configuration(udev);
@@ -929,33 +955,16 @@ submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
return xhci_ctrl_tx(udev, pipe, setup, length, buffer);
}
-/**
- * Intialises the XHCI host controller
- * and allocates the necessary data structures
- *
- * @param index index to the host controller data structure
- * @return pointer to the intialised controller
- */
-int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
+static int xhci_lowlevel_init(struct xhci_ctrl *ctrl)
{
+ struct xhci_hccr *hccr;
+ struct xhci_hcor *hcor;
uint32_t val;
uint32_t val2;
uint32_t reg;
- struct xhci_hccr *hccr;
- struct xhci_hcor *hcor;
- struct xhci_ctrl *ctrl;
-
- if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0)
- return -ENODEV;
-
- if (xhci_reset(hcor) != 0)
- return -ENODEV;
-
- ctrl = &xhcic[index];
-
- ctrl->hccr = hccr;
- ctrl->hcor = hcor;
+ hccr = ctrl->hccr;
+ hcor = ctrl->hcor;
/*
* Program the Number of Device Slots Enabled field in the CONFIG
* register with the max value of slots the HC can handle.
@@ -997,11 +1006,82 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
reg = HC_VERSION(xhci_readl(&hccr->cr_capbase));
printf("USB XHCI %x.%02x\n", reg >> 8, reg & 0xff);
- *controller = &xhcic[index];
+ return 0;
+}
+
+static int xhci_lowlevel_stop(struct xhci_ctrl *ctrl)
+{
+ u32 temp;
+
+ xhci_reset(ctrl->hcor);
+
+ debug("// Disabling event ring interrupts\n");
+ temp = xhci_readl(&ctrl->hcor->or_usbsts);
+ xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT);
+ temp = xhci_readl(&ctrl->ir_set->irq_pending);
+ xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp));
return 0;
}
+#ifndef CONFIG_DM_USB
+int submit_control_msg(struct usb_device *udev, unsigned long pipe,
+ void *buffer, int length, struct devrequest *setup)
+{
+ struct usb_device *hop = udev;
+
+ if (hop->parent)
+ while (hop->parent->parent)
+ hop = hop->parent;
+
+ return _xhci_submit_control_msg(udev, pipe, buffer, length, setup,
+ hop->portnr);
+}
+
+int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
+ int length)
+{
+ return _xhci_submit_bulk_msg(udev, pipe, buffer, length);
+}
+
+int submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
+ int length, int interval)
+{
+ return _xhci_submit_int_msg(udev, pipe, buffer, length, interval);
+}
+
+/**
+ * Intialises the XHCI host controller
+ * and allocates the necessary data structures
+ *
+ * @param index index to the host controller data structure
+ * @return pointer to the intialised controller
+ */
+int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
+{
+ struct xhci_hccr *hccr;
+ struct xhci_hcor *hcor;
+ struct xhci_ctrl *ctrl;
+ int ret;
+
+ if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0)
+ return -ENODEV;
+
+ if (xhci_reset(hcor) != 0)
+ return -ENODEV;
+
+ ctrl = &xhcic[index];
+
+ ctrl->hccr = hccr;
+ ctrl->hcor = hcor;
+
+ ret = xhci_lowlevel_init(ctrl);
+
+ *controller = &xhcic[index];
+
+ return ret;
+}
+
/**
* Stops the XHCI host controller
* and cleans up all the related data structures
@@ -1012,19 +1092,143 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
int usb_lowlevel_stop(int index)
{
struct xhci_ctrl *ctrl = (xhcic + index);
- u32 temp;
- xhci_reset(ctrl->hcor);
+ xhci_lowlevel_stop(ctrl);
+ xhci_hcd_stop(index);
+ xhci_cleanup(ctrl);
- debug("// Disabling event ring interrupts\n");
- temp = xhci_readl(&ctrl->hcor->or_usbsts);
- xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT);
- temp = xhci_readl(&ctrl->ir_set->irq_pending);
- xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp));
+ return 0;
+}
+#endif /* CONFIG_DM_USB */
- xhci_hcd_stop(index);
+#ifdef CONFIG_DM_USB
+/*
+static struct usb_device *get_usb_device(struct udevice *dev)
+{
+ struct usb_device *udev;
+ if (device_get_uclass_id(dev) == UCLASS_USB)
+ udev = dev_get_uclass_priv(dev);
+ else
+ udev = dev_get_parentdata(dev);
+
+ return udev;
+}
+*/
+static bool is_root_hub(struct udevice *dev)
+{
+ if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB)
+ return true;
+
+ return false;
+}
+
+static int xhci_submit_control_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ struct devrequest *setup)
+{
+ struct usb_device *uhop;
+ struct udevice *hub;
+ int root_portnr = 0;
+
+ debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__,
+ dev->name, udev, udev->dev->name, udev->portnr);
+ hub = udev->dev;
+ if (device_get_uclass_id(hub) == UCLASS_USB_HUB) {
+ /* Figure out our port number on the root hub */
+ if (is_root_hub(hub)) {
+ root_portnr = udev->portnr;
+ } else {
+ while (!is_root_hub(hub->parent))
+ hub = hub->parent;
+ uhop = dev_get_parentdata(hub);
+ root_portnr = uhop->portnr;
+ }
+ }
+/*
+ struct usb_device *hop = udev;
+
+ if (hop->parent)
+ while (hop->parent->parent)
+ hop = hop->parent;
+*/
+ return _xhci_submit_control_msg(udev, pipe, buffer, length, setup,
+ root_portnr);
+}
+
+static int xhci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _xhci_submit_bulk_msg(udev, pipe, buffer, length);
+}
+
+static int xhci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ int interval)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _xhci_submit_int_msg(udev, pipe, buffer, length, interval);
+}
+
+static int xhci_alloc_device(struct udevice *dev, struct usb_device *udev)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _xhci_alloc_device(udev);
+}
+
+int xhci_register(struct udevice *dev, struct xhci_hccr *hccr,
+ struct xhci_hcor *hcor)
+{
+ struct xhci_ctrl *ctrl = dev_get_priv(dev);
+ struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
+ int ret;
+
+ debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p\n", __func__, dev->name,
+ ctrl, hccr, hcor);
+
+ ctrl->dev = dev;
+
+ /*
+ * XHCI needs to issue a Address device command to setup
+ * proper device context structures, before it can interact
+ * with the device. So a get_descriptor will fail before any
+ * of that is done for XHCI unlike EHCI.
+ */
+ priv->desc_before_addr = false;
+
+ ret = xhci_reset(hcor);
+ if (ret)
+ goto err;
+
+ ctrl->hccr = hccr;
+ ctrl->hcor = hcor;
+ ret = xhci_lowlevel_init(ctrl);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ free(ctrl);
+ debug("%s: failed, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+int xhci_deregister(struct udevice *dev)
+{
+ struct xhci_ctrl *ctrl = dev_get_priv(dev);
+
+ xhci_lowlevel_stop(ctrl);
xhci_cleanup(ctrl);
return 0;
}
+
+struct dm_usb_ops xhci_usb_ops = {
+ .control = xhci_submit_control_msg,
+ .bulk = xhci_submit_bulk_msg,
+ .interrupt = xhci_submit_int_msg,
+ .alloc_device = xhci_alloc_device,
+};
+
+#endif
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 0951e87..2afa386 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1209,6 +1209,9 @@ void xhci_hcd_stop(int index);
#define XHCI_STS_CNR (1 << 11)
struct xhci_ctrl {
+#ifdef CONFIG_DM_USB
+ struct udevice *dev;
+#endif
struct xhci_hccr *hccr; /* R/O registers, not need for volatile */
struct xhci_hcor *hcor;
struct xhci_doorbell_array *dba;
@@ -1241,7 +1244,8 @@ void xhci_endpoint_copy(struct xhci_ctrl *ctrl,
void xhci_slot_copy(struct xhci_ctrl *ctrl,
struct xhci_container_ctx *in_ctx,
struct xhci_container_ctx *out_ctx);
-void xhci_setup_addressable_virt_dev(struct usb_device *udev);
+void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, int slot_id,
+ int speed, int hop_portnr);
void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr,
u32 slot_id, u32 ep_index, trb_type cmd);
void xhci_acknowledge_event(struct xhci_ctrl *ctrl);
@@ -1255,8 +1259,31 @@ void xhci_flush_cache(uintptr_t addr, u32 type_len);
void xhci_inval_cache(uintptr_t addr, u32 type_len);
void xhci_cleanup(struct xhci_ctrl *ctrl);
struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs);
-int xhci_alloc_virt_device(struct usb_device *udev);
+int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id);
int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
struct xhci_hcor *hcor);
+/**
+ * xhci_deregister() - Unregister an XHCI controller
+ *
+ * @dev: Controller device
+ * @return 0 if registered, -ve on error
+ */
+int xhci_deregister(struct udevice *dev);
+
+/**
+ * xhci_register() - Register a new XHCI controller
+ *
+ * @dev: Controller device
+ * @hccr: Host controller control registers
+ * @hcor: Not sure what this means
+ * @return 0 if registered, -ve on error
+ */
+int xhci_register(struct udevice *dev, struct xhci_hccr *hccr,
+ struct xhci_hcor *hcor);
+
+extern struct dm_usb_ops xhci_usb_ops;
+
+struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev);
+
#endif /* HOST_XHCI_H_ */
diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c
index 053d945..7d90ebc 100644
--- a/drivers/usb/musb-new/musb_uboot.c
+++ b/drivers/usb/musb-new/musb_uboot.c
@@ -180,7 +180,7 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
return NULL; /* URB still pending */
}
-void usb_reset_root_port(void)
+int usb_reset_root_port(void)
{
void *mbase = host->mregs;
u8 power;
@@ -208,6 +208,8 @@ void usb_reset_root_port(void)
(musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ?
USB_SPEED_FULL : USB_SPEED_LOW;
mdelay((host_speed == USB_SPEED_LOW) ? 200 : 50);
+
+ return 0;
}
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)