USB: new flag for resume-from-hibernation
[safe/jmp/linux-2.6] / drivers / usb / host / uhci-hcd.c
index fec9872..274751b 100644 (file)
@@ -53,7 +53,6 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v3.0"
 #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \
 Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \
 Alan Stern"
@@ -234,7 +233,7 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci)
        return 0;
 }
 
-static int remote_wakeup_is_broken(struct uhci_hcd *uhci)
+static int global_suspend_mode_is_broken(struct uhci_hcd *uhci)
 {
        int port;
        const char *sys_info;
@@ -261,33 +260,60 @@ __releases(uhci->lock)
 __acquires(uhci->lock)
 {
        int auto_stop;
-       int int_enable, egsm_enable;
+       int int_enable, egsm_enable, wakeup_enable;
+       struct usb_device *rhdev = uhci_to_hcd(uhci)->self.root_hub;
 
        auto_stop = (new_state == UHCI_RH_AUTO_STOPPED);
-       dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
-                       "%s%s\n", __FUNCTION__,
+       dev_dbg(&rhdev->dev, "%s%s\n", __func__,
                        (auto_stop ? " (auto-stop)" : ""));
 
-       /* If we get a suspend request when we're already auto-stopped
-        * then there's nothing to do.
+       /* Start off by assuming Resume-Detect interrupts and EGSM work
+        * and that remote wakeups should be enabled.
+        */
+       egsm_enable = USBCMD_EGSM;
+       uhci->RD_enable = 1;
+       int_enable = USBINTR_RESUME;
+       wakeup_enable = 1;
+
+       /* In auto-stop mode wakeups must always be detected, but
+        * Resume-Detect interrupts may be prohibited.  (In the absence
+        * of CONFIG_PM, they are always disallowed.)
+        */
+       if (auto_stop) {
+               if (!device_may_wakeup(&rhdev->dev))
+                       int_enable = 0;
+
+       /* In bus-suspend mode wakeups may be disabled, but if they are
+        * allowed then so are Resume-Detect interrupts.
         */
-       if (uhci->rh_state == UHCI_RH_AUTO_STOPPED) {
-               uhci->rh_state = new_state;
-               return;
+       } else {
+#ifdef CONFIG_PM
+               if (!rhdev->do_remote_wakeup)
+                       wakeup_enable = 0;
+#endif
        }
 
-       /* Enable resume-detect interrupts if they work.
-        * Then enter Global Suspend mode if _it_ works, still configured.
+       /* EGSM causes the root hub to echo a 'K' signal (resume) out any
+        * port which requests a remote wakeup.  According to the USB spec,
+        * every hub is supposed to do this.  But if we are ignoring
+        * remote-wakeup requests anyway then there's no point to it.
+        * We also shouldn't enable EGSM if it's broken.
         */
-       egsm_enable = USBCMD_EGSM;
-       uhci->working_RD = 1;
-       int_enable = USBINTR_RESUME;
-       if (remote_wakeup_is_broken(uhci))
+       if (!wakeup_enable || global_suspend_mode_is_broken(uhci))
                egsm_enable = 0;
-       if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable ||
-                       !device_may_wakeup(
-                               &uhci_to_hcd(uhci)->self.root_hub->dev))
-               uhci->working_RD = int_enable = 0;
+
+       /* If we're ignoring wakeup events then there's no reason to
+        * enable Resume-Detect interrupts.  We also shouldn't enable
+        * them if they are broken or disallowed.
+        *
+        * This logic may lead us to enabling RD but not EGSM.  The UHCI
+        * spec foolishly says that RD works only when EGSM is on, but
+        * there's no harm in enabling it anyway -- perhaps some chips
+        * will implement it!
+        */
+       if (!wakeup_enable || resume_detect_interrupts_are_broken(uhci) ||
+                       !int_enable)
+               uhci->RD_enable = int_enable = 0;
 
        outw(int_enable, uhci->io_addr + USBINTR);
        outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD);
@@ -308,14 +334,17 @@ __acquires(uhci->lock)
                        return;
        }
        if (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH))
-               dev_warn(&uhci_to_hcd(uhci)->self.root_hub->dev,
-                       "Controller not stopped yet!\n");
+               dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n");
 
        uhci_get_current_frame_number(uhci);
 
        uhci->rh_state = new_state;
        uhci->is_stopped = UHCI_IS_STOPPED;
-       uhci_to_hcd(uhci)->poll_rh = !int_enable;
+
+       /* If interrupts don't work and remote wakeup is enabled then
+        * the suspended root hub needs to be polled.
+        */
+       uhci_to_hcd(uhci)->poll_rh = (!int_enable && wakeup_enable);
 
        uhci_scan_schedule(uhci);
        uhci_fsbr_off(uhci);
