]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 28 Nov 2018 13:19:45 +0000 (14:19 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 28 Nov 2018 13:19:45 +0000 (14:19 +0100)
added patches:
usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch

queue-4.4/series
queue-4.4/usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch [new file with mode: 0644]

index 9d6c631684bba968f25929a2d024cf01fb2c2d0a..343348bc85ca49c8ddfb532d305e56af9219c8c0 100644 (file)
@@ -14,3 +14,4 @@ can-dev-__can_get_echo_skb-replace-struct-can_frame-by-canfd_frame-to-access-fra
 can-dev-__can_get_echo_skb-don-t-crash-the-kernel-if-can_priv-echo_skb-is-accessed-out-of-bounds.patch
 can-dev-__can_get_echo_skb-print-error-message-if-trying-to-echo-non-existing-skb.patch
 acpica-aml-interpreter-add-region-addresses-in-global-list-during-initialization.patch
+usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch
diff --git a/queue-4.4/usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch b/queue-4.4/usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch
new file mode 100644 (file)
index 0000000..bb97322
--- /dev/null
@@ -0,0 +1,146 @@
+From 2f31a67f01a8beb22cae754c53522cb61a005750 Mon Sep 17 00:00:00 2001
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+Date: Thu, 15 Nov 2018 11:38:41 +0200
+Subject: usb: xhci: Prevent bus suspend if a port connect change or polling state is detected
+
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+
+commit 2f31a67f01a8beb22cae754c53522cb61a005750 upstream.
+
+USB3 roothub might autosuspend before a plugged USB3 device is detected,
+causing USB3 device enumeration failure.
+
+USB3 devices don't show up as connected and enabled until USB3 link trainig
+completes. On a fast booting platform with a slow USB3 link training the
+link might reach the connected enabled state just as the bus is suspending.
+
+If this device is discovered first time by the xhci_bus_suspend() routine
+it will be put to U3 suspended state like the other ports which failed to
+suspend earlier.
+
+The hub thread will notice the connect change and resume the bus,
+moving the port back to U0
+
+This U0 -> U3 -> U0 transition right after being connected seems to be
+too much for some devices, causing them to first go to SS.Inactive state,
+and finally end up stuck in a polling state with reset asserted
+
+Fix this by failing the bus suspend if a port has a connect change or is
+in a polling state in xhci_bus_suspend().
+
+Don't do any port changes until all ports are checked, buffer all port
+changes and only write them in the end if suspend can proceed
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/host/xhci-hub.c |   60 +++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 46 insertions(+), 14 deletions(-)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -1298,13 +1298,16 @@ int xhci_bus_suspend(struct usb_hcd *hcd
+       __le32 __iomem **port_array;
+       struct xhci_bus_state *bus_state;
+       unsigned long flags;
++      u32 portsc_buf[USB_MAXCHILDREN];
++      bool wake_enabled;
+       max_ports = xhci_get_ports(hcd, &port_array);
+       bus_state = &xhci->bus_state[hcd_index(hcd)];
++      wake_enabled = hcd->self.root_hub->do_remote_wakeup;
+       spin_lock_irqsave(&xhci->lock, flags);
+-      if (hcd->self.root_hub->do_remote_wakeup) {
++      if (wake_enabled) {
+               if (bus_state->resuming_ports ||        /* USB2 */
+                   bus_state->port_remote_wakeup) {    /* USB3 */
+                       spin_unlock_irqrestore(&xhci->lock, flags);
+@@ -1312,26 +1315,36 @@ int xhci_bus_suspend(struct usb_hcd *hcd
+                       return -EBUSY;
+               }
+       }
+-
+-      port_index = max_ports;
++      /*
++       * Prepare ports for suspend, but don't write anything before all ports
++       * are checked and we know bus suspend can proceed
++       */
+       bus_state->bus_suspended = 0;
++      port_index = max_ports;
+       while (port_index--) {
+-              /* suspend the port if the port is not suspended */
+               u32 t1, t2;
+-              int slot_id;
+               t1 = readl(port_array[port_index]);
+               t2 = xhci_port_state_to_neutral(t1);
++              portsc_buf[port_index] = 0;
+-              if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
+-                      xhci_dbg(xhci, "port %d not suspended\n", port_index);
+-                      slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+-                                      port_index + 1);
+-                      if (slot_id) {
++              /* Bail out if a USB3 port has a new device in link training */
++              if ((t1 & PORT_PLS_MASK) == XDEV_POLLING) {
++                      bus_state->bus_suspended = 0;
++                      spin_unlock_irqrestore(&xhci->lock, flags);
++                      xhci_dbg(xhci, "Bus suspend bailout, port in polling\n");
++                      return -EBUSY;
++              }
++
++              /* suspend ports in U0, or bail out for new connect changes */
++              if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) {
++                      if ((t1 & PORT_CSC) && wake_enabled) {
++                              bus_state->bus_suspended = 0;
+                               spin_unlock_irqrestore(&xhci->lock, flags);
+-                              xhci_stop_device(xhci, slot_id, 1);
+-                              spin_lock_irqsave(&xhci->lock, flags);
++                              xhci_dbg(xhci, "Bus suspend bailout, port connect change\n");
++                              return -EBUSY;
+                       }
++                      xhci_dbg(xhci, "port %d not suspended\n", port_index);
+                       t2 &= ~PORT_PLS_MASK;
+                       t2 |= PORT_LINK_STROBE | XDEV_U3;
+                       set_bit(port_index, &bus_state->bus_suspended);
+@@ -1340,7 +1353,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd
+                * including the USB 3.0 roothub, but only if CONFIG_PM
+                * is enabled, so also enable remote wake here.
+                */
+-              if (hcd->self.root_hub->do_remote_wakeup) {
++              if (wake_enabled) {
+                       if (t1 & PORT_CONNECT) {
+                               t2 |= PORT_WKOC_E | PORT_WKDISC_E;
+                               t2 &= ~PORT_WKCONN_E;
+@@ -1353,7 +1366,26 @@ int xhci_bus_suspend(struct usb_hcd *hcd
+               t1 = xhci_port_state_to_neutral(t1);
+               if (t1 != t2)
+-                      writel(t2, port_array[port_index]);
++                      portsc_buf[port_index] = t2;
++      }
++
++      /* write port settings, stopping and suspending ports if needed */
++      port_index = max_ports;
++      while (port_index--) {
++              if (!portsc_buf[port_index])
++                      continue;
++              if (test_bit(port_index, &bus_state->bus_suspended)) {
++                      int slot_id;
++
++                      slot_id = xhci_find_slot_id_by_port(hcd, xhci,
++                                                          port_index + 1);
++                      if (slot_id) {
++                              spin_unlock_irqrestore(&xhci->lock, flags);
++                              xhci_stop_device(xhci, slot_id, 1);
++                              spin_lock_irqsave(&xhci->lock, flags);
++                      }
++              }
++              writel(portsc_buf[port_index], port_array[port_index]);
+       }
+       hcd->state = HC_STATE_SUSPENDED;
+       bus_state->next_statechange = jiffies + msecs_to_jiffies(10);