diff options
author | Zhuang Jin Can <jin.can.zhuang@intel.com> | 2015-07-21 10:20:30 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-07-22 17:19:37 -0400 |
commit | fac4271d1126c45ceaceb7f4a336317b771eb121 (patch) | |
tree | 7ea059fe9f19fe19b0e262aaf316eb147cee14a5 | |
parent | 243292a2ad3dc365849b820a64868927168894ac (diff) |
xhci: prevent bus_suspend if SS port resuming in phase 1
When the link is just waken, it's in Resume state, and driver sets PLS to
U0. This refers to Phase 1. Phase 2 refers to when the link has completed
the transition from Resume state to U0.
With the fix of xhci: report U3 when link is in resume state, it also
exposes an issue that usb3 roothub and controller can suspend right
after phase 1, and this causes a hard hang in controller.
To fix the issue, we need to prevent usb3 bus suspend if any port is
resuming in phase 1.
[merge separate USB2 and USB3 port resume checking to one -Mathias]
Cc: <stable@vger.kernel.org>
Signed-off-by: Zhuang Jin Can <jin.can.zhuang@intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 1 |
3 files changed, 7 insertions, 3 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 7df8878cc37b..fdca8edc5836 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
@@ -1123,10 +1123,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd) | |||
1123 | spin_lock_irqsave(&xhci->lock, flags); | 1123 | spin_lock_irqsave(&xhci->lock, flags); |
1124 | 1124 | ||
1125 | if (hcd->self.root_hub->do_remote_wakeup) { | 1125 | if (hcd->self.root_hub->do_remote_wakeup) { |
1126 | if (bus_state->resuming_ports) { | 1126 | if (bus_state->resuming_ports || /* USB2 */ |
1127 | bus_state->port_remote_wakeup) { /* USB3 */ | ||
1127 | spin_unlock_irqrestore(&xhci->lock, flags); | 1128 | spin_unlock_irqrestore(&xhci->lock, flags); |
1128 | xhci_dbg(xhci, "suspend failed because " | 1129 | xhci_dbg(xhci, "suspend failed because a port is resuming\n"); |
1129 | "a port is resuming\n"); | ||
1130 | return -EBUSY; | 1130 | return -EBUSY; |
1131 | } | 1131 | } |
1132 | } | 1132 | } |
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 94416ff70810..6a8fc52aed58 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c | |||
@@ -1546,6 +1546,9 @@ static void handle_port_status(struct xhci_hcd *xhci, | |||
1546 | usb_hcd_resume_root_hub(hcd); | 1546 | usb_hcd_resume_root_hub(hcd); |
1547 | } | 1547 | } |
1548 | 1548 | ||
1549 | if (hcd->speed == HCD_USB3 && (temp & PORT_PLS_MASK) == XDEV_INACTIVE) | ||
1550 | bus_state->port_remote_wakeup &= ~(1 << faked_port_index); | ||
1551 | |||
1549 | if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) { | 1552 | if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) { |
1550 | xhci_dbg(xhci, "port resume event for port %d\n", port_id); | 1553 | xhci_dbg(xhci, "port resume event for port %d\n", port_id); |
1551 | 1554 | ||
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 31e46cc55807..ed2ebf647c38 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h | |||
@@ -285,6 +285,7 @@ struct xhci_op_regs { | |||
285 | #define XDEV_U0 (0x0 << 5) | 285 | #define XDEV_U0 (0x0 << 5) |
286 | #define XDEV_U2 (0x2 << 5) | 286 | #define XDEV_U2 (0x2 << 5) |
287 | #define XDEV_U3 (0x3 << 5) | 287 | #define XDEV_U3 (0x3 << 5) |
288 | #define XDEV_INACTIVE (0x6 << 5) | ||
288 | #define XDEV_RESUME (0xf << 5) | 289 | #define XDEV_RESUME (0xf << 5) |
289 | /* true: port has power (see HCC_PPC) */ | 290 | /* true: port has power (see HCC_PPC) */ |
290 | #define PORT_POWER (1 << 9) | 291 | #define PORT_POWER (1 << 9) |