usb: musb: am335x: add support for dual instance
[firefly-linux-kernel-4.4.55.git] / drivers / usb / musb / musb_dsps.c
index 217808d9fbe1991298da67295404d50cd71e593a..796fc6085fce210ad735f8009f96a3d3baecef0c 100644 (file)
@@ -105,6 +105,8 @@ struct dsps_musb_wrapper {
        /* miscellaneous stuff */
        u32             musb_core_offset;
        u8              poll_seconds;
+       /* number of musb instances */
+       u8              instances;
 };
 
 /**
@@ -112,9 +114,10 @@ struct dsps_musb_wrapper {
  */
 struct dsps_glue {
        struct device *dev;
-       struct platform_device *musb;   /* child musb pdev */
+       struct platform_device *musb[2];        /* child musb pdev */
        const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
-       struct timer_list timer;        /* otg_workaround timer */
+       struct timer_list timer[2];     /* otg_workaround timer */
+       unsigned long last_timer[2];    /* last timer data for each instance */
 };
 
 /**
@@ -137,9 +140,8 @@ static void dsps_musb_enable(struct musb *musb)
        dsps_writel(reg_base, wrp->epintr_set, epmask);
        dsps_writel(reg_base, wrp->coreintr_set, coremask);
        /* Force the DRVVBUS IRQ so we can start polling for ID change. */
-       if (is_otg_enabled(musb))
-               dsps_writel(reg_base, wrp->coreintr_set,
-                           (1 << wrp->drvvbus) << wrp->usb_shift);
+       dsps_writel(reg_base, wrp->coreintr_set,
+                   (1 << wrp->drvvbus) << wrp->usb_shift);
 }
 
 /**
@@ -165,8 +167,8 @@ static void otg_timer(unsigned long _musb)
        struct musb *musb = (void *)_musb;
        void __iomem *mregs = musb->mregs;
        struct device *dev = musb->controller;
-       struct platform_device *pdev = to_platform_device(dev->parent);
-       struct dsps_glue *glue = platform_get_drvdata(pdev);
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dsps_glue *glue = dev_get_drvdata(dev->parent);
        const struct dsps_musb_wrapper *wrp = glue->wrp;
        u8 devctl;
        unsigned long flags;
@@ -200,12 +202,9 @@ static void otg_timer(unsigned long _musb)
                            MUSB_INTR_VBUSERROR << wrp->usb_shift);
                break;
        case OTG_STATE_B_IDLE:
-               if (!is_peripheral_enabled(musb))
-                       break;
-
                devctl = dsps_readb(mregs, MUSB_DEVCTL);
                if (devctl & MUSB_DEVCTL_BDEVICE)
-                       mod_timer(&glue->timer,
+                       mod_timer(&glue->timer[pdev->id],
                                        jiffies + wrp->poll_seconds * HZ);
                else
                        musb->xceiv->state = OTG_STATE_A_IDLE;
@@ -219,12 +218,8 @@ static void otg_timer(unsigned long _musb)
 static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
 {
        struct device *dev = musb->controller;
-       struct platform_device *pdev = to_platform_device(dev->parent);
-       struct dsps_glue *glue = platform_get_drvdata(pdev);
-       static unsigned long last_timer;
-
-       if (!is_otg_enabled(musb))
-               return;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dsps_glue *glue = dev_get_drvdata(dev->parent);
 
        if (timeout == 0)
                timeout = jiffies + msecs_to_jiffies(3);
@@ -234,22 +229,23 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
                                musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
                dev_dbg(musb->controller, "%s active, deleting timer\n",
                                otg_state_string(musb->xceiv->state));
-               del_timer(&glue->timer);
-               last_timer = jiffies;
+               del_timer(&glue->timer[pdev->id]);
+               glue->last_timer[pdev->id] = jiffies;
                return;
        }
 
-       if (time_after(last_timer, timeout) && timer_pending(&glue->timer)) {
+       if (time_after(glue->last_timer[pdev->id], timeout) &&
+                               timer_pending(&glue->timer[pdev->id])) {
                dev_dbg(musb->controller,
                        "Longer idle timer already pending, ignoring...\n");
                return;
        }
-       last_timer = timeout;
+       glue->last_timer[pdev->id] = timeout;
 
        dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
                otg_state_string(musb->xceiv->state),
                        jiffies_to_msecs(timeout - jiffies));
-       mod_timer(&glue->timer, timeout);
+       mod_timer(&glue->timer[pdev->id], timeout);
 }
 
 static irqreturn_t dsps_interrupt(int irq, void *hci)
@@ -257,8 +253,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
        struct musb  *musb = hci;
        void __iomem *reg_base = musb->ctrl_base;
        struct device *dev = musb->controller;
-       struct platform_device *pdev = to_platform_device(dev->parent);
-       struct dsps_glue *glue = platform_get_drvdata(pdev);
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dsps_glue *glue = dev_get_drvdata(dev->parent);
        const struct dsps_musb_wrapper *wrp = glue->wrp;
        unsigned long flags;
        irqreturn_t ret = IRQ_NONE;
@@ -293,7 +289,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
         * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
         * Also, DRVVBUS pulses for SRP (but not at 5V) ...
         */
