USB: EHCI: carry out port handover during each root-hub resume
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 3 Mar 2008 20:15:36 +0000 (15:15 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 25 Apr 2008 04:16:32 +0000 (21:16 -0700)
This patch (as1044) causes EHCI port handover for non-high-speed
devices to occur during every root-hub resume, not just in cases where
the controller lost power or was reset.  This is necessary because:

When some machines go into suspend, they remove power from
on-board USB devices while retaining suspend current for USB
controllers.

The user might well unplug a USB device while the system is
suspended and then plug it back in before resuming.

A corresponding change is made to the core resume routine; now
high-speed root hubs will always be resumed when the system wakes up,
even if they were suspended before the system went to sleep.  If this
weren't done then EHCI port handover wouldn't work, since it is called
when the EHCI root hub is resumed.

Finally, a comment is added to the hub driver explaining the khubd has
to be freezable; if it weren't frozen then it could interfere with
port handover.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/driver.c
drivers/usb/core/hub.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-pci.c

index 801b6f142fa7f874650a99c4d28c268c887a3114..ebccdefcc6f24fde54818da6e07045446a157ec2 100644 (file)
@@ -1523,9 +1523,14 @@ static int usb_suspend(struct device *dev, pm_message_t message)
        udev = to_usb_device(dev);
 
        /* If udev is already suspended, we can skip this suspend and
-        * we should also skip the upcoming system resume. */
+        * we should also skip the upcoming system resume.  High-speed
+        * root hubs are an exception; they need to resume whenever the
+        * system wakes up in order for USB-PERSIST port handover to work
+        * properly.
+        */
        if (udev->state == USB_STATE_SUSPENDED) {
-               udev->skip_sys_resume = 1;
+               if (udev->parent || udev->speed != USB_SPEED_HIGH)
+                       udev->skip_sys_resume = 1;
                return 0;
        }
 
index 57aeca160f3837a6cc39b580a8192d086cc15e9b..a42db75c2336e7a5f15cc4438d29af0ff9e16184 100644 (file)
@@ -2890,7 +2890,13 @@ loop:
 
 static int hub_thread(void *__unused)
 {
+       /* khubd needs to be freezable to avoid intefering with USB-PERSIST
+        * port handover.  Otherwise it might see that a full-speed device
+        * was gone before the EHCI controller had handed its port over to
+        * the companion full-speed controller.
+        */
        set_freezable();
+
        do {
                hub_events();
                wait_event_freezable(khubd_wait,
index 4e065e556e4b802c8c83a507973fb31a56931460..8d513a15d0cd9686a0545feca605247da52cf925 100644 (file)
@@ -281,9 +281,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
        ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
 
        spin_unlock_irq (&ehci->lock);
-
-       if (!power_okay)
-               ehci_handover_companion_ports(ehci);
+       ehci_handover_companion_ports(ehci);
        return 0;
 }
 
index 72ccd56e36dd46665712ea26d5944312f8a10635..040bd8632eb3da4655ece58f8341aa49560cdca8 100644 (file)
@@ -329,7 +329,6 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
 
        /* here we "know" root ports should always stay powered */
        ehci_port_power(ehci, 1);
-       ehci_handover_companion_ports(ehci);
 
        hcd->state = HC_STATE_SUSPENDED;
        return 0;