[PATCH] OHCI PM updates
David Brownell [Fri, 23 Sep 2005 05:42:53 +0000 (22:42 -0700)]
This simplifies the OHCI root hub suspend logic:

 - Uses new usbcore root hub calls to make autosuspend work again:
* Uses a newish usbcore root hub wakeup mechanism,
  making requests to khubd not keventd.
* Uses an even newer sibling suspend hook.

 - Expect someone always made usbcore call ohci_hub_suspend() before bus
   glue fires; and that ohci_hub_resume() is only called after that bus
   glue ran.  Previously, only CONFIG_USB_SUSPEND promised those things.
   (Includes updates to PCI and OMAP bus glue.)

 - Handle a not-noticed-before special case during resume from one of
   the swsusp snapshots when using "usb-handoff":  the controller isn't
   left in RESET state.  (A bug to fix in the usb-handoff code...)

Also cleans up a minor debug printk glitch, and switches an mdelay over
to an msleep (how did that stick around for so long?).

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

 drivers/usb/host/ohci-dbg.c  |    4 ----
 drivers/usb/host/ohci-hcd.c  |    2 +-
 drivers/usb/host/ohci-hub.c  |   42 ++++++++++++------------------------------
 drivers/usb/host/ohci-mem.c  |    1 -
 drivers/usb/host/ohci-omap.c |   36 ++++++++++++------------------------
 drivers/usb/host/ohci-pci.c  |   40 ++++++++--------------------------------
 drivers/usb/host/ohci.h      |    1 -
 7 files changed, 33 insertions(+), 93 deletions(-)

drivers/usb/host/ohci-dbg.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-hub.c
drivers/usb/host/ohci-mem.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci.h

index 7924c74..7bfffcb 100644 (file)
@@ -193,10 +193,6 @@ ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
 
        maybe_print_eds (controller, "donehead",
                        ohci_readl (controller, &regs->donehead), next, size);
-
-       /* broken fminterval means traffic won't flow! */ 
-       ohci_dbg (controller, "fminterval %08x\n", 
-                       ohci_readl (controller, &regs->fminterval));
 }
 
 #define dbg_port_sw(hc,num,value,next,size) \
index c26ab5f..5c0c6c8 100644 (file)
@@ -723,7 +723,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
                ohci_vdbg (ohci, "resume detect\n");
                ohci_writel (ohci, OHCI_INTR_RD, &regs->intrstatus);
                if (hcd->state != HC_STATE_QUIESCING)
-                       schedule_work(&ohci->rh_resume);
+                       usb_hcd_resume_root_hub(hcd);
        }
 
        if (ints & OHCI_INTR_WDH) {
index b96948e..39a60e7 100644 (file)
@@ -73,7 +73,6 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
        ohci_dbg (ohci, "suspend root hub\n");
 
        /* First stop any processing */
-       hcd->state = HC_STATE_QUIESCING;
        if (ohci->hc_control & OHCI_SCHED_ENABLES) {
                int             limit;
 
@@ -108,7 +107,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
        else
                ohci->hc_control &= ~OHCI_CTRL_RWE;
 
-       /* Suspend hub */
+       /* Suspend hub ... this is the "global (to this bus) suspend" mode,
+        * which doesn't imply ports will first be individually suspended.
+        */
        ohci->hc_control &= ~OHCI_CTRL_HCFS;
        ohci->hc_control |= OHCI_USB_SUSPEND;
        ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
@@ -118,8 +119,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
        ohci->next_statechange = jiffies + msecs_to_jiffies (5);
 
 done:
+       /* external suspend vs self autosuspend ... same effect */
        if (status == 0)
-               hcd->state = HC_STATE_SUSPENDED;
+               usb_hcd_suspend_root_hub(hcd);
        spin_unlock_irqrestore (&ohci->lock, flags);
        return status;
 }
@@ -146,7 +148,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
        ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
 
        if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
-               /* this can happen after suspend-to-disk */
+               /* this can happen after resuming a swsusp snapshot */
                if (hcd->state == HC_STATE_RESUMING) {
                        ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
                                        ohci->hc_control);
@@ -169,11 +171,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
                ohci_info (ohci, "wakeup\n");
                break;
        case OHCI_USB_OPER:
-               ohci_dbg (ohci, "already resumed\n");
-               status = 0;
+               /* this can happen after resuming a swsusp snapshot */
+               ohci_dbg (ohci, "snapshot resume? reinit\n");
+               status = -EBUSY;
                break;
        default:                /* RESET, we lost power */
-               ohci_dbg (ohci, "root hub hardware reset\n");
+               ohci_dbg (ohci, "lost power\n");
                status = -EBUSY;
        }
        spin_unlock_irq (&ohci->lock);
