usb: musb: omap2430: fix occasional musb breakage on boot
[pandora-kernel.git] / drivers / usb / musb / omap2430.c
index ba85f27..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);
                        }
@@ -282,7 +288,8 @@ static int musb_otg_notifications(struct notifier_block *nb,
 
 static int omap2430_musb_init(struct musb *musb)
 {
-       u32 l, status = 0;
+       u32 l;
+       int status = 0;
        struct device *dev = musb->controller;
        struct musb_hdrc_platform_data *plat = dev->platform_data;
        struct omap_musb_board_data *data = plat->board_data;
@@ -299,7 +306,7 @@ static int omap2430_musb_init(struct musb *musb)
 
        status = pm_runtime_get_sync(dev);
        if (status < 0) {
-               dev_err(dev, "pm_runtime_get_sync FAILED");
+               dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status);
                goto err1;
        }
 
@@ -451,14 +458,14 @@ static int __init omap2430_probe(struct platform_device *pdev)
                goto err2;
        }
 
+       pm_runtime_enable(&pdev->dev);
+
        ret = platform_device_add(musb);
        if (ret) {
                dev_err(&pdev->dev, "failed to register musb device\n");
                goto err2;
        }
 
-       pm_runtime_enable(&pdev->dev);
-
        return 0;
 
 err2:
@@ -491,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);
 
@@ -503,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;