usb: msm_otg: Add support for separate ASYNC IRQ to exit LPM
Some USB cores have a separate IRQ line for issuing ASYNC
interrupts in LPM. This interrupt needs to be enabled only
when hardware is put in low power state.
Change-Id: Ie7de237ccbaa90294b20d3fce594ec280cb1641a
Signed-off-by: Manu Gautam <mgautam@codeaurora.org>
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 355990a..4217ba0 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -879,6 +879,8 @@
if (device_may_wakeup(phy->dev)) {
enable_irq_wake(motg->irq);
+ if (motg->async_irq)
+ enable_irq_wake(motg->async_irq);
if (motg->pdata->pmic_id_irq)
enable_irq_wake(motg->pdata->pmic_id_irq);
if (pdata->otg_control == OTG_PHY_CONTROL &&
@@ -889,6 +891,9 @@
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
atomic_set(&motg->in_lpm, 1);
+ /* Enable ASYNC IRQ (if present) during LPM */
+ if (motg->async_irq)
+ enable_irq(motg->async_irq);
enable_irq(motg->irq);
wake_unlock(&motg->wlock);
@@ -979,6 +984,8 @@
skip_phy_resume:
if (device_may_wakeup(phy->dev)) {
disable_irq_wake(motg->irq);
+ if (motg->async_irq)
+ disable_irq_wake(motg->async_irq);
if (motg->pdata->pmic_id_irq)
disable_irq_wake(motg->pdata->pmic_id_irq);
if (pdata->otg_control == OTG_PHY_CONTROL &&
@@ -991,10 +998,15 @@
atomic_set(&motg->in_lpm, 0);
if (motg->async_int) {
+ /* Match the disable_irq call from ISR */
+ enable_irq(motg->async_int);
motg->async_int = 0;
- enable_irq(motg->irq);
}
+ /* If ASYNC IRQ is present then keep it enabled only during LPM */
+ if (motg->async_irq)
+ disable_irq(motg->async_irq);
+
dev_info(phy->dev, "USB exited from low power mode\n");
return 0;
@@ -2728,9 +2740,9 @@
irqreturn_t ret = IRQ_HANDLED;
if (atomic_read(&motg->in_lpm)) {
- pr_debug("OTG IRQ: in LPM\n");
+ pr_debug("OTG IRQ: %d in LPM\n", irq);
disable_irq_nosync(irq);
- motg->async_int = 1;
+ motg->async_int = irq;
if (atomic_read(&motg->pm_suspended)) {
motg->sm_work_pending = true;
if ((otg->phy->state == OTG_STATE_A_SUSPEND) ||
@@ -3397,7 +3409,7 @@
static int __init msm_otg_probe(struct platform_device *pdev)
{
- int ret = 0, disable_lpm = 0;
+ int ret = 0;
struct resource *res;
struct msm_otg *motg;
struct usb_phy *phy;
@@ -3415,8 +3427,6 @@
dev_err(&pdev->dev, "devices setup failed\n");
return ret;
}
- /* LPM not supported on targets using DT */
- disable_lpm = 1;
} else if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "No platform data given. Bailing out\n");
return -ENODEV;
@@ -3516,6 +3526,12 @@
goto free_regs;
}
+ motg->async_irq = platform_get_irq_byname(pdev, "async_irq");
+ if (motg->async_irq < 0) {
+ dev_dbg(&pdev->dev, "platform_get_irq for async_int failed\n");
+ motg->async_irq = 0;
+ }
+
motg->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "usb");
if (IS_ERR(motg->xo_handle)) {
dev_err(&pdev->dev, "%s not able to get the handle "
@@ -3602,6 +3618,16 @@
goto destroy_wlock;
}
+ if (motg->async_irq) {
+ ret = request_irq(motg->async_irq, msm_otg_irq, IRQF_SHARED,
+ "msm_otg", motg);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq failed (ASYNC INT)\n");
+ goto free_irq;
+ }
+ disable_irq(motg->async_irq);
+ }
+
if (pdata->otg_control == OTG_PHY_CONTROL && pdata->mpm_otgsessvld_int)
msm_mpm_enable_pin(pdata->mpm_otgsessvld_int, 1);
@@ -3620,7 +3646,7 @@
ret = usb_set_transceiver(&motg->phy);
if (ret) {
dev_err(&pdev->dev, "usb_set_transceiver failed\n");
- goto free_irq;
+ goto free_async_irq;
}
if (motg->pdata->mode == USB_OTG &&
@@ -3672,8 +3698,7 @@
wake_lock(&motg->wlock);
pm_runtime_set_active(&pdev->dev);
- if (!disable_lpm)
- pm_runtime_enable(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
if (motg->pdata->bus_scale_table) {
motg->bus_perf_client =
@@ -3689,6 +3714,9 @@
remove_phy:
usb_set_transceiver(NULL);
+free_async_irq:
+ if (motg->async_irq)
+ free_irq(motg->async_irq, motg);
free_irq:
free_irq(motg->irq, motg);
destroy_wlock: