Commit fac4271d authored by Zhuang Jin Can's avatar Zhuang Jin Can Committed by Greg Kroah-Hartman

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: default avatarZhuang Jin Can <jin.can.zhuang@intel.com>
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 243292a2
...@@ -1123,10 +1123,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd) ...@@ -1123,10 +1123,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
if (hcd->self.root_hub->do_remote_wakeup) { if (hcd->self.root_hub->do_remote_wakeup) {
if (bus_state->resuming_ports) { if (bus_state->resuming_ports || /* USB2 */
bus_state->port_remote_wakeup) { /* USB3 */
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg(xhci, "suspend failed because " xhci_dbg(xhci, "suspend failed because a port is resuming\n");
"a port is resuming\n");
return -EBUSY; return -EBUSY;
} }
} }
......
...@@ -1546,6 +1546,9 @@ static void handle_port_status(struct xhci_hcd *xhci, ...@@ -1546,6 +1546,9 @@ static void handle_port_status(struct xhci_hcd *xhci,
usb_hcd_resume_root_hub(hcd); usb_hcd_resume_root_hub(hcd);
} }
if (hcd->speed == HCD_USB3 && (temp & PORT_PLS_MASK) == XDEV_INACTIVE)
bus_state->port_remote_wakeup &= ~(1 << faked_port_index);
if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) { if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) {
xhci_dbg(xhci, "port resume event for port %d\n", port_id); xhci_dbg(xhci, "port resume event for port %d\n", port_id);
......
...@@ -285,6 +285,7 @@ struct xhci_op_regs { ...@@ -285,6 +285,7 @@ struct xhci_op_regs {
#define XDEV_U0 (0x0 << 5) #define XDEV_U0 (0x0 << 5)
#define XDEV_U2 (0x2 << 5) #define XDEV_U2 (0x2 << 5)
#define XDEV_U3 (0x3 << 5) #define XDEV_U3 (0x3 << 5)
#define XDEV_INACTIVE (0x6 << 5)
#define XDEV_RESUME (0xf << 5) #define XDEV_RESUME (0xf << 5)
/* true: port has power (see HCC_PPC) */ /* true: port has power (see HCC_PPC) */
#define PORT_POWER (1 << 9) #define PORT_POWER (1 << 9)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment