musb VBUS workaround
authorGrazvydas Ignotas <notasas@gmail.com>
Wed, 6 Oct 2010 22:12:09 +0000 (01:12 +0300)
committerGrazvydas Ignotas <notasas@gmail.com>
Sat, 26 Feb 2011 16:27:43 +0000 (18:27 +0200)
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h

index 99beebc..0a15fa0 100644 (file)
@@ -707,6 +707,15 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                        musb->is_active = 0;
                        break;
                }
+
+               switch (musb->xceiv->state) {
+               case OTG_STATE_B_IDLE:
+               case OTG_STATE_B_PERIPHERAL:
+                       cancel_delayed_work(&musb->vbus_workaround_work);
+                       schedule_delayed_work(&musb->vbus_workaround_work, HZ / 2);
+               default:
+                       break;
+               }
        }
 
 #ifdef CONFIG_USB_MUSB_HDRC_HCD
@@ -1058,6 +1067,8 @@ static void musb_shutdown(struct platform_device *pdev)
        musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
        musb_platform_exit(musb);
 
+       cancel_delayed_work(&musb->vbus_workaround_work);
+
        /* FIXME power down */
 }
 
@@ -1855,6 +1866,41 @@ static void musb_irq_work(struct work_struct *data)
        }
 }
 
+#include <linux/usb/ulpi.h>
+
+static void musb_vbus_workaround_work(struct work_struct *work)
+{
+       struct musb *musb = container_of(work, struct musb, vbus_workaround_work.work);
+       u8 devctl;
+       int ret;
+
+       if (musb_ulpi_access.write == NULL)
+               return;
+
+       devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+       /*
+        * I don't really know why but VBUS sometimes gets stuck and
+        * causes session to never end. It would look like some pullup
+        * is enabled when it shouldn't be on certain PHY states.
+        * Turning on pulldowns magically drains VBUS to zero and allows
+        * session to end, so let's do that here.
+        *
+        * XXX: probably better check VBUS on TWL?
+        * beagle sometimes has session bit set but no VBUS on twl?
+        */
+       if ((musb->xceiv->state == OTG_STATE_B_PERIPHERAL ||
+            musb->xceiv->state == OTG_STATE_B_IDLE) &&
+           (devctl & MUSB_DEVCTL_VBUS) != (3 << MUSB_DEVCTL_VBUS_SHIFT) &&
+           (devctl & MUSB_DEVCTL_VBUS) != (0 << MUSB_DEVCTL_VBUS_SHIFT)) {
+               DBG(1, "VBUS workaround..\n");
+               ret = musb_ulpi_access.write(musb->xceiv, ULPI_SET(ULPI_OTG_CTRL),
+                       ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN);
+               //if (ret)
+               //      dev_err(musb->controller, "VBUS workaround error\n");
+       }
+}
+
 /* --------------------------------------------------------------------------
  * Init support
  */
@@ -2077,6 +2123,8 @@ bad_config:
        /* Init IRQ workqueue before request_irq */
        INIT_WORK(&musb->irq_work, musb_irq_work);
 
+       INIT_DELAYED_WORK(&musb->vbus_workaround_work, musb_vbus_workaround_work);
+
        /* attach to the IRQ */
        if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) {
                dev_err(dev, "request_irq %d failed!\n", nIrq);
index febaabc..420e2dc 100644 (file)
@@ -456,6 +456,7 @@ struct musb {
 #ifdef MUSB_CONFIG_PROC_FS
        struct proc_dir_entry *proc_entry;
 #endif
+       struct delayed_work     vbus_workaround_work;
 };
 
 #ifdef CONFIG_PM