[PATCH v2 1/1] usb: chipidea: support runtime power management for otg fsm mode

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
 }
 
-- 
1.7.9.5

--
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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux