xhci: Add spurious wakeup quirk for LynxPoint-LP controllers
[pandora-kernel.git] / drivers / usb / host / xhci-hub.c
index 1e96d1f..8605813 100644 (file)
@@ -20,6 +20,7 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/gfp.h>
 #include <asm/unaligned.h>
 
 #include "xhci.h"
 #define        PORT_RWC_BITS   (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
                         PORT_RC | PORT_PLC | PORT_PE)
 
+/* usb 1.1 root hub device descriptor */
+static u8 usb_bos_descriptor [] = {
+       USB_DT_BOS_SIZE,                /*  __u8 bLength, 5 bytes */
+       USB_DT_BOS,                     /*  __u8 bDescriptorType */
+       0x0F, 0x00,                     /*  __le16 wTotalLength, 15 bytes */
+       0x1,                            /*  __u8 bNumDeviceCaps */
+       /* First device capability */
+       USB_DT_USB_SS_CAP_SIZE,         /*  __u8 bLength, 10 bytes */
+       USB_DT_DEVICE_CAPABILITY,       /* Device Capability */
+       USB_SS_CAP_TYPE,                /* bDevCapabilityType, SUPERSPEED_USB */
+       0x00,                           /* bmAttributes, LTM off by default */
+       USB_5GBPS_OPERATION, 0x00,      /* wSpeedsSupported, 5Gbps only */
+       0x03,                           /* bFunctionalitySupport,
+                                          USB 3.0 speed only */
+       0x00,                           /* bU1DevExitLat, set later. */
+       0x00, 0x00                      /* __le16 bU2DevExitLat, set later. */
+};
+
+
 static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
                struct usb_hub_descriptor *desc, int ports)
 {
@@ -75,7 +95,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
         */
        memset(port_removable, 0, sizeof(port_removable));
        for (i = 0; i < ports; i++) {
-               portsc = xhci_readl(xhci, xhci->usb3_ports[i]);
+               portsc = xhci_readl(xhci, xhci->usb2_ports[i]);
                /* If a device is removable, PORTSC reports a 0, same as in the
                 * hub descriptor DeviceRemovable bits.
                 */
@@ -232,7 +252,7 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
                        continue;
                speed = xhci->devs[i]->udev->speed;
                if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3))
-                               && xhci->devs[i]->port == port) {
+                               && xhci->devs[i]->fake_port == port) {
                        slot_id = i;
                        break;
                }
@@ -365,6 +385,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
                status = PORT_PLC;
                port_change_bit = "link state";
                break;
+       case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
+               status = PORT_CEC;
+               port_change_bit = "config error";
+               break;
        default:
                /* Should never happen */
                return;
@@ -392,13 +416,117 @@ static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array)
        return max_ports;
 }
 
