Commit 961ea496 authored by Li Jun's avatar Li Jun Committed by Greg Kroah-Hartman

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: default avatarLi Jun <jun.li@freescale.com>
Signed-off-by: default avatarPeter Chen <peter.chen@freescale.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 65945917
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
#define PORTSC_HSP BIT(9) #define PORTSC_HSP BIT(9)
#define PORTSC_PP BIT(12) #define PORTSC_PP BIT(12)
#define PORTSC_PTC (0x0FUL << 16) #define PORTSC_PTC (0x0FUL << 16)
#define PORTSC_WKCN BIT(20)
#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23))
/* PTS and PTW for non lpm version only */ /* PTS and PTW for non lpm version only */
#define PORTSC_PFSC BIT(24) #define PORTSC_PFSC BIT(24)
......
...@@ -798,11 +798,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -798,11 +798,11 @@ static int ci_hdrc_probe(struct platform_device *pdev)
: CI_ROLE_GADGET; : CI_ROLE_GADGET;
} }
/* only update vbus status for peripheral */
if (ci->role == CI_ROLE_GADGET)
ci_handle_vbus_change(ci);
if (!ci_otg_is_fsm_mode(ci)) { if (!ci_otg_is_fsm_mode(ci)) {
/* only update vbus status for peripheral */
if (ci->role == CI_ROLE_GADGET)
ci_handle_vbus_change(ci);
ret = ci_role_start(ci, ci->role); ret = ci_role_start(ci, ci->role);
if (ret) { if (ret) {
dev_err(dev, "can't start %s role\n", dev_err(dev, "can't start %s role\n",
...@@ -861,6 +861,33 @@ static int ci_hdrc_remove(struct platform_device *pdev) ...@@ -861,6 +861,33 @@ static int ci_hdrc_remove(struct platform_device *pdev)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
/* Prepare wakeup by SRP before suspend */
static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
{
if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
!hw_read_otgsc(ci, OTGSC_ID)) {
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
PORTSC_PP);
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN,
PORTSC_WKCN);
}
}
/* Handle SRP when wakeup by data pulse */
static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
{
if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
(ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
if (!hw_read_otgsc(ci, OTGSC_ID)) {
ci->fsm.a_srp_det = 1;
ci->fsm.a_bus_drop = 0;
} else {
ci->fsm.id = 1;
}
ci_otg_queue_work(ci);
}
}
static void ci_controller_suspend(struct ci_hdrc *ci) static void ci_controller_suspend(struct ci_hdrc *ci)
{ {
disable_irq(ci->irq); disable_irq(ci->irq);
...@@ -894,6 +921,8 @@ static int ci_controller_resume(struct device *dev) ...@@ -894,6 +921,8 @@ static int ci_controller_resume(struct device *dev)
pm_runtime_mark_last_busy(ci->dev); pm_runtime_mark_last_busy(ci->dev);
pm_runtime_put_autosuspend(ci->dev); pm_runtime_put_autosuspend(ci->dev);
enable_irq(ci->irq); enable_irq(ci->irq);
if (ci_otg_is_fsm_mode(ci))
ci_otg_fsm_wakeup_by_srp(ci);
} }
return 0; return 0;
...@@ -921,6 +950,9 @@ static int ci_suspend(struct device *dev) ...@@ -921,6 +950,9 @@ static int ci_suspend(struct device *dev)
} }
if (device_may_wakeup(dev)) { if (device_may_wakeup(dev)) {
if (ci_otg_is_fsm_mode(ci))
ci_otg_fsm_suspend_for_srp(ci);
usb_phy_set_wakeup(ci->usb_phy, true); usb_phy_set_wakeup(ci->usb_phy, true);
enable_irq_wake(ci->irq); enable_irq_wake(ci->irq);
} }
...@@ -963,6 +995,9 @@ static int ci_runtime_suspend(struct device *dev) ...@@ -963,6 +995,9 @@ static int ci_runtime_suspend(struct device *dev)
return 0; return 0;
} }
if (ci_otg_is_fsm_mode(ci))
ci_otg_fsm_suspend_for_srp(ci);
usb_phy_set_wakeup(ci->usb_phy, true); usb_phy_set_wakeup(ci->usb_phy, true);
ci_controller_suspend(ci); ci_controller_suspend(ci);
......
...@@ -225,6 +225,9 @@ static void ci_otg_add_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) ...@@ -225,6 +225,9 @@ static void ci_otg_add_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t)
return; return;
} }
if (list_empty(active_timers))
pm_runtime_get(ci->dev);
timer->count = timer->expires; timer->count = timer->expires;
list_add_tail(&timer->list, active_timers); list_add_tail(&timer->list, active_timers);
...@@ -241,17 +244,22 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) ...@@ -241,17 +244,22 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t)
struct ci_otg_fsm_timer *tmp_timer, *del_tmp; struct ci_otg_fsm_timer *tmp_timer, *del_tmp;
struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t]; struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t];
struct list_head *active_timers = &ci->fsm_timer->active_timers; struct list_head *active_timers = &ci->fsm_timer->active_timers;
int flag = 0;
if (t >= NUM_CI_OTG_FSM_TIMERS) if (t >= NUM_CI_OTG_FSM_TIMERS)
return; return;
list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list)
if (tmp_timer == timer) if (tmp_timer == timer) {
list_del(&timer->list); list_del(&timer->list);
flag = 1;
}
/* Disable 1ms irq if there is no any active timer */ /* Disable 1ms irq if there is no any active timer */
if (list_empty(active_timers)) if (list_empty(active_timers) && (flag == 1)) {
hw_write_otgsc(ci, OTGSC_1MSIE, 0); hw_write_otgsc(ci, OTGSC_1MSIE, 0);
pm_runtime_put(ci->dev);
}
} }
/* /*
...@@ -275,8 +283,10 @@ static inline int ci_otg_tick_timer(struct ci_hdrc *ci) ...@@ -275,8 +283,10 @@ static inline int ci_otg_tick_timer(struct ci_hdrc *ci)
} }
/* disable 1ms irq if there is no any timer active */ /* disable 1ms irq if there is no any timer active */
if ((expired == 1) && list_empty(active_timers)) if ((expired == 1) && list_empty(active_timers)) {
hw_write_otgsc(ci, OTGSC_1MSIE, 0); hw_write_otgsc(ci, OTGSC_1MSIE, 0);
pm_runtime_put(ci->dev);
}
return expired; return expired;
} }
...@@ -585,6 +595,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) ...@@ -585,6 +595,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
ci->fsm.otg->state < OTG_STATE_A_IDLE) ci->fsm.otg->state < OTG_STATE_A_IDLE)
return 0; return 0;
pm_runtime_get_sync(ci->dev);
if (otg_statemachine(&ci->fsm)) { if (otg_statemachine(&ci->fsm)) {
if (ci->fsm.otg->state == OTG_STATE_A_IDLE) { if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
/* /*
...@@ -609,8 +620,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) ...@@ -609,8 +620,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
*/ */
ci_otg_queue_work(ci); ci_otg_queue_work(ci);
} }
} else if (ci->fsm.otg->state == OTG_STATE_A_HOST) {
pm_runtime_mark_last_busy(ci->dev);
pm_runtime_put_autosuspend(ci->dev);
return 0;
} }
} }
pm_runtime_put_sync(ci->dev);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment