usb: msm_otg: schedule state machine only when state changes

While an asynchronous interrupt may wake up the system from low
power mode, the OTG PHY and controller must first be resumed and
then the IRQ enabled. If during this time the state machine is
scheduled, it may prematurely run before the interrupt handler has
a chance to fire and effect a state transition, and may erroneously
lead down the "chg_work cancel" path, which will reset the PHY
right after resuming. After this reset, the system will go back into
suspend thinking nothing happened.

Instead, since the "in LPM" part of the interrupt handler isn't
actually changing state, don't schedule any work just yet. Later,
when the resume callback enables the IRQ, and the interrupt can
occur naturally, any state change that happens as a result will
appropriately schedule the sm_work.

Change-Id: I887546ed18151a303a2efcb16d0c38fd193aa0b2
CRs-Fixed: 387377
Signed-off-by: Jack Pham <jackp@codeaurora.org>
Signed-off-by: Neha Pandey <nehap@codeaurora.org>
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 84ee944..81215fb 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -2800,14 +2800,8 @@
 		pr_debug("OTG IRQ: %d in LPM\n", irq);
 		disable_irq_nosync(irq);
 		motg->async_int = irq;
-		if (atomic_read(&motg->pm_suspended)) {
-			motg->sm_work_pending = true;
-			if ((otg->phy->state == OTG_STATE_A_SUSPEND) ||
-				(otg->phy->state == OTG_STATE_A_WAIT_BCON))
-				set_bit(A_BUS_REQ, &motg->inputs);
-		} else {
+		if (!atomic_read(&motg->pm_suspended))
 			pm_request_resume(otg->phy->dev);
-		}
 		return IRQ_HANDLED;
 	}
 
@@ -3948,9 +3942,7 @@
 	dev_dbg(dev, "OTG PM resume\n");
 
 	atomic_set(&motg->pm_suspended, 0);
-	if (motg->sm_work_pending) {
-		motg->sm_work_pending = false;
-
+	if (motg->async_int || motg->sm_work_pending) {
 		pm_runtime_get_noresume(dev);
 		ret = msm_otg_resume(motg);
 
@@ -3959,7 +3951,10 @@
 		pm_runtime_set_active(dev);
 		pm_runtime_enable(dev);
 
-		queue_work(system_nrt_wq, &motg->sm_work);
+		if (motg->sm_work_pending) {
+			motg->sm_work_pending = false;
+			queue_work(system_nrt_wq, &motg->sm_work);
+		}
 	}
 
 	return ret;