usb: musb: add a work_struct to recover from babble errors
authorDaniel Mack <zonque@gmail.com>
Wed, 2 Apr 2014 11:58:28 +0000 (13:58 +0200)
committerFelipe Balbi <balbi@ti.com>
Mon, 21 Apr 2014 19:07:28 +0000 (14:07 -0500)
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>
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h

index 0757690..61da471 100644 (file)
@@ -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);
index 5514e4c..47e8874 100644 (file)
@@ -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;