]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/usb/musb/musb_dsps.c
Merge tag 'platform-drivers-x86-v4.12-2' of git://git.infradead.org/linux-platform...
[karo-tx-linux.git] / drivers / usb / musb / musb_dsps.c
index 9f125e179acd444ca43c73a1a5d263b8e5c8893d..bc6a9be2ccc55696cb703f04b97e3d0fb08ca59c 100644 (file)
@@ -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,27 +242,33 @@ 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:
+               /* keep VBUS on for host-only mode */
+               if (musb->port_mode == MUSB_PORT_MODE_HOST) {
+                       dsps_mod_timer_optional(glue);
+                       break;
+               }
                musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
                skip_session = 1;
                /* fall */
 
        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 +306,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 +371,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 +401,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 +505,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 +654,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 +793,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 +863,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 +931,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)
+                       goto err_iounmap;
+       }
 
        platform_set_drvdata(pdev, glue);
        pm_runtime_enable(&pdev->dev);
@@ -803,6 +951,8 @@ static int dsps_probe(struct platform_device *pdev)
 
 err:
        pm_runtime_disable(&pdev->dev);
+err_iounmap:
+       iounmap(glue->usbss_base);
        return ret;
 }
 
@@ -813,6 +963,7 @@ static int dsps_remove(struct platform_device *pdev)
        platform_device_unregister(glue->musb);
 
        pm_runtime_disable(&pdev->dev);
+       iounmap(glue->usbss_base);
 
        return 0;
 }
@@ -880,6 +1031,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 +1046,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 +1058,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;
 }