usb: gadget: Add support for OTG
authorBenoit Goby <benoit@android.com>
Sat, 5 Jun 2010 02:36:06 +0000 (19:36 -0700)
committerColin Cross <ccross@android.com>
Wed, 6 Oct 2010 23:27:03 +0000 (16:27 -0700)
Based on work by Nvidia

Signed-off-by: Benoit Goby <benoit@android.com>
drivers/usb/gadget/fsl_udc_core.c

index f059f5ef7828062f9343b9b72a30bf2b0c6ed5a0..df6142948331dc0edadf9b03e21a3d1bedd14f5a 100644 (file)
@@ -291,16 +291,16 @@ static void dr_controller_run(struct fsl_udc *udc)
 #ifdef CONFIG_ARCH_TEGRA
        unsigned long timeout;
 #define FSL_UDC_RUN_TIMEOUT 1000
-#endif
-
-#ifdef CONFIG_ARCH_TEGRA
-       /* 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);
+       /* 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 */
@@ -1788,32 +1788,65 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
        fsl_writel(irq_src, &dr_regs->usbsts);
 
        /* VDBG("irq_src [0x%8x]", irq_src); */
-#if defined(CONFIG_ARCH_TEGRA)
-       /* VBUS A session detection interrupts. When the interrupt is received,
-        * the mark the vbus active shadow.
-        */
-       temp = fsl_readl(&usb_sys_regs->vbus_sensors);
-       if (temp & USB_SYS_VBUS_ASESSION_CHANGED) {
-               if (temp & USB_SYS_VBUS_ASESSION) {
-                       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 0
-               /* XXX */
-               if (udc->vbus_active) {
-                       fsl_udc_clk_resume();
+#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 {
-                       fsl_udc_clk_suspend();
+                       spin_unlock_irqrestore(&udc->lock, flags);
+                       return IRQ_HANDLED;
                }
-#endif
+       } else {
+               /* 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);
 
-               status = IRQ_HANDLED;
+                       if (udc->vbus_active)
+                               fsl_udc_clk_resume();
+                       else
+                               fsl_udc_clk_suspend();
+
+                       status = IRQ_HANDLED;
+               }
        }
 #endif
 
@@ -1910,6 +1943,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;
@@ -2514,12 +2550,13 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
        }
        create_proc_file();
 
-#if 0
-/* XXX */
+#ifdef CONFIG_USB_OTG_UTILS
+       udc_controller->transceiver = otg_get_transceiver();
+#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))
-               platform_udc_clk_suspend();
+               fsl_udc_clk_suspend();
 #endif
 #endif