diff options
-rw-r--r-- | arch/arm/configs/tegra_whistler_android_defconfig | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/platform.h | 1 | ||||
-rwxr-xr-x[-rw-r--r--] | arch/arm/mach-tegra/include/nvddk_usbphy.h | 6 | ||||
-rwxr-xr-x[-rw-r--r--] | arch/arm/mach-tegra/init_common.c | 71 | ||||
-rwxr-xr-x[-rw-r--r--] | arch/arm/mach-tegra/nvddk/nvddk_usbphy.c | 65 | ||||
-rwxr-xr-x[-rw-r--r--] | arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h | 8 | ||||
-rwxr-xr-x[-rw-r--r--] | arch/arm/mach-tegra/tegra_sysmap.c | 14 | ||||
-rwxr-xr-x[-rw-r--r--] | drivers/usb/gadget/fsl_udc_core.c | 146 | ||||
-rwxr-xr-x[-rw-r--r--] | drivers/usb/gadget/tegra_udc.c | 7 | ||||
-rwxr-xr-x[-rw-r--r--] | drivers/usb/host/ehci-tegra.c | 206 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 11 | ||||
-rwxr-xr-x[-rw-r--r--] | drivers/usb/otg/Kconfig | 9 | ||||
-rwxr-xr-x[-rw-r--r--] | drivers/usb/otg/Makefile | 1 | ||||
-rwxr-xr-x | drivers/usb/otg/tegra-otg.c | 273 | ||||
-rwxr-xr-x[-rw-r--r--] | include/linux/tegra_devices.h | 8 |
15 files changed, 743 insertions, 87 deletions
diff --git a/arch/arm/configs/tegra_whistler_android_defconfig b/arch/arm/configs/tegra_whistler_android_defconfig index 6894b5364305..fe2839172cd2 100644 --- a/arch/arm/configs/tegra_whistler_android_defconfig +++ b/arch/arm/configs/tegra_whistler_android_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.29 -# Wed Jan 13 20:07:37 2010 +# Mon Jan 18 12:46:38 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -1175,6 +1175,8 @@ CONFIG_UDC_FSL_NR_ENDPOINTS=32 # # OTG and related infrastructure # +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_TEGRA_OTG=y # CONFIG_USB_GPIO_VBUS is not set CONFIG_MMC=y CONFIG_EMBEDDED_MMC_START_OFFSET=y diff --git a/arch/arm/mach-tegra/include/mach/platform.h b/arch/arm/mach-tegra/include/mach/platform.h index f80632aff0a8..98064eab1bfa 100644 --- a/arch/arm/mach-tegra/include/mach/platform.h +++ b/arch/arm/mach-tegra/include/mach/platform.h @@ -21,6 +21,7 @@ #ifndef __MACH_TEGRA_PLATFORM_H extern unsigned long tegra_get_module_inst_base(const char *, int); +extern unsigned long tegra_get_module_inst_size(const char *, int); extern unsigned int tegra_get_module_inst_irq(const char *, int, int); #define TEGRA_PL310_BASE (tegra_get_module_inst_base("pl310", 0)) diff --git a/arch/arm/mach-tegra/include/nvddk_usbphy.h b/arch/arm/mach-tegra/include/nvddk_usbphy.h index 283a5d9ac43c..ef1be673e8d4 100644..100755 --- a/arch/arm/mach-tegra/include/nvddk_usbphy.h +++ b/arch/arm/mach-tegra/include/nvddk_usbphy.h @@ -272,22 +272,24 @@ void NvDdkUsbPhyClose(NvDdkUsbPhyHandle hUsbPhy); * reinitializing. * * @param hUsbPhy Handle acquired during the NvDdkUsbPhyOpen() call. + * @param IsHostMode indicates the host mode or not. * @param IsDpd Deep sleep power up or not . * * @retval NvSuccess * @retval NvError_Timeout If phy clock is not stable in expected time. */ -NvError NvDdkUsbPhyPowerUp(NvDdkUsbPhyHandle hUsbPhy, NvBool IsDpd); +NvError NvDdkUsbPhyPowerUp(NvDdkUsbPhyHandle hUsbPhy, NvBool IsHostMode, NvBool IsDpd); /** * Powers down the PHY. It could be low power mode or shutdown. * * @param hUsbPhy Handle acquired during the NvDdkUsbPhyOpen() call. + * @param IsHostMode indicates the host mode or not. * @param IsDpd Handle Deep sleep power down or not . * * @retval NvSuccess */ -NvError NvDdkUsbPhyPowerDown(NvDdkUsbPhyHandle hUsbPhy, NvBool IsDpd); +NvError NvDdkUsbPhyPowerDown(NvDdkUsbPhyHandle hUsbPhy, NvBool IsHostMode, NvBool IsDpd); /** * Perform an I/O control operation on the device. diff --git a/arch/arm/mach-tegra/init_common.c b/arch/arm/mach-tegra/init_common.c index 32746e5e4d50..97809fc0d086 100644..100755 --- a/arch/arm/mach-tegra/init_common.c +++ b/arch/arm/mach-tegra/init_common.c @@ -30,6 +30,7 @@ #include "nvrm_init.h" #include "nvrm_drf.h" #include "mach/nvrm_linux.h" +#include "mach/platform.h" #include "nvos.h" #include "nvutil.h" #include "nvassert.h" @@ -474,7 +475,8 @@ static void __init tegra_register_usb_gadget(void) /* fixme: add ulpi here? */ p = NvOdmQueryGetUsbProperty(NvOdmIoModule_Usb, i); - if (!p || !(p->UsbMode & NvOdmUsbModeType_Device)) + if (!p || !((p->UsbMode & NvOdmUsbModeType_Device) || + (p->UsbMode & NvOdmUsbModeType_OTG))) continue; irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, mod, 0); @@ -555,7 +557,9 @@ static void __init tegra_register_usb_host(void) /* fixme: add ulpi here? */ p = NvOdmQueryGetUsbProperty(NvOdmIoModule_Usb, i); - if (!p || !(p->UsbMode & NvOdmUsbModeType_Host) || tegra_is_udc(mod)) + if (!p || !((p->UsbMode & NvOdmUsbModeType_Host) || + (p->UsbMode & NvOdmUsbModeType_OTG) ) || + (tegra_is_udc(mod) && !(p->UsbMode & NvOdmUsbModeType_OTG))) continue; irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, mod, 0); @@ -588,8 +592,71 @@ fail: } #endif +#if !defined(CONFIG_USB_TEGRA_OTG) +#define tegra_register_usb_otg() do {} while (0) +#else + +static void __init tegra_register_usb_otg(void) +{ + NvU32 port_count, i; + + port_count = NvRmModuleGetNumInstances(s_hRmGlobal, NvRmModuleID_Usb2Otg); + + for (i=0; i<port_count; i++) { + const NvOdmUsbProperty *p; + struct platform_device *platdev = NULL; + struct resource *res; + struct tegra_otg_platform_data pdata; + + p = NvOdmQueryGetUsbProperty(NvOdmIoModule_Usb, i); + if (!p || !(p->UsbMode & NvOdmUsbModeType_OTG)) + continue; + + platdev = platform_device_alloc("tegra-otg", i); + if (!platdev) { + pr_err("unable to allocate device for tegra-otg\n"); + goto fail; + } + + res = kzalloc(sizeof(struct resource)*2, GFP_KERNEL); + if (!res) { + pr_err("unable to allocate resource for tegra-otg\n"); + goto fail; + } + + res[0].flags = IORESOURCE_MEM; + res[0].start = tegra_get_module_inst_base("usbotg", i); + res[0].end = res[0].start + tegra_get_module_inst_size("usbotg", i) - 1; + res[1].flags = IORESOURCE_IRQ; + res[1].start = res[1].end = tegra_get_module_inst_irq("usbotg", i, 0); + + if (platform_device_add_resources(platdev, res, 2)) { + pr_err("unable to add resources to tegra-otg device\n"); + goto fail; + } + + memset(&pdata, 0, sizeof(pdata)); + pdata.instance = i; + pdata.usb_property = p; + + if (platform_device_add_data(platdev, &pdata, sizeof(pdata))) { + pr_err("unable to add platform data to tegra-otg device\n"); + goto fail; + } + + if (platform_device_add(platdev)) { + pr_err("unable to add tegra-otg device\n"); + goto fail; + } + } +fail: + ; +} +#endif + void __init tegra_register_usb(void) { + tegra_register_usb_otg(); tegra_register_usb_gadget(); tegra_register_usb_host(); } diff --git a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c index 6b973c172746..fe3c3252d39f 100644..100755 --- a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c +++ b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c @@ -134,7 +134,7 @@ UsbPhyPowerRailEnable( if( !UsbPhyDiscover( pUsbPhy ) ) { // Do nothing if no power rail info is discovered - return; + return; } /* enable the power rail */ @@ -336,6 +336,39 @@ UsbPhyIoctlUsbBisuHintsOnOff( return UsbPhyDfsBusyHint(pUsbPhy, pOnOff->OnOff, pOnOff->BoostDurationMs); } +/* + * NvDdkUsbPhyHelperThread() - Thread to control the Busy hints and Vbus. + * + * Busy hints and Vbus are controlled based on the USB cable connection status. + * USB cable status change(connect/disconnect) is identified in the ISR. + * Busy hints function cannot be called from ISR as NvRmPowerBusyHintMulti() + * uses vfree and vmalloc functions which are not supposed to call from the + * ISR context. This helper thread is signaled from the ISR by calling Phy + * suspend/resume APIs to control the busy hints and VBUS. + */ +static void +NvDdkUsbPhyHelperThread( + void* pArgs) +{ + NvDdkUsbPhyHandle hUsbPhy = (NvDdkUsbPhyHandle)pArgs; + + hUsbPhy->Stopped = NV_FALSE; + + while (!hUsbPhy->Stopped) + { + /* wait for the signal to turn on/off the busy hints and vbus */ + NvOsSemaphoreWaitTimeout(hUsbPhy->HelperThreadSema, NV_WAIT_INFINITE); + + /* Turn on/off the USB busy hints */ + UsbPhyDfsBusyHint(hUsbPhy, hUsbPhy->IsPhyPoweredUp, NV_WAIT_INFINITE); + /* Turn on/off the vbus for host mode */ + if (hUsbPhy->IsHostMode) + { + UsbPrivEnableVbus(hUsbPhy, hUsbPhy->IsPhyPoweredUp); + } + } +} + NvError NvDdkUsbPhyOpen( NvRmDeviceHandle hRm, @@ -459,7 +492,15 @@ NvDdkUsbPhyOpen( NvRmPowerRegister(pUsbPhy->hRmDevice, pUsbPhy->hPwrEventSem, &pUsbPhy->RmPowerClientId)); - if (pUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) + NV_CHECK_ERROR_CLEANUP( + NvOsSemaphoreCreate(&pUsbPhy->HelperThreadSema, 0)); + + NV_CHECK_ERROR_CLEANUP( + NvOsThreadCreate(&NvDdkUsbPhyHelperThread, + (void*)pUsbPhy, &pUsbPhy->hThreadId)); + + if ((pUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) || + (pUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_OTG)) { UsbPrivEnableVbus(pUsbPhy, NV_TRUE); } @@ -471,6 +512,9 @@ NvDdkUsbPhyOpen( // Open the H/W interface UsbPhyOpenHwInterface(pUsbPhy); + /* enable the busy hints for USB */ + UsbPhyDfsBusyHint(pUsbPhy, NV_TRUE, NV_WAIT_INFINITE); + // Initialize the USB Phy NV_CHECK_ERROR_CLEANUP(UsbPhyInitialize(pUsbPhy)); } @@ -542,13 +586,18 @@ NvDdkUsbPhyClose( hUsbPhy->CloseHwInterface(hUsbPhy); } - if (hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) + if ((hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) || + (hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_OTG)) { UsbPrivEnableVbus(hUsbPhy, NV_FALSE); } UsbPhyPowerRailEnable(hUsbPhy, NV_FALSE); + hUsbPhy->Stopped = NV_TRUE; + NvOsThreadJoin(hUsbPhy->hThreadId); + NvOsSemaphoreDestroy(hUsbPhy->HelperThreadSema); + NvRmPhysicalMemUnmap( (void*)hUsbPhy->UsbVirAdr, hUsbPhy->UsbBankSize); @@ -562,6 +611,7 @@ NvDdkUsbPhyClose( NvError NvDdkUsbPhyPowerUp( NvDdkUsbPhyHandle hUsbPhy, + NvBool IsHostMode, NvBool IsDpd) { NvError e = NvSuccess; @@ -593,10 +643,12 @@ NvDdkUsbPhyPowerUp( NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NV_TRUE)); } - + // Power up the Phy NV_CHECK_ERROR_CLEANUP(hUsbPhy->PowerUp(hUsbPhy)); - hUsbPhy->IsPhyPoweredUp = NV_TRUE; + hUsbPhy->IsHostMode = IsHostMode; + // signal to set the busy hints and vbus + NvOsSemaphoreSignal(hUsbPhy->HelperThreadSema); //NvOsDebugPrintf("NvDdkUsbPhyPowerUp::VOLTAGE ON\n"); @@ -609,6 +661,7 @@ fail: NvError NvDdkUsbPhyPowerDown( NvDdkUsbPhyHandle hUsbPhy, + NvBool IsHostMode, NvBool IsDpd) { NvError e = NvSuccess; @@ -648,6 +701,8 @@ NvDdkUsbPhyPowerDown( } hUsbPhy->IsPhyPoweredUp = NV_FALSE; + hUsbPhy->IsHostMode = IsHostMode; + NvOsSemaphoreSignal(hUsbPhy->HelperThreadSema); //NvOsDebugPrintf("NvDdkUsbPhyPowerDown::VOLTAGE OFF\n"); diff --git a/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h b/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h index 9d7fb7c8e8e7..741ea1674399 100644..100755 --- a/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h +++ b/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h @@ -126,6 +126,14 @@ typedef struct NvDdkUsbPhyRec NvBool IsPhyPoweredUp; // Utmpi Pad Config control structure NvDdkUsbPhyUtmiPadConfig *pUtmiPadConfig; + // Thread ID for the helper thread + NvOsThreadHandle hThreadId; + // semphore for signaling the thread + NvOsSemaphoreHandle HelperThreadSema; + // variable to control the thread loop + NvBool Stopped; + // Indicates phy powered up for the host mode + NvBool IsHostMode; // Set of function pointers to access the usb phy hardware interface. // Pointer to the h/w specific PowerUp function. NvError (*PowerUp)(NvDdkUsbPhyHandle hUsbPhy); diff --git a/arch/arm/mach-tegra/tegra_sysmap.c b/arch/arm/mach-tegra/tegra_sysmap.c index cb5b88ced984..55154b546b12 100644..100755 --- a/arch/arm/mach-tegra/tegra_sysmap.c +++ b/arch/arm/mach-tegra/tegra_sysmap.c @@ -36,6 +36,8 @@ static NvRmModuleID tegra_map_name_to_mod(const char *name, int inst) return NVRM_MODULE_ID(NvRmPrivModuleID_ArmPerif, inst); else if (!strcmp(name, "pcie")) return NVRM_MODULE_ID(NvRmPrivModuleID_Pcie, inst); + else if (!strcmp(name, "usbotg")) + return NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, inst); return (NvRmModuleID) 0; } @@ -55,6 +57,18 @@ unsigned long tegra_get_module_inst_base(const char *name, int inst) return (unsigned long)phys; } +unsigned long tegra_get_module_inst_size(const char *name, int inst) +{ + NvRmModuleID mod_id = tegra_map_name_to_mod(name, inst); + NvRmPhysAddr phys = 0xffffffffUL; + NvU32 len = 0; + + if (mod_id) + NvRmModuleGetBaseAddress(s_hRmGlobal, mod_id, &phys, &len); + + return (unsigned long)len; +} + unsigned int tegra_get_module_inst_irq(const char *name, int inst, int mod_int_id) { diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index bbfedf5e9d20..a0753a9d9788 100644..100755 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -291,14 +291,16 @@ static void dr_controller_run(struct fsl_udc *udc) #if defined(CONFIG_ARCH_TEGRA) unsigned long timeout; #define FSL_UDC_RUN_TIMEOUT 1000 - - /* 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 @@ -1756,6 +1758,27 @@ static void reset_irq(struct fsl_udc *udc) #endif } +#if defined(CONFIG_ARCH_TEGRA) +/* + * Restart device controller in the OTG mode on VBUS detection + */ +static void fsl_udc_restart(struct fsl_udc *udc) +{ + /* setup the controller in the device mode */ + dr_controller_setup(udc); + /* Reset all internal used Queues */ + reset_queues(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; +} +#endif + /* * USB device controller interrupt handler */ @@ -1769,44 +1792,67 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) u32 temp; #endif - /* Disable ISR for OTG host mode */ - if (udc->stopped) - 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); */ #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_wakeup); - if (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) { - if (temp & USB_SYS_VBUS_STATUS) { - udc->vbus_active = 1; - //printk(KERN_INFO "USB cable connected\n"); + spin_lock_irqsave(&udc->lock, flags); + /* check OTG tranceiver is available or not */ + 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; + platform_udc_clk_resume(); + fsl_udc_restart(udc); + } + } else if (udc->transceiver->state == OTG_STATE_A_SUSPEND) { + if (udc->vbus_active) { + /* stop the controller and turn off the clocks */ + dr_controller_stop(udc); + platform_udc_clk_suspend(); + udc->vbus_active = 0; + udc->usb_state = USB_STATE_DEFAULT; + udc->transceiver->state = OTG_STATE_UNDEFINED; + } } else { - reset_queues(udc); - udc->vbus_active = 0; - udc->usb_state = USB_STATE_DEFAULT; - //printk("USB cable dis-connected\n"); + spin_unlock_irqrestore(&udc->lock, flags); + return IRQ_HANDLED; } + }else { + /* VBUS A session detection interrupts. When the interrupt is received, + * mark the vbus active shadow. + */ + temp = fsl_readl(&usb_sys_regs->vbus_wakeup); /* write back the register to clear the interrupt */ fsl_writel(temp, &usb_sys_regs->vbus_wakeup); - - if (udc->vbus_active) { - platform_udc_clk_resume(); - } else { - platform_udc_clk_suspend(); + if (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) { + if (temp & USB_SYS_VBUS_STATUS) { + udc->vbus_active = 1; + platform_udc_clk_resume(); + //printk(KERN_INFO "USB cable connected\n"); + } else { + reset_queues(udc); + udc->vbus_active = 0; + udc->usb_state = USB_STATE_DEFAULT; + platform_udc_clk_suspend(); + //printk("USB cable dis-connected\n"); + } + status = IRQ_HANDLED; } - - status = IRQ_HANDLED; } + spin_unlock_irqrestore(&udc->lock, flags); #endif + /* Disable ISR for OTG host mode */ + if (udc->stopped) + 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); */ + /* Need to resume? */ if (udc->usb_state == USB_STATE_SUSPENDED) if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0) @@ -1899,15 +1945,34 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) udc_controller->driver = NULL; goto out; } + printk(KERN_INFO "%s: bind to driver %s\n", + udc_controller->gadget.name, driver->driver.name); + +#if defined(CONFIG_ARCH_TEGRA) + if (udc_controller->transceiver) { + if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS)) { + /* If VBUS is not present then power down the clocks */ + udc_controller->vbus_active = 0; + platform_udc_clk_suspend(); + /* set the gadget driver and quit (don't run the controller) */ + otg_set_peripheral(udc_controller->transceiver, + &udc_controller->gadget); + goto out; + } else { + /* VBUS detected set the gadget driver and run the controller */ + otg_set_peripheral(udc_controller->transceiver, + &udc_controller->gadget); + udc_controller->transceiver->state = OTG_STATE_B_PERIPHERAL; + udc_controller->vbus_active = 1; + } + } +#endif /* Enable DR IRQ reg and Set usbcmd reg Run bit */ dr_controller_run(udc_controller); udc_controller->usb_state = USB_STATE_ATTACHED; udc_controller->ep0_state = WAIT_FOR_SETUP; udc_controller->ep0_dir = 0; - printk(KERN_INFO "%s: bind to driver %s\n", - udc_controller->gadget.name, driver->driver.name); - out: if (retval) printk("gadget driver register failed %d\n", retval); @@ -2504,6 +2569,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev) create_proc_file(); #if defined(CONFIG_ARCH_TEGRA) + /* Get the OTG transceiver. If OTG is enabled then transceiver will be set + * otherwise transceiver will be NULL */ + udc_controller->transceiver = otg_get_transceiver(); /* 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(); diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c index ec4e19746022..c55712c3f9fc 100644..100755 --- a/drivers/usb/gadget/tegra_udc.c +++ b/drivers/usb/gadget/tegra_udc.c @@ -38,6 +38,9 @@ int tegra_udc_clk_init(struct platform_device *pdev) if (nverr != NvSuccess) return -ENODEV; + /* Power up the USB phy */ + NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(s_hUsbPhy, NV_FALSE, 0)); + return 0; } @@ -51,10 +54,10 @@ void tegra_udc_clk_release(void) void tegra_udc_clk_suspend(void) { - NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerDown(s_hUsbPhy, 0)); + NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerDown(s_hUsbPhy, NV_FALSE, 0)); } void tegra_udc_clk_resume(void) { - NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(s_hUsbPhy, 0)); + NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(s_hUsbPhy, NV_FALSE, 0)); } diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 698e5f7f365e..71e59c4ee723 100644..100755 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -37,11 +37,100 @@ #include "nvrm_hardware_access.h" #include "nvddk_usbphy.h" -#define TEGRA_USB_ID_INT_ENABLE (1 << 0) -#define TEGRA_USB_ID_INT_STATUS (1 << 1) -#define TEGRA_USB_ID_PIN_STATUS (1 << 2) -#define TEGRA_USB_ID_PIN_WAKEUP_ENABLE (1 << 6) -#define TEGRA_USB_PHY_WAKEUP_REG_OFFSET (0x408) +#define TEGRA_USB_ID_INT_ENABLE (1 << 0) +#define TEGRA_USB_ID_INT_STATUS (1 << 1) +#define TEGRA_USB_ID_PIN_STATUS (1 << 2) +#define TEGRA_USB_ID_PIN_WAKEUP_ENABLE (1 << 6) +#define TEGRA_USB_PHY_WAKEUP_REG_OFFSET (0x408) +#define TEGRA_USB_USBMODE_REG_OFFSET (0x1a8) +#define TEGRA_USB_USBMODE_HOST (3) + + +static int tegra_ehci_hub_control ( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) { + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + u32 __iomem *status_reg = &ehci->regs->port_status[ + (wIndex & 0xff) - 1]; + u32 temp; + struct tegra_hcd_platform_data *pdata; + unsigned long flags; + int retval = 0; + + /* initialize the platform data pointer */ + pdata = hcd->self.controller->platform_data; + + /* In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits + * that are write on clear, by wrting back the register read value, so + * USB_PORT_FEAT_ENABLE is handled here by masking the set on clear bits */ + if ((typeReq == ClearPortFeature) && (wValue == USB_PORT_FEAT_ENABLE)) { + spin_lock_irqsave (&ehci->lock, flags); + temp = ehci_readl(ehci, status_reg); + ehci_writel(ehci, (temp & ~PORT_RWC_BITS) & ~PORT_PE, status_reg); + spin_unlock_irqrestore (&ehci->lock, flags); + return retval; + } + + /* Handle the hub control events here */ + retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); + + /* Power down the USB phy when there is no port connection and all + * HUB events are cleared by checking the lower four bits + * (PORT_CONNECT | PORT_CSC | PORT_PE | PORT_PEC) */ + if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG) + && ehci->transceiver) { + if (ehci->transceiver->state == OTG_STATE_A_SUSPEND) { + temp = ehci_readl(ehci, status_reg); + if (!(temp & (PORT_CONNECT | PORT_CSC | PORT_PE | PORT_PEC)) + && ehci->host_reinited) { + NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0); + ehci->transceiver->state = OTG_STATE_UNDEFINED; + ehci->host_reinited = 0; + } + } + } + + return retval; +} + + +static void tegra_ehci_restart (struct usb_hcd *hcd) +{ + unsigned int temp; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + /* Set to Host mode by setting bit 0-1 of USB device mode register */ + temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); + writel((temp | TEGRA_USB_USBMODE_HOST), + (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET)); + + /* reset the ehci controller */ + ehci->controller_resets_phy = 0; + ehci_reset(ehci); + ehci->controller_resets_phy = 1; + /* setup the frame list and Async q heads */ + ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); + ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); + /* setup the command register and set the controller in RUN mode */ + ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + ehci->command |= CMD_RUN; + ehci_writel(ehci, ehci->command, &ehci->regs->command); + + down_write(&ehci_cf_port_reset_rwsem); + hcd->state = HC_STATE_RUNNING; + /* unblock posted writes */ + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); + ehci_readl(ehci, &ehci->regs->command); + up_write(&ehci_cf_port_reset_rwsem); + + /* Turn On Interrupts */ + ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); +} static void tegra_ehci_shutdown (struct usb_hcd *hcd) { @@ -51,13 +140,14 @@ static void tegra_ehci_shutdown (struct usb_hcd *hcd) /* ehci_shutdown touches the USB controller registers, make sure * controller has clocks to it, if controller is already in power up * status, calling the NvDdkUsbPhyPowerUp will just return */ - NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(pdata->hUsbPhy, 0)); + NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0)); /* call ehci shut down */ ehci_shutdown(hcd); /* we are ready to shut down, powerdown the phy */ - NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerDown(pdata->hUsbPhy, 0)); + NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0)); } + static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); @@ -68,19 +158,38 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) spin_lock (&ehci->lock); - if (pdata->pUsbProperty->IdPinDetectionType == - NvOdmUsbIdPinType_CableId) { - /* read otgsc register for ID pin status change */ - status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); - writel(status, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET)); - - /* Check if there is any ID pin interrupt */ - if (status & TEGRA_USB_ID_INT_STATUS) { - /* Check pin status and enable/disable the power */ - if (status & TEGRA_USB_ID_PIN_STATUS) { - NvDdkUsbPhyPowerDown(pdata->hUsbPhy, 0); - } else { - NvDdkUsbPhyPowerUp(pdata->hUsbPhy, 0); + if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG) + && ehci->transceiver) { + if (ehci->transceiver->state == OTG_STATE_A_HOST) { + if (!ehci->host_reinited) { + ehci->host_reinited = 1; + NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0); + tegra_ehci_restart(hcd); + } + } else if (ehci->transceiver->state == OTG_STATE_A_SUSPEND) { + if (!ehci->host_reinited) { + spin_unlock (&ehci->lock); + return IRQ_HANDLED; + } + } else { + spin_unlock (&ehci->lock); + return IRQ_HANDLED; + } + } else { + if (pdata->pUsbProperty->IdPinDetectionType == + NvOdmUsbIdPinType_CableId) { + /* read otgsc register for ID pin status change */ + status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); + writel(status, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET)); + + /* Check if there is any ID pin interrupt */ + if (status & TEGRA_USB_ID_INT_STATUS) { + /* Check pin status and enable/disable the power */ + if (status & TEGRA_USB_ID_PIN_STATUS) { + NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0); + } else { + NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0); + } } } } @@ -90,6 +199,7 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) return ehci_irq(hcd); } + static int tegra_ehci_reinit(struct usb_hcd *hcd) { struct tegra_hcd_platform_data *pdata; @@ -99,6 +209,7 @@ static int tegra_ehci_reinit(struct usb_hcd *hcd) NV_CHECK_ERROR_CLEANUP(NvDdkUsbPhyOpen(s_hRmGlobal, pdata->instance, &pdata->hUsbPhy)); + NV_CHECK_ERROR_CLEANUP(NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0)); return 0; @@ -178,7 +289,7 @@ static const struct hc_driver tegra_ehci_hc_driver = { .endpoint_disable = ehci_endpoint_disable, .get_frame_number = ehci_get_frame, .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, + .hub_control = tegra_ehci_hub_control, .bus_suspend = tegra_ehci_bus_suspend, .bus_resume = tegra_ehci_bus_resume, .relinquish_port = ehci_relinquish_port, @@ -266,8 +377,9 @@ static int tegra_ehci_probe(struct platform_device *pdev) hcd->regs = vaddr; /* Set to Host mode by setting bit 0-1 of USB device mode register */ - temp = readl(hcd->regs + 0x1a8); - writel((temp | 0x3), (hcd->regs + 0x1a8)); + temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); + writel((temp | TEGRA_USB_USBMODE_HOST), + (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET)); irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, instance), 0); @@ -280,19 +392,43 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail; platform_set_drvdata(pdev, hcd); - if (pdata->pUsbProperty->IdPinDetectionType == - NvOdmUsbIdPinType_CableId) { - /* enable the cable ID interrupt */ - temp = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); - temp |= TEGRA_USB_ID_INT_ENABLE; - temp |= TEGRA_USB_ID_PIN_WAKEUP_ENABLE; - writel(temp, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET)); - - /* Check if we detect any device connected */ - if (temp & TEGRA_USB_ID_PIN_STATUS) { - NvDdkUsbPhyPowerDown(pdata->hUsbPhy, 0); + if (pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG) { + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + ehci->transceiver = otg_get_transceiver(); + if (ehci->transceiver) { + otg_set_host(ehci->transceiver, (struct usb_bus *)hcd); + /* Stop the controller and power down the phy, OTG will start the + * host driver based on the ID pin detection */ + ehci_halt(ehci); + /* reset the host and put the controller in idle mode */ + temp = ehci_readl(ehci, &ehci->regs->command); + temp |= CMD_RESET; + ehci_writel(ehci, temp, &ehci->regs->command); + temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); + writel((temp & ~TEGRA_USB_USBMODE_HOST), + (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET)); + NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0); + ehci->host_reinited = 0; } else { - NvDdkUsbPhyPowerUp(pdata->hUsbPhy, 0); + dev_err(&pdev->dev, "Cannot get OTG transceiver\n"); + e = -ENODEV; + goto fail; + } + } else { + if (pdata->pUsbProperty->IdPinDetectionType == + NvOdmUsbIdPinType_CableId) { + /* enable the cable ID interrupt */ + temp = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); + temp |= TEGRA_USB_ID_INT_ENABLE; + temp |= TEGRA_USB_ID_PIN_WAKEUP_ENABLE; + writel(temp, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET)); + + /* Check if we detect any device connected */ + if (temp & TEGRA_USB_ID_PIN_STATUS) { + NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0); + } else { + NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0); + } } } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 3126273c95ea..091e26e40850 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -106,6 +106,13 @@ struct ehci_hcd { /* one per controller */ unsigned long suspended_ports; /* which ports are suspended */ +#ifdef CONFIG_USB_OTG_UTILS + /* + * Transceiver decleration for OTG support; + */ + struct otg_transceiver *transceiver; +#endif + /* per-HC memory pools (could be per-bus, but ...) */ struct dma_pool *qh_pool; /* qh per active urb */ struct dma_pool *qtd_pool; /* one or more per qh */ @@ -126,6 +133,7 @@ struct ehci_hcd { /* one per controller */ unsigned big_endian_desc:1; unsigned has_amcc_usb23:1; unsigned controller_resets_phy:1; + unsigned host_reinited:1; /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) @@ -230,6 +238,9 @@ static void free_cached_itd_list(struct ehci_hcd *ehci); /*-------------------------------------------------------------------------*/ #include <linux/usb/ehci_def.h> +#ifdef CONFIG_USB_OTG_UTILS +#include <linux/usb/otg.h> +#endif /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index ee55b449ffde..0aed0b7b7fa5 100644..100755 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -17,6 +17,15 @@ if USB || USB_GADGET # # USB Transceiver Drivers # +config USB_TEGRA_OTG + boolean "NVIDIA Tegra OTG support" + depends on USB && ARCH_TEGRA && USB_EHCI_HCD && USB_GADGET_TEGRA + select USB_OTG_UTILS + help + This driver enables support for the OTG in NVIDIA Tegra SoCs by + providing simple transceiver interface for detecting the Host or + Device based on the USBID and VBUS sensors. + config USB_GPIO_VBUS tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" depends on GENERIC_GPIO diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index d73c7cf5e2f7..f1119a773efb 100644..100755 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o # transceiver drivers +obj-$(CONFIG_USB_TEGRA_OTG) += tegra-otg.o obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c new file mode 100755 index 000000000000..832939e1875d --- /dev/null +++ b/drivers/usb/otg/tegra-otg.c @@ -0,0 +1,273 @@ +/* + * tegra-otg.c + * + * OTG driver for detecting the USB ID and VBUS for NVIDIA Tegra SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/usb.h> +#include <linux/usb/otg.h> +#include <linux/usb/gadget.h> +#include <linux/tegra_devices.h> +#include <linux/platform_device.h> +#include <asm/io.h> +#include "../core/hcd.h" +#include "mach/nvrm_linux.h" + +#define TEGRA_USB_ID_INT_ENABLE (1 << 0) +#define TEGRA_USB_ID_INT_STATUS (1 << 1) +#define TEGRA_USB_ID_STATUS (1 << 2) +#define TEGRA_USB_ID_PIN_WAKEUP_ENABLE (1 << 6) +#define TEGRA_USB_VBUS_WAKEUP_ENABLE (1 << 30) +#define TEGRA_USB_VBUS_INT_ENABLE (1 << 8) +#define TEGRA_USB_VBUS_INT_STATUS (1 << 9) +#define TEGRA_USB_VBUS_STATUS (1 << 10) +#define TEGRA_USB_WAKEUP_REG_OFFSET (0x408) + +static const char driver_name[] = "tegra-otg"; + +/* + * Needs to be loaded before the UDC and Host driver that will use it. + */ +struct tegra_otg_data { + struct otg_transceiver otg; + struct device *dev; + spinlock_t lock; + int irq; /* irq allocated */ + void __iomem *regs; /* device memory/io */ + int instance; /* instance number of the controller */ + NvDdkUsbPhyHandle usb_phy; /* handle to the USB phy */ +}; + +static struct tegra_otg_data *sg_tegra_otg = NULL; + + +/* VBUS change IRQ handler */ +static irqreturn_t tegra_otg_irq(int irq, void *data) +{ + struct platform_device *pdev = data; + struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev); + struct usb_hcd *hcd = (struct usb_hcd *)tegra_otg->otg.host; + unsigned int status; + unsigned long flags; + + spin_lock_irqsave(&tegra_otg->lock, flags); + + status = readl(tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET); + writel(status, tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET); + + if (tegra_otg->otg.host) { + /* Check if there is any ID pin interrupt */ + if (status & TEGRA_USB_ID_INT_STATUS) { + if (status & TEGRA_USB_ID_STATUS) { + tegra_otg->otg.state = OTG_STATE_A_SUSPEND; + } else { + tegra_otg->otg.state = OTG_STATE_A_HOST; + hcd->state = HC_STATE_RUNNING; + } + } + } + + if (tegra_otg->otg.gadget && (tegra_otg->otg.state != OTG_STATE_A_HOST)) { + if (status & TEGRA_USB_VBUS_INT_STATUS) { + if (status & TEGRA_USB_VBUS_STATUS) { + tegra_otg->otg.state = OTG_STATE_B_PERIPHERAL; + } else { + tegra_otg->otg.state = OTG_STATE_A_SUSPEND; + } + } + } + spin_unlock_irqrestore(&tegra_otg->lock, flags); + return IRQ_HANDLED; +} + +/* OTG transceiver interface */ +static int tegra_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + unsigned int temp; + unsigned long flags; + + otg->gadget = gadget; + temp = readl(sg_tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET); + temp |= (TEGRA_USB_VBUS_INT_ENABLE | TEGRA_USB_VBUS_WAKEUP_ENABLE); + temp &= ~TEGRA_USB_VBUS_INT_STATUS; + writel(temp, (sg_tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET)); + + spin_lock_irqsave(&sg_tegra_otg->lock, flags); + /* Check if we detect any device connected */ + if (!(temp & TEGRA_USB_ID_STATUS)) { + otg->state = OTG_STATE_A_HOST; + NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_TRUE, 0); + } + spin_unlock_irqrestore(&sg_tegra_otg->lock, flags); + + return 0; +} + +static int tegra_otg_set_host(struct otg_transceiver *otg, + struct usb_bus *host) +{ + unsigned int temp; + struct tegra_otg_platform_data *pdata; + + pdata = sg_tegra_otg->dev->platform_data; + otg->host = host; + + if (pdata->usb_property->IdPinDetectionType == + NvOdmUsbIdPinType_CableId) { + /* enable the cable ID interrupt */ + temp = readl(sg_tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET); + temp |= (TEGRA_USB_ID_INT_ENABLE | TEGRA_USB_ID_PIN_WAKEUP_ENABLE); + writel(temp, (sg_tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET)); + } + + return 0; +} + +/* effective for B devices, ignored for A-peripheral */ +static int tegra_otg_set_power(struct otg_transceiver *otg, unsigned mA) +{ + /* Draw from the Host in device mode */ + + return 0; +} + +/* for non-OTG B devices: set/clear transceiver suspend mode */ +static int tegra_otg_set_suspend(struct otg_transceiver *otg, int suspend) +{ + /* Draw 0mA in the suspend mode */ + return 0; +} + +/* platform driver interface */ +static int __init tegra_otg_probe(struct platform_device *pdev) +{ + int err = 0; + struct resource *res; + int instance = pdev->id; + NvError e; + + sg_tegra_otg = kzalloc(sizeof(struct tegra_otg_data), GFP_KERNEL); + if (!sg_tegra_otg) + return -ENOMEM; + + spin_lock_init(&sg_tegra_otg->lock); + platform_set_drvdata(pdev, sg_tegra_otg); + + NV_CHECK_ERROR_CLEANUP( + NvDdkUsbPhyOpen(s_hRmGlobal, instance, &sg_tegra_otg->usb_phy) + ); + NV_CHECK_ERROR_CLEANUP( + NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_TRUE, 0) + ); + sg_tegra_otg->instance = pdev->id; + sg_tegra_otg->dev = &pdev->dev; + sg_tegra_otg->otg.label = driver_name; + sg_tegra_otg->otg.state = OTG_STATE_UNDEFINED; + sg_tegra_otg->otg.set_peripheral = tegra_otg_set_peripheral; + sg_tegra_otg->otg.set_host = tegra_otg_set_host; + sg_tegra_otg->otg.set_power = tegra_otg_set_power; + sg_tegra_otg->otg.set_suspend = tegra_otg_set_suspend; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENXIO; + goto err_irq; + } + + sg_tegra_otg->regs = ioremap(res->start, resource_size(res)); + if (!sg_tegra_otg->regs) { + err = -ENOMEM; + goto err_irq; + } + + sg_tegra_otg->irq = platform_get_irq(pdev, 0); + if (!sg_tegra_otg->irq) { + err = -ENODEV; + goto err_irq; + } + + err = request_irq(sg_tegra_otg->irq, tegra_otg_irq, IRQF_SHARED, + driver_name, pdev); + if (err) { + printk("cannot request irq %d err %d\n", + sg_tegra_otg->irq, err); + goto err_mem_map; + } + + /* only active when a gadget is registered */ + err = otg_set_transceiver(&sg_tegra_otg->otg); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_otg; + } + + NvDdkUsbPhyPowerDown(sg_tegra_otg->usb_phy, NV_TRUE, 0); + + return 0; + +err_otg: + free_irq(sg_tegra_otg->irq, &pdev->dev); +err_mem_map: + iounmap(sg_tegra_otg->regs); +err_irq: + NvDdkUsbPhyClose(sg_tegra_otg->usb_phy); +fail: + platform_set_drvdata(pdev, NULL); + kfree(sg_tegra_otg); + return err; +} + +static int __exit tegra_otg_remove(struct platform_device *pdev) +{ + struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev); + + otg_set_transceiver(NULL); + free_irq(tegra_otg->irq, &pdev->dev); + iounmap(tegra_otg->regs); + NvDdkUsbPhyClose(tegra_otg->usb_phy); + platform_set_drvdata(pdev, NULL); + kfree(tegra_otg); + sg_tegra_otg = NULL; + + return 0; +} + +static struct platform_driver tegra_otg_driver = { + .driver = { + .name = driver_name, + }, + .remove = __exit_p(tegra_otg_remove), + .probe = tegra_otg_probe, +}; + +static int __init tegra_otg_init(void) +{ + return platform_driver_register(&tegra_otg_driver); +} +module_init(tegra_otg_init); + +static void __exit tegra_otg_exit(void) +{ + platform_driver_unregister(&tegra_otg_driver); +} +module_exit(tegra_otg_exit); + +MODULE_DESCRIPTION("Tegra OTG driver"); diff --git a/include/linux/tegra_devices.h b/include/linux/tegra_devices.h index 93d7324d2e37..143037a4a64e 100644..100755 --- a/include/linux/tegra_devices.h +++ b/include/linux/tegra_devices.h @@ -48,10 +48,16 @@ struct tegra_hcd_platform_data { * macrocell interface) PHY on USB controllers 0 and 2. These 2 PHYs * have its own rails. */ - NvU32 phyPowerRail; + NvU32 phyPowerRail; NvDdkUsbPhyHandle hUsbPhy; }; +/* Platform data for USB OTG driver */ +struct tegra_otg_platform_data { + NvU32 instance; + const NvOdmUsbProperty *usb_property; +}; + /* Platfrom data for I2C bus driver */ struct tegra_i2c_platform_data { NvU32 IoModuleID; |