]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
EHCI: keep track of ports being resumed and indicate in hub_status_data
authorAlan Stern <stern@rowland.harvard.edu>
Tue, 3 Apr 2012 19:24:30 +0000 (15:24 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 9 Apr 2012 22:43:21 +0000 (15:43 -0700)
This patch (as1537) adds a bit-array to ehci-hcd for keeping track of
which ports are undergoing a resume transition.  If any of the bits
are set when ehci_hub_status_data() is called, the routine will return
a nonzero value even if no ports have any status changes pending.
This will allow usbcore to handle races between root-hub suspend and
port wakeup.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: Sarah Sharp <sarah.a.sharp@linux.intel.com>
CC: Chen Peter-B29397 <B29397@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-tegra.c
drivers/usb/host/ehci.h

index 057cdda7a48978999ebf5ac4cbb8ecee7acc60e0..806cc95317aa162b8cca938b6a87cd3a2915e972 100644 (file)
@@ -347,6 +347,8 @@ static int ehci_reset (struct ehci_hcd *ehci)
        if (ehci->debug)
                dbgp_external_startup();
 
+       ehci->port_c_suspend = ehci->suspended_ports =
+                       ehci->resuming_ports = 0;
        return retval;
 }
 
@@ -939,6 +941,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
                         * like usb_port_resume() does.
                         */
                        ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
+                       set_bit(i, &ehci->resuming_ports);
                        ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
                        mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
                }
index 256fbd42e48c19b02c808fccda6025b93a3392c5..38fe076231522875bec58c60ec74fc7b14932960 100644 (file)
@@ -223,15 +223,10 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
         * remote wakeup, we must fail the suspend.
         */
        if (hcd->self.root_hub->do_remote_wakeup) {
-               port = HCS_N_PORTS(ehci->hcs_params);
-               while (port--) {
-                       if (ehci->reset_done[port] != 0) {
-                               spin_unlock_irq(&ehci->lock);
-                               ehci_dbg(ehci, "suspend failed because "
-                                               "port %d is resuming\n",
-                                               port + 1);
-                               return -EBUSY;
-                       }
+               if (ehci->resuming_ports) {
+                       spin_unlock_irq(&ehci->lock);
+                       ehci_dbg(ehci, "suspend failed because a port is resuming\n");
+                       return -EBUSY;
                }
        }
 
@@ -554,16 +549,12 @@ static int
 ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
 {
        struct ehci_hcd *ehci = hcd_to_ehci (hcd);
-       u32             temp, status = 0;
+       u32             temp, status;
        u32             mask;
        int             ports, i, retval = 1;
        unsigned long   flags;
        u32             ppcd = 0;
 
-       /* if !USB_SUSPEND, root hub timers won't get shut down ... */
-       if (ehci->rh_state != EHCI_RH_RUNNING)
-               return 0;
-
        /* init status to no-changes */
        buf [0] = 0;
        ports = HCS_N_PORTS (ehci->hcs_params);
@@ -572,6 +563,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
                retval++;
        }
 
+       /* Inform the core about resumes-in-progress by returning
+        * a non-zero value even if there are no status changes.
+        */
+       status = ehci->resuming_ports;
+
        /* Some boards (mostly VIA?) report bogus overcurrent indications,
         * causing massive log spam unless we completely ignore them.  It
         * may be relevant that VIA VT8235 controllers, where PORT_POWER is
@@ -846,6 +842,7 @@ static int ehci_hub_control (
                                ehci_writel(ehci,
                                        temp & ~(PORT_RWC_BITS | PORT_RESUME),
                                        status_reg);
+                               clear_bit(wIndex, &ehci->resuming_ports);
                                retval = handshake(ehci, status_reg,
                                           PORT_RESUME, 0, 2000 /* 2msec */);
                                if (retval != 0) {
@@ -864,6 +861,7 @@ static int ehci_hub_control (
                                        ehci->reset_done[wIndex])) {
                        status |= USB_PORT_STAT_C_RESET << 16;
                        ehci->reset_done [wIndex] = 0;
+                       clear_bit(wIndex, &ehci->resuming_ports);
 
                        /* force reset to complete */
                        ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
@@ -884,8 +882,10 @@ static int ehci_hub_control (
                                        ehci_readl(ehci, status_reg));
                }
 
-               if (!(temp & (PORT_RESUME|PORT_RESET)))
+               if (!(temp & (PORT_RESUME|PORT_RESET))) {
                        ehci->reset_done[wIndex] = 0;
+                       clear_bit(wIndex, &ehci->resuming_ports);
+               }
 
                /* transfer dedicated ports to the companion hc */
                if ((temp & PORT_CONNECT) &&
@@ -920,6 +920,7 @@ static int ehci_hub_control (
                        status |= USB_PORT_STAT_SUSPEND;
                } else if (test_bit(wIndex, &ehci->suspended_ports)) {
                        clear_bit(wIndex, &ehci->suspended_ports);
+                       clear_bit(wIndex, &ehci->resuming_ports);
                        ehci->reset_done[wIndex] = 0;
                        if (temp & PORT_PE)
                                set_bit(wIndex, &ehci->port_c_suspend);
index 3de48a2d79550df7b9af8efe0132ff73e2535fdd..73544bd440bd09cfaca5d50ebd1daf862266dc3a 100644 (file)
@@ -224,6 +224,7 @@ static int tegra_ehci_hub_control(
                temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
                /* start resume signalling */
                ehci_writel(ehci, temp | PORT_RESUME, status_reg);
+               set_bit(wIndex-1, &ehci->resuming_ports);
 
                spin_unlock_irqrestore(&ehci->lock, flags);
                msleep(20);
@@ -236,6 +237,7 @@ static int tegra_ehci_hub_control(
                        pr_err("%s: timeout waiting for SUSPEND\n", __func__);
 
                ehci->reset_done[wIndex-1] = 0;
+               clear_bit(wIndex-1, &ehci->resuming_ports);
 
                tegra->port_resuming = 1;
                goto done;
index 8f9acbc96fde03bf320ed1a696f0fb7b164a45e3..2694ed6558d2d954c03a4ba3b77eabdc64f9c7f6 100644 (file)
@@ -117,6 +117,8 @@ struct ehci_hcd {                   /* one per controller */
                        the change-suspend feature turned on */
        unsigned long           suspended_ports;        /* which ports are
                        suspended */
+       unsigned long           resuming_ports;         /* which ports have
+                       started to resume */
 
        /* per-HC memory pools (could be per-bus, but ...) */
        struct dma_pool         *qh_pool;       /* qh per active urb */