xhci: Don't let USB3 ports stuck in polling state prevent suspend

Commit 2f31a67f01 ("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: 2f31a67f01 ("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 <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Mathias Nyman 2019-03-22 17:50:17 +02:00 committed by Greg Kroah-Hartman
parent 8867ea2621
commit d92f2c59cc
2 changed files with 20 additions and 7 deletions

View File

@ -1545,20 +1545,25 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
port_index = max_ports; port_index = max_ports;
while (port_index--) { while (port_index--) {
u32 t1, t2; u32 t1, t2;
int retries = 10;
retry:
t1 = readl(ports[port_index]->addr); t1 = readl(ports[port_index]->addr);
t2 = xhci_port_state_to_neutral(t1); t2 = xhci_port_state_to_neutral(t1);
portsc_buf[port_index] = 0; 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) { (t1 & PORT_PLS_MASK) == XDEV_POLLING) {
bus_state->bus_suspended = 0;
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg(xhci, "Bus suspend bailout, port in polling\n"); msleep(XHCI_PORT_POLLING_LFPS_TIME);
return -EBUSY; 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 */ /* suspend ports in U0, or bail out for new connect changes */
if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) { if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) {
if ((t1 & PORT_CSC) && wake_enabled) { if ((t1 & PORT_CSC) && wake_enabled) {

View File

@ -452,6 +452,14 @@ struct xhci_op_regs {
*/ */
#define XHCI_DEFAULT_BESL 4 #define XHCI_DEFAULT_BESL 4
/*
* USB3 specification define a 360ms tPollingLFPSTiemout for USB3 ports
* to complete link training. usually link trainig completes much faster
* so check status 10 times with 36ms sleep in places we need to wait for
* polling to complete.
*/
#define XHCI_PORT_POLLING_LFPS_TIME 36
/** /**
* struct xhci_intr_reg - Interrupt Register Set * struct xhci_intr_reg - Interrupt Register Set
* @irq_pending: IMAN - Interrupt Management Register. Used to enable * @irq_pending: IMAN - Interrupt Management Register. Used to enable