EHCI: centralize controller suspend/resume
authorAlan Stern <stern@rowland.harvard.edu>
Thu, 28 Jun 2012 15:19:02 +0000 (11:19 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 9 Jul 2012 15:54:18 +0000 (08:54 -0700)
This patch (as1563) removes a lot of duplicated code by moving the
EHCI controller suspend/resume routines into the core driver, where
the various platform drivers can invoke them as needed.

Not only does this simplify these platform drivers, this also makes it
easier for other platform drivers to add suspend/resume support in the
future.

Note: The patch does not touch the ehci-fsl.c file, because its
approach to suspend and resume is so different from all the others.
It will have to be handled specially by its maintainer.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/ehci-au1xxx.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-msm.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-platform.c
drivers/usb/host/ehci-s5p.c
drivers/usb/host/ehci-sead3.c
drivers/usb/host/ehci-spear.c

index bf7441afed16e15155e35a1b24a59de77853e3be..182d3956590654d1f842c1c380f2418e812ab0b3 100644 (file)
@@ -158,28 +158,10 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
 static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       unsigned long flags;
-       int rc = 0;
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(10);
-
-       /* Root hub was already suspended. Disable irq emission and
-        * mark HW unaccessible.  The PM and USB cores make sure that
-        * the root hub is either suspended or stopped.
-        */
-       ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
-       spin_lock_irqsave(&ehci->lock, flags);
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       (void)ehci_readl(ehci, &ehci->regs->intr_enable);
-
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       spin_unlock_irqrestore(&ehci->lock, flags);
-
-       // could save FLADJ in case of Vaux power loss
-       // ... we'd only use it to handle clock skew
+       bool do_wakeup = device_may_wakeup(dev);
+       int rc;
 
+       rc = ehci_suspend(hcd, do_wakeup);
        alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
 
        return rc;
@@ -188,56 +170,9 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
 static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 
        alchemy_usb_control(ALCHEMY_USB_EHCI0, 1);
-
-       // maybe restore FLADJ
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       /* Mark hardware accessible again as we are out of D3 state by now */
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       /* If CF is still set, we maintained PCI Vaux power.
-        * Just undo the effect of ehci_pci_suspend().
-        */
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
-               int     mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       ehci_dbg(ehci, "lost power, restarting\n");
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       /* Else reset, to cope with power loss or flush-to-storage
-        * style "resume" having let BIOS kick in during reboot.
-        */
-       (void) ehci_halt(ehci);
-       (void) ehci_reset(ehci);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
-
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
-
-       ehci->rh_state = EHCI_RH_SUSPENDED;
+       ehci_resume(hcd, false);
 
        return 0;
 }
index c49fc1e7895d190d595db061feec526817d5363a..e6823a0cf6426ff3deb76f12ca153f4144d82620 100644 (file)
@@ -1242,6 +1242,95 @@ static int ehci_get_frame (struct usb_hcd *hcd)
 }
 
 /*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+/* suspend/resume, section 4.3 */
+
+/* These routines handle the generic parts of controller suspend/resume */
+
+static int __maybe_unused ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+
+       if (time_before(jiffies, ehci->next_statechange))
+               msleep(10);
+
+       /*
+        * Root hub was already suspended.  Disable IRQ emission and
+        * mark HW unaccessible.  The PM and USB cores make sure that
+        * the root hub is either suspended or stopped.
+        */
+       ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
+
+       spin_lock_irq(&ehci->lock);
+       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+       (void) ehci_readl(ehci, &ehci->regs->intr_enable);
+
+       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+       spin_unlock_irq(&ehci->lock);
+
+       return 0;
+}
+
+/* Returns 0 if power was preserved, 1 if power was lost */
+static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+
+       if (time_before(jiffies, ehci->next_statechange))
+               msleep(100);
+
+       /* Mark hardware accessible again as we are back to full power by now */
+       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+       /*
+        * If CF is still set and we aren't resuming from hibernation
+        * then we maintained suspend power.
+        * Just undo the effect of ehci_suspend().
+        */
+       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
+                       !hibernated) {
+               int     mask = INTR_MASK;
+
+               ehci_prepare_ports_for_controller_resume(ehci);
+               if (!hcd->self.root_hub->do_remote_wakeup)
+                       mask &= ~STS_PCD;
+               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
+               ehci_readl(ehci, &ehci->regs->intr_enable);
+               return 0;
+       }
+
+       /*
+        * Else reset, to cope with power loss or resume from hibernation
+        * having let the firmware kick in during reboot.
+        */
+       usb_root_hub_lost_power(hcd->self.root_hub);
+       (void) ehci_halt(ehci);
+       (void) ehci_reset(ehci);
+
+       /* emptying the schedule aborts any urbs */
+       spin_lock_irq(&ehci->lock);
+       if (ehci->reclaim)
+               end_unlink_async(ehci);
+       ehci_work(ehci);
+       spin_unlock_irq(&ehci->lock);
+
+       ehci_writel(ehci, ehci->command, &ehci->regs->command);
+       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
+
+       /* here we "know" root ports should always stay powered */
+       ehci_port_power(ehci, 1);
+
+       ehci->rh_state = EHCI_RH_SUSPENDED;
+       return 1;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 /*
  * The EHCI in ChipIdea HDRC cannot be a separate module or device,
  * because its registers (and irq) are shared between host/gadget/otg
index db05e358677af4d1b6207ecf90a365b3fb1b1a64..b3e2d66e95bba35b83eaf1e19d35ee8a8c89af1d 100644 (file)
@@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
        ehci->owned_ports = 0;
 }
 
-static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci)
+static int ehci_port_change(struct ehci_hcd *ehci)
 {
        int i = HCS_N_PORTS(ehci->hcs_params);
 
@@ -128,7 +128,7 @@ static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci)
        return 0;
 }
 
-static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
+static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
                bool suspending, bool do_wakeup)
 {
        int             port;
index 6b4ffb598db1e395480270330939c118bea48a55..17dd9e94001ec679329b2492f6620b51b7ac373a 100644 (file)
@@ -198,24 +198,11 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
 static int ehci_msm_pm_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       bool wakeup = device_may_wakeup(dev);
+       bool do_wakeup = device_may_wakeup(dev);
 
        dev_dbg(dev, "ehci-msm PM suspend\n");
 
-       /*
-        * EHCI helper function has also the same check before manipulating
-        * port wakeup flags.  We do check here the same condition before
-        * calling the same helper function to avoid bringing hardware
-        * from Low power mode when there is no need for adjusting port
-        * wakeup flags.
-        */
-       if (hcd->self.root_hub->do_remote_wakeup && !wakeup) {
-               pm_runtime_resume(dev);
-               ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
-                               wakeup);
-       }
-
-       return 0;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static int ehci_msm_pm_resume(struct device *dev)
@@ -223,7 +210,7 @@ static int ehci_msm_pm_resume(struct device *dev)
        struct usb_hcd *hcd = dev_get_drvdata(dev);
 
        dev_dbg(dev, "ehci-msm PM resume\n");
