usb: ehci: tegra: Correctly handle GetPortStatus during Resume
Nathan Connell [Mon, 2 May 2011 19:32:36 +0000 (14:32 -0500)]
Multiple GetPortStatus requests can be made while the
USB bus is resuming.  All requests must be handled
properly to prevent incorrect disconnect detection
during Resume and improper indentification of
Resume signaling as a remote wakeup event.

Change-Id: Ib07f83a2bab5699b2d95533d26d0a6bf541c697d
Signed-off-by: Nathan Connell <w14185@motorola.com>

Rebase-Id: R6d2c8b1b85fb37d1e1d7dad3051098b0d42ed71a

drivers/usb/host/ehci-tegra.c

index bf1bbd6..37aea39 100644 (file)
@@ -156,9 +156,13 @@ static int tegra_ehci_hub_control(
 
        if (typeReq == GetPortStatus) {
                temp = ehci_readl(ehci, status_reg);
-               if (tegra->port_resuming && !(temp & PORT_SUSPEND)) {
+               if (tegra->port_resuming && !(temp & PORT_SUSPEND) &&
+                   time_after_eq(jiffies, ehci->reset_done[wIndex-1])) {
                        /* Resume completed, re-enable disconnect detection */
                        tegra->port_resuming = 0;
+                       clear_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
+                       ehci->reset_done[wIndex-1] = 0;
+                       clear_bit(wIndex-1, &ehci->resuming_ports);
                        tegra_usb_phy_postresume(tegra->phy);
                }
        }
@@ -211,11 +215,11 @@ static int tegra_ehci_hub_control(
                if (!(temp & PORT_SUSPEND))
                        goto done;
 
+               tegra->port_resuming = 1;
+
                /* Disable disconnect detection during port resume */
                tegra_usb_phy_preresume(tegra->phy);
 
-               ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25);
-
                ehci_dbg(ehci, "%s:USBSTS = 0x%x", __func__,
                        ehci_readl(ehci, &ehci->regs->status));
                usbsts_reg = ehci_readl(ehci, &ehci->regs->status);
@@ -237,26 +241,12 @@ static int tegra_ehci_hub_control(
 
                udelay(20);
                temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
-               /* start resume signalling */
+               /* start resume signaling */
                ehci_writel(ehci, temp | PORT_RESUME, status_reg);
                set_bit(wIndex-1, &ehci->resuming_ports);
 
-               spin_unlock_irqrestore(&ehci->lock, flags);
-               msleep(20);
-               spin_lock_irqsave(&ehci->lock, flags);
-
-               /* Poll until the controller clears RESUME and SUSPEND */
-               if (handshake(ehci, status_reg, PORT_RESUME, 0, 2000))
-                       pr_err("%s: timeout waiting for RESUME\n", __func__);
-               if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000))
-                       pr_err("%s: timeout waiting for SUSPEND\n", __func__);
-
-               ehci->reset_done[wIndex-1] = 0;
-               clear_bit(wIndex-1, &ehci->resuming_ports);
-
-               ehci->reset_done[wIndex-1] = 0;
-
-               tegra->port_resuming = 1;
+               ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25);
+               /* whoever resumes must GetPortStatus to complete it!! */
                goto done;
        }
 
@@ -575,8 +565,6 @@ static int controller_resume(struct device *dev)
        tegra_ehci_restart(hcd);
 
  done:
-       tegra_usb_phy_preresume(tegra->phy);
-       tegra->port_resuming = 1;
        return 0;
 }