From d92f2c59cc2cbca6bfb2cc54882b58ba76b15fd4 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 22 Mar 2019 17:50:17 +0200 Subject: xhci: Don't let USB3 ports stuck in polling state prevent suspend Commit 2f31a67f01a8 ("usb: xhci: Prevent bus suspend if a port connect change or polling state is detected") was intended to prevent ports that were still link training from being forced to U3 suspend state mid enumeration. This solved enumeration issues for devices with slow link training. Turns out some devices are stuck in the link training/polling state, and thus that patch will prevent suspend completely for these devices. This is seen with USB3 card readers in some MacBooks. Instead of preventing suspend, give some time to complete the link training. On successful training the port will end up as connected and enabled. If port instead is stuck in link training the bus suspend will continue suspending after 360ms (10 * 36ms) timeout (tPollingLFPSTimeout). Original patch was sent to stable, this one should go there as well Fixes: 2f31a67f01a8 ("usb: xhci: Prevent bus suspend if a port connect change or polling state is detected") Cc: stable@vger.kernel.org Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/usb/host/xhci-hub.c') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index e2eece693655..96a740543183 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1545,20 +1545,25 @@ int xhci_bus_suspend(struct usb_hcd *hcd) port_index = max_ports; while (port_index--) { u32 t1, t2; - + int retries = 10; +retry: t1 = readl(ports[port_index]->addr); t2 = xhci_port_state_to_neutral(t1); portsc_buf[port_index] = 0; - /* Bail out if a USB3 port has a new device in link training */ - if ((hcd->speed >= HCD_USB3) && + /* + * Give a USB3 port in link training time to finish, but don't + * prevent suspend as port might be stuck + */ + if ((hcd->speed >= HCD_USB3) && retries-- && (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; + msleep(XHCI_PORT_POLLING_LFPS_TIME); + spin_lock_irqsave(&xhci->lock, flags); + xhci_dbg(xhci, "port %d polling in bus suspend, waiting\n", + port_index); + goto retry; } - /* 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) { -- cgit v1.2.3