]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/usb/musb/omap2430.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / usb / musb / omap2430.c
index ed618bde1eecfefec05bae9e7195b14b72b5fdab..bc8badd16897f0fc2a1825572d53ba68a12f4e32 100644 (file)
 #include <linux/list.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
 
 #include "musb_core.h"
 #include "omap2430.h"
 
+struct omap2430_glue {
+       struct device           *dev;
+       struct platform_device  *musb;
+       struct clk              *clk;
+};
+#define glue_to_musb(g)                platform_get_drvdata(g->musb)
 
 static struct timer_list musb_idle_timer;
 
@@ -49,12 +57,8 @@ static void musb_do_idle(unsigned long _musb)
 
        spin_lock_irqsave(&musb->lock, flags);
 
-       devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
-
        switch (musb->xceiv->state) {
        case OTG_STATE_A_WAIT_BCON:
-               devctl &= ~MUSB_DEVCTL_SESSION;
-               musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
 
                devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
                if (devctl & MUSB_DEVCTL_BDEVICE) {
@@ -98,7 +102,7 @@ static void musb_do_idle(unsigned long _musb)
 }
 
 
-void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
+static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout)
 {
        unsigned long           default_timeout = jiffies + msecs_to_jiffies(3);
        static unsigned long    last_timer;
@@ -131,15 +135,11 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
        mod_timer(&musb_idle_timer, timeout);
 }
 
-void musb_platform_enable(struct musb *musb)
-{
-}
-void musb_platform_disable(struct musb *musb)
-{
-}
-static void omap_set_vbus(struct musb *musb, int is_on)
+static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
 {
        u8              devctl;
+       unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+       int ret = 1;
        /* HDRC controls CPEN, but beware current surges during device
         * connect.  They can trigger transient overcurrent conditions
         * that must be ignored.
@@ -148,12 +148,35 @@ static void omap_set_vbus(struct musb *musb, int is_on)
        devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
 
        if (is_on) {
-               musb->is_active = 1;
-               musb->xceiv->default_a = 1;
-               musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
-               devctl |= MUSB_DEVCTL_SESSION;
-
-               MUSB_HST_MODE(musb);
+               if (musb->xceiv->state == OTG_STATE_A_IDLE) {
+                       /* start the session */
+                       devctl |= MUSB_DEVCTL_SESSION;
+                       musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+                       /*
+                        * Wait for the musb to set as A device to enable the
+                        * VBUS
+                        */
+                       while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
+
+                               cpu_relax();
+
+                               if (time_after(jiffies, timeout)) {
+                                       dev_err(musb->controller,
+                                       "configured as A device timeout");
+                                       ret = -EINVAL;
+                                       break;
+                               }
+                       }
+
+                       if (ret && musb->xceiv->set_vbus)
+                               otg_set_vbus(musb->xceiv, 1);
+               } else {
+                       musb->is_active = 1;
+                       musb->xceiv->default_a = 1;
+                       musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+                       devctl |= MUSB_DEVCTL_SESSION;
+                       MUSB_HST_MODE(musb);
+               }
        } else {
                musb->is_active = 0;
 
@@ -175,9 +198,7 @@ static void omap_set_vbus(struct musb *musb, int is_on)
                musb_readb(musb->mregs, MUSB_DEVCTL));
 }
 
-static int musb_platform_resume(struct musb *musb);
-
-int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
+static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode)
 {
        u8      devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
 
@@ -187,10 +208,94 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
        return 0;
 }
 
