From dcc01c0864823f91c3bf3ffca6613e2351702b87 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 30 Sep 2013 17:26:29 +0300 Subject: [PATCH 01/27] usb: Disable USB 2.0 Link PM before device reset. Before the USB core resets a device, we need to disable the L1 timeout for the roothub, if USB 2.0 Link PM is enabled. Otherwise the port may transition into L1 in between descriptor fetches, before we know if the USB device descriptors changed. LPM will be re-enabled after the full device descriptors are fetched, and we can confirm the device still supports USB 2.0 LPM after the reset. We don't need to wait for the USB device to exit L1 before resetting the device, since the xHCI roothub port diagrams show a transition to the Reset state from any of the Ux states (see Figure 34 in the 2012-08-14 xHCI specification update). This patch should be backported to kernels as old as 3.2, that contain the commit 65580b4321eb36f16ae8b5987bfa1bb948fc5112 "xHCI: set USB2 hardware LPM". That was the first commit to enable USB 2.0 hardware-driven Link Power Management. Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/hub.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3f8933f10e7d..d3a1d79d663d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -5090,6 +5090,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } parent_hub = usb_hub_to_struct_hub(parent_hdev); + /* Disable USB2 hardware LPM. + * It will be re-enabled by the enumeration process. + */ + if (udev->usb2_hw_lpm_enabled == 1) + usb_set_usb2_hardware_lpm(udev, 0); + bos = udev->bos; udev->bos = NULL; From 58e21f73975ec927119370635bf68b9023831c56 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 7 Oct 2013 17:17:20 -0700 Subject: [PATCH 02/27] xhci: Set L1 device slot on USB2 LPM enable/disable. To enable USB 2.0 Link Power Management (LPM), the xHCI host controller needs the device slot ID to generate the device address used in L1 entry tokens. That information is set in the L1 device slot ID field of the USB 2.0 LPM registers. Currently, the L1 device slot ID is overwritten when the xHCI driver initiates the software test of USB 2.0 Link PM in xhci_usb2_software_lpm_test. It is never cleared when USB 2.0 Link PM is disabled for the device. That should be harmless, because the Hardware LPM Enable (HLE) bit is cleared when USB 2.0 Link PM is disabled, so the host should not pay attention to the slot ID. This patch should have no effect on host behavior, but since xhci_usb2_software_lpm_test is going away in an upcoming bug fix patch, we need to move that code to the function that enables and disables USB 2.0 Link PM. This patch should be backported to kernels as old as 3.11, that contain the commit a558ccdcc71c7770c5e80c926a31cfe8a3892a09 "usb: xhci: add USB2 Link power management BESL support". The upcoming bug fix patch is also marked for that stable kernel. Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/host/xhci.c | 4 ++-- drivers/usb/host/xhci.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1e36dbb48366..2983e5dd98bf 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4216,7 +4216,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, } pm_val &= ~PORT_HIRD_MASK; - pm_val |= PORT_HIRD(hird) | PORT_RWE; + pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id); xhci_writel(xhci, pm_val, pm_addr); pm_val = xhci_readl(xhci, pm_addr); pm_val |= PORT_HLE; @@ -4224,7 +4224,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, /* flush write */ xhci_readl(xhci, pm_addr); } else { - pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK); + pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK); xhci_writel(xhci, pm_val, pm_addr); /* flush write */ xhci_readl(xhci, pm_addr); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 289fbfbae746..466081934b68 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -383,6 +383,7 @@ struct xhci_op_regs { #define PORT_RWE (1 << 3) #define PORT_HIRD(p) (((p) & 0xf) << 4) #define PORT_HIRD_MASK (0xf << 4) +#define PORT_L1DS_MASK (0xff << 8) #define PORT_L1DS(p) (((p) & 0xff) << 8) #define PORT_HLE (1 << 16) From de68bab4fa96014cfaa6fcbcdb9750e32969fb86 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 30 Sep 2013 17:26:28 +0300 Subject: [PATCH 03/27] usb: Don't enable USB 2.0 Link PM by default. How it's supposed to work: -------------------------- USB 2.0 Link PM is a lower power state that some newer USB 2.0 devices support. USB 3.0 devices certified by the USB-IF are required to support it if they are plugged into a USB 2.0 only port, or a USB 2.0 cable is used. USB 2.0 Link PM requires both a USB device and a host controller that supports USB 2.0 hardware-enabled LPM. USB 2.0 Link PM is designed to be enabled once by software, and the host hardware handles transitions to the L1 state automatically. The premise of USB 2.0 Link PM is to be able to put the device into a lower power link state when the bus is idle or the device NAKs USB IN transfers for a specified amount of time. ...but hardware is broken: -------------------------- It turns out many USB 3.0 devices claim to support USB 2.0 Link PM (by setting the LPM bit in their USB 2.0 BOS descriptor), but they don't actually implement it correctly. This manifests as the USB device refusing to respond to transfers when it is plugged into a USB 2.0 only port under the Haswell-ULT/Lynx Point LP xHCI host. These devices pass the xHCI driver's simple test to enable USB 2.0 Link PM, wait for the port to enter L1, and then bring it back into L0. They only start to break when L1 entry is interleaved with transfers. Some devices then fail to respond to the next control transfer (usually a Set Configuration). This results in devices never enumerating. Other mass storage devices (such as a later model Western Digital My Passport USB 3.0 hard drive) respond fine to going into L1 between control transfers. They ACK the entry, come out of L1 when the host needs to send a control transfer, and respond properly to those control transfers. However, when the first READ10 SCSI command is sent, the device NAKs the data phase while it's reading from the spinning disk. Eventually, the host requests to put the link into L1, and the device ACKs that request. Then it never responds to the data phase of the READ10 command. This results in not being able to read from the drive. Some mass storage devices (like the Corsair Survivor USB 3.0 flash drive) are well behaved. They ACK the entry into L1 during control transfers, and when SCSI commands start coming in, they NAK the requests to go into L1, because they need to be at full power. Not all USB 3.0 devices advertise USB 2.0 link PM support. My Point Grey USB 3.0 webcam advertises itself as a USB 2.1 device, but doesn't have a USB 2.0 BOS descriptor, so we don't enable USB 2.0 Link PM. I suspect that means the device isn't certified. What do we do about it? ----------------------- There's really no good way for the kernel to test these devices. Therefore, the kernel needs to disable USB 2.0 Link PM by default, and distros will have to enable it by writing 1 to the sysfs file /sys/bus/usb/devices/../power/usb2_hardware_lpm. Rip out the xHCI Link PM test, since it's not sufficient to detect these buggy devices, and don't automatically enable LPM after the device is addressed. This patch should be backported to kernels as old as 3.11, that contain the commit a558ccdcc71c7770c5e80c926a31cfe8a3892a09 "usb: xhci: add USB2 Link power management BESL support". Without this fix, some USB 3.0 devices will not enumerate or work properly under USB 2.0 ports on Haswell-ULT systems. Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/driver.c | 3 + drivers/usb/core/hub.c | 1 + drivers/usb/core/sysfs.c | 6 +- drivers/usb/host/xhci-mem.c | 10 --- drivers/usb/host/xhci.c | 161 ++++-------------------------------- include/linux/usb.h | 4 +- 6 files changed, 29 insertions(+), 156 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index f7841d44feda..689433cdef25 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1790,6 +1790,9 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) struct usb_hcd *hcd = bus_to_hcd(udev->bus); int ret = -EPERM; + if (enable && !udev->usb2_hw_lpm_allowed) + return 0; + if (hcd->driver->set_usb2_hw_lpm) { ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable); if (!ret) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d3a1d79d663d..9be1a2d5fba6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -5203,6 +5203,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) done: /* Now that the alt settings are re-installed, enable LTM and LPM. */ + usb_set_usb2_hardware_lpm(udev, 1); usb_unlocked_enable_lpm(udev); usb_enable_ltm(udev); usb_release_bos_descriptor(udev); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 5cf431b0424c..52a97adf02a0 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -458,7 +458,7 @@ static ssize_t usb2_hardware_lpm_show(struct device *dev, struct usb_device *udev = to_usb_device(dev); const char *p; - if (udev->usb2_hw_lpm_enabled == 1) + if (udev->usb2_hw_lpm_allowed == 1) p = "enabled"; else p = "disabled"; @@ -478,8 +478,10 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev, ret = strtobool(buf, &value); - if (!ret) + if (!ret) { + udev->usb2_hw_lpm_allowed = value; ret = usb_set_usb2_hardware_lpm(udev, value); + } usb_unlock_device(udev); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 83bcd13622c3..49b8bd063fab 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1693,9 +1693,7 @@ void xhci_free_command(struct xhci_hcd *xhci, void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - struct dev_info *dev_info, *next; struct xhci_cd *cur_cd, *next_cd; - unsigned long flags; int size; int i, j, num_ports; @@ -1756,13 +1754,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) scratchpad_free(xhci); - spin_lock_irqsave(&xhci->lock, flags); - list_for_each_entry_safe(dev_info, next, &xhci->lpm_failed_devs, list) { - list_del(&dev_info->list); - kfree(dev_info); - } - spin_unlock_irqrestore(&xhci->lock, flags); - if (!xhci->rh_bw) goto no_bw; @@ -2231,7 +2222,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) u32 page_size, temp; int i; - INIT_LIST_HEAD(&xhci->lpm_failed_devs); INIT_LIST_HEAD(&xhci->cancel_cmd_list); page_size = xhci_readl(xhci, &xhci->op_regs->page_size); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 2983e5dd98bf..0ea253ec813d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4013,133 +4013,6 @@ static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev) return PORT_BESLD(besld) | PORT_L1_TIMEOUT(l1) | PORT_HIRDM(hirdm); } -static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, - struct usb_device *udev) -{ - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct dev_info *dev_info; - __le32 __iomem **port_array; - __le32 __iomem *addr, *pm_addr; - u32 temp, dev_id; - unsigned int port_num; - unsigned long flags; - int hird; - int ret; - - if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || - !udev->lpm_capable) - return -EINVAL; - - /* we only support lpm for non-hub device connected to root hub yet */ - if (!udev->parent || udev->parent->parent || - udev->descriptor.bDeviceClass == USB_CLASS_HUB) - return -EINVAL; - - spin_lock_irqsave(&xhci->lock, flags); - - /* Look for devices in lpm_failed_devs list */ - dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 | - le16_to_cpu(udev->descriptor.idProduct); - list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) { - if (dev_info->dev_id == dev_id) { - ret = -EINVAL; - goto finish; - } - } - - port_array = xhci->usb2_ports; - port_num = udev->portnum - 1; - - if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) { - xhci_dbg(xhci, "invalid port number %d\n", udev->portnum); - ret = -EINVAL; - goto finish; - } - - /* - * Test USB 2.0 software LPM. - * FIXME: some xHCI 1.0 hosts may implement a new register to set up - * hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1 - * in the June 2011 errata release. - */ - xhci_dbg(xhci, "test port %d software LPM\n", port_num); - /* - * Set L1 Device Slot and HIRD/BESL. - * Check device's USB 2.0 extension descriptor to determine whether - * HIRD or BESL shoule be used. See USB2.0 LPM errata. - */ - pm_addr = port_array[port_num] + PORTPMSC; - hird = xhci_calculate_hird_besl(xhci, udev); - temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird); - xhci_writel(xhci, temp, pm_addr); - - /* Set port link state to U2(L1) */ - addr = port_array[port_num]; - xhci_set_link_state(xhci, port_array, port_num, XDEV_U2); - - /* wait for ACK */ - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(10); - spin_lock_irqsave(&xhci->lock, flags); - - /* Check L1 Status */ - ret = xhci_handshake(xhci, pm_addr, - PORT_L1S_MASK, PORT_L1S_SUCCESS, 125); - if (ret != -ETIMEDOUT) { - /* enter L1 successfully */ - temp = xhci_readl(xhci, addr); - xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n", - port_num, temp); - ret = 0; - } else { - temp = xhci_readl(xhci, pm_addr); - xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n", - port_num, temp & PORT_L1S_MASK); - ret = -EINVAL; - } - - /* Resume the port */ - xhci_set_link_state(xhci, port_array, port_num, XDEV_U0); - - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(10); - spin_lock_irqsave(&xhci->lock, flags); - - /* Clear PLC */ - xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC); - - /* Check PORTSC to make sure the device is in the right state */ - if (!ret) { - temp = xhci_readl(xhci, addr); - xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp); - if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) || - (temp & PORT_PLS_MASK) != XDEV_U0) { - xhci_dbg(xhci, "port L1 resume fail\n"); - ret = -EINVAL; - } - } - - if (ret) { - /* Insert dev to lpm_failed_devs list */ - xhci_warn(xhci, "device LPM test failed, may disconnect and " - "re-enumerate\n"); - dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC); - if (!dev_info) { - ret = -ENOMEM; - goto finish; - } - dev_info->dev_id = dev_id; - INIT_LIST_HEAD(&dev_info->list); - list_add(&dev_info->list, &xhci->lpm_failed_devs); - } else { - xhci_ring_device(xhci, udev->slot_id); - } - -finish: - spin_unlock_irqrestore(&xhci->lock, flags); - return ret; -} - int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, struct usb_device *udev, int enable) { @@ -4267,24 +4140,26 @@ static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port, int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int ret; int portnum = udev->portnum - 1; - ret = xhci_usb2_software_lpm_test(hcd, udev); - if (!ret) { - xhci_dbg(xhci, "software LPM test succeed\n"); - if (xhci->hw_lpm_support == 1 && - xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) { - udev->usb2_hw_lpm_capable = 1; - udev->l1_params.timeout = XHCI_L1_TIMEOUT; - udev->l1_params.besl = XHCI_DEFAULT_BESL; - if (xhci_check_usb2_port_capability(xhci, portnum, - XHCI_BLC)) - udev->usb2_hw_lpm_besl_capable = 1; - ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1); - if (!ret) - udev->usb2_hw_lpm_enabled = 1; - } + if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || + !udev->lpm_capable) + return 0; + + /* we only support lpm for non-hub device connected to root hub yet */ + if (!udev->parent || udev->parent->parent || + udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return 0; + + if (xhci->hw_lpm_support == 1 && + xhci_check_usb2_port_capability( + xhci, portnum, XHCI_HLC)) { + udev->usb2_hw_lpm_capable = 1; + udev->l1_params.timeout = XHCI_L1_TIMEOUT; + udev->l1_params.besl = XHCI_DEFAULT_BESL; + if (xhci_check_usb2_port_capability(xhci, portnum, + XHCI_BLC)) + udev->usb2_hw_lpm_besl_capable = 1; } return 0; diff --git a/include/linux/usb.h b/include/linux/usb.h index 055ba74bee80..7454865ad148 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -475,7 +475,8 @@ struct usb3_lpm_parameters { * @lpm_capable: device supports LPM * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM - * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled + * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled + * @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled * @usb3_lpm_enabled: USB3 hardware LPM enabled * @string_langid: language ID for strings * @product: iProduct string, if present (static) @@ -548,6 +549,7 @@ struct usb_device { unsigned usb2_hw_lpm_capable:1; unsigned usb2_hw_lpm_besl_capable:1; unsigned usb2_hw_lpm_enabled:1; + unsigned usb2_hw_lpm_allowed:1; unsigned usb3_lpm_enabled:1; int string_langid; From 890dae88672175f1d39f6105444d9bdc71c89258 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Mon, 30 Sep 2013 17:26:31 +0300 Subject: [PATCH 04/27] xhci: Enable LPM support only for hardwired or BESL devices Some usb3 devices falsely claim they support usb2 hardware Link PM when connected to a usb2 port. We only trust hardwired devices or devices with the later BESL LPM support to be LPM enabled as default. [Note: Sarah re-worked the original patch to move the code into the USB core, and updated it to check whether the USB device supports BESL, instead of checking if the xHCI port it's connected to supports BESL encoding.] This patch should be backported to kernels as old as 3.11, that contain the commit a558ccdcc71c7770c5e80c926a31cfe8a3892a09 "usb: xhci: add USB2 Link power management BESL support". Without this fix, some USB 3.0 devices will not enumerate or work properly under USB 2.0 ports on Haswell-ULT systems. Signed-off-by: Mathias Nyman Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/hub.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 9be1a2d5fba6..566ac5531407 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3953,6 +3953,32 @@ static int hub_set_address(struct usb_device *udev, int devnum) return retval; } +/* + * There are reports of USB 3.0 devices that say they support USB 2.0 Link PM + * when they're plugged into a USB 2.0 port, but they don't work when LPM is + * enabled. + * + * Only enable USB 2.0 Link PM if the port is internal (hardwired), or the + * device says it supports the new USB 2.0 Link PM errata by setting the BESL + * support bit in the BOS descriptor. + */ +static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) +{ + int connect_type; + + if (!udev->usb2_hw_lpm_capable) + return; + + connect_type = usb_get_hub_port_connect_type(udev->parent, + udev->portnum); + + if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) || + connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) { + udev->usb2_hw_lpm_allowed = 1; + usb_set_usb2_hardware_lpm(udev, 1); + } +} + /* Reset device, (re)assign address, get device descriptor. * Device connection must be stable, no more debouncing needed. * Returns device in USB_STATE_ADDRESS, except on error. @@ -4246,6 +4272,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, /* notify HCD that we have a device connected and addressed */ if (hcd->driver->update_device) hcd->driver->update_device(hcd, udev); + hub_set_initial_usb2_lpm_policy(udev); fail: if (retval) { hub_port_disable(hub, port1, 0); From f468f7b9467b285b87ea61e2a3c8c0e641117b0e Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 8 Oct 2013 08:28:43 -0700 Subject: [PATCH 05/27] usb: Push USB2 LPM disable on disconnect into USB core. The USB core currently handles enabling and disabling optional USB power management features during device transitions (device suspend/resume, driver bind/unbind, device reset, and device disconnect). Those optional power features include Latency Tolerance Messaging (LTM), USB 3.0 Link PM, and USB 2.0 Link PM. The USB core currently enables LPM on device enumeration and disables USB 2.0 Link PM when the device is reset. However, the xHCI driver disables LPM when the device is disconnected and the device context is freed. Push the call up into the USB core, in order to be consistent with the core handling all power management enabling and disabling. Signed-off-by: Sarah Sharp --- drivers/usb/core/message.c | 4 ++++ drivers/usb/host/xhci.c | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 82927e1ed27d..bb315970e475 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1182,8 +1182,12 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) put_device(&dev->actconfig->interface[i]->dev); dev->actconfig->interface[i] = NULL; } + + if (dev->usb2_hw_lpm_enabled == 1) + usb_set_usb2_hardware_lpm(dev, 0); usb_unlocked_disable_lpm(dev); usb_disable_ltm(dev); + dev->actconfig = NULL; if (dev->state == USB_STATE_CONFIGURED) usb_set_device_state(dev, USB_STATE_ADDRESS); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 0ea253ec813d..e7571c90c40f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3571,11 +3571,6 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); } - if (udev->usb2_hw_lpm_enabled) { - xhci_set_usb2_hardware_lpm(hcd, udev, 0); - udev->usb2_hw_lpm_enabled = 0; - } - spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ state = xhci_readl(xhci, &xhci->op_regs->status); From 5f20cf12a63650afe1871c7b2c89ee84ec3c6182 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 16 Sep 2013 12:01:34 +0530 Subject: [PATCH 06/27] usb: xhci: Staticize xhci_del_comp_mod_timer 'xhci_del_comp_mod_timer' is local to this file. Signed-off-by: Sachin Kamat Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 773a6b28c4f1..5dd7b7de6633 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -524,7 +524,8 @@ static void xhci_hub_report_usb3_link_state(u32 *status, u32 status_reg) * the compliance mode timer is deleted. A port won't enter * compliance mode if it has previously entered U0. */ -void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex) +static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, + u16 wIndex) { u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1); bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0); From d194c031994d3fc1038fa09e9e92d9be24a21921 Mon Sep 17 00:00:00 2001 From: xiao jin Date: Fri, 11 Oct 2013 08:57:03 +0800 Subject: [PATCH 07/27] xhci: correct the usage of USB_CTRL_SET_TIMEOUT The usage of USB_CTRL_SET_TIMEOUT in xhci is incorrect. The definition of USB_CTRL_SET_TIMEOUT is 5000ms. The input timeout to wait_for_completion_interruptible_timeout is jiffies. That makes the timeout be longer than what we want, such as 50s in some platform. The patch is to use XHCI_CMD_DEFAULT_TIMEOUT instead of USB_CTRL_SET_TIMEOUT as command completion event timeout. Signed-off-by: xiao jin Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 2 +- drivers/usb/host/xhci.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 5dd7b7de6633..0c802081e33b 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -296,7 +296,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) /* Wait for last stop endpoint command to finish */ timeleft = wait_for_completion_interruptible_timeout( cmd->completion, - USB_CTRL_SET_TIMEOUT); + XHCI_CMD_DEFAULT_TIMEOUT); if (timeleft <= 0) { xhci_warn(xhci, "%s while waiting for stop endpoint command\n", timeleft == 0 ? "Timeout" : "Signal"); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index e7571c90c40f..d3f6abbda67e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3447,7 +3447,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) /* Wait for the Reset Device command to finish */ timeleft = wait_for_completion_interruptible_timeout( reset_device_cmd->completion, - USB_CTRL_SET_TIMEOUT); + XHCI_CMD_DEFAULT_TIMEOUT); if (timeleft <= 0) { xhci_warn(xhci, "%s while waiting for reset device command\n", timeleft == 0 ? "Timeout" : "Signal"); From 07a37e9e425a3176875a0c91e59ae8bc65d2ddb9 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:45 +0300 Subject: [PATCH 08/27] xhci: remove unused argument from xhci_giveback_urb_in_irq() This patch removes the "adjective" argument from xhci_giveback_urb_in_irq(), since it is not used in the function anymore. Signed-off-by: Xenia Ragiadakou Acked-by: Sarah Sharp Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 6bfbd80ec2b9..9a6b18e31d83 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -726,7 +726,7 @@ static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci, /* Must be called with xhci->lock held in interrupt context */ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, - struct xhci_td *cur_td, int status, char *adjective) + struct xhci_td *cur_td, int status) { struct usb_hcd *hcd; struct urb *urb; @@ -891,7 +891,7 @@ remove_finished_td: /* Doesn't matter what we pass for status, since the core will * just overwrite it (because the URB has been unlinked). */ - xhci_giveback_urb_in_irq(xhci, cur_td, 0, "cancelled"); + xhci_giveback_urb_in_irq(xhci, cur_td, 0); /* Stop processing the cancelled list if the watchdog timer is * running. @@ -1001,7 +1001,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) if (!list_empty(&cur_td->cancelled_td_list)) list_del_init(&cur_td->cancelled_td_list); xhci_giveback_urb_in_irq(xhci, cur_td, - -ESHUTDOWN, "killed"); + -ESHUTDOWN); } while (!list_empty(&temp_ep->cancelled_td_list)) { cur_td = list_first_entry( @@ -1010,7 +1010,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) cancelled_td_list); list_del_init(&cur_td->cancelled_td_list); xhci_giveback_urb_in_irq(xhci, cur_td, - -ESHUTDOWN, "killed"); + -ESHUTDOWN); } } } From 60b9593cf225827b6b887db4061ea9ece052fbf3 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:46 +0300 Subject: [PATCH 09/27] xhci: rename existing Command Completion Event handlers This patch renames the function handlers of a triggered Command Completion Event that correspond to each command type into 'xhci_handle_cmd_'. That is done to give a consistent naming space to all the functions that handle Command Completion Events and that will permit the code reader to reference to them more easily. Signed-off-by: Xenia Ragiadakou Acked-by: Sarah Sharp Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 9a6b18e31d83..f5d504740281 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -765,7 +765,7 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, * 2. Otherwise, we turn all the TRBs in the TD into No-op TRBs (with the chain * bit cleared) so that the HW will skip over them. */ -static void handle_stopped_endpoint(struct xhci_hcd *xhci, +static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, union xhci_trb *trb, struct xhci_event_cmd *event) { unsigned int slot_id; @@ -1077,9 +1077,8 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, * endpoint doorbell to restart the ring, but only if there aren't more * cancellations pending. */ -static void handle_set_deq_completion(struct xhci_hcd *xhci, - struct xhci_event_cmd *event, - union xhci_trb *trb) +static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, + struct xhci_event_cmd *event, union xhci_trb *trb) { unsigned int slot_id; unsigned int ep_index; @@ -1171,9 +1170,8 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } -static void handle_reset_ep_completion(struct xhci_hcd *xhci, - struct xhci_event_cmd *event, - union xhci_trb *trb) +static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, + struct xhci_event_cmd *event, union xhci_trb *trb) { int slot_id; unsigned int ep_index; @@ -1517,15 +1515,15 @@ bandwidth_change: complete(&xhci->addr_dev); break; case TRB_TYPE(TRB_STOP_RING): - handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue, event); + xhci_handle_cmd_stop_ep(xhci, xhci->cmd_ring->dequeue, event); break; case TRB_TYPE(TRB_SET_DEQ): - handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue); + xhci_handle_cmd_set_deq(xhci, event, xhci->cmd_ring->dequeue); break; case TRB_TYPE(TRB_CMD_NOOP): break; case TRB_TYPE(TRB_RESET_EP): - handle_reset_ep_completion(xhci, event, xhci->cmd_ring->dequeue); + xhci_handle_cmd_reset_ep(xhci, event, xhci->cmd_ring->dequeue); break; case TRB_TYPE(TRB_RESET_DEV): xhci_dbg(xhci, "Completed reset device command.\n"); From b244b431f89e152dd4bf35d71786f1c0eb8cba7e Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:47 +0300 Subject: [PATCH 10/27] xhci: refactor TRB_ENABLE_SLOT case into function The function that handles xHCI command completion is much too long and there is need to be broken up into individual functions for each command completion to improve code readablity. This patch refactors the code in TRB_ENABLE_SLOT switch case in handle_cmd_completion() into a fuction named xhci_handle_cmd_enable_slot(). Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f5d504740281..4cad420d7708 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1384,6 +1384,16 @@ static int handle_stopped_cmd_ring(struct xhci_hcd *xhci, return cur_trb_is_good; } +static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id, + u32 cmd_comp_code) +{ + if (cmd_comp_code == COMP_SUCCESS) + xhci->slot_id = slot_id; + else + xhci->slot_id = 0; + complete(&xhci->addr_dev); +} + static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) { @@ -1437,11 +1447,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]) & TRB_TYPE_BITMASK) { case TRB_TYPE(TRB_ENABLE_SLOT): - if (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_SUCCESS) - xhci->slot_id = slot_id; - else - xhci->slot_id = 0; - complete(&xhci->addr_dev); + xhci_handle_cmd_enable_slot(xhci, slot_id, + GET_COMP_CODE(le32_to_cpu(event->status))); break; case TRB_TYPE(TRB_DISABLE_SLOT): if (xhci->devs[slot_id]) { From 6c02dd147a7a3dbbfc7451abbf58b3b2a2a5483d Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:48 +0300 Subject: [PATCH 11/27] xhci: refactor TRB_DISABLE_SLOT case into function The function that handles xHCI command completion is much too long and there is need to be broken up into individual functions for each command completion to improve code readablity. This patch refactors the code in TRB_DISABLE_SLOT switch case in handle_cmd_completion() into a fuction named xhci_handle_cmd_disable_slot(). Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 4cad420d7708..0a4c86eeb385 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1394,6 +1394,19 @@ static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id, complete(&xhci->addr_dev); } +static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id) +{ + struct xhci_virt_device *virt_dev; + + virt_dev = xhci->devs[slot_id]; + if (!virt_dev) + return; + if (xhci->quirks & XHCI_EP_LIMIT_QUIRK) + /* Delete default control endpoint resources */ + xhci_free_device_endpoint_resources(xhci, virt_dev, true); + xhci_free_virt_device(xhci, slot_id); +} + static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) { @@ -1451,13 +1464,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, GET_COMP_CODE(le32_to_cpu(event->status))); break; case TRB_TYPE(TRB_DISABLE_SLOT): - if (xhci->devs[slot_id]) { - if (xhci->quirks & XHCI_EP_LIMIT_QUIRK) - /* Delete default control endpoint resources */ - xhci_free_device_endpoint_resources(xhci, - xhci->devs[slot_id], true); - xhci_free_virt_device(xhci, slot_id); - } + xhci_handle_cmd_disable_slot(xhci, slot_id); break; case TRB_TYPE(TRB_CONFIG_EP): virt_dev = xhci->devs[slot_id]; From 9b3103ac9d19525781c297c4fb1e544e077c8901 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:49 +0300 Subject: [PATCH 12/27] xhci: refactor TRB_ADDR_DEV case into function The function that handles xHCI command completion is much too long and there is need to be broken up into individual functions for each command completion to improve code readablity. This patch refactors the code in TRB_ADDR_DEV switch case in handle_cmd_completion() into a fuction named xhci_handle_cmd_addr_dev(). Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 0a4c86eeb385..e3b61b834aeb 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1407,6 +1407,13 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id) xhci_free_virt_device(xhci, slot_id); } +static void xhci_handle_cmd_addr_dev(struct xhci_hcd *xhci, int slot_id, + u32 cmd_comp_code) +{ + xhci->devs[slot_id]->cmd_status = cmd_comp_code; + complete(&xhci->addr_dev); +} + static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) { @@ -1525,8 +1532,8 @@ bandwidth_change: complete(&xhci->devs[slot_id]->cmd_completion); break; case TRB_TYPE(TRB_ADDR_DEV): - xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status)); - complete(&xhci->addr_dev); + xhci_handle_cmd_addr_dev(xhci, slot_id, + GET_COMP_CODE(le32_to_cpu(event->status))); break; case TRB_TYPE(TRB_STOP_RING): xhci_handle_cmd_stop_ep(xhci, xhci->cmd_ring->dequeue, event); From 20e7acb13ff48fbc884d5918c3697c27de63922a Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:50 +0300 Subject: [PATCH 13/27] xhci: use completion event's slot id rather than dig it out of command Since the slot id retrieved from the Reset Device TRB matches the slot id in the command completion event, which is available, there is no need to determine it again. This patch removes the uneccessary reassignment to slot id and adds a WARN_ON in case the two Slot ID fields differ (although according xhci spec rev1.0 they should not differ). Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e3b61b834aeb..88939b798ac6 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1547,9 +1547,9 @@ bandwidth_change: xhci_handle_cmd_reset_ep(xhci, event, xhci->cmd_ring->dequeue); break; case TRB_TYPE(TRB_RESET_DEV): + WARN_ON(slot_id != TRB_TO_SLOT_ID( + le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]))); xhci_dbg(xhci, "Completed reset device command.\n"); - slot_id = TRB_TO_SLOT_ID( - le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])); virt_dev = xhci->devs[slot_id]; if (virt_dev) handle_cmd_in_cmd_wait_list(xhci, virt_dev, event); From f681321b40d77fa360b8b2155af95cd5959d2dde Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:51 +0300 Subject: [PATCH 14/27] xhci: refactor TRB_RESET_DEV case into function The function that handles xHCI command completion is much too long and there is need to be broken up into individual functions for each command completion to improve code readablity. This patch refactors the code in TRB_RESET_DEV switch case in handle_cmd_completion() into a fuction named xhci_handle_cmd_reset_dev(). Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 88939b798ac6..64442ac995d9 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1414,6 +1414,20 @@ static void xhci_handle_cmd_addr_dev(struct xhci_hcd *xhci, int slot_id, complete(&xhci->addr_dev); } +static void xhci_handle_cmd_reset_dev(struct xhci_hcd *xhci, int slot_id, + struct xhci_event_cmd *event) +{ + struct xhci_virt_device *virt_dev; + + xhci_dbg(xhci, "Completed reset device command.\n"); + virt_dev = xhci->devs[slot_id]; + if (virt_dev) + handle_cmd_in_cmd_wait_list(xhci, virt_dev, event); + else + xhci_warn(xhci, "Reset device command completion " + "for disabled slot %u\n", slot_id); +} + static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) { @@ -1549,13 +1563,7 @@ bandwidth_change: case TRB_TYPE(TRB_RESET_DEV): WARN_ON(slot_id != TRB_TO_SLOT_ID( le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]))); - xhci_dbg(xhci, "Completed reset device command.\n"); - virt_dev = xhci->devs[slot_id]; - if (virt_dev) - handle_cmd_in_cmd_wait_list(xhci, virt_dev, event); - else - xhci_warn(xhci, "Reset device command completion " - "for disabled slot %u\n", slot_id); + xhci_handle_cmd_reset_dev(xhci, slot_id, event); break; case TRB_TYPE(TRB_NEC_GET_FW): if (!(xhci->quirks & XHCI_NEC_HOST)) { From 2c070821e2b2ce2e75861b2b56062df1757b9183 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:52 +0300 Subject: [PATCH 15/27] xhci: refactor TRB_NEC_GET_FW case into function The function that handles xHCI command completion is much too long and there is need to be broken up into individual functions for each command completion to improve code readablity. This patch refactors the code in TRB_NEC_GET_FW switch case in handle_cmd_completion() into a fuction named xhci_handle_cmd_nec_get_fw(). Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 64442ac995d9..f926a81c0417 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1428,6 +1428,19 @@ static void xhci_handle_cmd_reset_dev(struct xhci_hcd *xhci, int slot_id, "for disabled slot %u\n", slot_id); } +static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci, + struct xhci_event_cmd *event) +{ + if (!(xhci->quirks & XHCI_NEC_HOST)) { + xhci->error_bitmask |= 1 << 6; + return; + } + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "NEC firmware version %2x.%02x", + NEC_FW_MAJOR(le32_to_cpu(event->status)), + NEC_FW_MINOR(le32_to_cpu(event->status))); +} + static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) { @@ -1566,14 +1579,7 @@ bandwidth_change: xhci_handle_cmd_reset_dev(xhci, slot_id, event); break; case TRB_TYPE(TRB_NEC_GET_FW): - if (!(xhci->quirks & XHCI_NEC_HOST)) { - xhci->error_bitmask |= 1 << 6; - break; - } - xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "NEC firmware version %2x.%02x", - NEC_FW_MAJOR(le32_to_cpu(event->status)), - NEC_FW_MINOR(le32_to_cpu(event->status))); + xhci_handle_cmd_nec_get_fw(xhci, event); break; default: /* Skip over unknown commands on the event ring */ From 07948a8da6241984b8359830498855573d552d0d Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:53 +0300 Subject: [PATCH 16/27] xhci: refactor TRB_EVAL_CONTEXT case into function The function that handles xHCI command completion is much too long and there is need to be broken up into individual functions for each command completion to improve code readablity. This patch refactors the code in TRB_EVAL_CONTEXT switch case in handle_cmd_completion() into a fuction named xhci_handle_cmd_eval_ctx(). Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f926a81c0417..39a2bfbeefb5 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1407,6 +1407,18 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id) xhci_free_virt_device(xhci, slot_id); } +static void xhci_handle_cmd_eval_ctx(struct xhci_hcd *xhci, int slot_id, + struct xhci_event_cmd *event, u32 cmd_comp_code) +{ + struct xhci_virt_device *virt_dev; + + virt_dev = xhci->devs[slot_id]; + if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) + return; + virt_dev->cmd_status = cmd_comp_code; + complete(&virt_dev->cmd_completion); +} + static void xhci_handle_cmd_addr_dev(struct xhci_hcd *xhci, int slot_id, u32 cmd_comp_code) { @@ -1552,11 +1564,8 @@ bandwidth_change: complete(&xhci->devs[slot_id]->cmd_completion); break; case TRB_TYPE(TRB_EVAL_CONTEXT): - virt_dev = xhci->devs[slot_id]; - if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) - break; - xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status)); - complete(&xhci->devs[slot_id]->cmd_completion); + xhci_handle_cmd_eval_ctx(xhci, slot_id, event, + GET_COMP_CODE(le32_to_cpu(event->status))); break; case TRB_TYPE(TRB_ADDR_DEV): xhci_handle_cmd_addr_dev(xhci, slot_id, From fd54498733f8c372deca99892ae8cae0799dfe68 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:54 +0300 Subject: [PATCH 17/27] xhci: remove unused 'ep_ring' variable in handle_cmd_completion() This patch removes the variable 'ep_ring' that is assigned in TRB_CONFIG_EP switch case but never used. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 39a2bfbeefb5..0bef11b3dd22 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1462,7 +1462,6 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_input_control_ctx *ctrl_ctx; struct xhci_virt_device *virt_dev; unsigned int ep_index; - struct xhci_ring *ep_ring; unsigned int ep_state; cmd_dma = le64_to_cpu(event->cmd_trb); @@ -1542,7 +1541,6 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, ep_index != (unsigned int) -1 && le32_to_cpu(ctrl_ctx->add_flags) - SLOT_FLAG == le32_to_cpu(ctrl_ctx->drop_flags)) { - ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; if (!(ep_state & EP_HALTED)) goto bandwidth_change; From 6ed46d3337b1f4a8f9fa7438589cab5f1bb75e98 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:55 +0300 Subject: [PATCH 18/27] xhci: refactor TRB_CONFIG_EP case into function The function that handles xHCI command completion is much too long and there is need to be broken up into individual functions for each command completion to improve code readablity. This patch refactors the code in TRB_CONFIG_EP switch case, in handle_cmd_completion(), into a fuction named xhci_handle_cmd_config_ep(). There were added two additional variables, 'add_flags' and 'drop_flags', to reduce line length below 80 chars and improve code readability. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 114 +++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 0bef11b3dd22..3e8053284752 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1407,6 +1407,66 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id) xhci_free_virt_device(xhci, slot_id); } +static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id, + struct xhci_event_cmd *event, u32 cmd_comp_code) +{ + struct xhci_virt_device *virt_dev; + struct xhci_input_control_ctx *ctrl_ctx; + unsigned int ep_index; + unsigned int ep_state; + u32 add_flags, drop_flags; + + virt_dev = xhci->devs[slot_id]; + if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) + return; + /* + * Configure endpoint commands can come from the USB core + * configuration or alt setting changes, or because the HW + * needed an extra configure endpoint command after a reset + * endpoint command or streams were being configured. + * If the command was for a halted endpoint, the xHCI driver + * is not waiting on the configure endpoint command. + */ + ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); + if (!ctrl_ctx) { + xhci_warn(xhci, "Could not get input context, bad type.\n"); + return; + } + + add_flags = le32_to_cpu(ctrl_ctx->add_flags); + drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); + /* Input ctx add_flags are the endpoint index plus one */ + ep_index = xhci_last_valid_endpoint(add_flags) - 1; + + /* A usb_set_interface() call directly after clearing a halted + * condition may race on this quirky hardware. Not worth + * worrying about, since this is prototype hardware. Not sure + * if this will work for streams, but streams support was + * untested on this prototype. + */ + if (xhci->quirks & XHCI_RESET_EP_QUIRK && + ep_index != (unsigned int) -1 && + add_flags - SLOT_FLAG == drop_flags) { + ep_state = virt_dev->eps[ep_index].ep_state; + if (!(ep_state & EP_HALTED)) + goto bandwidth_change; + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Completed config ep cmd - " + "last ep index = %d, state = %d", + ep_index, ep_state); + /* Clear internal halted state and restart ring(s) */ + virt_dev->eps[ep_index].ep_state &= ~EP_HALTED; + ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + return; + } +bandwidth_change: + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Completed config ep cmd"); + virt_dev->cmd_status = cmd_comp_code; + complete(&virt_dev->cmd_completion); + return; +} + static void xhci_handle_cmd_eval_ctx(struct xhci_hcd *xhci, int slot_id, struct xhci_event_cmd *event, u32 cmd_comp_code) { @@ -1459,10 +1519,6 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); u64 cmd_dma; dma_addr_t cmd_dequeue_dma; - struct xhci_input_control_ctx *ctrl_ctx; - struct xhci_virt_device *virt_dev; - unsigned int ep_index; - unsigned int ep_state; cmd_dma = le64_to_cpu(event->cmd_trb); cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, @@ -1512,54 +1568,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, xhci_handle_cmd_disable_slot(xhci, slot_id); break; case TRB_TYPE(TRB_CONFIG_EP): - virt_dev = xhci->devs[slot_id]; - if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) - break; - /* - * Configure endpoint commands can come from the USB core - * configuration or alt setting changes, or because the HW - * needed an extra configure endpoint command after a reset - * endpoint command or streams were being configured. - * If the command was for a halted endpoint, the xHCI driver - * is not waiting on the configure endpoint command. - */ - ctrl_ctx = xhci_get_input_control_ctx(xhci, - virt_dev->in_ctx); - if (!ctrl_ctx) { - xhci_warn(xhci, "Could not get input context, bad type.\n"); - break; - } - /* Input ctx add_flags are the endpoint index plus one */ - ep_index = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)) - 1; - /* A usb_set_interface() call directly after clearing a halted - * condition may race on this quirky hardware. Not worth - * worrying about, since this is prototype hardware. Not sure - * if this will work for streams, but streams support was - * untested on this prototype. - */ - if (xhci->quirks & XHCI_RESET_EP_QUIRK && - ep_index != (unsigned int) -1 && - le32_to_cpu(ctrl_ctx->add_flags) - SLOT_FLAG == - le32_to_cpu(ctrl_ctx->drop_flags)) { - ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; - if (!(ep_state & EP_HALTED)) - goto bandwidth_change; - xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "Completed config ep cmd - " - "last ep index = %d, state = %d", - ep_index, ep_state); - /* Clear internal halted state and restart ring(s) */ - xhci->devs[slot_id]->eps[ep_index].ep_state &= - ~EP_HALTED; - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); - break; - } -bandwidth_change: - xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, - "Completed config ep cmd"); - xhci->devs[slot_id]->cmd_status = - GET_COMP_CODE(le32_to_cpu(event->status)); - complete(&xhci->devs[slot_id]->cmd_completion); + xhci_handle_cmd_config_ep(xhci, slot_id, event, + GET_COMP_CODE(le32_to_cpu(event->status))); break; case TRB_TYPE(TRB_EVAL_CONTEXT): xhci_handle_cmd_eval_ctx(xhci, slot_id, event, From e7a79a1d6af31c050b4264099c4ab0cbee9122b8 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:56 +0300 Subject: [PATCH 19/27] xhci: add variable 'cmd_comp_code' in handle_cmd_completion() This patch adds a new variable 'cmd_comp_code' to hold the command completion status code aiming to reduce code duplication and to improve code readability. Signed-off-by: Xenia Ragiadakou Acked-by: Sarah Sharp Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3e8053284752..6826ee1fb125 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1519,6 +1519,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); u64 cmd_dma; dma_addr_t cmd_dequeue_dma; + u32 cmd_comp_code; cmd_dma = le64_to_cpu(event->cmd_trb); cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, @@ -1537,16 +1538,15 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, trace_xhci_cmd_completion(&xhci->cmd_ring->dequeue->generic, (struct xhci_generic_trb *) event); - if ((GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_ABORT) || - (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_STOP)) { + cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); + if (cmd_comp_code == COMP_CMD_ABORT || cmd_comp_code == COMP_CMD_STOP) { /* If the return value is 0, we think the trb pointed by * command ring dequeue pointer is a good trb. The good * trb means we don't want to cancel the trb, but it have * been stopped by host. So we should handle it normally. * Otherwise, driver should invoke inc_deq() and return. */ - if (handle_stopped_cmd_ring(xhci, - GET_COMP_CODE(le32_to_cpu(event->status)))) { + if (handle_stopped_cmd_ring(xhci, cmd_comp_code)) { inc_deq(xhci, xhci->cmd_ring); return; } @@ -1561,23 +1561,19 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]) & TRB_TYPE_BITMASK) { case TRB_TYPE(TRB_ENABLE_SLOT): - xhci_handle_cmd_enable_slot(xhci, slot_id, - GET_COMP_CODE(le32_to_cpu(event->status))); + xhci_handle_cmd_enable_slot(xhci, slot_id, cmd_comp_code); break; case TRB_TYPE(TRB_DISABLE_SLOT): xhci_handle_cmd_disable_slot(xhci, slot_id); break; case TRB_TYPE(TRB_CONFIG_EP): - xhci_handle_cmd_config_ep(xhci, slot_id, event, - GET_COMP_CODE(le32_to_cpu(event->status))); + xhci_handle_cmd_config_ep(xhci, slot_id, event, cmd_comp_code); break; case TRB_TYPE(TRB_EVAL_CONTEXT): - xhci_handle_cmd_eval_ctx(xhci, slot_id, event, - GET_COMP_CODE(le32_to_cpu(event->status))); + xhci_handle_cmd_eval_ctx(xhci, slot_id, event, cmd_comp_code); break; case TRB_TYPE(TRB_ADDR_DEV): - xhci_handle_cmd_addr_dev(xhci, slot_id, - GET_COMP_CODE(le32_to_cpu(event->status))); + xhci_handle_cmd_addr_dev(xhci, slot_id, cmd_comp_code); break; case TRB_TYPE(TRB_STOP_RING): xhci_handle_cmd_stop_ep(xhci, xhci->cmd_ring->dequeue, event); From 9124b121e317f3ee3b442b0a3a508c9c13f602e2 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:57 +0300 Subject: [PATCH 20/27] xhci: add variable 'cmd_trb' in handle_cmd_completion() This patch adds a new variable 'cmd_trb' to hold the address of the command TRB, that is associated with the command completion event, and to replace repetitions of xhci->cmd_ring->dequeue into the code. Signed-off-by: Xenia Ragiadakou Acked-by: Sarah Sharp Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 6826ee1fb125..fd2f97c37136 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1520,10 +1520,12 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, u64 cmd_dma; dma_addr_t cmd_dequeue_dma; u32 cmd_comp_code; + union xhci_trb *cmd_trb; cmd_dma = le64_to_cpu(event->cmd_trb); + cmd_trb = xhci->cmd_ring->dequeue; cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, - xhci->cmd_ring->dequeue); + cmd_trb); /* Is the command ring deq ptr out of sync with the deq seg ptr? */ if (cmd_dequeue_dma == 0) { xhci->error_bitmask |= 1 << 4; @@ -1535,8 +1537,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, return; } - trace_xhci_cmd_completion(&xhci->cmd_ring->dequeue->generic, - (struct xhci_generic_trb *) event); + trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event); cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); if (cmd_comp_code == COMP_CMD_ABORT || cmd_comp_code == COMP_CMD_STOP) { @@ -1558,7 +1559,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, return; } - switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]) + switch (le32_to_cpu(cmd_trb->generic.field[3]) & TRB_TYPE_BITMASK) { case TRB_TYPE(TRB_ENABLE_SLOT): xhci_handle_cmd_enable_slot(xhci, slot_id, cmd_comp_code); @@ -1576,19 +1577,19 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, xhci_handle_cmd_addr_dev(xhci, slot_id, cmd_comp_code); break; case TRB_TYPE(TRB_STOP_RING): - xhci_handle_cmd_stop_ep(xhci, xhci->cmd_ring->dequeue, event); + xhci_handle_cmd_stop_ep(xhci, cmd_trb, event); break; case TRB_TYPE(TRB_SET_DEQ): - xhci_handle_cmd_set_deq(xhci, event, xhci->cmd_ring->dequeue); + xhci_handle_cmd_set_deq(xhci, event, cmd_trb); break; case TRB_TYPE(TRB_CMD_NOOP): break; case TRB_TYPE(TRB_RESET_EP): - xhci_handle_cmd_reset_ep(xhci, event, xhci->cmd_ring->dequeue); + xhci_handle_cmd_reset_ep(xhci, event, cmd_trb); break; case TRB_TYPE(TRB_RESET_DEV): WARN_ON(slot_id != TRB_TO_SLOT_ID( - le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]))); + le32_to_cpu(cmd_trb->generic.field[3]))); xhci_handle_cmd_reset_dev(xhci, slot_id, event); break; case TRB_TYPE(TRB_NEC_GET_FW): From b54fc46dce0c45ce42a012f733ab2abf4b9be5fe Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:58 +0300 Subject: [PATCH 21/27] xhci: add variable 'cmd_type' in handle_cmd_completion() This patch adds a new variable 'cmd_type' to hold the command type so that switch cases can be simplified by removing TRB_TYPE() macro improving code readability. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index fd2f97c37136..e0b59208f231 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1521,6 +1521,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, dma_addr_t cmd_dequeue_dma; u32 cmd_comp_code; union xhci_trb *cmd_trb; + u32 cmd_type; cmd_dma = le64_to_cpu(event->cmd_trb); cmd_trb = xhci->cmd_ring->dequeue; @@ -1559,40 +1560,40 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, return; } - switch (le32_to_cpu(cmd_trb->generic.field[3]) - & TRB_TYPE_BITMASK) { - case TRB_TYPE(TRB_ENABLE_SLOT): + cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3])); + switch (cmd_type) { + case TRB_ENABLE_SLOT: xhci_handle_cmd_enable_slot(xhci, slot_id, cmd_comp_code); break; - case TRB_TYPE(TRB_DISABLE_SLOT): + case TRB_DISABLE_SLOT: xhci_handle_cmd_disable_slot(xhci, slot_id); break; - case TRB_TYPE(TRB_CONFIG_EP): + case TRB_CONFIG_EP: xhci_handle_cmd_config_ep(xhci, slot_id, event, cmd_comp_code); break; - case TRB_TYPE(TRB_EVAL_CONTEXT): + case TRB_EVAL_CONTEXT: xhci_handle_cmd_eval_ctx(xhci, slot_id, event, cmd_comp_code); break; - case TRB_TYPE(TRB_ADDR_DEV): + case TRB_ADDR_DEV: xhci_handle_cmd_addr_dev(xhci, slot_id, cmd_comp_code); break; - case TRB_TYPE(TRB_STOP_RING): + case TRB_STOP_RING: xhci_handle_cmd_stop_ep(xhci, cmd_trb, event); break; - case TRB_TYPE(TRB_SET_DEQ): + case TRB_SET_DEQ: xhci_handle_cmd_set_deq(xhci, event, cmd_trb); break; - case TRB_TYPE(TRB_CMD_NOOP): + case TRB_CMD_NOOP: break; - case TRB_TYPE(TRB_RESET_EP): + case TRB_RESET_EP: xhci_handle_cmd_reset_ep(xhci, event, cmd_trb); break; - case TRB_TYPE(TRB_RESET_DEV): + case TRB_RESET_DEV: WARN_ON(slot_id != TRB_TO_SLOT_ID( le32_to_cpu(cmd_trb->generic.field[3]))); xhci_handle_cmd_reset_dev(xhci, slot_id, event); break; - case TRB_TYPE(TRB_NEC_GET_FW): + case TRB_NEC_GET_FW: xhci_handle_cmd_nec_get_fw(xhci, event); break; default: From bc752bde108400c80735ef72987acbca0eecefda Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:29:59 +0300 Subject: [PATCH 22/27] xhci: replace 'xhci->cmd_ring->dequeue' with 'trb' in stop_ep cmd handler This patch replaces 'xhci->cmd_ring->dequeue' with 'trb', the address of the command TRB, since it is available to reduce line length. Signed-off-by: Xenia Ragiadakou Acked-by: Sarah Sharp Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e0b59208f231..61243f59f914 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -779,10 +779,8 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, struct xhci_dequeue_state deq_state; - if (unlikely(TRB_TO_SUSPEND_PORT( - le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])))) { - slot_id = TRB_TO_SLOT_ID( - le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])); + if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) { + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); virt_dev = xhci->devs[slot_id]; if (virt_dev) handle_cmd_in_cmd_wait_list(xhci, virt_dev, From b8200c9479b8046a16a71648607fa796b548218b Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:30:00 +0300 Subject: [PATCH 23/27] xhci: add argument 'slot_id' in stop_ep, set_deq and reset_ep cmd handlers Since the Slot ID field in the command completion event matches the Slot ID field in the associated command TRB for the Stop Endpoint, Set Dequeue Pointer and Reset Endpoint commands, this patch adds in the handlers of their completion events a 'slot_id' argument and removes the slot id calculation in each of them. Also, a WARN_ON() was added in case the slot ids reported by command TRB and event TRB differ (although according to xhci spec rev1.0 that should not happen) Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 61243f59f914..b98c9852172a 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -765,10 +765,9 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, * 2. Otherwise, we turn all the TRBs in the TD into No-op TRBs (with the chain * bit cleared) so that the HW will skip over them. */ -static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, +static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, union xhci_trb *trb, struct xhci_event_cmd *event) { - unsigned int slot_id; unsigned int ep_index; struct xhci_virt_device *virt_dev; struct xhci_ring *ep_ring; @@ -780,7 +779,6 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, struct xhci_dequeue_state deq_state; if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) { - slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); virt_dev = xhci->devs[slot_id]; if (virt_dev) handle_cmd_in_cmd_wait_list(xhci, virt_dev, @@ -793,7 +791,6 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, } memset(&deq_state, 0, sizeof(deq_state)); - slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); ep = &xhci->devs[slot_id]->eps[ep_index]; @@ -1075,10 +1072,9 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, * endpoint doorbell to restart the ring, but only if there aren't more * cancellations pending. */ -static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, +static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, struct xhci_event_cmd *event, union xhci_trb *trb) { - unsigned int slot_id; unsigned int ep_index; unsigned int stream_id; struct xhci_ring *ep_ring; @@ -1086,7 +1082,6 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; - slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); dev = xhci->devs[slot_id]; @@ -1168,13 +1163,11 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } -static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, +static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, struct xhci_event_cmd *event, union xhci_trb *trb) { - int slot_id; unsigned int ep_index; - slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); /* This command will only fail if the endpoint wasn't halted, * but we don't care. @@ -1576,15 +1569,21 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, xhci_handle_cmd_addr_dev(xhci, slot_id, cmd_comp_code); break; case TRB_STOP_RING: - xhci_handle_cmd_stop_ep(xhci, cmd_trb, event); + WARN_ON(slot_id != TRB_TO_SLOT_ID( + le32_to_cpu(cmd_trb->generic.field[3]))); + xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event); break; case TRB_SET_DEQ: - xhci_handle_cmd_set_deq(xhci, event, cmd_trb); + WARN_ON(slot_id != TRB_TO_SLOT_ID( + le32_to_cpu(cmd_trb->generic.field[3]))); + xhci_handle_cmd_set_deq(xhci, slot_id, event, cmd_trb); break; case TRB_CMD_NOOP: break; case TRB_RESET_EP: - xhci_handle_cmd_reset_ep(xhci, event, cmd_trb); + WARN_ON(slot_id != TRB_TO_SLOT_ID( + le32_to_cpu(cmd_trb->generic.field[3]))); + xhci_handle_cmd_reset_ep(xhci, slot_id, event, cmd_trb); break; case TRB_RESET_DEV: WARN_ON(slot_id != TRB_TO_SLOT_ID( From c69a059783b241c42188d472e2e9460d3cd4a4cc Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Mon, 9 Sep 2013 13:30:01 +0300 Subject: [PATCH 24/27] xhci: replace 'event' with 'cmd_comp_code' in set_deq and reset_ep handlers This patch replaces the 'event' argument of xhci_handle_cmd_set_deq() and xhci_handle_cmd_reset_ep(), which is used to retrieve the command completion status code, with the cmd_comp_code directly, since it is available. Signed-off-by: Xenia Ragiadakou Acked-by: Sarah Sharp Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b98c9852172a..e57163f952ed 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1073,7 +1073,7 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, * cancellations pending. */ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, - struct xhci_event_cmd *event, union xhci_trb *trb) + union xhci_trb *trb, u32 cmd_comp_code) { unsigned int ep_index; unsigned int stream_id; @@ -1099,11 +1099,11 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); - if (GET_COMP_CODE(le32_to_cpu(event->status)) != COMP_SUCCESS) { + if (cmd_comp_code != COMP_SUCCESS) { unsigned int ep_state; unsigned int slot_state; - switch (GET_COMP_CODE(le32_to_cpu(event->status))) { + switch (cmd_comp_code) { case COMP_TRB_ERR: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because " "of stream ID configuration\n"); @@ -1126,7 +1126,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, default: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown " "completion code of %u.\n", - GET_COMP_CODE(le32_to_cpu(event->status))); + cmd_comp_code); break; } /* OK what do we do now? The endpoint state is hosed, and we @@ -1164,7 +1164,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, } static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, - struct xhci_event_cmd *event, union xhci_trb *trb) + union xhci_trb *trb, u32 cmd_comp_code) { unsigned int ep_index; @@ -1173,8 +1173,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, * but we don't care. */ xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, - "Ignoring reset ep completion code of %u", - GET_COMP_CODE(le32_to_cpu(event->status))); + "Ignoring reset ep completion code of %u", cmd_comp_code); /* HW with the reset endpoint quirk needs to have a configure endpoint * command complete before the endpoint can be used. Queue that here @@ -1576,14 +1575,14 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, case TRB_SET_DEQ: WARN_ON(slot_id != TRB_TO_SLOT_ID( le32_to_cpu(cmd_trb->generic.field[3]))); - xhci_handle_cmd_set_deq(xhci, slot_id, event, cmd_trb); + xhci_handle_cmd_set_deq(xhci, slot_id, cmd_trb, cmd_comp_code); break; case TRB_CMD_NOOP: break; case TRB_RESET_EP: WARN_ON(slot_id != TRB_TO_SLOT_ID( le32_to_cpu(cmd_trb->generic.field[3]))); - xhci_handle_cmd_reset_ep(xhci, slot_id, event, cmd_trb); + xhci_handle_cmd_reset_ep(xhci, slot_id, cmd_trb, cmd_comp_code); break; case TRB_RESET_DEV: WARN_ON(slot_id != TRB_TO_SLOT_ID( From fd1ac4cf524018198373c5eb90ca0e7968c5bbb8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 7 Oct 2013 11:58:20 -0700 Subject: [PATCH 25/27] usb: hub_activate kill an 'else' Remove a few extra lines and make it clear that all implementations disable the port by sharing the same line of code. Acked-by: Alan Stern Signed-off-by: Dan Williams Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 566ac5531407..3da607490d18 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1112,16 +1112,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* * USB3 protocol ports will automatically transition * to Enabled state when detect an USB3.0 device attach. - * Do not disable USB3 protocol ports. + * Do not disable USB3 protocol ports, just pretend + * power was lost */ - if (!hub_is_superspeed(hdev)) { + portstatus &= ~USB_PORT_STAT_ENABLE; + if (!hub_is_superspeed(hdev)) usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); - portstatus &= ~USB_PORT_STAT_ENABLE; - } else { - /* Pretend that power was lost for USB3 devs */ - portstatus &= ~USB_PORT_STAT_ENABLE; - } } /* Clear status-change flags; we'll debounce later */ From 4e341818ee9ee8d435977131cc7ce9e89c053fa6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 7 Oct 2013 11:58:34 -0700 Subject: [PATCH 26/27] usb: xhci: kill a conditional when toggling cycle Perform an unconditional toggle of the cycle bit with 'xor'. Signed-off-by: Dan Williams Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e57163f952ed..1e2f3f495843 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -178,7 +178,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->type == TYPE_EVENT && last_trb_on_last_seg(xhci, ring, ring->deq_seg, ring->dequeue)) { - ring->cycle_state = (ring->cycle_state ? 0 : 1); + ring->cycle_state ^= 1; } ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; From a2cdc3432c361bb885476d1c625e22b518e0bc07 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 16 Oct 2013 12:25:44 -0700 Subject: [PATCH 27/27] usb: xhci: remove the unused ->address field Only used for debug output, so we don't need to save it. Signed-off-by: Dan Williams Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 10 ++-------- drivers/usb/host/xhci.h | 2 -- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index d3f6abbda67e..b41c24c58bb9 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3704,9 +3704,6 @@ disable_slot: * the device). * We should be protected by the usb_address0_mutex in khubd's hub_port_init, so * we should only issue and wait on one address command at the same time. - * - * We add one to the device address issued by the hardware because the USB core - * uses address 1 for the root hubs (even though they're not really devices). */ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) { @@ -3851,16 +3848,13 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); trace_xhci_address_ctx(xhci, virt_dev->out_ctx, slot_ctx->dev_info >> 27); - /* Use kernel assigned address for devices; store xHC assigned - * address locally. */ - virt_dev->address = (le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK) - + 1; /* Zero the input context control for later use */ ctrl_ctx->add_flags = 0; ctrl_ctx->drop_flags = 0; xhci_dbg_trace(xhci, trace_xhci_dbg_address, - "Internal device address = %d", virt_dev->address); + "Internal device address = %d", + le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK); return 0; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 466081934b68..2c701c60cbf4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -935,8 +935,6 @@ struct xhci_virt_device { /* Rings saved to ensure old alt settings can be re-instated */ struct xhci_ring **ring_cache; int num_rings_cached; - /* Store xHC assigned device address */ - int address; #define XHCI_MAX_RINGS_CACHED 31 struct xhci_virt_ep eps[31]; struct completion cmd_completion;