aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2011-08-03 04:46:49 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-08-09 17:44:50 -0400
commit8a8ff2f9399b23b968901f585ccb5a70a537c5ae (patch)
tree544da721cca4455b4a66e85c43ed0bea1f8c3097 /drivers
parent5ac04bf190e6f8b17238aef179ebd7f2bdfec919 (diff)
xHCI: report USB2 port in resuming as suspend
When a USB2 port initiate a remote wakeup, software shall ensure that resume is signaled for at least 20ms, and then write '0' to the PLS field. According to this, xhci driver do the following things: 1. When receive a remote wakeup event in irq_handler, set the resume_done value as jiffies + 20ms, and modify rh_timer to poll root hub status at that time; 2. When receive a GetPortStatus request, if the jiffies is after the resume_done value, clear the resume signal and resume_done. However, if usb_port_resume() is called before the rh_timer triggered, it will indicate the port as Suspend Cleared and skip the clear resume signal part. The device will fail the usb_get_status request in finish_port_resume(), and usbcore will try a reset-resume instead. Device will work OK after reset-resume, but resume_done value is not cleared in this case, and xhci_bus_suspend() will fail because when it finds a non-zero resume_done value, it will regard the port as resuming and return -EBUSY. This causes issue on some platforms that the system fail to suspend after remote wakeup from suspend by USB2 devices connected to xHCI port. To fix this issue, report the port status as suspend if the resume is signaling less that 20ms, and usb_port_resume() will wait 25ms and check port status again, so xHCI driver can clear the resume signaling and resume_done value. This should be backported to kernels as old as 2.6.37. Signed-off-by: Andiry Xu <andiry.xu@amd.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: stable@kernel.org
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/xhci-hub.c15
1 files changed, 12 insertions, 3 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index cddcdccadbf7..1e96d1f1fe6b 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -463,11 +463,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
463 && (temp & PORT_POWER)) 463 && (temp & PORT_POWER))
464 status |= USB_PORT_STAT_SUSPEND; 464 status |= USB_PORT_STAT_SUSPEND;
465 } 465 }
466 if ((temp & PORT_PLS_MASK) == XDEV_RESUME) { 466 if ((temp & PORT_PLS_MASK) == XDEV_RESUME &&
467 !DEV_SUPERSPEED(temp)) {
467 if ((temp & PORT_RESET) || !(temp & PORT_PE)) 468 if ((temp & PORT_RESET) || !(temp & PORT_PE))
468 goto error; 469 goto error;
469 if (!DEV_SUPERSPEED(temp) && time_after_eq(jiffies, 470 if (time_after_eq(jiffies,
470 bus_state->resume_done[wIndex])) { 471 bus_state->resume_done[wIndex])) {
471 xhci_dbg(xhci, "Resume USB2 port %d\n", 472 xhci_dbg(xhci, "Resume USB2 port %d\n",
472 wIndex + 1); 473 wIndex + 1);
473 bus_state->resume_done[wIndex] = 0; 474 bus_state->resume_done[wIndex] = 0;
@@ -487,6 +488,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
487 xhci_ring_device(xhci, slot_id); 488 xhci_ring_device(xhci, slot_id);
488 bus_state->port_c_suspend |= 1 << wIndex; 489 bus_state->port_c_suspend |= 1 << wIndex;
489 bus_state->suspended_ports &= ~(1 << wIndex); 490 bus_state->suspended_ports &= ~(1 << wIndex);
491 } else {
492 /*
493 * The resume has been signaling for less than
494 * 20ms. Report the port status as SUSPEND,
495 * let the usbcore check port status again
496 * and clear resume signaling later.
497 */
498 status |= USB_PORT_STAT_SUSPEND;
490 } 499 }
491 } 500 }
492 if ((temp & PORT_PLS_MASK) == XDEV_U0 501 if ((temp & PORT_PLS_MASK) == XDEV_U0