USB: OTG: msm: Simplify Runtime PM usage
In current design, PM usage counter operations are scattered across
charging detection work, state machine work, interrupt handler and
resume work. This did not give any problems for typical device mode
or host mode use cases. But with ACA implementation, there will be
many corner cases where PM usage operations can go wrong and device
stays out of low power mode (LPM). Hence leave PM usage counter
operations to state machine work, which knows best about the device
state.
Don't create device and host controller platform devices as children
of OTG device. This helps OTG state machine to know about host bus
suspend and can take a decision whether low power mode is possible or
not.
Change-Id: I00474e396546d2567d09409e70591a6c9d325c06
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index d16fcda..04c1449 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -535,6 +535,24 @@
return 0;
}
+static int msm_otg_set_suspend(struct otg_transceiver *otg, int suspend)
+{
+ struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
+
+ /*
+ * Allow bus suspend only for host mode. Device mode bus suspend
+ * is not implemented yet.
+ */
+ if (!test_bit(ID, &motg->inputs) || test_bit(ID_A, &motg->inputs)) {
+ if (suspend)
+ pm_runtime_put(otg->dev);
+ else
+ pm_runtime_resume(otg->dev);
+ }
+
+ return 0;
+}
+
#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000)
#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
@@ -745,7 +763,6 @@
if (motg->async_int) {
motg->async_int = 0;
- pm_runtime_put(otg->dev);
enable_irq(motg->irq);
}
@@ -1329,7 +1346,6 @@
dev_dbg(otg->dev, "chg detection work\n");
switch (motg->chg_state) {
case USB_CHG_STATE_UNDEFINED:
- pm_runtime_get_sync(otg->dev);
msm_chg_block_on(motg);
msm_chg_enable_dcd(motg);
msm_chg_enable_aca_det(motg);
@@ -1462,6 +1478,7 @@
struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
struct otg_transceiver *otg = &motg->otg;
+ pm_runtime_resume(otg->dev);
switch (otg->state) {
case OTG_STATE_UNDEFINED:
dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n");
@@ -1494,6 +1511,8 @@
case USB_ACA_B_CHARGER:
msm_otg_notify_charger(motg,
IDEV_CHG_MAX);
+ pm_runtime_put_noidle(otg->dev);
+ pm_runtime_suspend(otg->dev);
break;
case USB_CDP_CHARGER:
case USB_ACA_C_CHARGER:
@@ -1515,20 +1534,14 @@
break;
}
} else {
- /*
- * If charger detection work is pending, decrement
- * the pm usage counter to balance with the one that
- * is incremented in charger detection work.
- */
- if (cancel_delayed_work_sync(&motg->chg_work)) {
- pm_runtime_put_sync(otg->dev);
+ if (cancel_delayed_work_sync(&motg->chg_work))
msm_otg_reset(otg);
- }
msm_otg_notify_charger(motg, 0);
motg->chg_state = USB_CHG_STATE_UNDEFINED;
motg->chg_type = USB_INVALID_CHARGER;
+ pm_runtime_put_noidle(otg->dev);
+ pm_runtime_suspend(otg->dev);
}
- pm_runtime_put_sync(otg->dev);
break;
case OTG_STATE_B_PERIPHERAL:
dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n");
@@ -1547,7 +1560,6 @@
schedule_work(w);
} else if (test_bit(ID_C, &motg->inputs)) {
msm_otg_notify_charger(motg, IDEV_CHG_MAX);
- pm_runtime_put_sync(otg->dev);
}
break;
case OTG_STATE_A_HOST:
@@ -1569,7 +1581,6 @@
motg->pdata->vbus_power(0);
msm_otg_notify_charger(motg,
IDEV_CHG_MIN - motg->mA_port);
- pm_runtime_put_sync(otg->dev);
} else if (!test_bit(ID, &motg->inputs)) {
motg->chg_state = USB_CHG_STATE_UNDEFINED;
motg->chg_type = USB_INVALID_CHARGER;
@@ -1577,7 +1588,6 @@
writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
if (motg->pdata->vbus_power)
motg->pdata->vbus_power(1);
- pm_runtime_put_sync(otg->dev);
}
break;
default:
@@ -1594,17 +1604,15 @@
if (atomic_read(&motg->in_lpm)) {
disable_irq_nosync(irq);
motg->async_int = 1;
- pm_runtime_get(otg->dev);
+ pm_request_resume(otg->dev);
return IRQ_HANDLED;
}
usbsts = readl(USB_USBSTS);
if ((usbsts & PHY_ALT_INT)) {
writel(PHY_ALT_INT, USB_USBSTS);
- if (msm_chg_check_aca_intr(motg)) {
- pm_runtime_get_noresume(otg->dev);
+ if (msm_chg_check_aca_intr(motg))
schedule_work(&motg->sm_work);
- }
return IRQ_HANDLED;
}
@@ -1619,7 +1627,6 @@
clear_bit(ID, &motg->inputs);
dev_dbg(otg->dev, "ID set/clear\n");
schedule_work(&motg->sm_work);
- pm_runtime_get_noresume(otg->dev);
} else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) {
if (otgsc & OTGSC_BSV)
set_bit(B_SESS_VLD, &motg->inputs);
@@ -1627,7 +1634,6 @@
clear_bit(B_SESS_VLD, &motg->inputs);
dev_dbg(otg->dev, "BSV set/clear\n");
schedule_work(&motg->sm_work);
- pm_runtime_get_noresume(otg->dev);
}
writel(otgsc, USB_OTGSC);
@@ -1749,7 +1755,7 @@
goto out;
}
- pm_runtime_get_sync(otg->dev);
+ pm_runtime_resume(otg->dev);
schedule_work(&motg->sm_work);
out:
return status;
@@ -1973,6 +1979,7 @@
otg->set_host = msm_otg_set_host;
otg->set_peripheral = msm_otg_set_peripheral;
otg->set_power = msm_otg_set_power;
+ otg->set_suspend = msm_otg_set_suspend;
otg->io_ops = &msm_otg_io_ops;
@@ -2145,16 +2152,10 @@
dev_dbg(dev, "OTG runtime idle\n");
- /*
- * It is observed some times that a spurious interrupt
- * comes when PHY is put into LPM immediately after PHY reset.
- * This 1 sec delay also prevents entering into LPM immediately
- * after asynchronous interrupt.
- */
- if (otg->state != OTG_STATE_UNDEFINED)
- pm_schedule_suspend(dev, 1000);
-
- return -EAGAIN;
+ if (otg->state == OTG_STATE_UNDEFINED)
+ return -EAGAIN;
+ else
+ return 0;
}
static int msm_otg_runtime_suspend(struct device *dev)
@@ -2170,6 +2171,7 @@
struct msm_otg *motg = dev_get_drvdata(dev);
dev_dbg(dev, "OTG runtime resume\n");
+ pm_runtime_get_noresume(dev);
return msm_otg_resume(motg);
}
#endif
@@ -2177,10 +2179,18 @@
#ifdef CONFIG_PM_SLEEP
static int msm_otg_pm_suspend(struct device *dev)
{
- struct msm_otg *motg = dev_get_drvdata(dev);
+ int ret;
dev_dbg(dev, "OTG PM suspend\n");
- return msm_otg_suspend(motg);
+
+#ifdef CONFIG_PM_RUNTIME
+ ret = pm_runtime_suspend(dev);
+ if (ret > 0)
+ ret = 0;
+#else
+ ret = msm_otg_suspend(dev_get_drvdata(dev));
+#endif
+ return ret;
}
static int msm_otg_pm_resume(struct device *dev)