usb: musb: omap2430: fix occasional musb breakage on boot
[pandora-kernel.git] / drivers / usb / musb / omap2430.c
index a8f0c09..58ca5e4 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <linux/err.h>
+#include <linux/delay.h>
 
 #include "musb_core.h"
 #include "omap2430.h"
@@ -145,6 +146,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
 
        if (is_on) {
                if (musb->xceiv->state == OTG_STATE_A_IDLE) {
+                       int loops = 100;
                        /* start the session */
                        devctl |= MUSB_DEVCTL_SESSION;
                        musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
@@ -154,9 +156,11 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
                         */
                        while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
 
+                               mdelay(5);
                                cpu_relax();
 
-                               if (time_after(jiffies, timeout)) {
+                               if (time_after(jiffies, timeout)
+                                   || loops-- <= 0) {
                                        dev_err(musb->controller,
                                        "configured as A device timeout");
                                        ret = -EINVAL;
@@ -262,6 +266,8 @@ static int musb_otg_notifications(struct notifier_block *nb,
 
                if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
                        if (musb->gadget_driver) {
+                               omap2430_musb_set_vbus(musb, 0);
+
                                pm_runtime_mark_last_busy(musb->controller);
                                pm_runtime_put_autosuspend(musb->controller);
                        }
@@ -492,6 +498,9 @@ static int omap2430_runtime_suspend(struct device *dev)
        struct omap2430_glue            *glue = dev_get_drvdata(dev);
        struct musb                     *musb = glue_to_musb(glue);
 
+       musb->context.otg_interfsel = musb_readl(musb->mregs,
+                                               OTG_INTERFSEL);
+
        omap2430_low_level_exit(musb);
        otg_set_suspend(musb->xceiv, 1);
 
@@ -504,6 +513,10 @@ static int omap2430_runtime_resume(struct device *dev)
        struct musb                     *musb = glue_to_musb(glue);
 
        omap2430_low_level_init(musb);
+       if (musb->context.otg_interfsel != 0)
+               musb_writel(musb->mregs, OTG_INTERFSEL,
+                                       musb->context.otg_interfsel);
+
        otg_set_suspend(musb->xceiv, 0);
 
        return 0;