usb: chipidea: support runtime power management for otg fsm mode
authorLi Jun <b47624@freescale.com>
Wed, 11 Feb 2015 04:45:03 +0000 (12:45 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 18 Mar 2015 15:19:12 +0000 (16:19 +0100)
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>
drivers/usb/chipidea/bits.h
drivers/usb/chipidea/core.c
drivers/usb/chipidea/otg_fsm.c

index e69424d6e42325f806849cb60d3763f53e220de5..3cb9bda51ddfde76f991581736af7fb6e111a4e3 100644 (file)
@@ -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)
index 4b22d7cb655731d7bee69d48f18f2552aa822893..74fea4fa41b156248ce6d7116db02b54990066a9 100644 (file)
@@ -798,11 +798,11 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                        : 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)) {
+               /* only update vbus status for peripheral */
+               if (ci->role == CI_ROLE_GADGET)
+                       ci_handle_vbus_change(ci);
+
                ret = ci_role_start(ci, ci->role);
                if (ret) {
                        dev_err(dev, "can't start %s role\n",
@@ -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);
 
index 562e581f67656fbf13eb72bd175f8766be59599f..e3cf5be66d3de869f21fc5a8768d4e204dd4360e 100644 (file)
@@ -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;
 }