Merge tag 'wberr-v4.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/jlayton...
[sfrench/cifs-2.6.git] / drivers / usb / host / xhci-hub.c
index 00721e8807ab472f1e886fff791e7bbbaeec3a13..ad89a6d4111b45e0c048ee437ffd391f5af846e0 100644 (file)
@@ -1179,6 +1179,39 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                                break;
                        }
 
+                       /*
+                        * For xHCI 1.1 according to section 4.19.1.2.4.1 a
+                        * root hub port's transition to compliance mode upon
+                        * detecting LFPS timeout may be controlled by an
+                        * Compliance Transition Enabled (CTE) flag (not
+                        * software visible). This flag is set by writing 0xA
+                        * to PORTSC PLS field which will allow transition to
+                        * compliance mode the next time LFPS timeout is
+                        * encountered. A warm reset will clear it.
+                        *
+                        * The CTE flag is only supported if the HCCPARAMS2 CTC
+                        * flag is set, otherwise, the compliance substate is
+                        * automatically entered as on 1.0 and prior.
+                        */
+                       if (link_state == USB_SS_PORT_LS_COMP_MOD) {
+                               if (!HCC2_CTC(xhci->hcc_params2)) {
+                                       xhci_dbg(xhci, "CTC flag is 0, port already supports entering compliance mode\n");
+                                       break;
+                               }
+
+                               if ((temp & PORT_CONNECT)) {
+                                       xhci_warn(xhci, "Can't set compliance mode when port is connected\n");
+                                       goto error;
+                               }
+
+                               xhci_dbg(xhci, "Enable compliance mode transition for port %d\n",
+                                               wIndex);
+                               xhci_set_link_state(xhci, port_array, wIndex,
+                                               link_state);
+                               temp = readl(port_array[wIndex]);
+                               break;
+                       }
+
                        /* Software should not attempt to set
                         * port link state above '3' (U3) and the port
                         * must be enabled.
@@ -1521,15 +1554,14 @@ static bool xhci_port_missing_cas_quirk(int port_index,
 int xhci_bus_resume(struct usb_hcd *hcd)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       int max_ports, port_index;
-       __le32 __iomem **port_array;
        struct xhci_bus_state *bus_state;
-       u32 temp;
+       __le32 __iomem **port_array;
        unsigned long flags;
-       unsigned long port_was_suspended = 0;
-       bool need_usb2_u3_exit = false;
+       int max_ports, port_index;
        int slot_id;
        int sret;
+       u32 next_state;
+       u32 temp, portsc;
 
        max_ports = xhci_get_ports(hcd, &port_array);
        bus_state = &xhci->bus_state[hcd_index(hcd)];
@@ -1548,68 +1580,77 @@ int xhci_bus_resume(struct usb_hcd *hcd)
        temp &= ~CMD_EIE;
        writel(temp, &xhci->op_regs->command);
 
+       /* bus specific resume for ports we suspended at bus_suspend */
+       if (hcd->speed >= HCD_USB3)
+               next_state = XDEV_U0;
+       else
+               next_state = XDEV_RESUME;
+
        port_index = max_ports;
        while (port_index--) {
-               /* Check whether need resume ports. If needed
-                  resume port and disable remote wakeup */
-               u32 temp;
-
-               temp = readl(port_array[port_index]);
+               portsc = readl(port_array[port_index]);
 
                /* warm reset CAS limited ports stuck in polling/compliance */
                if ((xhci->quirks & XHCI_MISSING_CAS) &&
                    (hcd->speed >= HCD_USB3) &&
                    xhci_port_missing_cas_quirk(port_index, port_array)) {
                        xhci_dbg(xhci, "reset stuck port %d\n", port_index);
+                       clear_bit(port_index, &bus_state->bus_suspended);
                        continue;
                }
-               if (DEV_SUPERSPEED_ANY(temp))
-                       temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
-               else
-                       temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
-               if (test_bit(port_index, &bus_state->bus_suspended) &&
-                   (temp & PORT_PLS_MASK)) {
-                       set_bit(port_index, &port_was_suspended);
-                       if (!DEV_SUPERSPEED_ANY(temp)) {
-                               xhci_set_link_state(xhci, port_array,
-                                               port_index, XDEV_RESUME);
-                               need_usb2_u3_exit = true;
+               /* resume if we suspended the link, and it is still suspended */
+               if (test_bit(port_index, &bus_state->bus_suspended))
+                       switch (portsc & PORT_PLS_MASK) {
+                       case XDEV_U3:
+                               portsc = xhci_port_state_to_neutral(portsc);
+                               portsc &= ~PORT_PLS_MASK;
+                               portsc |= PORT_LINK_STROBE | next_state;
+                               break;
+                       case XDEV_RESUME:
+                               /* resume already initiated */
+                               break;
+                       default:
+                               /* not in a resumeable state, ignore it */
+                               clear_bit(port_index,
+                                         &bus_state->bus_suspended);
+                               break;
                        }
-               } else
-                       writel(temp, port_array[port_index]);
-       }
-
-       if (need_usb2_u3_exit) {
-               spin_unlock_irqrestore(&xhci->lock, flags);
-               msleep(USB_RESUME_TIMEOUT);
-               spin_lock_irqsave(&xhci->lock, flags);
+               /* disable wake for all ports, write new link state if needed */
+               portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
+               writel(portsc, port_array[port_index]);
        }
 
-       port_index = max_ports;
-       while (port_index--) {
-               if (!(port_was_suspended & BIT(port_index)))
-                       continue;
-               /* Clear PLC to poll it later after XDEV_U0 */
-               xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC);
-               xhci_set_link_state(xhci, port_array, port_index, XDEV_U0);
+       /* USB2 specific resume signaling delay and U0 link state transition */
+       if (hcd->speed < HCD_USB3) {
+               if (bus_state->bus_suspended) {
+                       spin_unlock_irqrestore(&xhci->lock, flags);
+                       msleep(USB_RESUME_TIMEOUT);
+                       spin_lock_irqsave(&xhci->lock, flags);
+               }
+               for_each_set_bit(port_index, &bus_state->bus_suspended,
+                                BITS_PER_LONG) {
+                       /* Clear PLC to poll it later for U0 transition */
+                       xhci_test_and_clear_bit(xhci, port_array, port_index,
+                                               PORT_PLC);
+                       xhci_set_link_state(xhci, port_array, port_index,
+                                           XDEV_U0);
+               }
        }
 
-       port_index = max_ports;
-       while (port_index--) {
-               if (!(port_was_suspended & BIT(port_index)))
-                       continue;
-               /* Poll and Clear PLC */
+       /* poll for U0 link state complete, both USB2 and USB3 */
+       for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) {
                sret = xhci_handshake(port_array[port_index], PORT_PLC,
                                      PORT_PLC, 10 * 1000);
-               if (sret)
+               if (sret) {
                        xhci_warn(xhci, "port %d resume PLC timeout\n",
                                  port_index);
+                       continue;
+               }
                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);
                if (slot_id)
                        xhci_ring_device(xhci, slot_id);
        }
-
        (void) readl(&xhci->op_regs->command);
 
        bus_state->next_statechange = jiffies + msecs_to_jiffies(5);