|
@@ -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;
|