diff options
Diffstat (limited to 'drivers/usb/musb/da8xx.c')
| -rw-r--r-- | drivers/usb/musb/da8xx.c | 249 |
1 files changed, 185 insertions, 64 deletions
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 84427bebbf6..058775e647a 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -26,12 +26,16 @@ * */ -#include <linux/init.h> +#include <linux/module.h> #include <linux/clk.h> +#include <linux/err.h> #include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/usb/usb_phy_generic.h> #include <mach/da8xx.h> -#include <mach/usb.h> +#include <linux/platform_data/usb-davinci.h> #include "musb_core.h" @@ -78,6 +82,13 @@ #define CFGCHIP2 IO_ADDRESS(DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP2_REG) +struct da8xx_glue { + struct device *dev; + struct platform_device *musb; + struct platform_device *phy; + struct clk *clk; +}; + /* * REVISIT (PM): we should be able to keep the PHY in low power mode most * of the time (24 MHz oscillator and PLL off, etc.) by setting POWER.D0 @@ -131,9 +142,9 @@ static inline void phy_off(void) */ /** - * musb_platform_enable - enable interrupts + * da8xx_musb_enable - enable interrupts */ -void musb_platform_enable(struct musb *musb) +static void da8xx_musb_enable(struct musb *musb) { void __iomem *reg_base = musb->ctrl_base; u32 mask; @@ -145,15 +156,14 @@ void musb_platform_enable(struct musb *musb) musb_writel(reg_base, DA8XX_USB_INTR_MASK_SET_REG, mask); /* Force the DRVVBUS IRQ so we can start polling for ID change. */ - if (is_otg_enabled(musb)) - musb_writel(reg_base, DA8XX_USB_INTR_SRC_SET_REG, - DA8XX_INTR_DRVVBUS << DA8XX_INTR_USB_SHIFT); + musb_writel(reg_base, DA8XX_USB_INTR_SRC_SET_REG, + DA8XX_INTR_DRVVBUS << DA8XX_INTR_USB_SHIFT); } /** - * musb_platform_disable - disable HDRC and flush interrupts + * da8xx_musb_disable - disable HDRC and flush interrupts */ -void musb_platform_disable(struct musb *musb) +static void da8xx_musb_disable(struct musb *musb) { void __iomem *reg_base = musb->ctrl_base; @@ -164,13 +174,9 @@ void musb_platform_disable(struct musb *musb) musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0); } -#ifdef CONFIG_USB_MUSB_HDRC_HCD -#define portstate(stmt) stmt -#else -#define portstate(stmt) -#endif +#define portstate(stmt) stmt -static void da8xx_set_vbus(struct musb *musb, int is_on) +static void da8xx_musb_set_vbus(struct musb *musb, int is_on) { WARN_ON(is_on && is_peripheral_active(musb)); } @@ -191,7 +197,8 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "Poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); + dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, + usb_otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -224,9 +231,6 @@ static void otg_timer(unsigned long _musb) MUSB_INTR_VBUSERROR << DA8XX_INTR_USB_SHIFT); break; case OTG_STATE_B_IDLE: - if (!is_peripheral_enabled(musb)) - break; - /* * There's no ID-changed IRQ, so we have no good way to tell * when to switch to the A-Default state machine (by setting @@ -252,40 +256,40 @@ static void otg_timer(unsigned long _musb) spin_unlock_irqrestore(&musb->lock, flags); } -void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) { static unsigned long last_timer; - if (!is_otg_enabled(musb)) - return; - if (timeout == 0) timeout = jiffies + msecs_to_jiffies(3); /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { - DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + dev_dbg(musb->controller, "%s active, deleting timer\n", + usb_otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; return; } if (time_after(last_timer, timeout) && timer_pending(&otg_workaround)) { - DBG(4, "Longer idle timer already pending, ignoring...\n"); + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); return; } last_timer = timeout; - DBG(4, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb), jiffies_to_msecs(timeout - jiffies)); + dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", + usb_otg_state_string(musb->xceiv->state), + jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); } -static irqreturn_t da8xx_interrupt(int irq, void *hci) +static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) { struct musb *musb = hci; void __iomem *reg_base = musb->ctrl_base; + struct usb_otg *otg = musb->xceiv->otg; unsigned long flags; irqreturn_t ret = IRQ_NONE; u32 status; @@ -303,7 +307,7 @@ static irqreturn_t da8xx_interrupt(int irq, void *hci) goto eoi; musb_writel(reg_base, DA8XX_USB_INTR_SRC_CLEAR_REG, status); - DBG(4, "USB IRQ %08x\n", status); + dev_dbg(musb->controller, "USB IRQ %08x\n", status); musb->int_rx = (status & DA8XX_INTR_RX_MASK) >> DA8XX_INTR_RX_SHIFT; musb->int_tx = (status & DA8XX_INTR_TX_MASK) >> DA8XX_INTR_TX_SHIFT; @@ -323,8 +327,7 @@ static irqreturn_t da8xx_interrupt(int irq, void *hci) u8 devctl = musb_readb(mregs, MUSB_DEVCTL); int err; - err = is_host_enabled(musb) && (musb->int_usb & - MUSB_INTR_VBUSERROR); + err = musb->int_usb & MUSB_INTR_VBUSERROR; if (err) { /* * The Mentor core doesn't debounce VBUS as needed @@ -341,23 +344,23 @@ static irqreturn_t da8xx_interrupt(int irq, void *hci) musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); WARNING("VBUS error workaround (delay coming)\n"); - } else if (is_host_enabled(musb) && drvvbus) { + } else if (drvvbus) { MUSB_HST_MODE(musb); - musb->xceiv->default_a = 1; + otg->default_a = 1; musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; portstate(musb->port1_status |= USB_PORT_STAT_POWER); del_timer(&otg_workaround); } else { musb->is_active = 0; MUSB_DEV_MODE(musb); - musb->xceiv->default_a = 0; + otg->default_a = 0; musb->xceiv->state = OTG_STATE_B_IDLE; portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); } - DBG(2, "VBUS %s (%s)%s, devctl %02x\n", + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb), + usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; @@ -372,7 +375,7 @@ static irqreturn_t da8xx_interrupt(int irq, void *hci) musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0); /* Poll for ID change */ - if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE) + if (musb->xceiv->state == OTG_STATE_B_IDLE) mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); spin_unlock_irqrestore(&musb->lock, flags); @@ -380,58 +383,49 @@ static irqreturn_t da8xx_interrupt(int irq, void *hci) return ret; } -int musb_platform_set_mode(struct musb *musb, u8 musb_mode) +static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode) { u32 cfgchip2 = __raw_readl(CFGCHIP2); cfgchip2 &= ~CFGCHIP2_OTGMODE; switch (musb_mode) { -#ifdef CONFIG_USB_MUSB_HDRC_HCD case MUSB_HOST: /* Force VBUS valid, ID = 0 */ cfgchip2 |= CFGCHIP2_FORCE_HOST; break; -#endif -#ifdef CONFIG_USB_GADGET_MUSB_HDRC case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */ cfgchip2 |= CFGCHIP2_FORCE_DEVICE; break; -#endif -#ifdef CONFIG_USB_MUSB_OTG case MUSB_OTG: /* Don't override the VBUS/ID comparators */ cfgchip2 |= CFGCHIP2_NO_OVERRIDE; break; -#endif default: - DBG(2, "Trying to set unsupported mode %u\n", musb_mode); + dev_dbg(musb->controller, "Trying to set unsupported mode %u\n", musb_mode); } __raw_writel(cfgchip2, CFGCHIP2); return 0; } -int __init musb_platform_init(struct musb *musb, void *board_data) +static int da8xx_musb_init(struct musb *musb) { void __iomem *reg_base = musb->ctrl_base; u32 rev; + int ret = -ENODEV; musb->mregs += DA8XX_MENTOR_CORE_OFFSET; - clk_enable(musb->clock); - /* Returns zero if e.g. not clocked */ rev = musb_readl(reg_base, DA8XX_USB_REVISION_REG); if (!rev) goto fail; - usb_nop_xceiv_register(); - musb->xceiv = otg_get_transceiver(); - if (!musb->xceiv) + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(musb->xceiv)) { + ret = -EPROBE_DEFER; goto fail; + } - if (is_host_enabled(musb)) - setup_timer(&otg_workaround, otg_timer, (unsigned long)musb); - - musb->board_set_vbus = da8xx_set_vbus; + setup_timer(&otg_workaround, otg_timer, (unsigned long)musb); /* Reset the controller */ musb_writel(reg_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK); @@ -446,24 +440,151 @@ int __init musb_platform_init(struct musb *musb, void *board_data) rev, __raw_readl(CFGCHIP2), musb_readb(reg_base, DA8XX_USB_CTRL_REG)); - musb->isr = da8xx_interrupt; + musb->isr = da8xx_musb_interrupt; return 0; fail: - clk_disable(musb->clock); - return -ENODEV; + return ret; } -int musb_platform_exit(struct musb *musb) +static int da8xx_musb_exit(struct musb *musb) { - if (is_host_enabled(musb)) - del_timer_sync(&otg_workaround); + del_timer_sync(&otg_workaround); phy_off(); - otg_put_transceiver(musb->xceiv); - usb_nop_xceiv_unregister(); + usb_put_phy(musb->xceiv); - clk_disable(musb->clock); + return 0; +} + +static const struct musb_platform_ops da8xx_ops = { + .init = da8xx_musb_init, + .exit = da8xx_musb_exit, + + .enable = da8xx_musb_enable, + .disable = da8xx_musb_disable, + + .set_mode = da8xx_musb_set_mode, + .try_idle = da8xx_musb_try_idle, + + .set_vbus = da8xx_musb_set_vbus, +}; + +static const struct platform_device_info da8xx_dev_info = { + .name = "musb-hdrc", + .id = PLATFORM_DEVID_AUTO, + .dma_mask = DMA_BIT_MASK(32), +}; + +static int da8xx_probe(struct platform_device *pdev) +{ + struct resource musb_resources[2]; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct platform_device *musb; + struct da8xx_glue *glue; + struct platform_device_info pinfo; + struct clk *clk; + + int ret = -ENOMEM; + + glue = kzalloc(sizeof(*glue), GFP_KERNEL); + if (!glue) { + dev_err(&pdev->dev, "failed to allocate glue context\n"); + goto err0; + } + + clk = clk_get(&pdev->dev, "usb20"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(clk); + goto err3; + } + + ret = clk_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + goto err4; + } + + glue->dev = &pdev->dev; + glue->clk = clk; + + pdata->platform_ops = &da8xx_ops; + + glue->phy = usb_phy_generic_register(); + if (IS_ERR(glue->phy)) { + ret = PTR_ERR(glue->phy); + goto err5; + } + platform_set_drvdata(pdev, glue); + + memset(musb_resources, 0x00, sizeof(*musb_resources) * + ARRAY_SIZE(musb_resources)); + + musb_resources[0].name = pdev->resource[0].name; + musb_resources[0].start = pdev->resource[0].start; + musb_resources[0].end = pdev->resource[0].end; + musb_resources[0].flags = pdev->resource[0].flags; + + musb_resources[1].name = pdev->resource[1].name; + musb_resources[1].start = pdev->resource[1].start; + musb_resources[1].end = pdev->resource[1].end; + musb_resources[1].flags = pdev->resource[1].flags; + + pinfo = da8xx_dev_info; + pinfo.parent = &pdev->dev; + pinfo.res = musb_resources; + pinfo.num_res = ARRAY_SIZE(musb_resources); + pinfo.data = pdata; + pinfo.size_data = sizeof(*pdata); + + glue->musb = musb = platform_device_register_full(&pinfo); + if (IS_ERR(musb)) { + ret = PTR_ERR(musb); + dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); + goto err6; + } + + return 0; + +err6: + usb_phy_generic_unregister(glue->phy); + +err5: + clk_disable(clk); + +err4: + clk_put(clk); + +err3: + kfree(glue); + +err0: + return ret; +} + +static int da8xx_remove(struct platform_device *pdev) +{ + struct da8xx_glue *glue = platform_get_drvdata(pdev); + + platform_device_unregister(glue->musb); + usb_phy_generic_unregister(glue->phy); + clk_disable(glue->clk); + clk_put(glue->clk); + kfree(glue); return 0; } + +static struct platform_driver da8xx_driver = { + .probe = da8xx_probe, + .remove = da8xx_remove, + .driver = { + .name = "musb-da8xx", + }, +}; + +MODULE_DESCRIPTION("DA8xx/OMAP-L1x MUSB Glue Layer"); +MODULE_AUTHOR("Sergei Shtylyov <sshtylyov@ru.mvista.com>"); +MODULE_LICENSE("GPL v2"); +module_platform_driver(da8xx_driver); |
