浏览代码

USB: EHCI: msm: Add support for power management

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>
Pavankumar Kondeti 14 年之前
父节点
当前提交
8bb6a164b9
共有 2 个文件被更改,包括 57 次插入2 次删除
  1. 1 1
      drivers/usb/host/Kconfig
  2. 56 1
      drivers/usb/host/ehci-msm.c

+ 1 - 1
drivers/usb/host/Kconfig

@@ -150,7 +150,7 @@ config USB_EHCI_MSM
 	  Enables support for the USB Host controller present on the
 	  Enables support for the USB Host controller present on the
 	  Qualcomm chipsets. Root Hub has inbuilt TT.
 	  Qualcomm chipsets. Root Hub has inbuilt TT.
 	  This driver depends on OTG driver for PHY initialization,
 	  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
 config USB_EHCI_HCD_PPC_OF
 	bool "EHCI support for PPC USB controller on OF platform bus"
 	bool "EHCI support for PPC USB controller on OF platform bus"

+ 56 - 1
drivers/usb/host/ehci-msm.c

@@ -25,6 +25,7 @@
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/err.h>
+#include <linux/pm_runtime.h>
 
 
 #include <linux/usb/otg.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/msm_hsusb_hw.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,
 	 * 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();
 	otg = otg_get_transceiver();
 	if (!otg) {
 	if (!otg) {
@@ -255,6 +257,13 @@ static int ehci_msm_probe(struct platform_device *pdev)
 	}
 	}
 
 
 	device_init_wakeup(&pdev->dev, 1);
 	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;
 	return 0;
 
 
 put_transceiver:
 put_transceiver:
@@ -272,6 +281,8 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
 
 
 	device_init_wakeup(&pdev->dev, 0);
 	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
 
 
 	otg_set_host(otg, NULL);
 	otg_set_host(otg, NULL);
 	otg_put_transceiver(otg);
 	otg_put_transceiver(otg);
@@ -281,10 +292,54 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
 	return 0;
 	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 = {
 static struct platform_driver ehci_msm_driver = {
 	.probe	= ehci_msm_probe,
 	.probe	= ehci_msm_probe,
 	.remove	= __devexit_p(ehci_msm_remove),
 	.remove	= __devexit_p(ehci_msm_remove),
 	.driver = {
 	.driver = {
 		   .name = "msm_hsusb_host",
 		   .name = "msm_hsusb_host",
+		   .pm = &ehci_msm_dev_pm_ops,
 	},
 	},
 };
 };