-int __init musb_platform_init(struct musb *musb, void *board_data)
+static inline void omap2430_low_level_exit(struct musb *musb)
 {
        u32 l;
-       struct omap_musb_board_data *data = board_data;
+
+       /* in any role */
+       l = musb_readl(musb->mregs, OTG_FORCESTDBY);
+       l |= ENABLEFORCE;       /* enable MSTANDBY */
+       musb_writel(musb->mregs, OTG_FORCESTDBY, l);
+
+       l = musb_readl(musb->mregs, OTG_SYSCONFIG);
+       l |= ENABLEWAKEUP;      /* enable wakeup */
+       musb_writel(musb->mregs, OTG_SYSCONFIG, l);
+}
+
+static inline void omap2430_low_level_init(struct musb *musb)
+{
+       u32 l;
+
+       l = musb_readl(musb->mregs, OTG_SYSCONFIG);
+       l &= ~ENABLEWAKEUP;     /* disable wakeup */
+       musb_writel(musb->mregs, OTG_SYSCONFIG, l);
+
+       l = musb_readl(musb->mregs, OTG_FORCESTDBY);
+       l &= ~ENABLEFORCE;      /* disable MSTANDBY */
+       musb_writel(musb->mregs, OTG_FORCESTDBY, l);
+}
+
+/* blocking notifier support */
+static int musb_otg_notifications(struct notifier_block *nb,
+               unsigned long event, void *unused)
+{
+       struct musb     *musb = container_of(nb, struct musb, nb);
+       struct device *dev = musb->controller;
+       struct musb_hdrc_platform_data *pdata = dev->platform_data;
+       struct omap_musb_board_data *data = pdata->board_data;
+
+       switch (event) {
+       case USB_EVENT_ID:
+               DBG(4, "ID GND\n");
+
+               if (is_otg_enabled(musb)) {
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+                       if (musb->gadget_driver) {
+                               otg_init(musb->xceiv);
+
+                               if (data->interface_type ==
+                                               MUSB_INTERFACE_UTMI)
+                                       omap2430_musb_set_vbus(musb, 1);
+
+                       }
+#endif
+               } else {
+                       otg_init(musb->xceiv);
+                       if (data->interface_type ==
+                                       MUSB_INTERFACE_UTMI)
+                               omap2430_musb_set_vbus(musb, 1);
+               }
+               break;
+
+       case USB_EVENT_VBUS:
+               DBG(4, "VBUS Connect\n");
+
+               otg_init(musb->xceiv);
+               break;
+
+       case USB_EVENT_NONE:
+               DBG(4, "VBUS Disconnect\n");
+
+               if (data->interface_type == MUSB_INTERFACE_UTMI) {
+                       if (musb->xceiv->set_vbus)
+                               otg_set_vbus(musb->xceiv, 0);
+               }
+               otg_shutdown(musb->xceiv);
+               break;
+       default:
+               DBG(4, "ID float\n");
+               return NOTIFY_DONE;
+       }
+
+       return NOTIFY_OK;
+}
+
+static int omap2430_musb_init(struct musb *musb)
+{
+       u32 l, 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;
 
        /* We require some kind of external transceiver, hooked
         * up through ULPI.  TWL4030-family PMICs include one,
@@ -202,7 +307,7 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
                return -ENODEV;
        }
 
-       musb_platform_resume(musb);
+       omap2430_low_level_init(musb);
 
        l = musb_readl(musb->mregs, OTG_SYSCONFIG);
        l &= ~ENABLEWAKEUP;     /* disable wakeup */
@@ -239,87 +344,215 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
                        musb_readl(musb->mregs, OTG_INTERFSEL),
                        musb_readl(musb->mregs, OTG_SIMENABLE));
 
-       if (is_host_enabled(musb))
-               musb->board_set_vbus = omap_set_vbus;
+       musb->nb.notifier_call = musb_otg_notifications;
+       status = otg_register_notifier(musb->xceiv, &musb->nb);
+
+       if (status)
+               DBG(1, "notification register failed\n");
+
+       /* check whether cable is already connected */
+       if (musb->xceiv->state ==OTG_STATE_B_IDLE)
+               musb_otg_notifications(&musb->nb, 1,
+                                       musb->xceiv->gadget);
 
        setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
 
        return 0;
 }
 
-#ifdef CONFIG_PM
-void musb_platform_save_context(struct musb *musb,
-               struct musb_context_registers *musb_context)
+static int omap2430_musb_exit(struct musb *musb)
 {
-       musb_context->otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG);
-       musb_context->otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY);
-}
+       del_timer_sync(&musb_idle_timer);
 
-void musb_platform_restore_context(struct musb *musb,
-               struct musb_context_registers *musb_context)
-{
-       musb_writel(musb->mregs, OTG_SYSCONFIG, musb_context->otg_sysconfig);
-       musb_writel(musb->mregs, OTG_FORCESTDBY, musb_context->otg_forcestandby);
+       omap2430_low_level_exit(musb);
+       otg_put_transceiver(musb->xceiv);
+
+       return 0;
 }
-#endif
 
-static int musb_platform_suspend(struct musb *musb)
+static const struct musb_platform_ops omap2430_ops = {
+       .init           = omap2430_musb_init,
+       .exit           = omap2430_musb_exit,
+
+       .set_mode       = omap2430_musb_set_mode,
+       .try_idle       = omap2430_musb_try_idle,
+
+       .set_vbus       = omap2430_musb_set_vbus,
+};
+
+static u64 omap2430_dmamask = DMA_BIT_MASK(32);
+
+static int __init omap2430_probe(struct platform_device *pdev)
 {
-       u32 l;
+       struct musb_hdrc_platform_data  *pdata = pdev->dev.platform_data;
+       struct platform_device          *musb;
+       struct omap2430_glue            *glue;
+       struct clk                      *clk;
 
-       if (!musb->clock)
-               return 0;
+       int                             ret = -ENOMEM;
 
-       /* in any role */
-       l = musb_readl(musb->mregs, OTG_FORCESTDBY);
-       l |= ENABLEFORCE;       /* enable MSTANDBY */
-       musb_writel(musb->mregs, OTG_FORCESTDBY, l);
+       glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+       if (!glue) {
+               dev_err(&pdev->dev, "failed to allocate glue context\n");
+               goto err0;
+       }
 
-       l = musb_readl(musb->mregs, OTG_SYSCONFIG);
-       l |= ENABLEWAKEUP;      /* enable wakeup */
-       musb_writel(musb->mregs, OTG_SYSCONFIG, l);
+       musb = platform_device_alloc("musb-hdrc", -1);
+       if (!musb) {
+               dev_err(&pdev->dev, "failed to allocate musb device\n");
+               goto err1;
+       }
 
-       otg_set_suspend(musb->xceiv, 1);
+       clk = clk_get(&pdev->dev, "ick");
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "failed to get clock\n");
+               ret = PTR_ERR(clk);
+               goto err2;
+       }
 
