USB: EHCI: msm: Add support for power management
authorPavankumar Kondeti <pkondeti@codeaurora.org>
Tue, 7 Dec 2010 12:23:57 +0000 (17:53 +0530)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 10 Dec 2010 22:23:32 +0000 (14:23 -0800)
Enable runtime PM and mark no_callbacks flag.  OTG device, parent of
HCD takes care of putting hardware into low power mode.  Adjust port
power wakeup flags during system suspend and resume.

Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/Kconfig
drivers/usb/host/ehci-msm.c

index d8665ec0565ba6af1ad5cd5594e424462a1c776a..b9cc31172fbde6a3e399f08544fbafddae978adf 100644 (file)
@@ -150,7 +150,7 @@ config USB_EHCI_MSM
          Enables support for the USB Host controller present on the
          Qualcomm chipsets. Root Hub has inbuilt TT.
          This driver depends on OTG driver for PHY initialization,
-         clock management, powering up VBUS.
+         clock management, powering up VBUS, and power management.
 
 config USB_EHCI_HCD_PPC_OF
        bool "EHCI support for PPC USB controller on OF platform bus"
index 9ed855986599679d20ba8ebee481fc027ebe62d1..413f4deca532e62097d9d213e4102fcfa9ffa012 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/usb/otg.h>
 #include <linux/usb/msm_hsusb_hw.h>
@@ -239,7 +240,8 @@ static int ehci_msm_probe(struct platform_device *pdev)
 
        /*
         * OTG driver takes care of PHY initialization, clock management,
-        * powering up VBUS and mapping of registers address space.
+        * powering up VBUS, mapping of registers address space and power
+        * management.
         */
        otg = otg_get_transceiver();
        if (!otg) {
@@ -255,6 +257,13 @@ static int ehci_msm_probe(struct platform_device *pdev)
        }
 
        device_init_wakeup(&pdev->dev, 1);
+       /*
+        * OTG device parent of HCD takes care of putting
+        * hardware into low power mode.
+        */
+       pm_runtime_no_callbacks(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+
        return 0;
 
 put_transceiver:
@@ -272,6 +281,8 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 
        device_init_wakeup(&pdev->dev, 0);
+       pm_runtime_disable(&pdev->dev);
+       pm_runtime_set_suspended(&pdev->dev);
 
        otg_set_host(otg, NULL);
        otg_put_transceiver(otg);
@@ -281,10 +292,54 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int ehci_msm_pm_suspend(struct device *dev)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       bool 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;
+}
+
+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));
+
+       return 0;
+}
+#else
+#define ehci_msm_pm_suspend    NULL
+#define ehci_msm_pm_resume     NULL
+#endif
+
+static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
+       .suspend         = ehci_msm_pm_suspend,
+       .resume          = ehci_msm_pm_resume,
+};
+
 static struct platform_driver ehci_msm_driver = {
        .probe  = ehci_msm_probe,
        .remove = __devexit_p(ehci_msm_remove),
        .driver = {
                   .name = "msm_hsusb_host",
+                  .pm = &ehci_msm_dev_pm_ops,
        },
 };