blob: 8ff55f9b43d322997e616fc4d7a87e813f145c90 [file] [log] [blame]
From a8a2528d03669310db2ea91c596377cc4daadeaf Mon Sep 17 00:00:00 2001
From: Mathias Nyman <mathias.nyman@linux.intel.com>
Date: Thu, 21 Jun 2018 16:19:41 +0300
Subject: [PATCH 1454/1795] xhci: Fix perceived dead host due to runtime
suspend race with event handler
Don't rely on event interrupt (EINT) bit alone to detect pending port
change in resume. If no change event is detected the host may be suspended
again, oterwise roothubs are resumed.
There is a lag in xHC setting EINT. If we don't notice the pending change
in resume, and the controller is runtime suspeded again, it causes the
event handler to assume host is dead as it will fail to read xHC registers
once PCI puts the controller to D3 state.
[ 268.520969] xhci_hcd: xhci_resume: starting port polling.
[ 268.520985] xhci_hcd: xhci_hub_status_data: stopping port polling.
[ 268.521030] xhci_hcd: xhci_suspend: stopping port polling.
[ 268.521040] xhci_hcd: // Setting command ring address to 0x349bd001
[ 268.521139] xhci_hcd: Port Status Change Event for port 3
[ 268.521149] xhci_hcd: resume root hub
[ 268.521163] xhci_hcd: port resume event for port 3
[ 268.521168] xhci_hcd: xHC is not running.
[ 268.521174] xhci_hcd: handle_port_status: starting port polling.
[ 268.596322] xhci_hcd: xhci_hc_died: xHCI host controller not responding, assume dead
The EINT lag is described in a additional note in xhci specs 4.19.2:
"Due to internal xHC scheduling and system delays, there will be a lag
between a change bit being set and the Port Status Change Event that it
generated being written to the Event Ring. If SW reads the PORTSC and
sees a change bit set, there is no guarantee that the corresponding Port
Status Change Event has already been written into the Event Ring."
Cc: <stable@vger.kernel.org>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 229bc19fd7aca4f37964af06e3583c1c8f36b5d6)
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Conflicts:
drivers/usb/host/xhci.c
---
drivers/usb/host/xhci.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 0b41acd84137..f4e3db0826ef 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -910,7 +910,7 @@ static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
static bool xhci_pending_portevent(struct xhci_hcd *xhci)
{
- __le32 __iomem **port_array;
+ struct xhci_port **ports;
int port_index;
u32 status;
u32 portsc;
@@ -924,18 +924,18 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
* being written to the Event Ring. See note in xhci 1.1 section 4.19.2.
*/
- port_index = xhci->num_usb2_ports;
- port_array = xhci->usb2_ports;
+ port_index = xhci->usb2_rhub.num_ports;
+ ports = xhci->usb2_rhub.ports;
while (port_index--) {
- portsc = readl(port_array[port_index]);
+ portsc = readl(ports[port_index]->addr);
if (portsc & PORT_CHANGE_MASK ||
(portsc & PORT_PLS_MASK) == XDEV_RESUME)
return true;
}
- port_index = xhci->num_usb3_ports;
- port_array = xhci->usb3_ports;
+ port_index = xhci->usb3_rhub.num_ports;
+ ports = xhci->usb3_rhub.ports;
while (port_index--) {
- portsc = readl(port_array[port_index]);
+ portsc = readl(ports[port_index]->addr);
if (portsc & PORT_CHANGE_MASK ||
(portsc & PORT_PLS_MASK) == XDEV_RESUME)
return true;
@@ -3724,7 +3724,6 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
}
-
xhci_debugfs_remove_slot(xhci, udev->slot_id);
virt_dev->udev = NULL;
ret = xhci_disable_slot(xhci, udev->slot_id);
--
2.19.0