-       if (musb->set_clock)
-               musb->set_clock(musb->clock, 0);
-       else
-               clk_disable(musb->clock);
+       ret = clk_enable(clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable clock\n");
+               goto err3;
+       }
+
+       musb->dev.parent                = &pdev->dev;
+       musb->dev.dma_mask              = &omap2430_dmamask;
+       musb->dev.coherent_dma_mask     = omap2430_dmamask;
+
+       glue->dev                       = &pdev->dev;
+       glue->musb                      = musb;
+       glue->clk                       = clk;
+
+       pdata->platform_ops             = &omap2430_ops;
+
+       platform_set_drvdata(pdev, glue);
+
+       ret = platform_device_add_resources(musb, pdev->resource,
+                       pdev->num_resources);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to add resources\n");
+               goto err4;
+       }
+
+       ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
+       if (ret) {
+               dev_err(&pdev->dev, "failed to add platform_data\n");
+               goto err4;
+       }
+
+       ret = platform_device_add(musb);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register musb device\n");
+               goto err4;
+       }
 
        return 0;
+
+err4:
+       clk_disable(clk);
+
+err3:
+       clk_put(clk);
+
+err2:
+       platform_device_put(musb);
+
+err1:
+       kfree(glue);
+
+err0:
+       return ret;
 }
 
-static int musb_platform_resume(struct musb *musb)
+static int __exit omap2430_remove(struct platform_device *pdev)
 {
-       u32 l;
+       struct omap2430_glue            *glue = platform_get_drvdata(pdev);
 
-       if (!musb->clock)
-               return 0;
+       platform_device_del(glue->musb);
+       platform_device_put(glue->musb);
+       clk_disable(glue->clk);
+       clk_put(glue->clk);
+       kfree(glue);
 
-       otg_set_suspend(musb->xceiv, 0);
+       return 0;
+}
 
-       if (musb->set_clock)
-               musb->set_clock(musb->clock, 1);
-       else
-               clk_enable(musb->clock);
+#ifdef CONFIG_PM
+static void omap2430_save_context(struct musb *musb)
+{
+       musb->context.otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG);
+       musb->context.otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY);
+}
 
-       l = musb_readl(musb->mregs, OTG_SYSCONFIG);
-       l &= ~ENABLEWAKEUP;     /* disable wakeup */
-       musb_writel(musb->mregs, OTG_SYSCONFIG, l);
+static void omap2430_restore_context(struct musb *musb)
+{
+       musb_writel(musb->mregs, OTG_SYSCONFIG, musb->context.otg_sysconfig);
+       musb_writel(musb->mregs, OTG_FORCESTDBY, musb->context.otg_forcestandby);
+}
 
-       l = musb_readl(musb->mregs, OTG_FORCESTDBY);
-       l &= ~ENABLEFORCE;      /* disable MSTANDBY */
-       musb_writel(musb->mregs, OTG_FORCESTDBY, l);
+static int omap2430_suspend(struct device *dev)
+{
+       struct omap2430_glue            *glue = dev_get_drvdata(dev);
+       struct musb                     *musb = glue_to_musb(glue);
+
+       omap2430_low_level_exit(musb);
+       otg_set_suspend(musb->xceiv, 1);
+       omap2430_save_context(musb);
+       clk_disable(glue->clk);
 
        return 0;
 }
 
-
-int musb_platform_exit(struct musb *musb)
+static int omap2430_resume(struct device *dev)
 {
+       struct omap2430_glue            *glue = dev_get_drvdata(dev);
+       struct musb                     *musb = glue_to_musb(glue);
+       int                             ret;
+
+       ret = clk_enable(glue->clk);
+       if (ret) {
+               dev_err(dev, "faled to enable clock\n");
+               return ret;
+       }
 
-       musb_platform_suspend(musb);
+       omap2430_low_level_init(musb);
+       omap2430_restore_context(musb);
+       otg_set_suspend(musb->xceiv, 0);
 
-       otg_put_transceiver(musb->xceiv);
        return 0;
 }
+
+static struct dev_pm_ops omap2430_pm_ops = {
+       .suspend        = omap2430_suspend,
+       .resume         = omap2430_resume,
+};
+
+#define DEV_PM_OPS     (&omap2430_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif
+
+static struct platform_driver omap2430_driver = {
+       .remove         = __exit_p(omap2430_remove),
+       .driver         = {
+               .name   = "musb-omap2430",
+               .pm     = DEV_PM_OPS,
+       },
+};
+
+MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer");
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
+MODULE_LICENSE("GPL v2");
+
+static int __init omap2430_init(void)
+{
+       return platform_driver_probe(&omap2430_driver, omap2430_probe);
+}
+subsys_initcall(omap2430_init);
+
+static void __exit omap2430_exit(void)
+{
+       platform_driver_unregister(&omap2430_driver);
+}
+module_exit(omap2430_exit);