-       ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
+       ehci_resume(hcd, false);
 
        return 0;
 }
index 123481793a47afcdfc22cf476a8a8ed14301c6e1..6e767bce0605bb4f4d28c3d46f3398c019b7d478 100644 (file)
@@ -331,29 +331,7 @@ done:
 
 static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 {
-       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
-       unsigned long           flags;
-       int                     rc = 0;
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(10);
-
-       /* Root hub was already suspended. Disable irq emission and
-        * mark HW unaccessible.  The PM and USB cores make sure that
-        * the root hub is either suspended or stopped.
-        */
-       ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
-       spin_lock_irqsave (&ehci->lock, flags);
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       (void)ehci_readl(ehci, &ehci->regs->intr_enable);
-
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       spin_unlock_irqrestore (&ehci->lock, flags);
-
-       // could save FLADJ in case of Vaux power loss
-       // ... we'd only use it to handle clock skew
-
-       return rc;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev)
@@ -402,54 +380,8 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
        if (usb_is_intel_switchable_ehci(pdev))
                ehci_enable_xhci_companion();
 
-       // maybe restore FLADJ
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       /* Mark hardware accessible again as we are out of D3 state by now */
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       /* If CF is still set and we aren't resuming from hibernation
-        * then we maintained PCI Vaux power.
-        * Just undo the effect of ehci_pci_suspend().
-        */
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
-                               !hibernated) {
-               int     mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       /* Else reset, to cope with power loss or flush-to-storage
-        * style "resume" having let BIOS kick in during reboot.
-        */
-       (void) ehci_halt(ehci);
-       (void) ehci_reset(ehci);
-       (void) ehci_pci_reinit(ehci, pdev);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
-
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
-
-       ehci->rh_state = EHCI_RH_SUSPENDED;
+       if (ehci_resume(hcd, hibernated) != 0)
+               (void) ehci_pci_reinit(ehci, pdev);
        return 0;
 }
 #endif
index dfe881a34ae254294483c8ce5bff67d62d741340..4b1d896d5a2214f75f32a5e943e9a4e5804f24f9 100644 (file)
@@ -153,17 +153,16 @@ static int __devexit ehci_platform_remove(struct platform_device *dev)
 static int ehci_platform_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       bool wakeup = device_may_wakeup(dev);
+       bool do_wakeup = device_may_wakeup(dev);
 
-       ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup);
-       return 0;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static int ehci_platform_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
 
-       ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
+       ehci_resume(hcd, false);
        return 0;
 }
 
