xhci: workaround for hosts missing CAS bit
If a device is unplugged and replugged during Sx system suspend some Intel xHC hosts will overwrite the CAS (Cold attach status) flag and no device connection is noticed in resume. A device in this state can be identified in resume if its link state is in polling or compliance mode, and the current connect status is 0. A device in this state needs to be warm reset. Intel 100/c230 series PCH specification update Doc #332692-006 Errata #8 Observed on Cherryview and Apollolake as they go into compliance mode if LFPS times out during polling, and re-plugged devices are not discovered at resume. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> CC: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
4c39135aa4
commit
346e99736c
@@ -1355,6 +1355,35 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3.
|
||||
* warm reset a USB3 device stuck in polling or compliance mode after resume.
|
||||
* See Intel 100/c230 series PCH specification update Doc #332692-006 Errata #8
|
||||
*/
|
||||
static bool xhci_port_missing_cas_quirk(int port_index,
|
||||
__le32 __iomem **port_array)
|
||||
{
|
||||
u32 portsc;
|
||||
|
||||
portsc = readl(port_array[port_index]);
|
||||
|
||||
/* if any of these are set we are not stuck */
|
||||
if (portsc & (PORT_CONNECT | PORT_CAS))
|
||||
return false;
|
||||
|
||||
if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) &&
|
||||
((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE))
|
||||
return false;
|
||||
|
||||
/* clear wakeup/change bits, and do a warm port reset */
|
||||
portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
|
||||
portsc |= PORT_WR;
|
||||
writel(portsc, port_array[port_index]);
|
||||
/* flush write */
|
||||
readl(port_array[port_index]);
|
||||
return true;
|
||||
}
|
||||
|
||||
int xhci_bus_resume(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
@@ -1392,6 +1421,14 @@ int xhci_bus_resume(struct usb_hcd *hcd)
|
||||
u32 temp;
|
||||
|
||||
temp = readl(port_array[port_index]);
|
||||
|
||||
/* warm reset CAS limited ports stuck in polling/compliance */
|
||||
if ((xhci->quirks & XHCI_MISSING_CAS) &&
|
||||
(hcd->speed >= HCD_USB3) &&
|
||||
xhci_port_missing_cas_quirk(port_index, port_array)) {
|
||||
xhci_dbg(xhci, "reset stuck port %d\n", port_index);
|
||||
continue;
|
||||
}
|
||||
if (DEV_SUPERSPEED_ANY(temp))
|
||||
temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
|
||||
else
|
||||
|
Reference in New Issue
Block a user