usb: gadget: Fix tegra udc in OTG mode
[firefly-linux-kernel-4.4.55.git] / drivers / usb / gadget / fsl_udc_core.c
index 08a9a62a39e34910ecbcb3fdb8381c044fa851e4..7bbe2fb69d9d432144fc9fe0a6c803a44dd4d44c 100644 (file)
 
 #include "fsl_usb2_udc.h"
 
+#ifdef CONFIG_ARCH_TEGRA
+#define        DRIVER_DESC     "NVidia Tegra High-Speed USB SOC Device Controller driver"
+#else
 #define        DRIVER_DESC     "Freescale High-Speed USB SOC Device Controller driver"
+#endif
 #define        DRIVER_AUTHOR   "Li Yang/Jiang Bo"
 #define        DRIVER_VERSION  "Apr 20, 2007"
 
 #define        DMA_ADDR_INVALID        (~(dma_addr_t)0)
 
+#ifdef CONFIG_ARCH_TEGRA
+static const char driver_name[] = "fsl-tegra-udc";
+#else
 static const char driver_name[] = "fsl-usb2-udc";
+#endif
 static const char driver_desc[] = DRIVER_DESC;
 
 static struct usb_dr_device *dr_regs;
@@ -178,7 +186,7 @@ static void nuke(struct fsl_ep *ep, int status)
 static int dr_controller_setup(struct fsl_udc *udc)
 {
        unsigned int tmp, portctrl;
-#ifndef CONFIG_ARCH_MXC
+#if !defined(CONFIG_ARCH_MXC) && !defined(CONFIG_ARCH_TEGRA)
        unsigned int ctrl;
 #endif
        unsigned long timeout;
@@ -231,6 +239,19 @@ static int dr_controller_setup(struct fsl_udc *udc)
        tmp |= USB_MODE_SETUP_LOCK_OFF;
        fsl_writel(tmp, &dr_regs->usbmode);
 
+#ifdef CONFIG_ARCH_TEGRA
+       /* Wait for controller to switch to device mode */
+       timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
+       while ((fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_DEVICE) !=
+              USB_MODE_CTRL_MODE_DEVICE) {
+               if (time_after(jiffies, timeout)) {
+                       ERR("udc device mode setup timeout!\n");
+                       return -ETIMEDOUT;
+               }
+               cpu_relax();
+       }
+#endif
+
        /* Clear the setup status */
        fsl_writel(0, &dr_regs->usbsts);
 
@@ -243,7 +264,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
                fsl_readl(&dr_regs->endpointlistaddr));
 
        /* Config control enable i/o output, cpu endian register */
-#ifndef CONFIG_ARCH_MXC
+#if !defined(CONFIG_ARCH_MXC) && !defined(CONFIG_ARCH_TEGRA)
        ctrl = __raw_readl(&usb_sys_regs->control);
        ctrl |= USB_CTRL_IOENB;
        __raw_writel(ctrl, &usb_sys_regs->control);
@@ -267,6 +288,20 @@ static int dr_controller_setup(struct fsl_udc *udc)
 static void dr_controller_run(struct fsl_udc *udc)
 {
        u32 temp;
+#ifdef CONFIG_ARCH_TEGRA
+       unsigned long timeout;
+#define FSL_UDC_RUN_TIMEOUT 1000
+       /* If OTG transceiver is available, then it handles the VBUS detection */
+       if (!udc_controller->transceiver) {
+               /* Enable cable detection interrupt, without setting the
+                * USB_SYS_VBUS_WAKEUP_INT bit. USB_SYS_VBUS_WAKEUP_INT is
+                * clear on write */
+               temp = fsl_readl(&usb_sys_regs->vbus_wakeup);
+               temp |= (USB_SYS_VBUS_WAKEUP_INT_ENABLE | USB_SYS_VBUS_WAKEUP_ENABLE);
+               temp &= ~USB_SYS_VBUS_WAKEUP_INT_STATUS;
+               fsl_writel(temp, &usb_sys_regs->vbus_wakeup);
+       }
+#endif
 
        /* Enable DR irq reg */
        temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
@@ -288,6 +323,19 @@ static void dr_controller_run(struct fsl_udc *udc)
        temp |= USB_CMD_RUN_STOP;
        fsl_writel(temp, &dr_regs->usbcmd);
 
+#ifdef CONFIG_ARCH_TEGRA
+       /* Wait for controller to start */
+       timeout = jiffies + FSL_UDC_RUN_TIMEOUT;
+       while ((fsl_readl(&dr_regs->usbcmd) & USB_CMD_RUN_STOP) !=
+              USB_CMD_RUN_STOP) {
+               if (time_after(jiffies, timeout)) {
+                       ERR("udc start timeout!\n");
+                       return;
+               }
+               cpu_relax();
+       }
+#endif
+
        return;
 }
 
@@ -550,13 +598,19 @@ static int fsl_ep_disable(struct usb_ep *_ep)
        }
 
        /* disable ep on controller */