+void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+                               int port_id, u32 link_state)
+{
+       u32 temp;
+
+       temp = xhci_readl(xhci, port_array[port_id]);
+       temp = xhci_port_state_to_neutral(temp);
+       temp &= ~PORT_PLS_MASK;
+       temp |= PORT_LINK_STROBE | link_state;
+       xhci_writel(xhci, temp, port_array[port_id]);
+}
+
+/* Test and clear port RWC bit */
+void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+                               int port_id, u32 port_bit)
+{
+       u32 temp;
+
+       temp = xhci_readl(xhci, port_array[port_id]);
+       if (temp & port_bit) {
+               temp = xhci_port_state_to_neutral(temp);
+               temp |= port_bit;
+               xhci_writel(xhci, temp, port_array[port_id]);
+       }
+}
+
+/* Updates Link Status for super Speed port */
+static void xhci_hub_report_link_state(struct xhci_hcd *xhci,
+               u32 *status, u32 status_reg)
+{
+       u32 pls = status_reg & PORT_PLS_MASK;
+
+       /* resume state is a xHCI internal state.
+        * Do not report it to usb core, instead, pretend to be U3,
+        * thus usb core knows it's not ready for transfer
+        */
+       if (pls == XDEV_RESUME) {
+               *status |= USB_SS_PORT_LS_U3;
+               return;
+       }
+
+       /* When the CAS bit is set then warm reset
+        * should be performed on port
+        */
+       if (status_reg & PORT_CAS) {
+               /* The CAS bit can be set while the port is
+                * in any link state.
+                * Only roothubs have CAS bit, so we
+                * pretend to be in compliance mode
+                * unless we're already in compliance
+                * or the inactive state.
+                */
+               if (pls != USB_SS_PORT_LS_COMP_MOD &&
+                   pls != USB_SS_PORT_LS_SS_INACTIVE) {
+                       pls = USB_SS_PORT_LS_COMP_MOD;
+               }
+               /* Return also connection bit -
+                * hub state machine resets port
+                * when this bit is set.
+                */
+               pls |= USB_PORT_STAT_CONNECTION;
+       } else {
+               /*
+                * If CAS bit isn't set but the Port is already at
+                * Compliance Mode, fake a connection so the USB core
+                * notices the Compliance state and resets the port.
+                * This resolves an issue generated by the SN65LVPE502CP
+                * in which sometimes the port enters compliance mode
+                * caused by a delay on the host-device negotiation.
+                */
+               if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+                               (pls == USB_SS_PORT_LS_COMP_MOD))
+                       pls |= USB_PORT_STAT_CONNECTION;
+       }
+
+       /* update status field */
+       *status |= pls;
+}
+
+/*
+ * Function for Compliance Mode Quirk.
+ *
+ * This Function verifies if all xhc USB3 ports have entered U0, if so,
+ * 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)
+{
+       u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1);
+       bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
+
+       if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK))
+               return;
+
+       if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) {
+               xhci->port_status_u0 |= 1 << wIndex;
+               if (xhci->port_status_u0 == all_ports_seen_u0) {
+                       del_timer_sync(&xhci->comp_mode_recovery_timer);
+                       xhci_dbg(xhci, "All USB3 ports have entered U0 already!\n");
+                       xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted.\n");
+               }
+       }
+}
+
 int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                u16 wIndex, char *buf, u16 wLength)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        int max_ports;
        unsigned long flags;
-       u32 temp, temp1, status;
+       u32 temp, status;
        int retval = 0;
        __le32 __iomem **port_array;
        int slot_id;
@@ -429,6 +557,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                xhci_hub_descriptor(hcd, xhci,
                                (struct usb_hub_descriptor *) buf);
                break;
+       case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+               if ((wValue & 0xff00) != (USB_DT_BOS << 8))
+                       goto error;
+
+               if (hcd->speed != HCD_USB3)
+                       goto error;
+
+               memcpy(buf, &usb_bos_descriptor,
+                               USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
+               temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
+               buf[12] = HCS_U1_LATENCY(temp);
+               put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
+
+               spin_unlock_irqrestore(&xhci->lock, flags);
+               return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
        case GetPortStatus:
                if (!wIndex || wIndex > max_ports)
                        goto error;
@@ -452,10 +595,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        status |= USB_PORT_STAT_C_RESET << 16;
                /* USB3.0 only */
                if (hcd->speed == HCD_USB3) {
-                       if ((temp & PORT_PLC))
+                       /* Port link change with port in resume state should not be
+                        * reported to usbcore, as this is an internal state to be
+                        * handled by xhci driver. Reporting PLC to usbcore may
+                        * cause usbcore clearing PLC first and port change event
+                        * irq won't be generated.
+                        */
+                       if ((temp & PORT_PLC) &&
+                               (temp & PORT_PLS_MASK) != XDEV_RESUME)
                                status |= USB_PORT_STAT_C_LINK_STATE << 16;
                        if ((temp & PORT_WRC))
                                status |= USB_PORT_STAT_C_BH_RESET << 16;
+                       if ((temp & PORT_CEC))
+                               status |= USB_PORT_STAT_C_CONFIG_ERROR << 16;
                }
 
                if (hcd->speed != HCD_USB3) {
@@ -472,11 +624,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                                xhci_dbg(xhci, "Resume USB2 port %d\n",
                                        wIndex + 1);
                                bus_state->resume_done[wIndex] = 0;
-                               temp1 = xhci_port_state_to_neutral(temp);
-                               temp1 &= ~PORT_PLS_MASK;
-                               temp1 |= PORT_LINK_STROBE | XDEV_U0;
-                               xhci_writel(xhci, temp1, port_array[wIndex]);
-
+                               xhci_set_link_state(xhci, port_array, wIndex,
+                                                       XDEV_U0);
                                xhci_dbg(xhci, "set port %d resume\n",
                                        wIndex + 1);
                                slot_id = xhci_find_slot_id_by_port(hcd, xhci,
@@ -521,13 +670,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        else
                                status |= USB_PORT_STAT_POWER;
                }
-               /* Port Link State */
+               /* Update Port Link State for super speed ports*/
                if (hcd->speed == HCD_USB3) {
-                       /* resume state is a xHCI internal state.
-                        * Do not report it to usb core.
+                       xhci_hub_report_link_state(xhci, &status, temp);
+                       /*
+                        * Verify if all USB3 Ports Have entered U0 already.
+                        * Delete Compliance Mode Timer if so.
                         */
-                       if ((temp & PORT_PLS_MASK) != XDEV_RESUME)
-                               status |= (temp & PORT_PLS_MASK);
+                       xhci_del_comp_mod_timer(xhci, temp, wIndex);
                }
                if (bus_state->port_c_suspend & (1 << wIndex))
                        status |= 1 << USB_PORT_FEAT_C_SUSPEND;
@@ -551,10 +701,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
                        temp = xhci_readl(xhci, port_array[wIndex]);
+                       if ((temp & PORT_PLS_MASK) != XDEV_U0) {
+                               /* Resume the port to U0 first */
+                               xhci_set_link_state(xhci, port_array, wIndex,
+                                                       XDEV_U0);
+                               spin_unlock_irqrestore(&xhci->lock, flags);
+                               msleep(10);
+                               spin_lock_irqsave(&xhci->lock, flags);
+                       }
                        /* In spec software should not attempt to suspend
                         * a port unless the port reports that it is in the
                         * enabled (PED = ‘1’,PLS < ‘3’) state.
                         */
+                       temp = xhci_readl(xhci, port_array[wIndex]);
                        if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
                                || (temp & PORT_PLS_MASK) >= XDEV_U3) {
                                xhci_warn(xhci, "USB core suspending device "
@@ -573,10 +732,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        xhci_stop_device(xhci, slot_id, 1);
                        spin_lock_irqsave(&xhci->lock, flags);
 
-                       temp = xhci_port_state_to_neutral(temp);
-                       temp &= ~PORT_PLS_MASK;
-                       temp |= PORT_LINK_STROBE | XDEV_U3;
-                       xhci_writel(xhci, temp, port_array[wIndex]);
+                       xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3);
 
                        spin_unlock_irqrestore(&xhci->lock, flags);
                        msleep(10); /* wait device to enter */
@@ -587,12 +743,39 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        break;
                case USB_PORT_FEAT_LINK_STATE:
                        temp = xhci_readl(xhci, port_array[wIndex]);
+
+                       /* Disable port */
+                       if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
+                               xhci_dbg(xhci, "Disable port %d\n", wIndex);
+                               temp = xhci_port_state_to_neutral(temp);
+                               /*
+                                * Clear all change bits, so that we get a new
+                                * connection event.
+                                */
+                               temp |= PORT_CSC | PORT_PEC | PORT_WRC |
+                                       PORT_OCC | PORT_RC | PORT_PLC |
+                                       PORT_CEC;
+                               xhci_writel(xhci, temp | PORT_PE,
+                                       port_array[wIndex]);
+                               temp = xhci_readl(xhci, port_array[wIndex]);
+                               break;
+                       }
+
+                       /* Put link in RxDetect (enable port) */
+                       if (link_state == USB_SS_PORT_LS_RX_DETECT) {
+                               xhci_dbg(xhci, "Enable port %d\n", wIndex);
+                               xhci_set_link_state(xhci, port_array, wIndex,
+                                               link_state);
+                               temp = xhci_readl(xhci, port_array[wIndex]);
+                               break;
+                       }
+
                        /* Software should not attempt to set
-                        * port link state above '5' (Rx.Detect) and the port
+                        * port link state above '3' (U3) and the port
                         * must be enabled.
                         */
                        if ((temp & PORT_PE) == 0 ||
-                               (link_state > USB_SS_PORT_LS_RX_DETECT)) {
+                               (link_state > USB_SS_PORT_LS_U3)) {
                                xhci_warn(xhci, "Cannot set link state.\n");
                                goto error;
                        }
