diff options
author | Li Jun <b47624@freescale.com> | 2015-02-10 23:45:03 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-03-18 11:19:12 -0400 |
commit | 961ea496facda611eeb153d8133a4d40055e56ca (patch) | |
tree | da1267b53586d89f2510dcdd27b636315ac7eb6e /drivers/usb | |
parent | 6594591741883e004aaba105951337878698b054 (diff) |
usb: chipidea: support runtime power management for otg fsm mode
This patch adds runtime power management support for otg fsm mode, since
A-device in a_idle state cannot detect data pulse irq after suspended, here
enable wakeup by connection before suspend to make it can be resumed by DP;
and handle wakeup from that state like SRP.
Signed-off-by: Li Jun <jun.li@freescale.com>
Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/chipidea/bits.h | 1 | ||||
-rw-r--r-- | drivers/usb/chipidea/core.c | 43 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg_fsm.c | 22 |
3 files changed, 59 insertions, 7 deletions
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index e69424d6e423..3cb9bda51ddf 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h | |||
@@ -63,6 +63,7 @@ | |||
63 | #define PORTSC_HSP BIT(9) | 63 | #define PORTSC_HSP BIT(9) |
64 | #define PORTSC_PP BIT(12) | 64 | #define PORTSC_PP BIT(12) |
65 | #define PORTSC_PTC (0x0FUL << 16) | 65 | #define PORTSC_PTC (0x0FUL << 16) |
66 | #define PORTSC_WKCN BIT(20) | ||
66 | #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) | 67 | #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) |
67 | /* PTS and PTW for non lpm version only */ | 68 | /* PTS and PTW for non lpm version only */ |
68 | #define PORTSC_PFSC BIT(24) | 69 | #define PORTSC_PFSC BIT(24) |
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 4b22d7cb6557..74fea4fa41b1 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c | |||
@@ -798,11 +798,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) | |||
798 | : CI_ROLE_GADGET; | 798 | : CI_ROLE_GADGET; |
799 | } | 799 | } |
800 | 800 | ||
801 | /* only update vbus status for peripheral */ | ||
802 | if (ci->role == CI_ROLE_GADGET) | ||
803 | ci_handle_vbus_change(ci); | ||
804 | |||
805 | if (!ci_otg_is_fsm_mode(ci)) { | 801 | if (!ci_otg_is_fsm_mode(ci)) { |
802 | /* only update vbus status for peripheral */ | ||
803 | if (ci->role == CI_ROLE_GADGET) | ||
804 | ci_handle_vbus_change(ci); | ||
805 | |||
806 | ret = ci_role_start(ci, ci->role); | 806 | ret = ci_role_start(ci, ci->role); |
807 | if (ret) { | 807 | if (ret) { |
808 | dev_err(dev, "can't start %s role\n", | 808 | dev_err(dev, "can't start %s role\n", |
@@ -861,6 +861,33 @@ static int ci_hdrc_remove(struct platform_device *pdev) | |||
861 | } | 861 | } |
862 | 862 | ||
863 | #ifdef CONFIG_PM | 863 | #ifdef CONFIG_PM |
864 | /* Prepare wakeup by SRP before suspend */ | ||
865 | static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci) | ||
866 | { | ||
867 | if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && | ||
868 | !hw_read_otgsc(ci, OTGSC_ID)) { | ||
869 | hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, | ||
870 | PORTSC_PP); | ||
871 | hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN, | ||
872 | PORTSC_WKCN); | ||
873 | } | ||
874 | } | ||
875 | |||
876 | /* Handle SRP when wakeup by data pulse */ | ||
877 | static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci) | ||
878 | { | ||
879 | if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && | ||
880 | (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) { | ||
881 | if (!hw_read_otgsc(ci, OTGSC_ID)) { | ||
882 | ci->fsm.a_srp_det = 1; | ||
883 | ci->fsm.a_bus_drop = 0; | ||
884 | } else { | ||
885 | ci->fsm.id = 1; | ||
886 | } | ||
887 | ci_otg_queue_work(ci); | ||
888 | } | ||
889 | } | ||
890 | |||
864 | static void ci_controller_suspend(struct ci_hdrc *ci) | 891 | static void ci_controller_suspend(struct ci_hdrc *ci) |
865 | { | 892 | { |
866 | disable_irq(ci->irq); | 893 | disable_irq(ci->irq); |
@@ -894,6 +921,8 @@ static int ci_controller_resume(struct device *dev) | |||
894 | pm_runtime_mark_last_busy(ci->dev); | 921 | pm_runtime_mark_last_busy(ci->dev); |
895 | pm_runtime_put_autosuspend(ci->dev); | 922 | pm_runtime_put_autosuspend(ci->dev); |
896 | enable_irq(ci->irq); | 923 | enable_irq(ci->irq); |
924 | if (ci_otg_is_fsm_mode(ci)) | ||
925 | ci_otg_fsm_wakeup_by_srp(ci); | ||
897 | } | 926 | } |
898 | 927 | ||
899 | return 0; | 928 | return 0; |
@@ -921,6 +950,9 @@ static int ci_suspend(struct device *dev) | |||
921 | } | 950 | } |
922 | 951 | ||
923 | if (device_may_wakeup(dev)) { | 952 | if (device_may_wakeup(dev)) { |
953 | if (ci_otg_is_fsm_mode(ci)) | ||
954 | ci_otg_fsm_suspend_for_srp(ci); | ||
955 | |||
924 | usb_phy_set_wakeup(ci->usb_phy, true); | 956 | usb_phy_set_wakeup(ci->usb_phy, true); |
925 | enable_irq_wake(ci->irq); | 957 | enable_irq_wake(ci->irq); |
926 | } | 958 | } |
@@ -963,6 +995,9 @@ static int ci_runtime_suspend(struct device *dev) | |||
963 | return 0; | 995 | return 0; |
964 | } | 996 | } |
965 | 997 | ||
998 | if (ci_otg_is_fsm_mode(ci)) | ||
999 | ci_otg_fsm_suspend_for_srp(ci); | ||
1000 | |||
966 | usb_phy_set_wakeup(ci->usb_phy, true); | 1001 | usb_phy_set_wakeup(ci->usb_phy, true); |
967 | ci_controller_suspend(ci); | 1002 | ci_controller_suspend(ci); |
968 | 1003 | ||
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 562e581f6765..e3cf5be66d3d 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c | |||
@@ -225,6 +225,9 @@ static void ci_otg_add_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) | |||
225 | return; | 225 | return; |
226 | } | 226 | } |
227 | 227 | ||
228 | if (list_empty(active_timers)) | ||
229 | pm_runtime_get(ci->dev); | ||
230 | |||
228 | timer->count = timer->expires; | 231 | timer->count = timer->expires; |
229 | list_add_tail(&timer->list, active_timers); | 232 | list_add_tail(&timer->list, active_timers); |
230 | 233 | ||
@@ -241,17 +244,22 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) | |||
241 | struct ci_otg_fsm_timer *tmp_timer, *del_tmp; | 244 | struct ci_otg_fsm_timer *tmp_timer, *del_tmp; |
242 | struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t]; | 245 | struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t]; |
243 | struct list_head *active_timers = &ci->fsm_timer->active_timers; | 246 | struct list_head *active_timers = &ci->fsm_timer->active_timers; |
247 | int flag = 0; | ||
244 | 248 | ||
245 | if (t >= NUM_CI_OTG_FSM_TIMERS) | 249 | if (t >= NUM_CI_OTG_FSM_TIMERS) |
246 | return; | 250 | return; |
247 | 251 | ||
248 | list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) | 252 | list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) |
249 | if (tmp_timer == timer) | 253 | if (tmp_timer == timer) { |
250 | list_del(&timer->list); | 254 | list_del(&timer->list); |
255 | flag = 1; | ||
256 | } | ||
251 | 257 | ||
252 | /* Disable 1ms irq if there is no any active timer */ | 258 | /* Disable 1ms irq if there is no any active timer */ |
253 | if (list_empty(active_timers)) | 259 | if (list_empty(active_timers) && (flag == 1)) { |
254 | hw_write_otgsc(ci, OTGSC_1MSIE, 0); | 260 | hw_write_otgsc(ci, OTGSC_1MSIE, 0); |
261 | pm_runtime_put(ci->dev); | ||
262 | } | ||
255 | } | 263 | } |
256 | 264 | ||
257 | /* | 265 | /* |
@@ -275,8 +283,10 @@ static inline int ci_otg_tick_timer(struct ci_hdrc *ci) | |||
275 | } | 283 | } |
276 | 284 | ||
277 | /* disable 1ms irq if there is no any timer active */ | 285 | /* disable 1ms irq if there is no any timer active */ |
278 | if ((expired == 1) && list_empty(active_timers)) | 286 | if ((expired == 1) && list_empty(active_timers)) { |
279 | hw_write_otgsc(ci, OTGSC_1MSIE, 0); | 287 | hw_write_otgsc(ci, OTGSC_1MSIE, 0); |
288 | pm_runtime_put(ci->dev); | ||
289 | } | ||
280 | 290 | ||
281 | return expired; | 291 | return expired; |
282 | } | 292 | } |
@@ -585,6 +595,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) | |||
585 | ci->fsm.otg->state < OTG_STATE_A_IDLE) | 595 | ci->fsm.otg->state < OTG_STATE_A_IDLE) |
586 | return 0; | 596 | return 0; |
587 | 597 | ||
598 | pm_runtime_get_sync(ci->dev); | ||
588 | if (otg_statemachine(&ci->fsm)) { | 599 | if (otg_statemachine(&ci->fsm)) { |
589 | if (ci->fsm.otg->state == OTG_STATE_A_IDLE) { | 600 | if (ci->fsm.otg->state == OTG_STATE_A_IDLE) { |
590 | /* | 601 | /* |
@@ -609,8 +620,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) | |||
609 | */ | 620 | */ |
610 | ci_otg_queue_work(ci); | 621 | ci_otg_queue_work(ci); |
611 | } | 622 | } |
623 | } else if (ci->fsm.otg->state == OTG_STATE_A_HOST) { | ||
624 | pm_runtime_mark_last_busy(ci->dev); | ||
625 | pm_runtime_put_autosuspend(ci->dev); | ||
626 | return 0; | ||
612 | } | 627 | } |
613 | } | 628 | } |
629 | pm_runtime_put_sync(ci->dev); | ||
614 | return 0; | 630 | return 0; |
615 | } | 631 | } |
616 | 632 | ||