--- /dev/null
+From 057d476fff778f1d3b9f861fdb5437ea1a3cfc99 Mon Sep 17 00:00:00 2001
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+Date: Wed, 11 Dec 2019 16:20:03 +0200
+Subject: xhci: fix USB3 device initiated resume race with roothub autosuspend
+
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+
+commit 057d476fff778f1d3b9f861fdb5437ea1a3cfc99 upstream.
+
+A race in xhci USB3 remote wake handling may force device back to suspend
+after it initiated resume siganaling, causing a missed resume event or warm
+reset of device.
+
+When a USB3 link completes resume signaling and goes to enabled (UO)
+state a interrupt is issued and the interrupt handler will clear the
+bus_state->port_remote_wakeup resume flag, allowing bus suspend.
+
+If the USB3 roothub thread just finished reading port status before
+the interrupt, finding ports still in suspended (U3) state, but hasn't
+yet started suspending the hub, then the xhci interrupt handler will clear
+the flag that prevented roothub suspend and allow bus to suspend, forcing
+all port links back to suspended (U3) state.
+
+Example case:
+usb_runtime_suspend() # because all ports still show suspended U3
+ usb_suspend_both()
+ hub_suspend(); # successful as hub->wakeup_bits not set yet
+==> INTERRUPT
+xhci_irq()
+ handle_port_status()
+ clear bus_state->port_remote_wakeup
+ usb_wakeup_notification()
+ sets hub->wakeup_bits;
+ kick_hub_wq()
+<== END INTERRUPT
+ hcd_bus_suspend()
+ xhci_bus_suspend() # success as port_remote_wakeup bits cleared
+
+Fix this by increasing roothub usage count during port resume to prevent
+roothub autosuspend, and by making sure bus_state->port_remote_wakeup
+flag is only cleared after resume completion is visible, i.e.
+after xhci roothub returned U0 or other non-U3 link state link on a
+get port status request.
+
+Issue rootcaused by Chiasheng Lee
+
+Cc: <stable@vger.kernel.org>
+Cc: Lee, Hou-hsun <hou-hsun.lee@intel.com>
+Reported-by: Lee, Chiasheng <chiasheng.lee@intel.com>
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Link: https://lore.kernel.org/r/20191211142007.8847-3-mathias.nyman@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/host/xhci-hub.c | 8 ++++++++
+ drivers/usb/host/xhci-ring.c | 6 +-----
+ 2 files changed, 9 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -887,6 +887,14 @@ static u32 xhci_get_port_status(struct u
+ status |= USB_PORT_STAT_C_BH_RESET << 16;
+ if ((raw_port_status & PORT_CEC))
+ status |= USB_PORT_STAT_C_CONFIG_ERROR << 16;
++
++ /* USB3 remote wake resume signaling completed */
++ if (bus_state->port_remote_wakeup & (1 << wIndex) &&
++ (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME &&
++ (raw_port_status & PORT_PLS_MASK) != XDEV_RECOVERY) {
++ bus_state->port_remote_wakeup &= ~(1 << wIndex);
++ usb_hcd_end_port_resume(&hcd->self, wIndex);
++ }
+ }
+
+ if (hcd->speed < HCD_USB3) {
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -1679,9 +1679,6 @@ static void handle_port_status(struct xh
+ usb_hcd_resume_root_hub(hcd);
+ }
+
+- if (hcd->speed >= HCD_USB3 && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE)
+- bus_state->port_remote_wakeup &= ~(1 << faked_port_index);
+-
+ if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
+ xhci_dbg(xhci, "port resume event for port %d\n", port_id);
+
+@@ -1700,6 +1697,7 @@ static void handle_port_status(struct xh
+ bus_state->port_remote_wakeup |= 1 << faked_port_index;
+ xhci_test_and_clear_bit(xhci, port_array,
+ faked_port_index, PORT_PLC);
++ usb_hcd_start_port_resume(&hcd->self, faked_port_index);
+ xhci_set_link_state(xhci, port_array, faked_port_index,
+ XDEV_U0);
+ /* Need to wait until the next link state change
+@@ -1737,8 +1735,6 @@ static void handle_port_status(struct xh
+ if (slot_id && xhci->devs[slot_id])
+ xhci_ring_device(xhci, slot_id);
+ if (bus_state->port_remote_wakeup & (1 << faked_port_index)) {
+- bus_state->port_remote_wakeup &=
+- ~(1 << faked_port_index);
+ xhci_test_and_clear_bit(xhci, port_array,
+ faked_port_index, PORT_PLC);
+ usb_wakeup_notification(hcd->self.root_hub,