@@ -610,10 +793,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                                }
                        }
 
-                       temp = xhci_port_state_to_neutral(temp);
-                       temp &= ~PORT_PLS_MASK;
-                       temp |= PORT_LINK_STROBE | link_state;
-                       xhci_writel(xhci, temp, port_array[wIndex]);
+                       xhci_set_link_state(xhci, port_array, wIndex,
+                                               link_state);
 
                        spin_unlock_irqrestore(&xhci->lock, flags);
                        msleep(20); /* wait device to enter */
@@ -677,24 +858,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                                if ((temp & PORT_PE) == 0)
                                        goto error;
 
-                               temp = xhci_port_state_to_neutral(temp);
-                               temp &= ~PORT_PLS_MASK;
-                               temp |= PORT_LINK_STROBE | XDEV_RESUME;
-                               xhci_writel(xhci, temp,
-                                               port_array[wIndex]);
-
-                               spin_unlock_irqrestore(&xhci->lock,
-                                                      flags);
+                               xhci_set_link_state(xhci, port_array, wIndex,
+                                                       XDEV_RESUME);
+                               spin_unlock_irqrestore(&xhci->lock, flags);
                                msleep(20);
                                spin_lock_irqsave(&xhci->lock, flags);
-
-                               temp = xhci_readl(xhci,
-                                               port_array[wIndex]);
-                               temp = xhci_port_state_to_neutral(temp);
-                               temp &= ~PORT_PLS_MASK;
-                               temp |= PORT_LINK_STROBE | XDEV_U0;
-                               xhci_writel(xhci, temp,
-                                               port_array[wIndex]);
+                               xhci_set_link_state(xhci, port_array, wIndex,
+                                                       XDEV_U0);
                        }
                        bus_state->port_c_suspend |= 1 << wIndex;
 