-       ep_num = ep_index(ep);
-       epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
-       if (ep_is_in(ep))
-               epctrl &= ~EPCTRL_TX_ENABLE;
-       else
-               epctrl &= ~EPCTRL_RX_ENABLE;
-       fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+#ifdef CONFIG_ARCH_TEGRA
+       /* Touch the registers if cable is connected and phy is on */
+       if (fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS)
+#endif
+       {
+               ep_num = ep_index(ep);
+               epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+               if (ep_is_in(ep))
+                       epctrl &= ~EPCTRL_TX_ENABLE;
+               else
+                       epctrl &= ~EPCTRL_RX_ENABLE;
+               fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+       }
 
        udc = (struct fsl_udc *)ep->udc;
        spin_lock_irqsave(&udc->lock, flags);
@@ -616,6 +670,9 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
                ? (1 << (ep_index(ep) + 16))
                : (1 << (ep_index(ep)));
 
+       /* Flush all the dTD structs out to memory */
+       wmb();
+
        /* check if the pipe is empty */
        if (!(list_empty(&ep->queue))) {
                /* Add td to the end */
@@ -623,6 +680,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
                lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
                lastreq->tail->next_td_ptr =
                        cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK);
+               wmb();
                /* Read prime bit, if 1 goto done */
                if (fsl_readl(&dr_regs->endpointprime) & bitmask)
                        goto out;
@@ -978,6 +1036,12 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep)
        unsigned long timeout;
 #define FSL_UDC_FLUSH_TIMEOUT 1000
 
+#ifdef CONFIG_ARCH_TEGRA
+       /* Touch the registers if cable is connected and phy is on */
+       if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS))
+               return;
+#endif
+
        if (!_ep) {
                return;
        } else {
@@ -1516,7 +1580,12 @@ static void dtd_complete_irq(struct fsl_udc *udc)
        if (!bit_pos)
                return;
 
+#ifdef CONFIG_ARCH_TEGRA
+       /* XXX what's going on here */
+       for (i = 0; i < udc->max_ep; i++) {
+#else
        for (i = 0; i < udc->max_ep * 2; i++) {
+#endif
                ep_num = i >> 1;
                direction = i % 2;
 
@@ -1664,6 +1733,15 @@ static void reset_irq(struct fsl_udc *udc)
        /* Write 1s to the flush register */
        fsl_writel(0xffffffff, &dr_regs->endptflush);
 
+#if defined(CONFIG_ARCH_TEGRA)
+       /* When the bus reset is seen on Tegra, the PORTSCX_PORT_RESET bit
+        * is not set */
+       VDBG("Bus reset");
+       /* Reset all the queues, include XD, dTD, EP queue
+        * head and TR Queue */
+       reset_queues(udc);
+       udc->usb_state = USB_STATE_DEFAULT;
+#else
        if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
                VDBG("Bus reset");
                /* Reset all the queues, include XD, dTD, EP queue
@@ -1685,6 +1763,7 @@ static void reset_irq(struct fsl_udc *udc)
                dr_controller_run(udc);
                udc->usb_state = USB_STATE_ATTACHED;
        }
+#endif
 }
 
 /*
@@ -1696,17 +1775,89 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
        u32 irq_src;
        irqreturn_t status = IRQ_NONE;
        unsigned long flags;
+#if defined(CONFIG_ARCH_TEGRA)
+       u32 temp;
+#endif
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+#ifdef CONFIG_ARCH_TEGRA
+       if (udc->transceiver) {
+               if (udc->transceiver->state == OTG_STATE_B_PERIPHERAL) {
+                       if (!udc->vbus_active) {
+                               /* set vbus active  and enable the usb clocks */
+                               udc->vbus_active = 1;
+                               fsl_udc_clk_resume();
+                               /* setup the controller in the device mode */
+                               dr_controller_setup(udc);
+                               /* setup EP0 for setup packet */
+                               ep0_setup(udc);
+                               /* start the controller */
+                               dr_controller_run(udc);
+                               /* initialize the USB and EP states */
+                               udc->usb_state = USB_STATE_ATTACHED;
+                               udc->ep0_state = WAIT_FOR_SETUP;
+                               udc->ep0_dir = 0;
+                       }
+               } else if (udc->transceiver->state == OTG_STATE_A_SUSPEND) {
+                       if (udc->vbus_active) {
+                               /* Reset all internal Queues and inform client driver */
+                               reset_queues(udc);
+                               /* stop the controller and turn off the clocks */
+                               dr_controller_stop(udc);
+                               fsl_udc_clk_suspend();
+                               udc->vbus_active = 0;
+                               udc->usb_state = USB_STATE_DEFAULT;
+                       } else {
+                               spin_unlock_irqrestore(&udc->lock, flags);
+                               return IRQ_HANDLED;
+                       }
+               } else {
+                       spin_unlock_irqrestore(&udc->lock, flags);
+                       return IRQ_HANDLED;
+               }
+       }
+#endif
 
        /* Disable ISR for OTG host mode */
-       if (udc->stopped)
+       if (udc->stopped) {
+               spin_unlock_irqrestore(&udc->lock, flags);
                return IRQ_NONE;
-       spin_lock_irqsave(&udc->lock, flags);
+       }
+
        irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
        /* Clear notification bits */
        fsl_writel(irq_src, &dr_regs->usbsts);
 
        /* VDBG("irq_src [0x%8x]", irq_src); */
 
