diff options
Diffstat (limited to 'drivers/usb/musb/musb_dsps.c')
-rw-r--r-- | drivers/usb/musb/musb_dsps.c | 204 |
1 files changed, 175 insertions, 29 deletions
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 9f125e179acd..7c047c4a2565 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -118,9 +118,11 @@ struct dsps_glue { struct device *dev; struct platform_device *musb; /* child musb pdev */ const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ + int vbus_irq; /* optional vbus irq */ struct timer_list timer; /* otg_workaround timer */ unsigned long last_timer; /* last timer data for each instance */ bool sw_babble_enabled; + void __iomem *usbss_base; struct dsps_context context; struct debugfs_regset32 regset; @@ -145,6 +147,36 @@ static const struct debugfs_reg32 dsps_musb_regs[] = { { "mode", 0xe8 }, }; +static void dsps_mod_timer(struct dsps_glue *glue, int wait_ms) +{ + int wait; + + if (wait_ms < 0) + wait = msecs_to_jiffies(glue->wrp->poll_timeout); + else + wait = msecs_to_jiffies(wait_ms); + + mod_timer(&glue->timer, jiffies + wait); +} + +/* + * If no vbus irq from the PMIC is configured, we need to poll VBUS status. + */ +static void dsps_mod_timer_optional(struct dsps_glue *glue) +{ + if (glue->vbus_irq) + return; + + dsps_mod_timer(glue, -1); +} + +/* USBSS / USB AM335x */ +#define USBSS_IRQ_STATUS 0x28 +#define USBSS_IRQ_ENABLER 0x2c +#define USBSS_IRQ_CLEARR 0x30 + +#define USBSS_IRQ_PD_COMP (1 << 2) + /** * dsps_musb_enable - enable interrupts */ @@ -167,8 +199,7 @@ static void dsps_musb_enable(struct musb *musb) /* start polling for ID change in dual-role idle mode */ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer(glue, -1); } /** @@ -186,7 +217,6 @@ static void dsps_musb_disable(struct musb *musb) musb_writel(reg_base, wrp->epintr_clear, wrp->txep_bitmap | wrp->rxep_bitmap); del_timer_sync(&glue->timer); - musb_writeb(musb->mregs, MUSB_DEVCTL, 0); } /* Caller must take musb->lock */ @@ -199,6 +229,9 @@ static int dsps_check_status(struct musb *musb, void *unused) u8 devctl; int skip_session = 0; + if (glue->vbus_irq) + del_timer(&glue->timer); + /* * We poll because DSPS IP's won't expose several OTG-critical * status change events (from the transceiver) otherwise. @@ -209,8 +242,7 @@ static int dsps_check_status(struct musb *musb, void *unused) switch (musb->xceiv->otg->state) { case OTG_STATE_A_WAIT_VRISE: - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer_optional(glue); break; case OTG_STATE_A_WAIT_BCON: musb_writeb(musb->mregs, MUSB_DEVCTL, 0); @@ -219,17 +251,19 @@ static int dsps_check_status(struct musb *musb, void *unused) case OTG_STATE_A_IDLE: case OTG_STATE_B_IDLE: - if (devctl & MUSB_DEVCTL_BDEVICE) { - musb->xceiv->otg->state = OTG_STATE_B_IDLE; - MUSB_DEV_MODE(musb); - } else { - musb->xceiv->otg->state = OTG_STATE_A_IDLE; - MUSB_HST_MODE(musb); + if (!glue->vbus_irq) { + if (devctl & MUSB_DEVCTL_BDEVICE) { + musb->xceiv->otg->state = OTG_STATE_B_IDLE; + MUSB_DEV_MODE(musb); + } else { + musb->xceiv->otg->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + } + if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) + musb_writeb(mregs, MUSB_DEVCTL, + MUSB_DEVCTL_SESSION); } - if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) - musb_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer_optional(glue); break; case OTG_STATE_A_WAIT_VFALL: musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; @@ -267,7 +301,7 @@ static void otg_timer(unsigned long _musb) pm_runtime_put_autosuspend(dev); } -void dsps_musb_clear_ep_rxintr(struct musb *musb, int epnum) +static void dsps_musb_clear_ep_rxintr(struct musb *musb, int epnum) { u32 epintr; struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent); @@ -332,15 +366,13 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) */ musb->int_usb &= ~MUSB_INTR_VBUSERROR; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL; - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer_optional(glue); WARNING("VBUS error workaround (delay coming)\n"); } else if (drvvbus) { MUSB_HST_MODE(musb); musb->xceiv->otg->default_a = 1; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer_optional(glue); } else { musb->is_active = 0; MUSB_DEV_MODE(musb); @@ -364,8 +396,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) switch (musb->xceiv->otg->state) { case OTG_STATE_B_IDLE: case OTG_STATE_A_WAIT_BCON: - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer_optional(glue); break; default: break; @@ -469,8 +500,7 @@ static int dsps_musb_init(struct musb *musb) musb_writeb(musb->mregs, MUSB_BABBLE_CTL, val); } - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(glue->wrp->poll_timeout)); + dsps_mod_timer(glue, -1); return dsps_musb_dbg_init(musb, glue); } @@ -619,14 +649,76 @@ static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } } +#ifdef CONFIG_USB_TI_CPPI41_DMA +static void dsps_dma_controller_callback(struct dma_controller *c) +{ + struct musb *musb = c->musb; + struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent); + void __iomem *usbss_base = glue->usbss_base; + u32 status; + + status = musb_readl(usbss_base, USBSS_IRQ_STATUS); + if (status & USBSS_IRQ_PD_COMP) + musb_writel(usbss_base, USBSS_IRQ_STATUS, USBSS_IRQ_PD_COMP); +} + +static struct dma_controller * +dsps_dma_controller_create(struct musb *musb, void __iomem *base) +{ + struct dma_controller *controller; + struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent); + void __iomem *usbss_base = glue->usbss_base; + + controller = cppi41_dma_controller_create(musb, base); + if (IS_ERR_OR_NULL(controller)) + return controller; + + musb_writel(usbss_base, USBSS_IRQ_ENABLER, USBSS_IRQ_PD_COMP); + controller->dma_callback = dsps_dma_controller_callback; + + return controller; +} + +static void dsps_dma_controller_destroy(struct dma_controller *c) +{ + struct musb *musb = c->musb; + struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent); + void __iomem *usbss_base = glue->usbss_base; + + musb_writel(usbss_base, USBSS_IRQ_CLEARR, USBSS_IRQ_PD_COMP); + cppi41_dma_controller_destroy(c); +} + +#ifdef CONFIG_PM_SLEEP +static void dsps_dma_controller_suspend(struct dsps_glue *glue) +{ + void __iomem *usbss_base = glue->usbss_base; + + musb_writel(usbss_base, USBSS_IRQ_CLEARR, USBSS_IRQ_PD_COMP); +} + +static void dsps_dma_controller_resume(struct dsps_glue *glue) +{ + void __iomem *usbss_base = glue->usbss_base; + + musb_writel(usbss_base, USBSS_IRQ_ENABLER, USBSS_IRQ_PD_COMP); +} +#endif +#else /* CONFIG_USB_TI_CPPI41_DMA */ +#ifdef CONFIG_PM_SLEEP +static void dsps_dma_controller_suspend(struct dsps_glue *glue) {} +static void dsps_dma_controller_resume(struct dsps_glue *glue) {} +#endif +#endif /* CONFIG_USB_TI_CPPI41_DMA */ + static struct musb_platform_ops dsps_ops = { .quirks = MUSB_DMA_CPPI41 | MUSB_INDEXED_EP, .init = dsps_musb_init, .exit = dsps_musb_exit, #ifdef CONFIG_USB_TI_CPPI41_DMA - .dma_init = cppi41_dma_controller_create, - .dma_exit = cppi41_dma_controller_destroy, + .dma_init = dsps_dma_controller_create, + .dma_exit = dsps_dma_controller_destroy, #endif .enable = dsps_musb_enable, .disable = dsps_musb_disable, @@ -696,7 +788,8 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, resources[1] = *res; /* allocate the child platform device */ - musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); + musb = platform_device_alloc("musb-hdrc", + (resources[0].start & 0xFFF) == 0x400 ? 0 : 1); if (!musb) { dev_err(dev, "failed to allocate musb device\n"); return -ENOMEM; @@ -765,6 +858,47 @@ err: return ret; } +static irqreturn_t dsps_vbus_threaded_irq(int irq, void *priv) +{ + struct dsps_glue *glue = priv; + struct musb *musb = platform_get_drvdata(glue->musb); + + if (!musb) + return IRQ_NONE; + + dev_dbg(glue->dev, "VBUS interrupt\n"); + dsps_mod_timer(glue, 0); + + return IRQ_HANDLED; +} + +static int dsps_setup_optional_vbus_irq(struct platform_device *pdev, + struct dsps_glue *glue) +{ + int error; + + glue->vbus_irq = platform_get_irq_byname(pdev, "vbus"); + if (glue->vbus_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (glue->vbus_irq <= 0) { + glue->vbus_irq = 0; + return 0; + } + + error = devm_request_threaded_irq(glue->dev, glue->vbus_irq, + NULL, dsps_vbus_threaded_irq, + IRQF_ONESHOT, + "vbus", glue); + if (error) { + glue->vbus_irq = 0; + return error; + } + dev_dbg(glue->dev, "VBUS irq %i configured\n", glue->vbus_irq); + + return 0; +} + static int dsps_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -792,6 +926,15 @@ static int dsps_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->wrp = wrp; + glue->usbss_base = of_iomap(pdev->dev.parent->of_node, 0); + if (!glue->usbss_base) + return -ENXIO; + + if (usb_get_dr_mode(&pdev->dev) == USB_DR_MODE_PERIPHERAL) { + ret = dsps_setup_optional_vbus_irq(pdev, glue); + if (ret) + return ret; + } platform_set_drvdata(pdev, glue); pm_runtime_enable(&pdev->dev); @@ -880,6 +1023,8 @@ static int dsps_suspend(struct device *dev) glue->context.tx_mode = musb_readl(mbase, wrp->tx_mode); glue->context.rx_mode = musb_readl(mbase, wrp->rx_mode); + dsps_dma_controller_suspend(glue); + return 0; } @@ -893,6 +1038,8 @@ static int dsps_resume(struct device *dev) if (!musb) return 0; + dsps_dma_controller_resume(glue); + mbase = musb->ctrl_base; musb_writel(mbase, wrp->control, glue->context.control); musb_writel(mbase, wrp->epintr_set, glue->context.epintr); @@ -903,8 +1050,7 @@ static int dsps_resume(struct device *dev) musb_writel(mbase, wrp->rx_mode, glue->context.rx_mode); if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer(glue, -1); return 0; } |