usb: msm_otg: system resume only resumes hardware when needed
Part of commit d44455a0b838b1d691e63913f0b159552ac01803 introduced
a regression affecting resume behavior in that the controller's system
resume callback is being followed by an immediate runtime suspend; if
the system resume was caused by an OTG event, there would be a pending
runtime resume in the state machine function, which might fail due to
this race, causing the PHY to be unnecessarily reset.
This patch limits the actual hardware resumes to happen only when needed.
If a possible wakeup event such as VBUS or ID interrupt occurs while
in system suspend, don't allow runtime resume to execute in the wakeup
handler but rather defer the resuming to the system resume callback.
Also by incrementing the PM runtime usage counter within the system
resume, it may help prevent the premature runtime suspend from happening
immediately afterward. Otherwise, if there is no such OTG event, and
the controller is not a suspended host, do nothing.
If the bus is instead runtime suspended (and not system suspended) when
a wakeup event occurs, then normal runtime resume will bring the
hardware out of low power mode.
CRs-fixed: 358761
Change-Id: I4249690485949ecb3072e258859608ecea52ec9b
Signed-off-by: Jack Pham <jackp@codeaurora.org>
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 4dd6aff..deb4064 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -697,6 +697,9 @@
/* Remote wakeup or resume */
set_bit(A_BUS_REQ, &motg->inputs);
otg->state = OTG_STATE_A_HOST;
+
+ /* ensure hardware is not in low power mode */
+ pm_runtime_resume(otg->dev);
break;
default:
break;
@@ -2511,7 +2514,10 @@
pr_debug("OTG IRQ: in LPM\n");
disable_irq_nosync(irq);
motg->async_int = 1;
- pm_request_resume(otg->dev);
+ if (atomic_read(&motg->pm_suspended))
+ motg->sm_work_pending = true;
+ else
+ pm_request_resume(otg->dev);
return IRQ_HANDLED;
}
@@ -2676,7 +2682,10 @@
return;
}
- queue_work(system_nrt_wq, &motg->sm_work);
+ if (atomic_read(&motg->pm_suspended))
+ motg->sm_work_pending = true;
+ else
+ queue_work(system_nrt_wq, &motg->sm_work);
}
static irqreturn_t msm_pmic_id_irq(int irq, void *data)
@@ -2695,8 +2704,12 @@
set_bit(A_BUS_REQ, &motg->inputs);
}
- if (motg->otg.state != OTG_STATE_UNDEFINED)
- queue_work(system_nrt_wq, &motg->sm_work);
+ if (motg->otg.state != OTG_STATE_UNDEFINED) {
+ if (atomic_read(&motg->pm_suspended))
+ motg->sm_work_pending = true;
+ else
+ queue_work(system_nrt_wq, &motg->sm_work);
+ }
return IRQ_HANDLED;
}
@@ -3533,28 +3546,42 @@
#ifdef CONFIG_PM_SLEEP
static int msm_otg_pm_suspend(struct device *dev)
{
+ int ret = 0;
struct msm_otg *motg = dev_get_drvdata(dev);
dev_dbg(dev, "OTG PM suspend\n");
- return msm_otg_suspend(motg);
+
+ atomic_set(&motg->pm_suspended, 1);
+ ret = msm_otg_suspend(motg);
+ if (ret)
+ atomic_set(&motg->pm_suspended, 0);
+
+ return ret;
}
static int msm_otg_pm_resume(struct device *dev)
{
+ int ret = 0;
struct msm_otg *motg = dev_get_drvdata(dev);
- int ret;
dev_dbg(dev, "OTG PM resume\n");
- ret = msm_otg_resume(motg);
- if (ret)
- return ret;
- /* Update runtime PM status */
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
+ atomic_set(&motg->pm_suspended, 0);
+ if (motg->sm_work_pending) {
+ motg->sm_work_pending = false;
- return 0;
+ pm_runtime_get_noresume(dev);
+ ret = msm_otg_resume(motg);
+
+ /* Update runtime PM status */
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ queue_work(system_nrt_wq, &motg->sm_work);
+ }
+
+ return ret;
}
#endif