On Mon, Feb 09, 2015 at 02:45:30PM +0800, Li Jun wrote: > From: Li Jun <b47624@xxxxxxxxxxxxx> > > 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@xxxxxxxxxxxxx> > --- > drivers/usb/chipidea/bits.h | 1 + > drivers/usb/chipidea/core.c | 35 +++++++++++++++++++++++++++++++++++ > drivers/usb/chipidea/otg_fsm.c | 22 +++++++++++++++++++--- > 3 files changed, 55 insertions(+), 3 deletions(-) > > diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h > index e69424d..3cb9bda 100644 > --- a/drivers/usb/chipidea/bits.h > +++ b/drivers/usb/chipidea/bits.h > @@ -63,6 +63,7 @@ > #define PORTSC_HSP BIT(9) > #define PORTSC_PP BIT(12) > #define PORTSC_PTC (0x0FUL << 16) > +#define PORTSC_WKCN BIT(20) > #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) > /* PTS and PTW for non lpm version only */ > #define PORTSC_PFSC BIT(24) > diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c > index 4b22d7c..5a186e3 100644 > --- a/drivers/usb/chipidea/core.c > +++ b/drivers/usb/chipidea/core.c > @@ -861,6 +861,33 @@ static int ci_hdrc_remove(struct platform_device *pdev) > } > > #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) > { > disable_irq(ci->irq); > @@ -894,6 +921,8 @@ static int ci_controller_resume(struct device *dev) > pm_runtime_mark_last_busy(ci->dev); > pm_runtime_put_autosuspend(ci->dev); > enable_irq(ci->irq); > + if (ci_otg_is_fsm_mode(ci)) > + ci_otg_fsm_wakeup_by_srp(ci); > } > > return 0; > @@ -921,6 +950,9 @@ static int ci_suspend(struct device *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); > enable_irq_wake(ci->irq); > } > @@ -963,6 +995,9 @@ static int ci_runtime_suspend(struct device *dev) > return 0; > } > > + if (ci_otg_is_fsm_mode(ci)) > + ci_otg_fsm_suspend_for_srp(ci); > + > usb_phy_set_wakeup(ci->usb_phy, true); > ci_controller_suspend(ci); > > diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c > index 562e581..e3cf5be 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) > return; > } > > + if (list_empty(active_timers)) > + pm_runtime_get(ci->dev); > + > timer->count = timer->expires; > 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) > struct ci_otg_fsm_timer *tmp_timer, *del_tmp; > struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t]; > struct list_head *active_timers = &ci->fsm_timer->active_timers; > + int flag = 0; > > if (t >= NUM_CI_OTG_FSM_TIMERS) > return; > > list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) > - if (tmp_timer == timer) > + if (tmp_timer == timer) { > list_del(&timer->list); > + flag = 1; > + } > > /* 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); > + pm_runtime_put(ci->dev); > + } > } > > /* > @@ -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 */ > - if ((expired == 1) && list_empty(active_timers)) > + if ((expired == 1) && list_empty(active_timers)) { > hw_write_otgsc(ci, OTGSC_1MSIE, 0); > + pm_runtime_put(ci->dev); > + } > > return expired; > } > @@ -585,6 +595,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) > ci->fsm.otg->state < OTG_STATE_A_IDLE) > return 0; > > + pm_runtime_get_sync(ci->dev); > if (otg_statemachine(&ci->fsm)) { > if (ci->fsm.otg->state == OTG_STATE_A_IDLE) { > /* > @@ -609,8 +620,13 @@ int ci_otg_fsm_work(struct ci_hdrc *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; > } > > -- I meet system hang at my imx6sx board after running below commands at B device, at A device, it did not meet the system hang during the boots up with this patch. root@imx6sxsabresd:~# modprobe g_mass_storage file=/dev/mmcblk0p1 removable=1 [ 43.202180] Number of LUNs=8 [ 43.205103] Mass Storage Function, version: 2009/09/11 [ 43.211669] LUN: removable file: (no medium) [ 43.217236] Number of LUNs=1 [ 43.221121] LUN: removable file: /dev/mmcblk0p1 [ 43.225802] Number of LUNs=1 [ 43.231027] g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11 [ 43.238126] g_mass_storage gadget: userspace failed to provide iSerialNumber [ 43.245249] g_mass_storage gadget: g_mass_storage ready -- Best Regards, Peter Chen -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html