@@ -198,8 +201,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
        }
 
        /* Some controllers (lucent erratum) need extra-long delays */
-       hcd->state = HC_STATE_RESUMING;
-       mdelay (20 /* usb 11.5.1.10 */ + 15);
+       msleep (20 /* usb 11.5.1.10 */ + 12 /* 32 msec counter */ + 1);
 
        temp = ohci_readl (ohci, &ohci->regs->control);
        temp &= OHCI_CTRL_HCFS;
@@ -273,27 +275,9 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
                (void) ohci_readl (ohci, &ohci->regs->control);
        }
 
-       hcd->state = HC_STATE_RUNNING;
        return 0;
 }
 
-static void ohci_rh_resume (void *_hcd)
-{
-       struct usb_hcd  *hcd = _hcd;
-
-       usb_lock_device (hcd->self.root_hub);
-       (void) ohci_hub_resume (hcd);
-       usb_unlock_device (hcd->self.root_hub);
-}
-
-#else
-
-static void ohci_rh_resume (void *_hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci (_hcd);
-       ohci_dbg(ohci, "rh_resume ??\n");
-}
-
 #endif /* CONFIG_PM */
 
 /*-------------------------------------------------------------------------*/
@@ -367,7 +351,6 @@ done:
 #ifdef CONFIG_PM
        /* save power by suspending idle root hubs;
         * INTR_RD wakes us when there's work
-        * NOTE: if we can do this, we don't need a root hub timer!
         */
        if (can_suspend
                        && !changed
@@ -380,7 +363,6 @@ done:
                        ) {
                ohci_vdbg (ohci, "autosuspend\n");
                (void) ohci_hub_suspend (hcd);
-               hcd->state = HC_STATE_RUNNING;
                usb_unlock_device (hcd->self.root_hub);
        }
 #endif
@@ -554,7 +536,7 @@ static int ohci_hub_control (
                        temp = RH_PS_POCI;
                        if ((ohci->hc_control & OHCI_CTRL_HCFS)
                                        != OHCI_USB_OPER)
-                               schedule_work (&ohci->rh_resume);
+                               usb_hcd_resume_root_hub(hcd);
                        break;
                case USB_PORT_FEAT_C_SUSPEND:
                        temp = RH_PS_PSSC;
index 9fb83df..bfbe328 100644 (file)
@@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
        ohci->next_statechange = jiffies;
        spin_lock_init (&ohci->lock);
        INIT_LIST_HEAD (&ohci->pending);
-       INIT_WORK (&ohci->rh_resume, ohci_rh_resume, ohci_to_hcd(ohci));
        ohci->reboot_notifier.notifier_call = ohci_reboot;
 }
 
