[PATCH 3/6] usb: musb: Fix PM for hub disconnect

2016-11-16 Thread Bin Liu
From: Tony Lindgren 

With a USB hub disconnected, devctl can be 0x19 for about a second
on am335x and will stay forever on at least omap3. And we get no
further interrupts when devctl session bit clears. This keeps
PM runtime active.

Let's fix the issue by polling devctl until the session bit clears
or times out. We can do this by making musb->irq_work into
delayed_work.

And with the polling implemented, we can now also have the quirk
for invalid VBUS it to avoid disconnecting too early while VBUS
is ramping up.

Fixes: 467d5c980709 ("usb: musb: Implement session bit based runtime
PM for musb-core")
Fixes: 65b3f50ed6fa ("usb: musb: Add PM runtime support for MUSB DSPS
Tested-by: Ladislav Michl 
Tested-by: Laurent Pinchart 
Signed-off-by: Tony Lindgren 
Signed-off-by: Bin Liu 
---
 drivers/usb/musb/musb_core.c   | 29 +++--
 drivers/usb/musb/musb_core.h   |  4 ++--
 drivers/usb/musb/musb_gadget.c |  6 +++---
 drivers/usb/musb/tusb6010.c|  6 +++---
 4 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 384de6cd26f5..c3e172e15ec3 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -986,7 +986,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 
int_usb,
}
 #endif
 
-   schedule_work(&musb->irq_work);
+   schedule_delayed_work(&musb->irq_work, 0);
 
return handled;
 }
