]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
OHCI: fix regression upon awakening from hibernation
authorAlan Stern <stern@rowland.harvard.edu>
Thu, 8 May 2008 18:21:22 +0000 (14:21 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 15 May 2008 14:50:00 +0000 (07:50 -0700)
commit 43bbb7e015c4380064796c5868b536437b165615 in upstream

Drivers in the ohci-hcd family should perform certain tasks whenever
their controller device is resumed.  These include checking for loss
of power during suspend, turning on port power, and enabling interrupt
requests.

Until now these jobs have been carried out when the root hub is
resumed, not when the controller is.  Many drivers work around the
resulting awkwardness by automatically resuming their root hub
whenever the controller is resumed.  But this is wasteful and
unnecessary.

In 2.6.25, ohci-pci doesn't even do that.  After waking up from
hibernation, it simply leaves the controller in a RESET state, which
is useless.

To simplify the situation, this patch (as1066b) adds a new core
routine, ohci_finish_controller_resume(), which can be used by all the
OHCI-variant drivers.  They can call the new routine instead of
resuming their root hubs.  And ohci-pci.c can call it instead of using
its own special-purpose handler.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/ohci-at91.c
drivers/usb/host/ohci-ep93xx.c
drivers/usb/host/ohci-hub.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci-pxa27x.c
drivers/usb/host/ohci-sm501.c
drivers/usb/host/ohci-ssb.c

index d72dc07dda017de12a51eaedc68b9c0626d26cb3..e534f9de0f05f9bbfc20c4f9938cb29c66a31f7a 100644 (file)
@@ -348,6 +348,7 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
        if (!clocked)
                at91_start_clock();
 
+       ohci_finish_controller_resume(hcd);
        return 0;
 }
 #else
index 156e93a9d0df3f3ee2ee96c7fc29241e9885a05c..963ed60cef42a8fe2e351667d66f246efa27bac5 100644 (file)
@@ -194,8 +194,8 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev)
 
        ep93xx_start_hc(&pdev->dev);
        pdev->dev.power.power_state = PMSG_ON;
-       usb_hcd_resume_root_hub(hcd);
 
+       ohci_finish_controller_resume(hcd);
        return 0;
 }
 #endif
index 48e4b11f4d3ea53a2d837636e5ae72de4a0724af..9eff2de5a8012ecb488a338392443a43711b3cbe 100644 (file)
@@ -326,6 +326,49 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
        return rc;
 }
 
+/* Carry out the final steps of resuming the controller device */
+static void ohci_finish_controller_resume(struct usb_hcd *hcd)
+{
+       struct ohci_hcd         *ohci = hcd_to_ohci(hcd);
+       int                     port;
+       bool                    need_reinit = false;
+
+       /* See if the controller is already running or has been reset */
+       ohci->hc_control = ohci_readl(ohci, &ohci->regs->control);
+       if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
+               need_reinit = true;
+       } else {
+               switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+               case OHCI_USB_OPER:
+               case OHCI_USB_RESET:
+                       need_reinit = true;
+               }
+       }
+
+       /* If needed, reinitialize and suspend the root hub */
+       if (need_reinit) {
+               spin_lock_irq(&ohci->lock);
+               hcd->state = HC_STATE_RESUMING;
+               ohci_rh_resume(ohci);
+               hcd->state = HC_STATE_QUIESCING;
+               ohci_rh_suspend(ohci, 0);
+               hcd->state = HC_STATE_SUSPENDED;
+               spin_unlock_irq(&ohci->lock);
+       }
+
+       /* Normally just turn on port power and enable interrupts */
+       else {
+               ohci_dbg(ohci, "powerup ports\n");
+               for (port = 0; port < ohci->num_ports; port++)
+                       ohci_writel(ohci, RH_PS_PPS,
+                                       &ohci->regs->roothub.portstatus[port]);
+
+               ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrenable);
+               ohci_readl(ohci, &ohci->regs->intrenable);
+               msleep(20);
+       }
+}
+
 /* Carry out polling-, autostop-, and autoresume-related state changes */
 static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
                int any_connected)
