From 0e06e44e332a60811d75f522a3641342daf3207a Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Fri, 4 Jun 2010 19:36:06 -0700 Subject: [PATCH] usb: gadget: Add support for OTG Based on work by Nvidia Signed-off-by: Benoit Goby --- drivers/usb/gadget/fsl_udc_core.c | 107 ++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index f059f5ef7828..df6142948331 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -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 -- 2.34.1