index e67c07d..b3498b9 100644 (file)
@@ -458,41 +458,29 @@ static int ohci_hcd_omap_drv_remove(struct device *dev)
 static int ohci_omap_suspend(struct device *dev, pm_message_t message)
 {
        struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
-       int             status = -EINVAL;
-
-       down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
-       status = ohci_hub_suspend(ohci_to_hcd(ohci));
-       if (status == 0) {
-               omap_ohci_clock_power(0);
-               ohci_to_hcd(ohci)->self.root_hub->state =
-                       USB_STATE_SUSPENDED;
-               ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
-               dev->power.power_state = PMSG_SUSPEND;
-       }
-       up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
-       return status;
+
+       if (time_before(jiffies, ohci->next_statechange))
+               msleep(5);
+       ohci->next_statechange = jiffies;
+
+       omap_ohci_clock_power(0);
+       ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
+       dev->power.power_state = PMSG_SUSPEND;
+       return 0;
 }
 
 static int ohci_omap_resume(struct device *dev)
 {
        struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
-       int             status = 0;
 
        if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
        ohci->next_statechange = jiffies;
+
        omap_ohci_clock_power(1);
-#ifdef CONFIG_USB_SUSPEND
-       /* get extra cleanup even if remote wakeup isn't in use */
-       status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub);
-#else
-       down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
-       status = ohci_hub_resume(ohci_to_hcd(ohci));
-       up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
-#endif
-       if (status == 0)
-               dev->power.power_state = PMSG_ON;
-       return status;
+       dev->power.power_state = PMSG_ON;
+       usb_hcd_resume_root_hub(dev_get_drvdata(dev));
+       return 0;
 }
 
 #endif
index 0afa237..99a0ad4 100644 (file)
@@ -112,23 +112,13 @@ ohci_pci_start (struct usb_hcd *hcd)
 
 static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
 {
-       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
-
-       /* suspend root hub, hoping it keeps power during suspend */
-       if (time_before (jiffies, ohci->next_statechange))
-               msleep (100);
-
-#ifdef CONFIG_USB_SUSPEND
-       (void) usb_suspend_device (hcd->self.root_hub);
-#else
-       usb_lock_device (hcd->self.root_hub);
-       (void) ohci_hub_suspend (hcd);
-       usb_unlock_device (hcd->self.root_hub);
-#endif
+       /* root hub was already suspended */
 
-       /* let things settle down a bit */
-       msleep (100);
-       
+       /* FIXME these PMAC things get called in the wrong places.  ASIC
+        * clocks should be turned off AFTER entering D3, and on BEFORE
+        * trying to enter D0.  Evidently the PCI layer doesn't currently
+        * provide the right sort of platform hooks for this ...
+        */
 #ifdef CONFIG_PPC_PMAC
        if (_machine == _MACH_Pmac) {
                struct device_node      *of_node;
@@ -145,9 +135,6 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
 
 static int ohci_pci_resume (struct usb_hcd *hcd)
 {
-       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
-       int                     retval = 0;
-
 #ifdef CONFIG_PPC_PMAC
        if (_machine == _MACH_Pmac) {
                struct device_node *of_node;
@@ -159,19 +146,8 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
        }
 #endif /* CONFIG_PPC_PMAC */
 
-       /* resume root hub */
-       if (time_before (jiffies, ohci->next_statechange))
-               msleep (100);
-#ifdef CONFIG_USB_SUSPEND
-       /* get extra cleanup even if remote wakeup isn't in use */
-       retval = usb_resume_device (hcd->self.root_hub);
-#else
-       usb_lock_device (hcd->self.root_hub);
-       retval = ohci_hub_resume (hcd);
-       usb_unlock_device (hcd->self.root_hub);
-#endif
-
-       return retval;
+       usb_hcd_resume_root_hub(hcd);
+       return 0;
 }
 
 #endif /* CONFIG_PM */
index 8a9b9d9..caacf14 100644 (file)
@@ -389,7 +389,6 @@ struct ohci_hcd {
        unsigned long           next_statechange;       /* suspend/resume */
        u32                     fminterval;             /* saved register */
 
-       struct work_struct      rh_resume;
        struct notifier_block   reboot_notifier;
 
        unsigned long           flags;          /* for HC bugs */