-       if ((usbintr & MUSB_INTR_BABBLE) && is_host_enabled(musb))
+       if (usbintr & MUSB_INTR_BABBLE)
                pr_info("CAUTION: musb: Babble Interrupt Occured\n");
 
        if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
@@ -302,8 +298,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
                u8 devctl = dsps_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
@@ -318,15 +313,15 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
                         */
                        musb->int_usb &= ~MUSB_INTR_VBUSERROR;
                        musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
-                       mod_timer(&glue->timer,
+                       mod_timer(&glue->timer[pdev->id],
                                        jiffies + wrp->poll_seconds * HZ);
                        WARNING("VBUS error workaround (delay coming)\n");
-               } else if (is_host_enabled(musb) && drvvbus) {
+               } else if (drvvbus) {
                        musb->is_active = 1;
                        MUSB_HST_MODE(musb);
                        musb->xceiv->otg->default_a = 1;
                        musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
-                       del_timer(&glue->timer);
+                       del_timer(&glue->timer[pdev->id]);
                } else {
                        musb->is_active = 0;
                        MUSB_DEV_MODE(musb);
@@ -352,8 +347,9 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
                dsps_writel(reg_base, wrp->eoi, 1);
 
        /* Poll for ID change */
-       if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE)
-               mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
+       if (musb->xceiv->state == OTG_STATE_B_IDLE)
+               mod_timer(&glue->timer[pdev->id],
+                        jiffies + wrp->poll_seconds * HZ);
 
        spin_unlock_irqrestore(&musb->lock, flags);
 
@@ -364,8 +360,8 @@ static int dsps_musb_init(struct musb *musb)
 {
        struct device *dev = musb->controller;
        struct musb_hdrc_platform_data *plat = dev->platform_data;
-       struct platform_device *pdev = to_platform_device(dev->parent);
-       struct dsps_glue *glue = platform_get_drvdata(pdev);
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dsps_glue *glue = dev_get_drvdata(dev->parent);
        const struct dsps_musb_wrapper *wrp = glue->wrp;
        struct omap_musb_board_data *data = plat->board_data;
        void __iomem *reg_base = musb->ctrl_base;
@@ -388,8 +384,7 @@ static int dsps_musb_init(struct musb *musb)
                goto err0;
        }
 
-       if (is_host_enabled(musb))
-               setup_timer(&glue->timer, otg_timer, (unsigned long) musb);
+       setup_timer(&glue->timer[pdev->id], otg_timer, (unsigned long) musb);
 
        /* Reset the musb */
        dsps_writel(reg_base, wrp->control, (1 << wrp->reset));
@@ -420,11 +415,10 @@ static int dsps_musb_exit(struct musb *musb)
        struct device *dev = musb->controller;
        struct musb_hdrc_platform_data *plat = dev->platform_data;
        struct omap_musb_board_data *data = plat->board_data;
-       struct platform_device *pdev = to_platform_device(dev->parent);
-       struct dsps_glue *glue = platform_get_drvdata(pdev);
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dsps_glue *glue = dev_get_drvdata(dev->parent);
 
-       if (is_host_enabled(musb))
-               del_timer_sync(&glue->timer);
+       del_timer_sync(&glue->timer[pdev->id]);
 
        /* Shutdown the on-chip PHY and its PLL. */
        if (data->set_phy_power)
@@ -458,7 +452,7 @@ static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id)
        struct resource *res;
        struct resource resources[2];
        char res_name[10];
