From a4aff629f384f33eda62b84cf7cd66d7862c6ced Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 19 Jul 2017 21:51:09 +0800 Subject: [PATCH 1/6] usb: hub: Use 'struct usb_hub_device' as hub device's uclass_priv Use USB hub device's dev->uclass_priv to point to 'usb_hub_device' so that with driver model usb_hub_reset() and usb_hub_allocate() are no longer needed. Signed-off-by: Bin Meng Reviewed-by: Simon Glass (cherry picked from commit dfa96e06761e223288e59d7a3e1d574f014b5a0d) Signed-off-by: Santan Kumar Conflicts: common/usb_hub.c Change-Id: I7a46a12c62aa7429ef2241c9543dd877abe6ac0b --- common/usb_hub.c | 27 ++++++++++++++++++++++----- drivers/usb/host/usb-uclass.c | 2 -- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/common/usb_hub.c b/common/usb_hub.c index 0eb8f15631..fa9fb4dbe0 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -46,9 +46,6 @@ DECLARE_GLOBAL_DATA_PTR; #define USB_BUFSIZ 512 -/* TODO(sjg@chromium.org): Remove this when CONFIG_DM_USB is defined */ -static struct usb_hub_device hub_dev[USB_MAX_HUB]; -static int usb_hub_index; __weak void usb_hub_reset_devices(int port) { @@ -140,6 +137,10 @@ static void usb_hub_power_on(struct usb_hub_device *hub) mdelay(pgood_delay + 1000); } +#ifndef CONFIG_DM_USB +static struct usb_hub_device hub_dev[USB_MAX_HUB]; +static int usb_hub_index; + void usb_hub_reset(void) { usb_hub_index = 0; @@ -153,6 +154,7 @@ static struct usb_hub_device *usb_hub_allocate(void) printf("ERROR: USB_MAX_HUB (%d) reached\n", USB_MAX_HUB); return NULL; } +#endif #define MAX_TRIES 5 @@ -351,6 +353,20 @@ int usb_hub_port_connect_change(struct usb_device *dev, int port) } +static struct usb_hub_device *usb_get_hub_device(struct usb_device *dev) +{ + struct usb_hub_device *hub; + +#ifndef CONFIG_DM_USB + /* "allocate" Hub device */ + hub = usb_hub_allocate(); +#else + hub = dev_get_uclass_priv(dev->dev); +#endif + + return hub; +} + static int usb_hub_configure(struct usb_device *dev) { int i, length; @@ -362,11 +378,11 @@ static int usb_hub_configure(struct usb_device *dev) __maybe_unused struct usb_hub_status *hubsts; int ret; - /* "allocate" Hub device */ - hub = usb_hub_allocate(); + hub = usb_get_hub_device(dev); if (hub == NULL) return -ENOMEM; hub->pusb_dev = dev; + /* Get the the hub descriptor */ ret = usb_get_hub_descriptor(dev, buffer, 4); if (ret < 0) { @@ -677,6 +693,7 @@ UCLASS_DRIVER(usb_hub) = { .child_pre_probe = usb_child_pre_probe, .per_child_auto_alloc_size = sizeof(struct usb_device), .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata), + .per_device_auto_alloc_size = sizeof(struct usb_hub_device), }; static const struct usb_device_id hub_id_table[] = { diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index ec8b403215..0b268d1964 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -174,7 +174,6 @@ int usb_stop(void) #ifdef CONFIG_USB_STORAGE usb_stor_reset(); #endif - usb_hub_reset(); uc_priv->companion_device_count = 0; usb_started = 0; @@ -227,7 +226,6 @@ int usb_init(void) int ret; asynch_allowed = 1; - usb_hub_reset(); ret = uclass_get(UCLASS_USB, &uc); if (ret) From f345bf95176b5c2c9f389b67bf1f6dba08f4d331 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 19 Jul 2017 21:51:14 +0800 Subject: [PATCH 2/6] usb: xhci: Change xhci_setup_addressable_virt_dev() signature For future extension, change xhci_setup_addressable_virt_dev() signature to accept a pointer to 'struct usb_device', instead of its members slot_id & speed, as the struct already contains these two plus some other useful information of the device. Signed-off-by: Bin Meng Reviewed-by: Simon Glass (cherry picked from commit daec4691449fa4728f29260554701602e8f91d5c) Signed-off-by: Santan Kumar Change-Id: Ibd7991073f0de4a97babbd9a96270f53f77cb185 --- drivers/usb/host/xhci-mem.c | 6 ++++-- drivers/usb/host/xhci.c | 3 +-- drivers/usb/host/xhci.h | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 37444526f7..b9adf28969 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -626,14 +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 xhci_ctrl *ctrl, int slot_id, - int speed, int hop_portnr) +void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, + struct usb_device *udev, int hop_portnr) { 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; + int slot_id = udev->slot_id; + int speed = udev->speed; virt_dev = ctrl->devs[slot_id]; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3a0c79d8d9..97d64ca208 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -415,8 +415,7 @@ static int xhci_address_device(struct usb_device *udev, int root_portnr) * so setting up the slot context. */ debug("Setting up addressable devices %p\n", ctrl->dcbaa); - xhci_setup_addressable_virt_dev(ctrl, udev->slot_id, udev->speed, - root_portnr); + xhci_setup_addressable_virt_dev(ctrl, udev, root_portnr); ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b6838506f1..c081b3c5d5 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1244,8 +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 xhci_ctrl *ctrl, int slot_id, - int speed, int hop_portnr); +void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, + struct usb_device *udev, 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); From 3db02616a7ccd66867bf43b1a143ee8652834c7a Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 19 Jul 2017 21:51:13 +0800 Subject: [PATCH 3/6] usb: hub: Support 'set hub depth' request for USB 3.0 hubs USB 3.0 hub uses a hub depth value multiplied by four as an offset into the 'route string' to locate the bits it uses to determine the downstream port number. We shall set the hub depth value of a USB 3.0 hub after it is configured. Signed-off-by: Bin Meng Reviewed-by: Simon Glass (cherry picked from commit bbc6f06c0031249bf1983b875e54cb7549bafe60) Signed-off-by: Santan Kumar Conflicts: include/usb.h Change-Id: I7fa67c83c854c136fa9401201189e0597912e2a8 --- common/usb_hub.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++ include/usb.h | 1 + include/usb_defs.h | 3 +++ 3 files changed, 56 insertions(+) diff --git a/common/usb_hub.c b/common/usb_hub.c index fa9fb4dbe0..50813b712a 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -65,6 +65,16 @@ bool usb_hub_is_root_hub(struct udevice *hub) return false; } + +static int usb_set_hub_depth(struct usb_device *dev, int depth) +{ + if (depth < 0 || depth > 4) + return -EINVAL; + + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_HUB_DEPTH, USB_DIR_OUT | USB_RT_HUB, + depth, 0, NULL, 0, USB_CNTL_TIMEOUT); +} #endif static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) @@ -492,6 +502,48 @@ static int usb_hub_configure(struct usb_device *dev) debug("%sover-current condition exists\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \ "" : "no "); + +#ifdef CONFIG_DM_USB + /* + * A maximum of seven tiers are allowed in a USB topology, and the + * root hub occupies the first tier. The last tier ends with a normal + * USB device. USB 3.0 hubs use a 20-bit field called 'route string' + * to route packets to the designated downstream port. The hub uses a + * hub depth value multiplied by four as an offset into the 'route + * string' to locate the bits it uses to determine the downstream + * port number. + */ + if (usb_hub_is_root_hub(dev->dev)) { + hub->hub_depth = -1; + } else { + struct udevice *hdev; + int depth = 0; + + hdev = dev->dev->parent; + while (!usb_hub_is_root_hub(hdev)) { + depth++; + hdev = hdev->parent; + } + + hub->hub_depth = depth; + + if (usb_hub_is_superspeed(dev)) { + debug("set hub (%p) depth to %d\n", dev, depth); + /* + * This request sets the value that the hub uses to + * determine the index into the 'route string index' + * for this hub. + */ + ret = usb_set_hub_depth(dev, depth); + if (ret < 0) { + debug("%s: failed to set hub depth (%lX)\n", + __func__, dev->status); + return ret; + } + } + } +#endif + usb_hub_power_on(hub); /* diff --git a/include/usb.h b/include/usb.h index f2bd76e5b0..fea4cb2d4c 100644 --- a/include/usb.h +++ b/include/usb.h @@ -566,6 +566,7 @@ struct usb_hub_descriptor { struct usb_hub_device { struct usb_device *pusb_dev; struct usb_hub_descriptor desc; + int hub_depth; /* USB 3.0 hub depth */ }; #ifdef CONFIG_DM_USB diff --git a/include/usb_defs.h b/include/usb_defs.h index 608a0ca568..1cc1e400a6 100644 --- a/include/usb_defs.h +++ b/include/usb_defs.h @@ -301,6 +301,9 @@ /* Mask for wIndex in get/set port feature */ #define USB_HUB_PORT_MASK 0xf +/* Hub class request codes */ +#define USB_REQ_SET_HUB_DEPTH 0x0c + /* * CBI style */ From 1bc23dd4ce8910b52ebcec9f801c30dd51af6afb Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 19 Jul 2017 21:51:15 +0800 Subject: [PATCH 4/6] usb: xhci: Program 'route string' in the input slot context xHCI spec says: the values of the 'route string' field shall be initialized by the first 'Address Device' command issued to a device slot, and shall not be modified by any other command. So far U-Boot does not program this field, and it does not prevent SS device directly attached to root port, or HS device behind an HS hub, from working, due to the fact that 'route string' is used by the xHC to target SS packets. But in order to enumerate devices behind an SS hub, this field must be programmed. With this commit and along with previous commits, now SS & HS devices attached to a USB 3.0 hub can be enumerated by U-Boot. As usual, this new feature is only available when DM is on. Signed-off-by: Bin Meng Reviewed-by: Simon Glass (cherry picked from commit 493b8dd070f2412b49190b71b44baddbb2b24e37) Signed-off-by: Santan Kumar Change-Id: I367a0f1fa5e310c1c6af2d4675a86a3293337d64 --- drivers/usb/host/xhci-mem.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index b9adf28969..d6cefc1df1 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -636,6 +636,11 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, u64 trb_64 = 0; int slot_id = udev->slot_id; int speed = udev->speed; + int route = 0; +#ifdef CONFIG_DM_USB + struct usb_device *dev = udev; + struct usb_hub_device *hub; +#endif virt_dev = ctrl->devs[slot_id]; @@ -646,7 +651,32 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->in_ctx); /* Only the control endpoint is valid - one endpoint context */ - slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | 0); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1)); + +#ifdef CONFIG_DM_USB + /* Calculate the route string for this device */ + port_num = dev->portnr; + while (!usb_hub_is_root_hub(dev->dev)) { + hub = dev_get_uclass_priv(dev->dev); + /* + * Each hub in the topology is expected to have no more than + * 15 ports in order for the route string of a device to be + * unique. SuperSpeed hubs are restricted to only having 15 + * ports, but FS/LS/HS hubs are not. The xHCI specification + * says that if the port number the device is greater than 15, + * that portion of the route string shall be set to 15. + */ + if (port_num > 15) + port_num = 15; + route |= port_num << (hub->hub_depth * 4); + dev = dev_get_parent_priv(dev->dev); + port_num = dev->portnr; + dev = dev_get_parent_priv(dev->dev->parent); + } + + debug("route string %x\n", route); +#endif + slot_ctx->dev_info |= route; switch (speed) { case USB_SPEED_SUPER: From d7842cf5dd931bd79e5b33e172811a8893b74701 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 19 Jul 2017 21:50:01 +0800 Subject: [PATCH 5/6] usb: hub: Add 3.0 hub port status mask of 2.0 hub USB 3.0 hub port status has different bit position regarding to port power, port speed, etc. But others are the same as 2.0 hubs. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Reviewed-by: Stefan Roese Tested-by: Stefan Roese (cherry picked from commit 98ba3d6c23963852c49b08e19fdbe1738edffe5b) Signed-off-by: Santan Kumar Change-Id: I77b72af7669aa05070ebacd1d95123413761c11c --- include/usb_defs.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/usb_defs.h b/include/usb_defs.h index 1cc1e400a6..273337f46a 100644 --- a/include/usb_defs.h +++ b/include/usb_defs.h @@ -262,12 +262,17 @@ /* * Changes to wPortStatus bit field in USB 3.0 - * See USB 3.0 spec Table 10-11 + * See USB 3.0 spec Table 10-10 */ #define USB_SS_PORT_STAT_LINK_STATE 0x01e0 #define USB_SS_PORT_STAT_POWER 0x0200 #define USB_SS_PORT_STAT_SPEED 0x1c00 #define USB_SS_PORT_STAT_SPEED_5GBPS 0x0000 +/* Bits that are the same from USB 2.0 */ +#define USB_SS_PORT_STAT_MASK (USB_PORT_STAT_CONNECTION | \ + USB_PORT_STAT_ENABLE | \ + USB_PORT_STAT_OVERCURRENT | \ + USB_PORT_STAT_RESET) /* wPortChange bits */ #define USB_PORT_STAT_C_CONNECTION 0x0001 From 0854db78bc463f236f34b87fdb1299ceef438d09 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 19 Jul 2017 21:51:12 +0800 Subject: [PATCH 6/6] usb: hub: Translate USB 3.0 hub port status into old version USB 3.0 hub port status field has different bit positions from 2.0 hubs. Since U-Boot only understands the old version, translate the new one into the old one. Since we are going to add USB 3.0 hub support, this feature is only available with driver model USB. Signed-off-by: Bin Meng Reviewed-by: Simon Glass (cherry picked from commit 74ffc7cbb1d2d1f218b1bd67d1bd3cc1cba8aa79) Signed-off-by: Santan Kumar Conflicts: common/usb_hub.c Change-Id: I0456a1753f879592572ea40bc352d943065f8666 --- common/usb_hub.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/common/usb_hub.c b/common/usb_hub.c index 50813b712a..c9b530b1bf 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -112,9 +112,40 @@ static int usb_get_hub_status(struct usb_device *dev, void *data) int usb_get_port_status(struct usb_device *dev, int port, void *data) { - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + int ret; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); + +#ifdef CONFIG_DM_USB + if (ret < 0) + return ret; + + /* + * Translate the USB 3.0 hub port status field into the old version + * that U-Boot understands. Do this only when the hub is not root hub. + * For root hub, the port status field has already been translated + * in the host controller driver (see xhci_submit_root() in xhci.c). + * + * Note: this only supports driver model. + */ + + if (!usb_hub_is_root_hub(dev->dev) && usb_hub_is_superspeed(dev)) { + struct usb_port_status *status = (struct usb_port_status *)data; + u16 tmp = (status->wPortStatus) & USB_SS_PORT_STAT_MASK; + + if (status->wPortStatus & USB_SS_PORT_STAT_POWER) + tmp |= USB_PORT_STAT_POWER; + if ((status->wPortStatus & USB_SS_PORT_STAT_SPEED) == + USB_SS_PORT_STAT_SPEED_5GBPS) + tmp |= USB_PORT_STAT_SUPER_SPEED; + + status->wPortStatus = tmp; + } +#endif + + return ret; }