index 7bfca1ed1b585e73413fbba86aaf16a5d9c3c42f..611bc9f3a8ae98eb3c63e1bb10b1866b2d861d15 100644 (file)
@@ -519,7 +519,7 @@ static int ohci_omap_resume(struct platform_device *dev)
 
        omap_ohci_clock_power(1);
        dev->dev.power.power_state = PMSG_ON;
-       usb_hcd_resume_root_hub(platform_get_drvdata(dev));
+       ohci_finish_controller_resume(hcd);
        return 0;
 }
 
index b0e2275755c82bcdd41f48afed4e4b381e5715f1..b4886e1de3b7d9ac99c41ace72fd525f0c37d79c 100644 (file)
@@ -238,42 +238,6 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
        return ret;
 }
 
-#if    defined(CONFIG_USB_PERSIST) && (defined(CONFIG_USB_EHCI_HCD) || \
-               defined(CONFIG_USB_EHCI_HCD_MODULE))
-
-/* Following a power loss, we must prepare to regain control of the ports
- * we used to own.  This means turning on the port power before ehci-hcd
- * tries to switch ownership.
- *
- * This isn't a 100% perfect solution.  On most systems the OHCI controllers
- * lie at lower PCI addresses than the EHCI controller, so they will be
- * discovered (and hence resumed) first.  But there is no guarantee things
- * will always work this way.  If the EHCI controller is resumed first and
- * the OHCI ports are unpowered, then the handover will fail.
- */
-static void prepare_for_handover(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int             port;
-
-       /* Here we "know" root ports should always stay powered */
-       ohci_dbg(ohci, "powerup ports\n");
-       for (port = 0; port < ohci->num_ports; port++)
-               ohci_writel(ohci, RH_PS_PPS,
-                               &ohci->regs->roothub.portstatus[port]);
-
-       /* Flush those writes */
-       ohci_readl(ohci, &ohci->regs->control);
-       msleep(20);
-}
-
-#else
-
-static inline void prepare_for_handover(struct usb_hcd *hcd)
-{ }
-
-#endif /* CONFIG_USB_PERSIST etc. */
-
 #ifdef CONFIG_PM
 
 static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
@@ -312,13 +276,8 @@ 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);
-
        set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       /* FIXME: we should try to detect loss of VBUS power here */
-       prepare_for_handover(hcd);
-       ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrenable);
+       ohci_finish_controller_resume(hcd);
        return 0;
 }
 
index 8ad9b3b604b5e0aeb9185281af70649617c7b48d..3de3b00b7da70b33bd330bcfd9553912a4da94ec 100644 (file)
@@ -358,8 +358,7 @@ static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev)
                return status;
 
        pdev->dev.power.power_state = PMSG_ON;
-       usb_hcd_resume_root_hub(hcd);
-
+       ohci_finish_controller_resume(hcd);
        return 0;
 }
 #endif
index 4ea92762fb289521431431c8af5ae8b3475b5261..c852f0356fe88de595df98739f874ce2d096ddf7 100644 (file)
@@ -239,7 +239,7 @@ static int ohci_sm501_resume(struct platform_device *pdev)
 
        sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1);
        dev->power.power_state = PMSG_ON;
-       usb_hcd_resume_root_hub(platform_get_drvdata(pdev));
+       ohci_finish_controller_resume(hcd);
        return 0;
 }
 #endif
index 6e9c2d6db88756370c5b2f7d9a1bf9cc5d2beabe..1c0867a52ebdf36d12df3482f968e87aa4c34ea4 100644 (file)
@@ -224,6 +224,7 @@ static int ssb_ohci_resume(struct ssb_device *dev)
 
        ssb_device_enable(dev, ohcidev->enable_flags);
 
+       ohci_finish_controller_resume(hcd);
        return 0;
 }