USB: EHCI: fix remote-wakeup support for ARC/TDI core
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 6 Oct 2008 15:25:53 +0000 (11:25 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 17 Oct 2008 21:41:03 +0000 (14:41 -0700)
This patch (as1147) fixes the remote-wakeup support for EHCI
controllers using the ARC/TDI "embedded-TT" core.  These controllers
turn off the RESUME bit by themselves when a port resume is complete;
hence we need to keep separate track of which ports are suspended or
in the process of resuming.

The patch also makes a couple of small improvements in ehci_irq(),
replacing reads of the command register with the value already stored
in a local variable.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Tested-by: Thomas Reitmayr <treitmayr@devbase.at>
CC: David Brownell <david-b@pacbell.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci.h

index 358df2a..d343afa 100644 (file)
@@ -706,7 +706,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
                pcd_status = status;
 
                /* resume root hub? */
-               if (!(ehci_readl(ehci, &ehci->regs->command) & CMD_RUN))
+               if (!(cmd & CMD_RUN))
                        usb_hcd_resume_root_hub(hcd);
 
                while (i--) {
@@ -715,8 +715,11 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
 
                        if (pstatus & PORT_OWNER)
                                continue;
-                       if (!(pstatus & PORT_RESUME)
-                                       || ehci->reset_done [i] != 0)
+                       if (!(test_bit(i, &ehci->suspended_ports) &&
+                                       ((pstatus & PORT_RESUME) ||
+                                               !(pstatus & PORT_SUSPEND)) &&
+                                       (pstatus & PORT_PE) &&
+                                       ehci->reset_done[i] == 0))
                                continue;
 
                        /* start 20 msec resume signaling from this port,
@@ -731,9 +734,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
 
        /* PCI errors [4.15.2.4] */
        if (unlikely ((status & STS_FATAL) != 0)) {
-               dbg_cmd (ehci, "fatal", ehci_readl(ehci,
-                                                  &ehci->regs->command));
-               dbg_status (ehci, "fatal", status);
+               dbg_cmd(ehci, "fatal", cmd);
+               dbg_status(ehci, "fatal", status);
                if (status & STS_HALT) {
                        ehci_err (ehci, "fatal error\n");
 dead:
index 740835b..218f966 100644 (file)
@@ -236,10 +236,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
                temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
                temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
                if (test_bit(i, &ehci->bus_suspended) &&
-                               (temp & PORT_SUSPEND)) {
-                       ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
+                               (temp & PORT_SUSPEND))
                        temp |= PORT_RESUME;
-               }
                ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
        }
        i = HCS_N_PORTS (ehci->hcs_params);
@@ -482,10 +480,9 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
                 * controller by the user.
                 */
 
-               if ((temp & mask) != 0
-                               || ((temp & PORT_RESUME) != 0
-                                       && time_after_eq(jiffies,
-                                               ehci->reset_done[i]))) {
+               if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
+                               || (ehci->reset_done[i] && time_after_eq(
+                                       jiffies, ehci->reset_done[i]))) {
                        if (i < 7)
                            buf [0] |= 1 << (i + 1);
                        else
@@ -688,6 +685,7 @@ static int ehci_hub_control (
                        /* resume completed? */
                        else if (time_after_eq(jiffies,
                                        ehci->reset_done[wIndex])) {
+                               clear_bit(wIndex, &ehci->suspended_ports);
                                set_bit(wIndex, &ehci->port_c_suspend);
                                ehci->reset_done[wIndex] = 0;
 
@@ -734,6 +732,9 @@ static int ehci_hub_control (
                                        ehci_readl(ehci, status_reg));
                }
 
+               if (!(temp & (PORT_RESUME|PORT_RESET)))
+                       ehci->reset_done[wIndex] = 0;
+
                /* transfer dedicated ports to the companion hc */
                if ((temp & PORT_CONNECT) &&
                                test_bit(wIndex, &ehci->companion_ports)) {
@@ -757,8 +758,17 @@ static int ehci_hub_control (
                }
                if (temp & PORT_PE)
                        status |= 1 << USB_PORT_FEAT_ENABLE;
-               if (temp & (PORT_SUSPEND|PORT_RESUME))
+
+               /* maybe the port was unsuspended without our knowledge */
+               if (temp & (PORT_SUSPEND|PORT_RESUME)) {
                        status |= 1 << USB_PORT_FEAT_SUSPEND;
+               } else if (test_bit(wIndex, &ehci->suspended_ports)) {
+                       clear_bit(wIndex, &ehci->suspended_ports);
+                       ehci->reset_done[wIndex] = 0;
+                       if (temp & PORT_PE)
+                               set_bit(wIndex, &ehci->port_c_suspend);
+               }
+
                if (temp & PORT_OC)
                        status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
                if (temp & PORT_RESET)
@@ -803,6 +813,7 @@ static int ehci_hub_control (
                                        || (temp & PORT_RESET) != 0)
                                goto error;
                        ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+                       set_bit(wIndex, &ehci->suspended_ports);
                        break;
                case USB_PORT_FEAT_POWER:
                        if (HCS_PPC (ehci->hcs_params))
index a6fd550..b11798d 100644 (file)
@@ -99,6 +99,8 @@ struct ehci_hcd {                     /* one per controller */
                        owned by the companion during a bus suspend */
        unsigned long           port_c_suspend;         /* which ports have
                        the change-suspend feature turned on */
+       unsigned long           suspended_ports;        /* which ports are
+                       suspended */
 
        /* per-HC memory pools (could be per-bus, but ...) */
        struct dma_pool         *qh_pool;       /* qh per active urb */