| From fac4271d1126c45ceaceb7f4a336317b771eb121 Mon Sep 17 00:00:00 2001 |
| From: Zhuang Jin Can <jin.can.zhuang@intel.com> |
| Date: Tue, 21 Jul 2015 17:20:30 +0300 |
| Subject: xhci: prevent bus_suspend if SS port resuming in phase 1 |
| |
| From: Zhuang Jin Can <jin.can.zhuang@intel.com> |
| |
| commit fac4271d1126c45ceaceb7f4a336317b771eb121 upstream. |
| |
| 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] |
| 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> |
| |
| --- |
| drivers/usb/host/xhci-hub.c | 6 +++--- |
| drivers/usb/host/xhci-ring.c | 3 +++ |
| drivers/usb/host/xhci.h | 1 + |
| 3 files changed, 7 insertions(+), 3 deletions(-) |
| |
| --- a/drivers/usb/host/xhci-hub.c |
| +++ b/drivers/usb/host/xhci-hub.c |
| @@ -1048,10 +1048,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd |
| spin_lock_irqsave(&xhci->lock, flags); |
| |
| 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); |
| - xhci_dbg(xhci, "suspend failed because " |
| - "a port is resuming\n"); |
| + xhci_dbg(xhci, "suspend failed because a port is resuming\n"); |
| return -EBUSY; |
| } |
| } |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -1669,6 +1669,9 @@ static void handle_port_status(struct xh |
| 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) { |
| xhci_dbg(xhci, "port resume event for port %d\n", port_id); |
| |
| --- a/drivers/usb/host/xhci.h |
| +++ b/drivers/usb/host/xhci.h |
| @@ -280,6 +280,7 @@ struct xhci_op_regs { |
| #define XDEV_U0 (0x0 << 5) |
| #define XDEV_U2 (0x2 << 5) |
| #define XDEV_U3 (0x3 << 5) |
| +#define XDEV_INACTIVE (0x6 << 5) |
| #define XDEV_RESUME (0xf << 5) |
| /* true: port has power (see HCC_PPC) */ |
| #define PORT_POWER (1 << 9) |