-       int ret;
+       int ret, musbid;
 
        /* get memory resource */
        sprintf(res_name, "musb%d", id);
@@ -483,52 +477,63 @@ static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id)
        res->parent = NULL;
        resources[1] = *res;
 
+       /* get the musb id */
+       musbid = musb_get_id(dev, GFP_KERNEL);
+       if (musbid < 0) {
+               dev_err(dev, "failed to allocate musb id\n");
+               ret = -ENOMEM;
+               goto err0;
+       }
        /* allocate the child platform device */
-       musb = platform_device_alloc("musb-hdrc", -1);
+       musb = platform_device_alloc("musb-hdrc", musbid);
        if (!musb) {
                dev_err(dev, "failed to allocate musb device\n");
                ret = -ENOMEM;
-               goto err0;
+               goto err1;
        }
 
+       musb->id                        = musbid;
        musb->dev.parent                = dev;
        musb->dev.dma_mask              = &musb_dmamask;
        musb->dev.coherent_dma_mask     = musb_dmamask;
 
-       glue->musb                      = musb;
+       glue->musb[id]                  = musb;
 
        pdata->platform_ops             = &dsps_ops;
 
        ret = platform_device_add_resources(musb, resources, 2);
        if (ret) {
                dev_err(dev, "failed to add resources\n");
-               goto err1;
+               goto err2;
        }
 
        ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
        if (ret) {
                dev_err(dev, "failed to add platform_data\n");
-               goto err1;
+               goto err2;
        }
 
        ret = platform_device_add(musb);
        if (ret) {
                dev_err(dev, "failed to register musb device\n");
-               goto err1;
+               goto err2;
        }
 
        return 0;
 
-err1:
+err2:
        platform_device_put(musb);
+err1:
+       musb_put_id(dev, musbid);
 err0:
        return ret;
 }
 
-static void __devexit dsps_delete_musb_pdev(struct dsps_glue *glue)
+static void dsps_delete_musb_pdev(struct dsps_glue *glue, u8 id)
 {
-       platform_device_del(glue->musb);
-       platform_device_put(glue->musb);
+       musb_put_id(glue->dev, glue->musb[id]->id);
+       platform_device_del(glue->musb[id]);
+       platform_device_put(glue->musb[id]);
 }
 
 static int __devinit dsps_probe(struct platform_device *pdev)
@@ -538,7 +543,7 @@ static int __devinit dsps_probe(struct platform_device *pdev)
                                (struct dsps_musb_wrapper *)id->driver_data;
        struct dsps_glue *glue;
        struct resource *iomem;
-       int ret;
+       int ret, i;
 
        /* allocate glue */
        glue = kzalloc(sizeof(*glue), GFP_KERNEL);
@@ -579,7 +584,16 @@ static int __devinit dsps_probe(struct platform_device *pdev)
        ret = pm_runtime_get_sync(&pdev->dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "pm_runtime_get_sync FAILED");
-               goto err3;
+       /* create the child platform device for all instances of musb */
+       for (i = 0; i < wrp->instances ; i++) {
+               ret = dsps_create_musb_pdev(glue, i);
+               if (ret != 0) {
+                       dev_err(&pdev->dev, "failed to create child pdev\n");
+                       /* release resources of previously created instances */
+                       for (i--; i >= 0 ; i--)
+                               dsps_delete_musb_pdev(glue, i);
+                       goto err3;
+               }
        }
 
        return 0;
@@ -596,9 +610,12 @@ err0:
 static int __devexit dsps_remove(struct platform_device *pdev)
 {
        struct dsps_glue *glue = platform_get_drvdata(pdev);
+       const struct dsps_musb_wrapper *wrp = glue->wrp;
+       int i;
 
        /* delete the child platform device */
-       dsps_delete_musb_pdev(glue);
+       for (i = 0; i < wrp->instances ; i++)
+               dsps_delete_musb_pdev(glue, i);
 
        /* disable usbss clocks */
        pm_runtime_put(&pdev->dev);
@@ -664,6 +681,7 @@ static const struct dsps_musb_wrapper ti81xx_driver_data __devinitconst = {
        .rxep_bitmap            = (0xfffe << 16),
        .musb_core_offset       = 0x400,
        .poll_seconds           = 2,
+       .instances              = 2,
 };
 
 static const struct platform_device_id musb_dsps_id_table[] __devinitconst = {