+#ifdef CONFIG_ARCH_TEGRA
+       if (!udc->transceiver) {
+               /* VBUS A session detection interrupts. When the interrupt is received,
+                * the mark the vbus active shadow.
+                */
+               temp = fsl_readl(&usb_sys_regs->vbus_wakeup);
+               if (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) {
+                       if (temp & USB_SYS_VBUS_STATUS) {
+                               udc->vbus_active = 1;
+                       } else {
+                               reset_queues(udc);
+                               udc->vbus_active = 0;
+                               udc->usb_state = USB_STATE_DEFAULT;
+                       }
+                       /* write back the register to clear the interrupt */
+                       fsl_writel(temp, &usb_sys_regs->vbus_wakeup);
+
+                       if (udc->vbus_active)
+                               fsl_udc_clk_resume();
+                       else
+                               fsl_udc_clk_suspend();
+
+                       status = IRQ_HANDLED;
+               }
+       }
+#endif
+
        /* Need to resume? */
        if (udc->usb_state == USB_STATE_SUSPENDED)
                if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0)
@@ -1800,6 +1951,9 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
                goto out;
        }
 
+       if (udc_controller->transceiver)
+               otg_set_peripheral(udc_controller->transceiver, &udc_controller->gadget);
+
        /* Enable DR IRQ reg and Set usbcmd reg  Run bit */
        dr_controller_run(udc_controller);
        udc_controller->usb_state = USB_STATE_ATTACHED;
@@ -1869,7 +2023,11 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
 
 #include <linux/seq_file.h>
 
+#ifdef CONFIG_ARCH_TEGRA
+static const char proc_filename[] = "driver/fsl_tegra_udc";
+#else
 static const char proc_filename[] = "driver/fsl_usb2_udc";
+#endif
 
 static int fsl_proc_read(char *page, char **start, off_t off, int count,
                int *eof, void *_dev)
@@ -2051,7 +2209,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
        size -= t;
        next += t;
 
-#ifndef CONFIG_ARCH_MXC
+#if !defined(CONFIG_ARCH_MXC) && !defined(CONFIG_ARCH_TEGRA)
        tmp_reg = usb_sys_regs->snoop1;
        t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
        size -= t;
@@ -2166,6 +2324,13 @@ static int __init struct_udc_setup(struct fsl_udc *udc,
                return -1;
        }
 
+#ifdef CONFIG_ARCH_TEGRA
+       /* Tegra uses hardware queue heads */
+       size = udc->max_ep * sizeof(struct ep_queue_head);
+       udc->ep_qh = (struct ep_queue_head *)((u8 *)dr_regs + QH_OFFSET);
+       udc->ep_qh_dma = platform_get_resource(pdev, IORESOURCE_MEM, 0)->start +
+               QH_OFFSET;
+#else
        /* initialized QHs, take care of alignment */
        size = udc->max_ep * sizeof(struct ep_queue_head);
        if (size < QH_ALIGNMENT)
@@ -2181,6 +2346,7 @@ static int __init struct_udc_setup(struct fsl_udc *udc,
                kfree(udc->eps);
                return -1;
        }
+#endif
 
        udc->ep_qh_size = size;
 
@@ -2245,6 +2411,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
        int ret = -ENODEV;
        unsigned int i;
        u32 dccparams;
+#if defined(CONFIG_ARCH_TEGRA)
+       struct resource *res_sys = NULL;
+#endif
 
        if (strcmp(pdev->name, driver_name)) {
                VDBG("Wrong device");
@@ -2279,7 +2448,21 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
                goto err_release_mem_region;
        }
 
-#ifndef CONFIG_ARCH_MXC
+#if defined(CONFIG_ARCH_TEGRA)
+       /* If the PHY registers are NOT provided as a seperate aperture, then
+        * we should be using the registers inside the controller aperture. */
+       res_sys = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (res_sys)  {
+               usb_sys_regs = ioremap(res_sys->start, resource_size(res_sys));
+               if (!usb_sys_regs)
+                       goto err_release_mem_region;
+       } else {
+               usb_sys_regs = (struct usb_sys_interface *)
+                       ((u32)dr_regs + USB_DR_SYS_OFFSET);
+       }
+#endif
+
+#if !defined(CONFIG_ARCH_MXC) && !defined(CONFIG_ARCH_TEGRA)
        usb_sys_regs = (struct usb_sys_interface *)
                        ((u32)dr_regs + USB_DR_SYS_OFFSET);
 #endif
@@ -2374,6 +2557,19 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
                goto err_unregister;
        }
        create_proc_file();
+
+#ifdef CONFIG_USB_OTG_UTILS
+       udc_controller->transceiver = otg_get_transceiver();
+       if (udc_controller->transceiver)
+               udc_controller->vbus_active = 1;
+#else
+#ifdef CONFIG_ARCH_TEGRA
+       /* Power down the phy if cable is not connected */
+       if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS))
+               fsl_udc_clk_suspend();
+#endif
+#endif
+
        return 0;
 
 err_unregister: