Skip to content

Commit 2bff391

Browse files
tmlindgregkh
authored andcommitted
usb: musb: Fix PM for hub disconnect
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: 467d5c9 ("usb: musb: Implement session bit based runtime PM for musb-core") Fixes: 65b3f50 ("usb: musb: Add PM runtime support for MUSB DSPS Tested-by: Ladislav Michl <ladis@linux-mips.org> Tested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Bin Liu <b-liu@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent ea2f35c commit 2bff391

4 files changed

Lines changed: 27 additions & 18 deletions

File tree

drivers/usb/musb/musb_core.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
986986
}
987987
#endif
988988

989-
schedule_work(&musb->irq_work);
989+
schedule_delayed_work(&musb->irq_work, 0);
990990

991991
return handled;
992992
}
@@ -1855,14 +1855,23 @@ static void musb_pm_runtime_check_session(struct musb *musb)
18551855
MUSB_DEVCTL_HR;
18561856
switch (devctl & ~s) {
18571857
case MUSB_QUIRK_B_INVALID_VBUS_91:
1858-
if (!musb->session && !musb->quirk_invalid_vbus) {
1859-
musb->quirk_invalid_vbus = true;
1858+
if (musb->quirk_retries--) {
18601859
musb_dbg(musb,
1861-
"First invalid vbus, assume no session");
1860+
"Poll devctl on invalid vbus, assume no session");
1861+
schedule_delayed_work(&musb->irq_work,
1862+
msecs_to_jiffies(1000));
1863+
18621864
return;
18631865
}
1864-
break;
18651866
case MUSB_QUIRK_A_DISCONNECT_19:
1867+
if (musb->quirk_retries--) {
1868+
musb_dbg(musb,
1869+
"Poll devctl on possible host mode disconnect");
1870+
schedule_delayed_work(&musb->irq_work,
1871+
msecs_to_jiffies(1000));
1872+
1873+
return;
1874+
}
18661875
if (!musb->session)
18671876
break;
18681877
musb_dbg(musb, "Allow PM on possible host mode disconnect");
@@ -1886,9 +1895,9 @@ static void musb_pm_runtime_check_session(struct musb *musb)
18861895
if (error < 0)
18871896
dev_err(musb->controller, "Could not enable: %i\n",
18881897
error);
1898+
musb->quirk_retries = 3;
18891899
} else {
18901900
musb_dbg(musb, "Allow PM with no session: %02x", devctl);
1891-
musb->quirk_invalid_vbus = false;
18921901
pm_runtime_mark_last_busy(musb->controller);
18931902
pm_runtime_put_autosuspend(musb->controller);
18941903
}
@@ -1899,7 +1908,7 @@ static void musb_pm_runtime_check_session(struct musb *musb)
18991908
/* Only used to provide driver mode change events */
19001909
static void musb_irq_work(struct work_struct *data)
19011910
{
1902-
struct musb *musb = container_of(data, struct musb, irq_work);
1911+
struct musb *musb = container_of(data, struct musb, irq_work.work);
19031912

19041913
musb_pm_runtime_check_session(musb);
19051914

@@ -2288,7 +2297,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
22882297
musb_generic_disable(musb);
22892298

22902299
/* Init IRQ workqueue before request_irq */
2291-
INIT_WORK(&musb->irq_work, musb_irq_work);
2300+
INIT_DELAYED_WORK(&musb->irq_work, musb_irq_work);
22922301
INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
22932302
INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
22942303

@@ -2385,7 +2394,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
23852394
musb_host_cleanup(musb);
23862395

23872396
fail3:
2388-
cancel_work_sync(&musb->irq_work);
2397+
cancel_delayed_work_sync(&musb->irq_work);
23892398
cancel_delayed_work_sync(&musb->finish_resume_work);
23902399
cancel_delayed_work_sync(&musb->deassert_reset_work);
23912400
if (musb->dma_controller)
@@ -2452,7 +2461,7 @@ static int musb_remove(struct platform_device *pdev)
24522461
*/
24532462
musb_exit_debugfs(musb);
24542463

2455-
cancel_work_sync(&musb->irq_work);
2464+
cancel_delayed_work_sync(&musb->irq_work);
24562465
cancel_delayed_work_sync(&musb->finish_resume_work);
24572466
cancel_delayed_work_sync(&musb->deassert_reset_work);
24582467
pm_runtime_get_sync(musb->controller);

drivers/usb/musb/musb_core.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ struct musb {
310310
struct musb_context_registers context;
311311

312312
irqreturn_t (*isr)(int, void *);
313-
struct work_struct irq_work;
313+
struct delayed_work irq_work;
314314
struct delayed_work deassert_reset_work;
315315
struct delayed_work finish_resume_work;
316316
struct delayed_work gadget_work;
@@ -381,7 +381,7 @@ struct musb {
381381

382382
int port_mode; /* MUSB_PORT_MODE_* */
383383
bool session;
384-
bool quirk_invalid_vbus;
384+
unsigned long quirk_retries;
385385
bool is_host;
386386

387387
int a_wait_bcon; /* VBUS timeout in msecs */

drivers/usb/musb/musb_gadget.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
11141114
musb_ep->dma ? "dma, " : "",
11151115
musb_ep->packet_sz);
11161116

1117-
schedule_work(&musb->irq_work);
1117+
schedule_delayed_work(&musb->irq_work, 0);
11181118

11191119
fail:
11201120
spin_unlock_irqrestore(&musb->lock, flags);
@@ -1158,7 +1158,7 @@ static int musb_gadget_disable(struct usb_ep *ep)
11581158
musb_ep->desc = NULL;
11591159
musb_ep->end_point.desc = NULL;
11601160

1161-
schedule_work(&musb->irq_work);
1161+
schedule_delayed_work(&musb->irq_work, 0);
11621162

11631163
spin_unlock_irqrestore(&(musb->lock), flags);
11641164

@@ -1994,7 +1994,7 @@ static int musb_gadget_stop(struct usb_gadget *g)
19941994
*/
19951995

19961996
/* Force check of devctl register for PM runtime */
1997-
schedule_work(&musb->irq_work);
1997+
schedule_delayed_work(&musb->irq_work, 0);
19981998

19991999
pm_runtime_mark_last_busy(musb->controller);
20002000
pm_runtime_put_autosuspend(musb->controller);

drivers/usb/musb/tusb6010.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
724724
dev_dbg(musb->controller, "vbus change, %s, otg %03x\n",
725725
usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
726726
idle_timeout = jiffies + (1 * HZ);
727-
schedule_work(&musb->irq_work);
727+
schedule_delayed_work(&musb->irq_work, 0);
728728

729729
} else /* A-dev state machine */ {
730730
dev_dbg(musb->controller, "vbus change, %s, otg %03x\n",
@@ -814,7 +814,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
814814
break;
815815
}
816816
}
817-
schedule_work(&musb->irq_work);
817+
schedule_delayed_work(&musb->irq_work, 0);
818818

819819
return idle_timeout;
820820
}
@@ -864,7 +864,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci)
864864
musb_writel(tbase, TUSB_PRCM_WAKEUP_CLEAR, reg);
865865
if (reg & ~TUSB_PRCM_WNORCS) {
866866
musb->is_active = 1;
867-
schedule_work(&musb->irq_work);
867+
schedule_delayed_work(&musb->irq_work, 0);
868868
}
869869
dev_dbg(musb->controller, "wake %sactive %02x\n",
870870
musb->is_active ? "" : "in", reg);

0 commit comments

Comments
 (0)