diff options
Diffstat (limited to 'drivers/usb/host/xhci.c')
-rw-r--r-- | drivers/usb/host/xhci.c | 196 |
1 files changed, 36 insertions, 160 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1e36dbb4836..4265b48856f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -730,6 +730,9 @@ void xhci_shutdown(struct usb_hcd *hcd) spin_lock_irq(&xhci->lock); xhci_halt(xhci); + /* Workaround for spurious wakeups at shutdown with HSW */ + if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) + xhci_reset(xhci); spin_unlock_irq(&xhci->lock); xhci_cleanup_msix(xhci); @@ -737,6 +740,10 @@ void xhci_shutdown(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_shutdown completed - status = %x", xhci_readl(xhci, &xhci->op_regs->status)); + + /* Yet another workaround for spurious wakeups at shutdown with HSW */ + if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) + pci_set_power_state(to_pci_dev(hcd->self.controller), PCI_D3hot); } #ifdef CONFIG_PM @@ -839,6 +846,7 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) int xhci_suspend(struct xhci_hcd *xhci) { int rc = 0; + unsigned int delay = XHCI_MAX_HALT_USEC; struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; @@ -861,8 +869,12 @@ int xhci_suspend(struct xhci_hcd *xhci) command = xhci_readl(xhci, &xhci->op_regs->command); command &= ~CMD_RUN; xhci_writel(xhci, command, &xhci->op_regs->command); + + /* Some chips from Fresco Logic need an extraordinary delay */ + delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1; + if (xhci_handshake(xhci, &xhci->op_regs->status, - STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC)) { + STS_HALT, STS_HALT, delay)) { xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n"); spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; @@ -3447,7 +3459,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"); @@ -3571,11 +3583,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); @@ -3709,9 +3716,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) { @@ -3856,16 +3860,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; } @@ -4013,133 +4014,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) { @@ -4216,7 +4090,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 +4098,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); @@ -4267,24 +4141,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; |