@@ -342,7 +371,7 @@ __releases(uhci->lock)
 __acquires(uhci->lock)
 {
        dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
-                       "%s%s\n", __FUNCTION__,
+                       "%s%s\n", __func__,
                        uhci->rh_state == UHCI_RH_AUTO_STOPPED ?
                                " (auto-start)" : "");
 
@@ -351,9 +380,12 @@ __acquires(uhci->lock)
         * for 20 ms.
         */
        if (uhci->rh_state == UHCI_RH_SUSPENDED) {
+               unsigned egsm;
+
+               /* Keep EGSM on if it was set before */
+               egsm = inw(uhci->io_addr + USBCMD) & USBCMD_EGSM;
                uhci->rh_state = UHCI_RH_RESUMING;
-               outw(USBCMD_FGR | USBCMD_EGSM | USBCMD_CF,
-                               uhci->io_addr + USBCMD);
+               outw(USBCMD_FGR | USBCMD_CF | egsm, uhci->io_addr + USBCMD);
                spin_unlock_irq(&uhci->lock);
                msleep(20);
                spin_lock_irq(&uhci->lock);
@@ -737,12 +769,12 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
        return rc;
 }
 
-static int uhci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
+static int uhci_pci_suspend(struct usb_hcd *hcd)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
        int rc = 0;
 
-       dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
+       dev_dbg(uhci_dev(uhci), "%s\n", __func__);
 
        spin_lock_irq(&uhci->lock);
        if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
@@ -763,10 +795,6 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
 
        /* FIXME: Enable non-PME# remote wakeup? */
 
-       /* make sure snapshot being resumed re-enumerates everything */
-       if (message.event == PM_EVENT_PRETHAW)
-               uhci_hc_died(uhci);
-
 done_okay:
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
 done:
@@ -774,11 +802,11 @@ done:
        return rc;
 }
 
-static int uhci_pci_resume(struct usb_hcd *hcd)
+static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
-       dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
+       dev_dbg(uhci_dev(uhci), "%s\n", __func__);
 
        /* Since we aren't in D3 any more, it's safe to set this flag
         * even if the controller was dead.
@@ -788,6 +816,10 @@ static int uhci_pci_resume(struct usb_hcd *hcd)
 
        spin_lock_irq(&uhci->lock);
 
+       /* Make sure resume from hibernation re-enumerates everything */
+       if (hibernated)
+               uhci_hc_died(uhci);
+
        /* FIXME: Disable non-PME# remote wakeup? */
 
        /* The firmware or a boot kernel may have changed the controller
@@ -808,8 +840,10 @@ static int uhci_pci_resume(struct usb_hcd *hcd)
 
        spin_unlock_irq(&uhci->lock);
 
-       if (!uhci->working_RD) {
-               /* Suspended root hub needs to be polled */
+       /* If interrupts don't work and remote wakeup is enabled then
+        * the suspended root hub needs to be polled.
+        */
+       if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) {
                hcd->poll_rh = 1;
                usb_hcd_poll_rh_status(hcd);
        }
@@ -906,27 +940,29 @@ static struct pci_driver uhci_pci_driver = {
        .remove =       usb_hcd_pci_remove,
        .shutdown =     uhci_shutdown,
 
-#ifdef CONFIG_PM
-       .suspend =      usb_hcd_pci_suspend,
-       .resume =       usb_hcd_pci_resume,
-#endif /* PM */
+#ifdef CONFIG_PM_SLEEP
+       .driver =       {
+               .pm =   &usb_hcd_pci_pm_ops
+       },
+#endif
 };
  
 static int __init uhci_hcd_init(void)
 {
        int retval = -ENOMEM;
 
-       printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "%s\n",
-                       ignore_oc ? ", overcurrent ignored" : "");
-
        if (usb_disabled())
                return -ENODEV;
 
+       printk(KERN_INFO "uhci_hcd: " DRIVER_DESC "%s\n",
+                       ignore_oc ? ", overcurrent ignored" : "");
+       set_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
+
        if (DEBUG_CONFIGURED) {
                errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
                if (!errbuf)
                        goto errbuf_failed;
-               uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
+               uhci_debugfs_root = debugfs_create_dir("uhci", usb_debug_root);
                if (!uhci_debugfs_root)
                        goto debug_failed;
        }
@@ -953,6 +989,7 @@ debug_failed:
 
 errbuf_failed:
 
+       clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
        return retval;
 }
 
@@ -962,6 +999,7 @@ static void __exit uhci_hcd_cleanup(void)
        kmem_cache_destroy(uhci_up_cachep);
        debugfs_remove(uhci_debugfs_root);
        kfree(errbuf);
+       clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
 }
 
 module_init(uhci_hcd_init);