diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 41 |
1 files changed, 39 insertions, 2 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 730b9fd26685..0ef16900efed 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
@@ -1166,7 +1166,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
1166 | xhci_set_link_state(xhci, port_array, wIndex, | 1166 | xhci_set_link_state(xhci, port_array, wIndex, |
1167 | XDEV_RESUME); | 1167 | XDEV_RESUME); |
1168 | spin_unlock_irqrestore(&xhci->lock, flags); | 1168 | spin_unlock_irqrestore(&xhci->lock, flags); |
1169 | msleep(20); | 1169 | msleep(USB_RESUME_TIMEOUT); |
1170 | spin_lock_irqsave(&xhci->lock, flags); | 1170 | spin_lock_irqsave(&xhci->lock, flags); |
1171 | xhci_set_link_state(xhci, port_array, wIndex, | 1171 | xhci_set_link_state(xhci, port_array, wIndex, |
1172 | XDEV_U0); | 1172 | XDEV_U0); |
@@ -1355,6 +1355,35 @@ int xhci_bus_suspend(struct usb_hcd *hcd) | |||
1355 | return 0; | 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 | int xhci_bus_resume(struct usb_hcd *hcd) | 1387 | int xhci_bus_resume(struct usb_hcd *hcd) |
1359 | { | 1388 | { |
1360 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | 1389 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
@@ -1392,6 +1421,14 @@ int xhci_bus_resume(struct usb_hcd *hcd) | |||
1392 | u32 temp; | 1421 | u32 temp; |
1393 | 1422 | ||
1394 | temp = readl(port_array[port_index]); | 1423 | temp = readl(port_array[port_index]); |
1424 | |||
1425 | /* warm reset CAS limited ports stuck in polling/compliance */ | ||
1426 | if ((xhci->quirks & XHCI_MISSING_CAS) && | ||
1427 | (hcd->speed >= HCD_USB3) && | ||
1428 | xhci_port_missing_cas_quirk(port_index, port_array)) { | ||
1429 | xhci_dbg(xhci, "reset stuck port %d\n", port_index); | ||
1430 | continue; | ||
1431 | } | ||
1395 | if (DEV_SUPERSPEED_ANY(temp)) | 1432 | if (DEV_SUPERSPEED_ANY(temp)) |
1396 | temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); | 1433 | temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); |
1397 | else | 1434 | else |
@@ -1410,7 +1447,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) | |||
1410 | 1447 | ||
1411 | if (need_usb2_u3_exit) { | 1448 | if (need_usb2_u3_exit) { |
1412 | spin_unlock_irqrestore(&xhci->lock, flags); | 1449 | spin_unlock_irqrestore(&xhci->lock, flags); |
1413 | msleep(20); | 1450 | msleep(USB_RESUME_TIMEOUT); |
1414 | spin_lock_irqsave(&xhci->lock, flags); | 1451 | spin_lock_irqsave(&xhci->lock, flags); |
1415 | } | 1452 | } |
1416 | 1453 | ||