summaryrefslogtreecommitdiffstats
path: root/drivers/usb/musb
diff options
context:
space:
mode:
authorDaniel Mack2014-04-02 13:58:28 +0200
committerFelipe Balbi2014-04-21 21:07:28 +0200
commitca88fc2ef0d790a1da37804219102336f7622b97 (patch)
tree6fba0f43345cda2e2a4463ec6cc3f3a38c8e8125 /drivers/usb/musb
parentusb: musb: add reset hook to platform ops (diff)
downloadkernel-qcow2-linux-ca88fc2ef0d790a1da37804219102336f7622b97.tar.gz
kernel-qcow2-linux-ca88fc2ef0d790a1da37804219102336f7622b97.tar.xz
kernel-qcow2-linux-ca88fc2ef0d790a1da37804219102336f7622b97.zip
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.c35
-rw-r--r--drivers/usb/musb/musb_core.h1
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;