aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-01-22 16:08:53 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-07 15:44:39 -0800
commit629e4427aa817d5c9f11885420abf54b8f5967dc (patch)
tree33c5a88751c5934df34252998a562fa757451b14
parent1d619f128ba911cd3e6d6ad3475f146eb92f5c27 (diff)
EHCI: fix interrupt-driven remote wakeup
Now that port status change notifications are interrupt-driven, ehci-hcd needs to tell usbcore when a remote-wakeup resume operation is finished -- we can no longer rely on the core to poll and find out. This patch (as843) uses the root-hub status timer to force a poll after the resume is complete. The patch also changes the test for detecting when the TDRSMDN resume period has expired. It's necessary to use time_after_eq() instead of time_after(), since the polling is triggered precisely by a timer. The same change is made for TDRSTR reset expiration, for consistency. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: David Brownell <david-b@pacbell.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/host/ehci-hub.c50
1 files changed, 32 insertions, 18 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 3cfba69e076..0d83c6df1a3 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -379,8 +379,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
ehci->reset_done [i] = 0;
if ((temp & mask) != 0
|| ((temp & PORT_RESUME) != 0
- && time_after (jiffies,
- ehci->reset_done [i]))) {
+ && time_after_eq(jiffies,
+ ehci->reset_done[i]))) {
if (i < 7)
buf [0] |= 1 << (i + 1);
else
@@ -554,31 +554,45 @@ static int ehci_hub_control (
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
/* whoever resumes must GetPortStatus to complete it!! */
- if ((temp & PORT_RESUME)
- && time_after (jiffies,
- ehci->reset_done [wIndex])) {
- status |= 1 << USB_PORT_FEAT_C_SUSPEND;
- ehci->reset_done [wIndex] = 0;
+ if (temp & PORT_RESUME) {
- /* stop resume signaling */
- temp = ehci_readl(ehci, status_reg);
- ehci_writel(ehci,
+ /* Remote Wakeup received? */
+ if (!ehci->reset_done[wIndex]) {
+ /* resume signaling for 20 msec */
+ ehci->reset_done[wIndex] = jiffies
+ + msecs_to_jiffies(20);
+ /* check the port again */
+ mod_timer(&ehci_to_hcd(ehci)->rh_timer,
+ ehci->reset_done[wIndex]);
+ }
+
+ /* resume completed? */
+ else if (time_after_eq(jiffies,
+ ehci->reset_done[wIndex])) {
+ status |= 1 << USB_PORT_FEAT_C_SUSPEND;
+ ehci->reset_done[wIndex] = 0;
+
+ /* stop resume signaling */
+ temp = ehci_readl(ehci, status_reg);
+ ehci_writel(ehci,
temp & ~(PORT_RWC_BITS | PORT_RESUME),
status_reg);
- retval = handshake(ehci, status_reg,
+ retval = handshake(ehci, status_reg,
PORT_RESUME, 0, 2000 /* 2msec */);
- if (retval != 0) {
- ehci_err (ehci, "port %d resume error %d\n",
- wIndex + 1, retval);
- goto error;
+ if (retval != 0) {
+ ehci_err(ehci,
+ "port %d resume error %d\n",
+ wIndex + 1, retval);
+ goto error;
+ }
+ temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
}
- temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
}
/* whoever resets must GetPortStatus to complete it!! */
if ((temp & PORT_RESET)
- && time_after (jiffies,
- ehci->reset_done [wIndex])) {
+ && time_after_eq(jiffies,
+ ehci->reset_done[wIndex])) {
status |= 1 << USB_PORT_FEAT_C_RESET;
ehci->reset_done [wIndex] = 0;