diff options
Diffstat (limited to 'drivers/usb/musb/omap2430.c')
-rw-r--r-- | drivers/usb/musb/omap2430.c | 378 |
1 files changed, 305 insertions, 73 deletions
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index ed618bde1ee..a3f12333fc4 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -31,10 +31,18 @@ #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,214 @@ 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); -} -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); |