@@ -1855,14 +1855,23 @@ static void musb_pm_runtime_check_session(struct musb 
*musb)
MUSB_DEVCTL_HR;
switch (devctl & ~s) {
case MUSB_QUIRK_B_INVALID_VBUS_91:
-   if (!musb->session && !musb->quirk_invalid_vbus) {
-   musb->quirk_invalid_vbus = true;
+   if (musb->quirk_retries--) {
musb_dbg(musb,
-"First invalid vbus, assume no session");
+"Poll devctl on invalid vbus, assume no 
session");
+   schedule_delayed_work(&musb->irq_work,
+ msecs_to_jiffies(1000));
+
return;
}
-   break;
case MUSB_QUIRK_A_DISCONNECT_19:
+   if (musb->quirk_retries--) {
+   musb_dbg(musb,
+"Poll devctl on possible host mode 
disconnect");
+   schedule_delayed_work(&musb->irq_work,
+ msecs_to_jiffies(1000));
+
+   return;
+   }
if (!musb->session)
break;
musb_dbg(musb, "Allow PM on possible host mode disconnect");
@@ -1886,9 +1895,9 @@ static void musb_pm_runtime_check_session(struct musb 
*musb)
if (error < 0)
dev_err(musb->controller, "Could not enable: %i\n",
error);
+   musb->quirk_retries = 3;
} else {
musb_dbg(musb, "Allow PM with no session: %02x", devctl);
-   musb->quirk_invalid_vbus = false;
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
}
@@ -1899,7 +1908,7 @@ static void musb_pm_runtime_check_session(struct musb 
*musb)
 /* Only used to provide driver mode change events */
 static void musb_irq_work(struct work_struct *data)
 {
-   struct musb *musb = container_of(data, struct musb, irq_work);
+   struct musb *musb = container_of(data, struct musb, irq_work.work);
 
musb_pm_runtime_check_session(musb);
 
@@ -2288,7 +2297,7 @@ static void musb_deassert_reset(struct work_struct *work)
musb_generic_disable(musb);
 
/* Init IRQ workqueue before request_irq */
-   INIT_WORK(&musb->irq_work, musb_irq_work);
+   INIT_DELAYED_WORK(&musb->irq_work, musb_irq_work);
INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
 
@@ -2385,7 +2394,7 @@ static void musb_deassert_reset(struct work_struct *work)
musb_host_cleanup(musb);
 
 fail3:
-   cancel_work_sync(&musb->irq_work);
+   cancel_delayed_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
@@ -2452,7 +2461,7 @@ static int musb_remove(struct platform_device *pdev)
 */
musb_exit_debugfs(musb);
 
-   cancel_work_sync(&musb->irq_work);
+   cancel_delayed_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
pm_runtime_get_sync(musb->controller);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 15b1f93c7037..91817d77d59c

[PATCH 3/6] usb: musb: Fix PM for hub disconnect

2016-11-15 Thread Tony Lindgren
With a USB hub disconnected, devctl can be 0x19 for about a second
on am335x and will stay forever on at least omap3. And we get no
further interrupts when devctl session bit clears. This keeps
PM runtime active.

Let's fix the issue by polling devctl until the session bit clears
or times out. We can do this by making musb->irq_work into
delayed_work.

And with the polling implemented, we can now also have the quirk
for invalid VBUS it to avoid disconnecting too early while VBUS
is ramping up.

Fixes: 467d5c980709 ("usb: musb: Implement session bit based runtime
PM for musb-core")
Fixes: 65b3f50ed6fa ("usb: musb: Add PM runtime support for MUSB DSPS
Tested-by: Ladislav Michl 
Tested-by: Laurent Pinchart 
Signed-off-by: Tony Lindgren 
---
 drivers/usb/musb/musb_core.c   | 29 +++--
 drivers/usb/musb/musb_core.h   |  4 ++--
 drivers/usb/musb/musb_gadget.c |  6 +++---
 drivers/usb/musb/tusb6010.c|  6 +++---
 4 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -986,7 +986,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 
int_usb,
}
 #endif
 
-   schedule_work(&musb->irq_work);
+   schedule_delayed_work(&musb->irq_work, 0);
 
return handled;
 }
@@ -1855,14 +1855,23 @@ static void musb_pm_runtime_check_session(struct musb 
*musb)
MUSB_DEVCTL_HR;
switch (devctl & ~s) {
case MUSB_QUIRK_B_INVALID_VBUS_91:
-   if (!musb->session && !musb->quirk_invalid_vbus) {
-   musb->quirk_invalid_vbus = true;
+   if (musb->quirk_retries--) {
musb_dbg(musb,
-"First invalid vbus, assume no session");
+"Poll devctl on invalid vbus, assume no 
session");
+   schedule_delayed_work(&musb->irq_work,
+ msecs_to_jiffies(1000));
+
return;
}
-   break;
case MUSB_QUIRK_A_DISCONNECT_19:
+   if (musb->quirk_retries--) {
+   musb_dbg(musb,
+"Poll devctl on possible host mode 
disconnect");
+   schedule_delayed_work(&musb->irq_work,
+ msecs_to_jiffies(1000));
+
+   return;
+   }
if (!musb->session)
break;
musb_dbg(musb, "Allow PM on possible host mode disconnect");
@@ -1886,9 +1895,9 @@ static void musb_pm_runtime_check_session(struct musb 
*musb)
if (error < 0)
dev_err(musb->controller, "Could not enable: %i\n",
error);
+   musb->quirk_retries = 3;
} else {
musb_dbg(musb, "Allow PM with no session: %02x", devctl);
-   musb->quirk_invalid_vbus = false;
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
}
@@ -1899,7 +1908,7 @@ static void musb_pm_runtime_check_session(struct musb 
*musb)
 /* Only used to provide driver mode change events */
 static void musb_irq_work(struct work_struct *data)
 {
-   struct musb *musb = container_of(data, struct musb, irq_work);
+   struct musb *musb = container_of(data, struct musb, irq_work.work);
 
musb_pm_runtime_check_session(musb);
 
@@ -2288,7 +2297,7 @@ musb_init_controller(struct device *dev, int nIrq, void 
__iomem *ctrl)
musb_generic_disable(musb);
 
/* Init IRQ workqueue before request_irq */
-   INIT_WORK(&musb->irq_work, musb_irq_work);
+   INIT_DELAYED_WORK(&musb->irq_work, musb_irq_work);
INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
 
@@ -2385,7 +2394,7 @@ musb_init_controller(struct device *dev, int nIrq, void 
__iomem *ctrl)
musb_host_cleanup(musb);
 
 fail3:
-   cancel_work_sync(&musb->irq_work);
+   cancel_delayed_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
@@ -2452,7 +2461,7 @@ static int musb_remove(struct platform_device *pdev)
 */
musb_exit_debugfs(musb);
 
-   cancel_work_sync(&musb->irq_work);
+   cancel_delayed_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
pm_runtime_get_sync(musb->controller);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -310,7 +310,7 @@ 

[PATCH 3/6] usb: musb: Fix PM for hub disconnect

2016-11-11 Thread Tony Lindgren
With a USB hub disconnected, devctl can be 0x19 for about a second
on am335x and will stay forever on at least omap3. And we get no
further interrupts when devctl session bit clears. This keeps
PM runtime active.

Let's fix the issue by polling devctl until the session bit clears
or times out. We can do this by making musb->irq_work into
delayed_work.

And with the polling implemented, we can now also have the quirk
for invalid VBUS it to avoid disconnecting too early while VBUS
is ramping up.

Fixes: 467d5c980709 ("usb: musb: Implement session bit based runtime
PM for musb-core")
Fixes: 65b3f50ed6fa ("usb: musb: Add PM runtime support for MUSB DSPS
Tested-by: Ladislav Michl 
Tested-by: Laurent Pinchart 
Signed-off-by: Tony Lindgren 
---
 drivers/usb/musb/musb_core.c   | 29 +++--
 drivers/usb/musb/musb_core.h   |  4 ++--
 drivers/usb/musb/musb_gadget.c |  6 +++---
 drivers/usb/musb/tusb6010.c|  6 +++---
 4 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -986,7 +986,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 
int_usb,
}
 #endif
 
-   schedule_work(&musb->irq_work);
+   schedule_delayed_work(&musb->irq_work, 0);
 
return handled;
 }
@@ -1855,14 +1855,23 @@ static void musb_pm_runtime_check_session(struct musb 
*musb)
MUSB_DEVCTL_HR;
switch (devctl & ~s) {
case MUSB_QUIRK_B_INVALID_VBUS_91:
-   if (!musb->session && !musb->quirk_invalid_vbus) {
-   musb->quirk_invalid_vbus = true;
+   if (musb->quirk_retries--) {
musb_dbg(musb,
-"First invalid vbus, assume no session");
+"Poll devctl on invalid vbus, assume no 
session");
+   schedule_delayed_work(&musb->irq_work,
+ msecs_to_jiffies(1000));
+
return;
}
-   break;
case MUSB_QUIRK_A_DISCONNECT_19:
+   if (musb->quirk_retries--) {
+   musb_dbg(musb,
+"Poll devctl on possible host mode 
disconnect");
+   schedule_delayed_work(&musb->irq_work,
+ msecs_to_jiffies(1000));
+
+   return;
+   }
if (!musb->session)
break;
musb_dbg(musb, "Allow PM on possible host mode disconnect");
@@ -1886,9 +1895,9 @@ static void musb_pm_runtime_check_session(struct musb 
*musb)
if (error < 0)
dev_err(musb->controller, "Could not enable: %i\n",
error);
+   musb->quirk_retries = 3;
} else {
musb_dbg(musb, "Allow PM with no session: %02x", devctl);
-   musb->quirk_invalid_vbus = false;
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
}
@@ -1899,7 +1908,7 @@ static void musb_pm_runtime_check_session(struct musb 
*musb)
 /* Only used to provide driver mode change events */
 static void musb_irq_work(struct work_struct *data)
 {
-   struct musb *musb = container_of(data, struct musb, irq_work);
+   struct musb *musb = container_of(data, struct musb, irq_work.work);
 
musb_pm_runtime_check_session(musb);
 
@@ -2288,7 +2297,7 @@ musb_init_controller(struct device *dev, int nIrq, void 
__iomem *ctrl)
musb_generic_disable(musb);
 
/* Init IRQ workqueue before request_irq */
-   INIT_WORK(&musb->irq_work, musb_irq_work);
+   INIT_DELAYED_WORK(&musb->irq_work, musb_irq_work);
INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
 
@@ -2385,7 +2394,7 @@ musb_init_controller(struct device *dev, int nIrq, void 
__iomem *ctrl)
musb_host_cleanup(musb);
 
 fail3:
-   cancel_work_sync(&musb->irq_work);
+   cancel_delayed_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
@@ -2452,7 +2461,7 @@ static int musb_remove(struct platform_device *pdev)
 */
musb_exit_debugfs(musb);
 
-   cancel_work_sync(&musb->irq_work);
+   cancel_delayed_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
pm_runtime_get_sync(musb->controller);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -310,7 +310,7 @@