diff options
author | Daniel Mack <zonque@gmail.com> | 2014-04-02 13:58:28 +0200 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-04-21 14:07:28 -0500 |
commit | ca88fc2ef0d790a1da37804219102336f7622b97 (patch) | |
tree | 6fba0f43345cda2e2a4463ec6cc3f3a38c8e8125 /drivers/usb/musb | |
parent | 1e42d20c88f2f56c0d81363d7b0f1d3762037f53 (diff) |
usb: musb: add a work_struct to recover from babble errors
Handle BABBLE interrupt error conditions from a work struct handler.
This indirection is necessary as we can't be certain that the phy
functions don't sleep.
Platform layer implementation may pass a babble error down to the core
in order to handle it.
Signed-off-by: Daniel Mack <zonque@gmail.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/musb')
-rw-r--r-- | drivers/usb/musb/musb_core.c | 35 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.h | 1 |
2 files changed, 36 insertions, 0 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 07576907e2c6..61da471b7aed 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -848,6 +848,10 @@ b_host: } } + /* handle babble condition */ + if (int_usb & MUSB_INTR_BABBLE) + schedule_work(&musb->recover_work); + #if 0 /* REVISIT ... this would be for multiplexing periodic endpoints, or * supporting transfer phasing to prevent exceeding ISO bandwidth @@ -1746,6 +1750,34 @@ static void musb_irq_work(struct work_struct *data) } } +/* Recover from babble interrupt conditions */ +static void musb_recover_work(struct work_struct *data) +{ + struct musb *musb = container_of(data, struct musb, recover_work); + int status; + + musb_platform_reset(musb); + + usb_phy_vbus_off(musb->xceiv); + udelay(100); + + usb_phy_vbus_on(musb->xceiv); + udelay(100); + + /* + * When a babble condition occurs, the musb controller removes the + * session bit and the endpoint config is lost. + */ + if (musb->dyn_fifo) + status = ep_config_from_table(musb); + else + status = ep_config_from_hw(musb); + + /* start the session again */ + if (status == 0) + musb_start(musb); +} + /* -------------------------------------------------------------------------- * Init support */ @@ -1913,6 +1945,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* Init IRQ workqueue before request_irq */ INIT_WORK(&musb->irq_work, musb_irq_work); + INIT_WORK(&musb->recover_work, musb_recover_work); INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); @@ -2008,6 +2041,7 @@ fail4: fail3: cancel_work_sync(&musb->irq_work); + cancel_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) @@ -2073,6 +2107,7 @@ static int musb_remove(struct platform_device *pdev) dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); + cancel_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); musb_free(musb); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 5514e4c82932..47e88747e3a7 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -297,6 +297,7 @@ struct musb { irqreturn_t (*isr)(int, void *); struct work_struct irq_work; + struct work_struct recover_work; struct delayed_work deassert_reset_work; struct delayed_work finish_resume_work; u16 hwvers; |