Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Mathias Nyman and committed by
Greg Kroah-Hartman
346e9973 4c39135a

+46
+37
drivers/usb/host/xhci-hub.c
··· 1355 1355 return 0; 1356 1356 } 1357 1357 1358 + /* 1359 + * Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3. 1360 + * warm reset a USB3 device stuck in polling or compliance mode after resume. 1361 + * See Intel 100/c230 series PCH specification update Doc #332692-006 Errata #8 1362 + */ 1363 + static bool xhci_port_missing_cas_quirk(int port_index, 1364 + __le32 __iomem **port_array) 1365 + { 1366 + u32 portsc; 1367 + 1368 + portsc = readl(port_array[port_index]); 1369 + 1370 + /* if any of these are set we are not stuck */ 1371 + if (portsc & (PORT_CONNECT | PORT_CAS)) 1372 + return false; 1373 + 1374 + if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) && 1375 + ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE)) 1376 + return false; 1377 + 1378 + /* clear wakeup/change bits, and do a warm port reset */ 1379 + portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); 1380 + portsc |= PORT_WR; 1381 + writel(portsc, port_array[port_index]); 1382 + /* flush write */ 1383 + readl(port_array[port_index]); 1384 + return true; 1385 + } 1386 + 1358 1387 int xhci_bus_resume(struct usb_hcd *hcd) 1359 1388 { 1360 1389 struct xhci_hcd *xhci = hcd_to_xhci(hcd); ··· 1421 1392 u32 temp; 1422 1393 1423 1394 temp = readl(port_array[port_index]); 1395 + 1396 + /* warm reset CAS limited ports stuck in polling/compliance */ 1397 + if ((xhci->quirks & XHCI_MISSING_CAS) && 1398 + (hcd->speed >= HCD_USB3) && 1399 + xhci_port_missing_cas_quirk(port_index, port_array)) { 1400 + xhci_dbg(xhci, "reset stuck port %d\n", port_index); 1401 + continue; 1402 + } 1424 1403 if (DEV_SUPERSPEED_ANY(temp)) 1425 1404 temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); 1426 1405 else
+6
drivers/usb/host/xhci-pci.c
··· 51 51 #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f 52 52 #define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8 53 53 #define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8 54 + #define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8 54 55 55 56 static const char hcd_name[] = "xhci_hcd"; 56 57 ··· 172 171 pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { 173 172 xhci->quirks |= XHCI_SSIC_PORT_UNUSED; 174 173 } 174 + if (pdev->vendor == PCI_VENDOR_ID_INTEL && 175 + (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || 176 + pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) 177 + xhci->quirks |= XHCI_MISSING_CAS; 178 + 175 179 if (pdev->vendor == PCI_VENDOR_ID_ETRON && 176 180 pdev->device == PCI_DEVICE_ID_EJ168) { 177 181 xhci->quirks |= XHCI_RESET_ON_RESUME;
+3
drivers/usb/host/xhci.h
··· 314 314 #define XDEV_U2 (0x2 << 5) 315 315 #define XDEV_U3 (0x3 << 5) 316 316 #define XDEV_INACTIVE (0x6 << 5) 317 + #define XDEV_POLLING (0x7 << 5) 318 + #define XDEV_COMP_MODE (0xa << 5) 317 319 #define XDEV_RESUME (0xf << 5) 318 320 /* true: port has power (see HCC_PPC) */ 319 321 #define PORT_POWER (1 << 9) ··· 1655 1653 #define XHCI_MTK_HOST (1 << 21) 1656 1654 #define XHCI_SSIC_PORT_UNUSED (1 << 22) 1657 1655 #define XHCI_NO_64BIT_SUPPORT (1 << 23) 1656 + #define XHCI_MISSING_CAS (1 << 24) 1658 1657 unsigned int num_active_eps; 1659 1658 unsigned int limit_active_eps; 1660 1659 /* There are two roothubs to keep track of bus suspend info for */