@@ -714,6 +884,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                case USB_PORT_FEAT_C_OVER_CURRENT:
                case USB_PORT_FEAT_C_ENABLE:
                case USB_PORT_FEAT_C_PORT_LINK_STATE:
+               case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
                        xhci_clear_port_change_bit(xhci, wValue, wIndex,
                                        port_array[wIndex], temp);
                        break;
@@ -752,6 +923,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
        int max_ports;
        __le32 __iomem **port_array;
        struct xhci_bus_state *bus_state;
+       bool reset_change = false;
 
        max_ports = xhci_get_ports(hcd, &port_array);
        bus_state = &xhci->bus_state[hcd_index(hcd)];
@@ -761,7 +933,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
        memset(buf, 0, retval);
        status = 0;
 
-       mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC;
+       mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC | PORT_CEC;
 
        spin_lock_irqsave(&xhci->lock, flags);
        /* For each port, did anything change?  If so, set that bit in buf. */
@@ -778,6 +950,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
                        buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
                        status = 1;
                }
+               if ((temp & PORT_RC))
+                       reset_change = true;
+       }
+       if (!status && !reset_change) {
+               xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
+               clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
        }
        spin_unlock_irqrestore(&xhci->lock, flags);
        return status ? retval : 0;
@@ -848,20 +1026,6 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
                t1 = xhci_port_state_to_neutral(t1);
                if (t1 != t2)
                        xhci_writel(xhci, t2, port_array[port_index]);