index 1e483f052ff7eff3c98dabc9d26723f70600009b..c7e0936d4a7cffd717bdcee0e7e52183656ae8f3 100644 (file)
@@ -200,27 +200,12 @@ static int s5p_ehci_suspend(struct device *dev)
 {
        struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
        struct usb_hcd *hcd = s5p_ehci->hcd;
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       bool do_wakeup = device_may_wakeup(dev);
        struct platform_device *pdev = to_platform_device(dev);
        struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
-       unsigned long flags;
-       int rc = 0;
+       int rc;
 
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(20);
-
-       /*
-        * Root hub was already suspended. Disable irq emission and
-        * mark HW unaccessible.  The PM and USB cores make sure that
-        * the root hub is either suspended or stopped.
-        */
-       ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
-       spin_lock_irqsave(&ehci->lock, flags);
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       (void)ehci_readl(ehci, &ehci->regs->intr_enable);
-
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       spin_unlock_irqrestore(&ehci->lock, flags);
+       rc = ehci_suspend(hcd, do_wakeup);
 
        if (pdata && pdata->phy_exit)
                pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
@@ -234,7 +219,6 @@ static int s5p_ehci_resume(struct device *dev)
 {
        struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
        struct usb_hcd *hcd = s5p_ehci->hcd;
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        struct platform_device *pdev = to_platform_device(dev);
        struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
 
@@ -246,44 +230,7 @@ static int s5p_ehci_resume(struct device *dev)
        /* DMA burst Enable */
        writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
 
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       /* Mark hardware accessible again as we are out of D3 state by now */
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
-               int     mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       (void) ehci_halt(ehci);
-       (void) ehci_reset(ehci);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
-
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
-
-       ehci->rh_state = EHCI_RH_SUSPENDED;
-
+       ehci_resume(hcd, false);
        return 0;
 }
 #else
index cc199e87a7a94cd05112e46b6f4f1412bf6bd704..58c96bd50d22853d10a8ac423b39acf0e375eeb7 100644 (file)
@@ -160,84 +160,16 @@ static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev)
 static int ehci_hcd_sead3_drv_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       unsigned long flags;
-       int rc = 0;
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(20);
-
-       /* Root hub was already suspended. Disable irq emission and
-        * mark HW unaccessible.  The PM and USB cores make sure that
-        * the root hub is either suspended or stopped.
-        */
-       ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
-       spin_lock_irqsave(&ehci->lock, flags);
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       (void)ehci_readl(ehci, &ehci->regs->intr_enable);
-
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       spin_unlock_irqrestore(&ehci->lock, flags);
+       bool do_wakeup = device_may_wakeup(dev);
 
-       /* could save FLADJ in case of Vaux power loss
-        * ... we'd only use it to handle clock skew
-        */
-
-       return rc;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static int ehci_hcd_sead3_drv_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-
-       /* maybe restore FLADJ. */
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       /* Mark hardware accessible again as we are out of D3 state by now */
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       /* If CF is still set, we maintained PCI Vaux power.
-        * Just undo the effect of ehci_pci_suspend().
-        */
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
-               int     mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       ehci_dbg(ehci, "lost power, restarting\n");
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       /* Else reset, to cope with power loss or flush-to-storage
-        * style "resume" having let BIOS kick in during reboot.
-        */
-       (void) ehci_halt(ehci);
-       (void) ehci_reset(ehci);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
-
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
-
-       ehci->rh_state = EHCI_RH_SUSPENDED;
 
+       ehci_resume(hcd, false);
        return 0;
 }
 
index 37ba8c8d2fd06b05daa2e969a349a139305916f4..7ed533e6cca8e2e695a1764ef99f813fccbd62ff 100644 (file)
@@ -97,71 +97,16 @@ static const struct hc_driver ehci_spear_hc_driver = {
 static int ehci_spear_drv_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       unsigned long flags;
-       int rc = 0;
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(10);
-
-       /*
-        * Root hub was already suspended. Disable irq emission and mark HW
-        * unaccessible. The PM and USB cores make sure that the root hub is
-        * either suspended or stopped.
-        */
-       spin_lock_irqsave(&ehci->lock, flags);
-       ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
-       ehci_readl(ehci, &ehci->regs->intr_enable);
-       spin_unlock_irqrestore(&ehci->lock, flags);
+       bool do_wakeup = device_may_wakeup(dev);
 
-       return rc;
+       return ehci_suspend(hcd, do_wakeup);
 }
 
 static int ehci_spear_drv_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-
-       if (time_before(jiffies, ehci->next_statechange))
-               msleep(100);
-
-       if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
-               int mask = INTR_MASK;
-
-               ehci_prepare_ports_for_controller_resume(ehci);
-
-               if (!hcd->self.root_hub->do_remote_wakeup)
-                       mask &= ~STS_PCD;
-
-               ehci_writel(ehci, mask, &ehci->regs->intr_enable);
-               ehci_readl(ehci, &ehci->regs->intr_enable);
-               return 0;
-       }
-
-       usb_root_hub_lost_power(hcd->self.root_hub);
-
-       /*
-        * Else reset, to cope with power loss or flush-to-storage style
-        * "resume" having let BIOS kick in during reboot.
-        */
-       ehci_halt(ehci);
-       ehci_reset(ehci);
-
-       /* emptying the schedule aborts any urbs */
-       spin_lock_irq(&ehci->lock);
-       if (ehci->reclaim)
-               end_unlink_async(ehci);
-
-       ehci_work(ehci);
-       spin_unlock_irq(&ehci->lock);
-
-       ehci_writel(ehci, ehci->command, &ehci->regs->command);
-       ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
-       ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
 
-       /* here we "know" root ports should always stay powered */
-       ehci_port_power(ehci, 1);
+       ehci_resume(hcd, false);
        return 0;
 }
 #endif /* CONFIG_PM */