aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Chen <peter.chen@freescale.com>2015-02-10 23:44:45 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-03-18 11:19:08 -0400
commit1f874edcb7318c5dd71025df9f3849715b4e4f71 (patch)
tree740938fad546965bbe8a371fbd1062fc39046af1
parenta4cf1b14cfbc57a12ea2d997b93735a99f70d810 (diff)
usb: chipidea: add runtime power management support
Add runtime power management support. Signed-off-by: Peter Chen <peter.chen@freescale.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/chipidea/ci.h6
-rw-r--r--drivers/usb/chipidea/core.c100
-rw-r--r--drivers/usb/chipidea/otg.c2
-rw-r--r--include/linux/usb/chipidea.h1
4 files changed, 103 insertions, 6 deletions
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 65913d48f0c8..a0aeb8df9280 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -169,6 +169,9 @@ struct hw_bank {
169 * @b_sess_valid_event: indicates there is a vbus event, and handled 169 * @b_sess_valid_event: indicates there is a vbus event, and handled
170 * at ci_otg_work 170 * at ci_otg_work
171 * @imx28_write_fix: Freescale imx28 needs swp instruction for writing 171 * @imx28_write_fix: Freescale imx28 needs swp instruction for writing
172 * @supports_runtime_pm: if runtime pm is supported
173 * @in_lpm: if the core in low power mode
174 * @wakeup_int: if wakeup interrupt occur
172 */ 175 */
173struct ci_hdrc { 176struct ci_hdrc {
174 struct device *dev; 177 struct device *dev;
@@ -211,6 +214,9 @@ struct ci_hdrc {
211 bool id_event; 214 bool id_event;
212 bool b_sess_valid_event; 215 bool b_sess_valid_event;
213 bool imx28_write_fix; 216 bool imx28_write_fix;
217 bool supports_runtime_pm;
218 bool in_lpm;
219 bool wakeup_int;
214}; 220};
215 221
216static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) 222static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index a57dc8866fc5..63d2b398c9a0 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -491,6 +491,13 @@ static irqreturn_t ci_irq(int irq, void *data)
491 irqreturn_t ret = IRQ_NONE; 491 irqreturn_t ret = IRQ_NONE;
492 u32 otgsc = 0; 492 u32 otgsc = 0;
493 493
494 if (ci->in_lpm) {
495 disable_irq_nosync(irq);
496 ci->wakeup_int = true;
497 pm_runtime_get(ci->dev);
498 return IRQ_HANDLED;
499 }
500
494 if (ci->is_otg) { 501 if (ci->is_otg) {
495 otgsc = hw_read_otgsc(ci, ~0); 502 otgsc = hw_read_otgsc(ci, ~0);
496 if (ci_otg_is_fsm_mode(ci)) { 503 if (ci_otg_is_fsm_mode(ci)) {
@@ -673,6 +680,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
673 ci->platdata = dev_get_platdata(dev); 680 ci->platdata = dev_get_platdata(dev);
674 ci->imx28_write_fix = !!(ci->platdata->flags & 681 ci->imx28_write_fix = !!(ci->platdata->flags &
675 CI_HDRC_IMX28_WRITE_FIX); 682 CI_HDRC_IMX28_WRITE_FIX);
683 ci->supports_runtime_pm = !!(ci->platdata->flags &
684 CI_HDRC_SUPPORTS_RUNTIME_PM);
676 685
677 ret = hw_device_init(ci, base); 686 ret = hw_device_init(ci, base);
678 if (ret < 0) { 687 if (ret < 0) {
@@ -788,6 +797,14 @@ static int ci_hdrc_probe(struct platform_device *pdev)
788 if (ret) 797 if (ret)
789 goto stop; 798 goto stop;
790 799
800 if (ci->supports_runtime_pm) {
801 pm_runtime_set_active(&pdev->dev);
802 pm_runtime_enable(&pdev->dev);
803 pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
804 pm_runtime_mark_last_busy(ci->dev);
805 pm_runtime_use_autosuspend(&pdev->dev);
806 }
807
791 if (ci_otg_is_fsm_mode(ci)) 808 if (ci_otg_is_fsm_mode(ci))
792 ci_hdrc_otg_fsm_start(ci); 809 ci_hdrc_otg_fsm_start(ci);
793 810
@@ -807,6 +824,12 @@ static int ci_hdrc_remove(struct platform_device *pdev)
807{ 824{
808 struct ci_hdrc *ci = platform_get_drvdata(pdev); 825 struct ci_hdrc *ci = platform_get_drvdata(pdev);
809 826
827 if (ci->supports_runtime_pm) {
828 pm_runtime_get_sync(&pdev->dev);
829 pm_runtime_disable(&pdev->dev);
830 pm_runtime_put_noidle(&pdev->dev);
831 }
832
810 dbg_remove_files(ci); 833 dbg_remove_files(ci);
811 ci_role_destroy(ci); 834 ci_role_destroy(ci);
812 ci_hdrc_enter_lpm(ci, true); 835 ci_hdrc_enter_lpm(ci, true);
@@ -815,13 +838,14 @@ static int ci_hdrc_remove(struct platform_device *pdev)
815 return 0; 838 return 0;
816} 839}
817 840
818#ifdef CONFIG_PM_SLEEP 841#ifdef CONFIG_PM
819static void ci_controller_suspend(struct ci_hdrc *ci) 842static void ci_controller_suspend(struct ci_hdrc *ci)
820{ 843{
844 disable_irq(ci->irq);
821 ci_hdrc_enter_lpm(ci, true); 845 ci_hdrc_enter_lpm(ci, true);
822 846 usb_phy_set_suspend(ci->usb_phy, 1);
823 if (ci->usb_phy) 847 ci->in_lpm = true;
824 usb_phy_set_suspend(ci->usb_phy, 1); 848 enable_irq(ci->irq);
825} 849}
826 850
827static int ci_controller_resume(struct device *dev) 851static int ci_controller_resume(struct device *dev)
@@ -830,23 +854,49 @@ static int ci_controller_resume(struct device *dev)
830 854
831 dev_dbg(dev, "at %s\n", __func__); 855 dev_dbg(dev, "at %s\n", __func__);
832 856
833 ci_hdrc_enter_lpm(ci, false); 857 if (!ci->in_lpm) {
858 WARN_ON(1);
859 return 0;
860 }
834 861
862 ci_hdrc_enter_lpm(ci, false);
835 if (ci->usb_phy) { 863 if (ci->usb_phy) {
836 usb_phy_set_suspend(ci->usb_phy, 0); 864 usb_phy_set_suspend(ci->usb_phy, 0);
837 usb_phy_set_wakeup(ci->usb_phy, false); 865 usb_phy_set_wakeup(ci->usb_phy, false);
838 hw_wait_phy_stable(); 866 hw_wait_phy_stable();
839 } 867 }
840 868
869 ci->in_lpm = false;
870 if (ci->wakeup_int) {
871 ci->wakeup_int = false;
872 pm_runtime_mark_last_busy(ci->dev);
873 pm_runtime_put_autosuspend(ci->dev);
874 enable_irq(ci->irq);
875 }
876
841 return 0; 877 return 0;
842} 878}
843 879
880#ifdef CONFIG_PM_SLEEP
844static int ci_suspend(struct device *dev) 881static int ci_suspend(struct device *dev)
845{ 882{
846 struct ci_hdrc *ci = dev_get_drvdata(dev); 883 struct ci_hdrc *ci = dev_get_drvdata(dev);
847 884
848 if (ci->wq) 885 if (ci->wq)
849 flush_workqueue(ci->wq); 886 flush_workqueue(ci->wq);
887 /*
888 * Controller needs to be active during suspend, otherwise the core
889 * may run resume when the parent is at suspend if other driver's
890 * suspend fails, it occurs before parent's suspend has not started,
891 * but the core suspend has finished.
892 */
893 if (ci->in_lpm)
894 pm_runtime_resume(dev);
895
896 if (ci->in_lpm) {
897 WARN_ON(1);
898 return 0;
899 }
850 900
851 ci_controller_suspend(ci); 901 ci_controller_suspend(ci);
852 902
@@ -855,13 +905,51 @@ static int ci_suspend(struct device *dev)
855 905
856static int ci_resume(struct device *dev) 906static int ci_resume(struct device *dev)
857{ 907{
858 return ci_controller_resume(dev); 908 struct ci_hdrc *ci = dev_get_drvdata(dev);
909 int ret;
910
911 ret = ci_controller_resume(dev);
912 if (ret)
913 return ret;
914
915 if (ci->supports_runtime_pm) {
916 pm_runtime_disable(dev);
917 pm_runtime_set_active(dev);
918 pm_runtime_enable(dev);
919 }
920
921 return ret;
859} 922}
860#endif /* CONFIG_PM_SLEEP */ 923#endif /* CONFIG_PM_SLEEP */
861 924
925static int ci_runtime_suspend(struct device *dev)
926{
927 struct ci_hdrc *ci = dev_get_drvdata(dev);
928
929 dev_dbg(dev, "at %s\n", __func__);
930
931 if (ci->in_lpm) {
932 WARN_ON(1);
933 return 0;
934 }
935
936 usb_phy_set_wakeup(ci->usb_phy, true);
937 ci_controller_suspend(ci);
938
939 return 0;
940}
941
942static int ci_runtime_resume(struct device *dev)
943{
944 return ci_controller_resume(dev);
945}
946
947#endif /* CONFIG_PM */
862static const struct dev_pm_ops ci_pm_ops = { 948static const struct dev_pm_ops ci_pm_ops = {
863 SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume) 949 SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume)
950 SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL)
864}; 951};
952
865static struct platform_driver ci_hdrc_driver = { 953static struct platform_driver ci_hdrc_driver = {
866 .probe = ci_hdrc_probe, 954 .probe = ci_hdrc_probe,
867 .remove = ci_hdrc_remove, 955 .remove = ci_hdrc_remove,
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index a048b08b9d4d..ad6c87a4653c 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -96,6 +96,7 @@ static void ci_otg_work(struct work_struct *work)
96 return; 96 return;
97 } 97 }
98 98
99 pm_runtime_get_sync(ci->dev);
99 if (ci->id_event) { 100 if (ci->id_event) {
100 ci->id_event = false; 101 ci->id_event = false;
101 ci_handle_id_switch(ci); 102 ci_handle_id_switch(ci);
@@ -104,6 +105,7 @@ static void ci_otg_work(struct work_struct *work)
104 ci_handle_vbus_change(ci); 105 ci_handle_vbus_change(ci);
105 } else 106 } else
106 dev_err(ci->dev, "unexpected event occurs at %s\n", __func__); 107 dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
108 pm_runtime_put_sync(ci->dev);
107 109
108 enable_irq(ci->irq); 110 enable_irq(ci->irq);
109} 111}
diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h
index 535997a6681b..39ba00f0d1d5 100644
--- a/include/linux/usb/chipidea.h
+++ b/include/linux/usb/chipidea.h
@@ -19,6 +19,7 @@ struct ci_hdrc_platform_data {
19 enum usb_phy_interface phy_mode; 19 enum usb_phy_interface phy_mode;
20 unsigned long flags; 20 unsigned long flags;
21#define CI_HDRC_REGS_SHARED BIT(0) 21#define CI_HDRC_REGS_SHARED BIT(0)
22#define CI_HDRC_SUPPORTS_RUNTIME_PM BIT(2)
22#define CI_HDRC_DISABLE_STREAMING BIT(3) 23#define CI_HDRC_DISABLE_STREAMING BIT(3)
23 /* 24 /*
24 * Only set it when DCCPARAMS.DC==1 and DCCPARAMS.HC==1, 25 * Only set it when DCCPARAMS.DC==1 and DCCPARAMS.HC==1,