-
-               if (hcd->speed != HCD_USB3) {
-                       /* enable remote wake up for USB 2.0 */
-                       __le32 __iomem *addr;
-                       u32 tmp;
-
-                       /* Add one to the port status register address to get
-                        * the port power control register address.
-                        */
-                       addr = port_array[port_index] + 1;
-                       tmp = xhci_readl(xhci, addr);
-                       tmp |= PORT_RWE;
-                       xhci_writel(xhci, tmp, addr);
-               }
        }
        hcd->state = HC_STATE_SUSPENDED;
        bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
@@ -910,25 +1074,18 @@ int xhci_bus_resume(struct usb_hcd *hcd)
                if (test_bit(port_index, &bus_state->bus_suspended) &&
                    (temp & PORT_PLS_MASK)) {
                        if (DEV_SUPERSPEED(temp)) {
-                               temp = xhci_port_state_to_neutral(temp);
-                               temp &= ~PORT_PLS_MASK;
-                               temp |= PORT_LINK_STROBE | XDEV_U0;
-                               xhci_writel(xhci, temp, port_array[port_index]);
+                               xhci_set_link_state(xhci, port_array,
+                                                       port_index, XDEV_U0);
                        } else {
-                               temp = xhci_port_state_to_neutral(temp);
-                               temp &= ~PORT_PLS_MASK;
-                               temp |= PORT_LINK_STROBE | XDEV_RESUME;
-                               xhci_writel(xhci, temp, port_array[port_index]);
+                               xhci_set_link_state(xhci, port_array,
+                                               port_index, XDEV_RESUME);
 
                                spin_unlock_irqrestore(&xhci->lock, flags);
                                msleep(20);
                                spin_lock_irqsave(&xhci->lock, flags);
 
-                               temp = xhci_readl(xhci, port_array[port_index]);
-                               temp = xhci_port_state_to_neutral(temp);
-                               temp &= ~PORT_PLS_MASK;
-                               temp |= PORT_LINK_STROBE | XDEV_U0;
-                               xhci_writel(xhci, temp, port_array[port_index]);
+                               xhci_set_link_state(xhci, port_array,
+                                                       port_index, XDEV_U0);
                        }
                        /* wait for the port to enter U0 and report port link
                         * state change.
@@ -938,12 +1095,8 @@ int xhci_bus_resume(struct usb_hcd *hcd)
                        spin_lock_irqsave(&xhci->lock, flags);
 
                        /* Clear PLC */
-                       temp = xhci_readl(xhci, port_array[port_index]);
-                       if (temp & PORT_PLC) {
-                               temp = xhci_port_state_to_neutral(temp);
-                               temp |= PORT_PLC;
-                               xhci_writel(xhci, temp, port_array[port_index]);
-                       }
+                       xhci_test_and_clear_bit(xhci, port_array, port_index,
+                                               PORT_PLC);
 
                        slot_id = xhci_find_slot_id_by_port(hcd,
                                        xhci, port_index + 1);
@@ -951,20 +1104,6 @@ int xhci_bus_resume(struct usb_hcd *hcd)
                                xhci_ring_device(xhci, slot_id);
                } else
                        xhci_writel(xhci, temp, port_array[port_index]);
-
-               if (hcd->speed != HCD_USB3) {
-                       /* disable remote wake up for USB 2.0 */
-                       __le32 __iomem *addr;
-                       u32 tmp;
-
-                       /* Add one to the port status register address to get
-                        * the port power control register address.
-                        */
-                       addr = port_array[port_index] + 1;
-                       tmp = xhci_readl(xhci, addr);
-                       tmp &= ~PORT_RWE;
-                       xhci_writel(xhci, tmp, addr);
-               }
        }
 
        (void) xhci_readl(xhci, &xhci->op_regs->command);