diff options
author | Venkat Moganty <vmoganty@nvidia.com> | 2010-04-29 20:44:29 +0530 |
---|---|---|
committer | Yu-Huan Hsu <yhsu@nvidia.com> | 2010-05-02 17:58:43 -0700 |
commit | 1b8406f6f9556f8d205ebf01035350e587c11764 (patch) | |
tree | 4086f6d63860feb729938e6e44d79973981e632c | |
parent | 7eb7a42ddadfe5dbcb529e7d0e908dbb0c1c2b18 (diff) |
tegra usb:Modifications to usb power up/down sequence
Removed helper thread and replaced it with Worker Queues in udc and ehci
drivers to handle usbphy power up/down sequence. Made changes to turn off
usb power rail based on vbus detection mechanism is selected as PMU.
Fixed usb host LP0 exit sequence.
Bug 667912: AVDD_USB_Power is consuming 3.82mW of power in OSIdle and ULP
audio playback case.
Change-Id: I3a77d0ecb4f0b81dafe705100451c42641f0bfb9
Reviewed-on: http://git-master/r/1221
Tested-by: Hanumanth Venkateswa Moganty <vmoganty@nvidia.com>
Tested-by: Dara Ramesh <dramesh@nvidia.com>
Reviewed-by: Seshendra Gadagottu <sgadagottu@nvidia.com>
Tested-by: Seshendra Gadagottu <sgadagottu@nvidia.com>
Reviewed-by: Narendra Damahe <ndamahe@nvidia.com>
Tested-by: Narendra Damahe <ndamahe@nvidia.com>
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/include/nvodm_services.h | 7 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvddk/nvddk_usbphy.c | 298 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h | 10 | ||||
-rwxr-xr-x | arch/arm/mach-tegra/nvodm/nvodm_services.c | 83 | ||||
-rwxr-xr-x | drivers/usb/gadget/fsl_udc_core.c | 113 | ||||
-rwxr-xr-x | drivers/usb/gadget/fsl_usb2_udc.h | 3 | ||||
-rwxr-xr-x | drivers/usb/host/ehci-tegra.c | 296 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 1 | ||||
-rwxr-xr-x | drivers/usb/otg/tegra-otg.c | 8 |
9 files changed, 537 insertions, 282 deletions
diff --git a/arch/arm/mach-tegra/include/nvodm_services.h b/arch/arm/mach-tegra/include/nvodm_services.h index 904821ca8049..53b4fa5eecd7 100644 --- a/arch/arm/mach-tegra/include/nvodm_services.h +++ b/arch/arm/mach-tegra/include/nvodm_services.h @@ -1684,6 +1684,13 @@ NvBool NvOdmUsbIsConnected(void); */ NvOdmUsbChargerType NvOdmUsbChargingType(NvU32 Instance); +/** + * Enables/Disables the USB power rail. + * + * @param Enable NV_TRUE to enable, or NV_FALSE to disable. + */ +void NvOdmEnableUsbPhyPowerRail(NvBool Enable); + #if defined(__cplusplus) } #endif diff --git a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c index 05827683bfce..7f15b34f6b9c 100644 --- a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c +++ b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c @@ -44,6 +44,7 @@ #include "nvrm_pmu.h" #include "nvrm_hardware_access.h" #include "nvddk_usbphy_priv.h" +#include "nvodm_services.h" #define MAX_USB_INSTANCES 5 @@ -53,31 +54,8 @@ static NvDdkUsbPhy *s_pUsbPhy = NULL; static NvDdkUsbPhyUtmiPadConfig *s_pUtmiPadConfig = NULL; +static NvOsMutexHandle s_UsbPhyMutex = NULL; -static NvBool -UsbPhyDiscover( - NvDdkUsbPhy *pUsbPhy) -{ - NvU64 guid = NV_VDD_USB_ODM_ID; - NvOdmPeripheralConnectivity const *pConnectivity; - - if( pUsbPhy->pConnectivity ) - { - return NV_TRUE; - } - - /* get the connectivity info */ - pConnectivity = NvOdmPeripheralGetGuid( guid ); - if( !pConnectivity ) - { - return NV_FALSE; - } - - pUsbPhy->Guid = guid; - pUsbPhy->pConnectivity = pConnectivity; - - return NV_TRUE; -} static void UsbPrivEnableVbus(NvDdkUsbPhy *pUsbPhy, NvBool Enable) { @@ -125,62 +103,42 @@ static void UsbPrivEnableVbus(NvDdkUsbPhy *pUsbPhy, NvBool Enable) } } -static void -UsbPhyPowerRailEnable( - NvDdkUsbPhy *pUsbPhy, - NvBool Enable) +static NvBool UsbPhyTurnOffPowerRail(NvU32 MaxInstances) { - NvU32 i; - NvOdmPeripheralConnectivity const *pConnectivity; - NvU32 settle_time_us; + NvBool TurnOff = NV_FALSE; + NvU32 instance = 0; + const NvOdmUsbProperty *pProperty = NULL; - /* get the peripheral config */ - if( !UsbPhyDiscover( pUsbPhy ) ) + for (instance = 0; instance < MaxInstances; instance++) { - // Do nothing if no power rail info is discovered - return; - } - - /* enable the power rail */ - pConnectivity = pUsbPhy->pConnectivity; + pProperty = NvOdmQueryGetUsbProperty(NvOdmIoModule_Usb, instance); - if (Enable) - { - for( i = 0; i < pConnectivity->NumAddress; i++ ) + if (pProperty) { - if( pConnectivity->AddressList[i].Interface == NvOdmIoModule_Vdd ) + if (pProperty->UsbMode == NvOdmUsbModeType_None) { - NvRmPmuVddRailCapabilities cap; - - /* address is the vdd rail id */ - NvRmPmuGetCapabilities( - pUsbPhy->hRmDevice, - pConnectivity->AddressList[i].Address, &cap ); - - /* set the rail volatage to the recommended */ - NvRmPmuSetVoltage( - pUsbPhy->hRmDevice, pConnectivity->AddressList[i].Address, - cap.requestMilliVolts, &settle_time_us ); - - /* wait for the rail to settle */ - NvOsWaitUS( settle_time_us ); + continue; } - } - } - else - { - for( i = 0; i < pConnectivity->NumAddress; i++ ) - { - if( pConnectivity->AddressList[i].Interface == NvOdmIoModule_Vdd ) + else if (((pProperty->UsbMode == NvOdmUsbModeType_Device) || + (pProperty->UsbMode == NvOdmUsbModeType_OTG)) && + (!pProperty->UseInternalPhyWakeup)) + { + TurnOff = NV_TRUE; + } + else if (((pProperty->UsbMode == NvOdmUsbModeType_Host) && + (pProperty->IdPinDetectionType == NvOdmUsbIdPinType_CableId)) && + (!pProperty->UseInternalPhyWakeup)) { - /* set the rail volatage to the recommended */ - NvRmPmuSetVoltage( - pUsbPhy->hRmDevice, pConnectivity->AddressList[i].Address, - ODM_VOLTAGE_OFF, 0 ); + TurnOff = NV_TRUE; + } + else + { + TurnOff = NV_FALSE; } } } + return TurnOff; } @@ -370,10 +328,32 @@ UsbPhyInitialize( NvRmModuleReset(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance)); - // Power Up the USB Phy - NV_CHECK_ERROR_CLEANUP(hUsbPhy->PowerUp(hUsbPhy)); - hUsbPhy->IsPhyPoweredUp = NV_TRUE; + // On AP20 H-CLK should not be turned off + // This is required to detect the sensor interrupts. + // However, phy can be programmed to put in the low power mode + if (!hUsbPhy->Caps.PhyRegInController) + { + // Disable the clock + NV_CHECK_ERROR_CLEANUP( + NvRmPowerModuleClockControl(hUsbPhy->hRmDevice, + NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), + hUsbPhy->RmPowerClientId, NV_FALSE)); + } + + // Disable power + NV_CHECK_ERROR_CLEANUP( + NvRmPowerVoltageControl(hUsbPhy->hRmDevice, + NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), + hUsbPhy->RmPowerClientId, NvRmVoltsOff, NvRmVoltsOff, + NULL, 0, NULL)); + + // if we are not turning off the power rail during power up and down + // then turn on only once during the initalization. + if (!hUsbPhy->TurnOffPowerRail) + { + NvOdmEnableUsbPhyPowerRail(NV_TRUE); + } fail: @@ -395,42 +375,6 @@ 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); - - if (!(hUsbPhy->IsPhyPoweredUp && hUsbPhy->IsHostMode)) - { - /* 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, @@ -440,6 +384,7 @@ NvDdkUsbPhyOpen( NvError e; NvU32 MaxInstances = 0; NvDdkUsbPhy *pUsbPhy = NULL; + NvOsMutexHandle UsbPhyMutex = NULL; NvRmModuleInfo info[MAX_USB_INSTANCES]; NvU32 j; @@ -466,15 +411,32 @@ NvDdkUsbPhyOpen( return NvError_ModuleNotPresent; } + if (!s_UsbPhyMutex) + { + e = NvOsMutexCreate(&UsbPhyMutex); + if (e!=NvSuccess) + return e; + + if (NvOsAtomicCompareExchange32( + (NvS32*)&s_UsbPhyMutex, 0, (NvS32)UsbPhyMutex)!=0) + { + NvOsMutexDestroy(UsbPhyMutex); + } + } + + NvOsMutexLock(s_UsbPhyMutex); if (!s_pUsbPhy) { s_pUsbPhy = NvOsAlloc(MaxInstances * sizeof(NvDdkUsbPhy)); if (s_pUsbPhy) NvOsMemset(s_pUsbPhy, 0, MaxInstances * sizeof(NvDdkUsbPhy)); - else - return NvError_InsufficientMemory; } + NvOsMutexUnlock(s_UsbPhyMutex); + + if (!s_pUsbPhy) + return NvError_InsufficientMemory; + NvOsMutexLock(s_UsbPhyMutex); if (!s_pUtmiPadConfig) { s_pUtmiPadConfig = NvOsAlloc(sizeof(NvDdkUsbPhyUtmiPadConfig)); @@ -493,17 +455,19 @@ NvDdkUsbPhyOpen( PhyAddr, s_pUtmiPadConfig->BankSize, NVOS_MEM_READ_WRITE, NvOsMemAttribute_Uncached, (void **)&s_pUtmiPadConfig->pVirAdr)); } - else - { - return NvError_InsufficientMemory; - } } + NvOsMutexUnlock(s_UsbPhyMutex); + + if (!s_pUtmiPadConfig) + return NvError_InsufficientMemory; pUsbPhy = &s_pUsbPhy[Instance]; + NvOsMutexLock(s_UsbPhyMutex); if (!pUsbPhy->RefCount) { NvRmPhysAddr PhysAddr; + NvOsMutexHandle ThreadSafetyMutex = NULL; NvOsMemset(pUsbPhy, 0, sizeof(NvDdkUsbPhy)); pUsbPhy->Instance = Instance; @@ -513,6 +477,15 @@ NvDdkUsbPhyOpen( pUsbPhy->pUtmiPadConfig = s_pUtmiPadConfig; pUsbPhy->pProperty = NvOdmQueryGetUsbProperty( NvOdmIoModule_Usb, pUsbPhy->Instance); + pUsbPhy->TurnOffPowerRail = UsbPhyTurnOffPowerRail(MaxInstances); + + NV_CHECK_ERROR_CLEANUP(NvOsMutexCreate(&ThreadSafetyMutex)); + if (NvOsAtomicCompareExchange32( + (NvS32*)&pUsbPhy->ThreadSafetyMutex, 0, + (NvS32)ThreadSafetyMutex)!=0) + { + NvOsMutexDestroy(ThreadSafetyMutex); + } NvRmModuleGetBaseAddress( pUsbPhy->hRmDevice, @@ -555,32 +528,9 @@ NvDdkUsbPhyOpen( NvRmPowerRegister(pUsbPhy->hRmDevice, pUsbPhy->hPwrEventSem, &pUsbPhy->RmPowerClientId)); - 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); - } - - #if !NV_OAL - UsbPhyPowerRailEnable(pUsbPhy, NV_TRUE); - #endif - // Open the H/W interface UsbPhyOpenHwInterface(pUsbPhy); - /* Enable the busy hints for USB device mode, for host mode * - * transaction based busy hints are on/off mechanism is present */ - if (pUsbPhy->pProperty->UsbMode != NvOdmUsbModeType_Host) - { - UsbPhyDfsBusyHint(pUsbPhy, NV_TRUE, NV_WAIT_INFINITE); - } // Initialize the USB Phy NV_CHECK_ERROR_CLEANUP(UsbPhyInitialize(pUsbPhy)); } @@ -590,13 +540,14 @@ NvDdkUsbPhyOpen( } *hUsbPhy = pUsbPhy; + NvOsMutexUnlock(s_UsbPhyMutex); return NvSuccess; fail: NvDdkUsbPhyClose(pUsbPhy); - + NvOsMutexUnlock(s_UsbPhyMutex); return e; } @@ -608,13 +559,19 @@ NvDdkUsbPhyClose( if (!hUsbPhy) return; + NvOsMutexLock(s_UsbPhyMutex); + if (!hUsbPhy->RefCount) + { + NvOsMutexUnlock(s_UsbPhyMutex); return; + } --hUsbPhy->RefCount; if (hUsbPhy->RefCount) { + NvOsMutexUnlock(s_UsbPhyMutex); return; } @@ -623,6 +580,7 @@ NvDdkUsbPhyClose( NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), NV_TRUE); + NvOsMutexLock(hUsbPhy->ThreadSafetyMutex); if (hUsbPhy->RmPowerClientId) { if (hUsbPhy->IsPhyPoweredUp) @@ -646,6 +604,9 @@ NvDdkUsbPhyClose( NvRmPowerUnRegister(hUsbPhy->hRmDevice, hUsbPhy->RmPowerClientId); NvOsSemaphoreDestroy(hUsbPhy->hPwrEventSem); } + NvOsMutexUnlock(hUsbPhy->ThreadSafetyMutex); + + NvOsMutexDestroy(hUsbPhy->ThreadSafetyMutex); if (hUsbPhy->CloseHwInterface) { @@ -658,11 +619,7 @@ NvDdkUsbPhyClose( UsbPrivEnableVbus(hUsbPhy, NV_FALSE); } - UsbPhyPowerRailEnable(hUsbPhy, NV_FALSE); - - hUsbPhy->Stopped = NV_TRUE; - NvOsThreadJoin(hUsbPhy->hThreadId); - NvOsSemaphoreDestroy(hUsbPhy->HelperThreadSema); + NvOdmEnableUsbPhyPowerRail(NV_FALSE); NvRmPhysicalMemUnmap( (void*)hUsbPhy->UsbVirAdr, hUsbPhy->UsbBankSize); @@ -671,6 +628,7 @@ NvDdkUsbPhyClose( (void*)hUsbPhy->MiscVirAdr, hUsbPhy->MiscBankSize); NvOsMemset(hUsbPhy, 0, sizeof(NvDdkUsbPhy)); + NvOsMutexUnlock(s_UsbPhyMutex); } @@ -684,14 +642,16 @@ NvDdkUsbPhyPowerUp( NV_ASSERT(hUsbPhy); + NvOsMutexLock(hUsbPhy->ThreadSafetyMutex); if (hUsbPhy->IsPhyPoweredUp) + { + NvOsMutexUnlock(hUsbPhy->ThreadSafetyMutex); return e; + } - // On wake up from Deep power down mode turn on the rails based on odm query - if (IsDpd) + if (hUsbPhy->TurnOffPowerRail) { - if (hUsbPhy->pProperty->UsbRailPoweOffInDeepSleep) - UsbPhyPowerRailEnable(hUsbPhy, NV_TRUE); + NvOdmEnableUsbPhyPowerRail(NV_TRUE); } // Enable power for USB module @@ -709,21 +669,30 @@ 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; + if (hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) { hUsbPhy->RestoreContext(hUsbPhy); } - // signal to set the busy hints and vbus - NvOsSemaphoreSignal(hUsbPhy->HelperThreadSema); - //NvOsDebugPrintf("NvDdkUsbPhyPowerUp::VOLTAGE ON\n"); + if (hUsbPhy->IsHostMode = IsHostMode) + { + UsbPrivEnableVbus(hUsbPhy, NV_TRUE); + } + else + { + /* Turn on the USB busy hints */ + UsbPhyDfsBusyHint(hUsbPhy, NV_TRUE, NV_WAIT_INFINITE); + } + hUsbPhy->IsPhyPoweredUp = NV_TRUE; + fail: + NvOsMutexUnlock(hUsbPhy->ThreadSafetyMutex); return e; } @@ -738,12 +707,23 @@ NvDdkUsbPhyPowerDown( NV_ASSERT(hUsbPhy); + NvOsMutexLock(hUsbPhy->ThreadSafetyMutex); if (!hUsbPhy->IsPhyPoweredUp) + { + NvOsMutexUnlock(hUsbPhy->ThreadSafetyMutex); return e; + } + if (hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) { hUsbPhy->SaveContext(hUsbPhy); } + + /* Turn on/off the vbus for host mode */ + if (hUsbPhy->IsHostMode = IsHostMode) + { + UsbPrivEnableVbus(hUsbPhy, NV_FALSE); + } // Power down the USB Phy NV_CHECK_ERROR_CLEANUP(hUsbPhy->PowerDown(hUsbPhy)); @@ -766,21 +746,20 @@ NvDdkUsbPhyPowerDown( hUsbPhy->RmPowerClientId, NvRmVoltsOff, NvRmVoltsOff, NULL, 0, NULL)); - // In Deep power down mode turn off the rails based on odm query - if (IsDpd) + /* Turn off the USB busy hints */ + UsbPhyDfsBusyHint(hUsbPhy, NV_FALSE, NV_WAIT_INFINITE); + + if (hUsbPhy->TurnOffPowerRail) { - if (hUsbPhy->pProperty->UsbRailPoweOffInDeepSleep) - UsbPhyPowerRailEnable(hUsbPhy, NV_FALSE); + NvOdmEnableUsbPhyPowerRail(NV_FALSE); + NvOdmEnableOtgCircuitry(NV_FALSE); } hUsbPhy->IsPhyPoweredUp = NV_FALSE; - hUsbPhy->IsHostMode = IsHostMode; - NvOsSemaphoreSignal(hUsbPhy->HelperThreadSema); - - //NvOsDebugPrintf("NvDdkUsbPhyPowerDown::VOLTAGE OFF\n"); fail: + NvOsMutexUnlock(hUsbPhy->ThreadSafetyMutex); return e; } @@ -818,4 +797,3 @@ NvDdkUsbPhyIoctl( } return ErrStatus; } - diff --git a/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h b/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h index d4199b45983b..8088eb63a1f9 100644 --- a/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h +++ b/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h @@ -172,12 +172,10 @@ typedef struct NvDdkUsbPhyRec NvDdkUsbPhyUtmiPadConfig *pUtmiPadConfig; // Usb Controller context NvDdkUsbPhyControllerContext Context; - // Thread ID for the helper thread - NvOsThreadHandle hThreadId; - // semphore for signaling the thread - NvOsSemaphoreHandle HelperThreadSema; - // variable to control the thread loop - NvBool Stopped; + // Contains the mutex for providing the thread safety + NvOsMutexHandle ThreadSafetyMutex; + // Indicator for turning off the USB power rail + NvBool TurnOffPowerRail; // Indicates phy powered up for the host mode NvBool IsHostMode; // Set of function pointers to access the usb phy hardware interface. diff --git a/arch/arm/mach-tegra/nvodm/nvodm_services.c b/arch/arm/mach-tegra/nvodm/nvodm_services.c index 97e950e59d9b..c965cdd9fdbc 100755 --- a/arch/arm/mach-tegra/nvodm/nvodm_services.c +++ b/arch/arm/mach-tegra/nvodm/nvodm_services.c @@ -935,13 +935,90 @@ NvOdmPwmConfig(NvOdmServicesPwmHandle hOdmPwm, pCurrentFreqHzOrPeriod); } +void +NvOdmEnableUsbPhyPowerRail( + NvBool Enable) +{ + NvU32 i; + NvU32 settle_time_us; + NvU64 guid = NV_VDD_USB_ODM_ID; + NvOdmPeripheralConnectivity const *pConnectivity; + NvRmDeviceHandle hRmDevice; + /* get the connectivity info */ + pConnectivity = NvOdmPeripheralGetGuid( guid ); + if( !pConnectivity ) + { + // Do nothing if no power rail info is discovered + return; + } + + if (NvRmOpen(&hRmDevice, 0) != NvSuccess) + { + return; + } + + /* enable the power rail */ + if (Enable) + { + for( i = 0; i < pConnectivity->NumAddress; i++ ) + { + if( pConnectivity->AddressList[i].Interface == NvOdmIoModule_Vdd ) + { + NvRmPmuVddRailCapabilities cap; + + /* address is the vdd rail id */ + NvRmPmuGetCapabilities( + hRmDevice, + pConnectivity->AddressList[i].Address, &cap ); + + /* set the rail volatage to the recommended */ + NvRmPmuSetVoltage( + hRmDevice, pConnectivity->AddressList[i].Address, + cap.requestMilliVolts, &settle_time_us ); + + /* wait for the rail to settle */ + NvOsWaitUS( settle_time_us ); + } + } + } + else + { + for( i = 0; i < pConnectivity->NumAddress; i++ ) + { + if( pConnectivity->AddressList[i].Interface == NvOdmIoModule_Vdd ) + { + /* set the rail volatage to the recommended */ + NvRmPmuSetVoltage( + hRmDevice, pConnectivity->AddressList[i].Address, + ODM_VOLTAGE_OFF, 0 ); + } + } + } + + + NvRmClose(hRmDevice); +} + + void NvOdmEnableOtgCircuitry(NvBool Enable) { - // Rm analog interface calls related to usb are deleted. This API does nothing. - // This API should not be called for usb phy related operations - return; + const NvOdmUsbProperty *pProperty = NULL; + static NvBool s_PowerEnabled = NV_FALSE; + + if ((s_PowerEnabled && Enable) || (!s_PowerEnabled && !Enable)) + return; + + pProperty = NvOdmQueryGetUsbProperty(NvOdmIoModule_Usb, 0); + + if (pProperty && (!pProperty->UseInternalPhyWakeup) && + ((pProperty->UsbMode == NvOdmUsbModeType_Device) || + (pProperty->UsbMode == NvOdmUsbModeType_OTG))) + { + NvOdmEnableUsbPhyPowerRail(s_PowerEnabled = Enable); + } } + NvBool NvOdmUsbIsConnected(void) { NV_ASSERT("Not Supported "); diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 199034307a37..cbf6313585c3 100755 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -74,8 +74,8 @@ static struct usb_sys_interface *usb_sys_regs; /* Charger current limit=1800mA, as per the USB charger spec */ #define USB_CHARGING_CURRENT_LIMIT_MA 1800 -/* 100msec wait time for charger detection after vbus is detected */ -#define USB_CHARGER_DETECTION_WAIT_TIME_MS 100 +/* 1 sec wait time for charger detection after vbus is detected */ +#define USB_CHARGER_DETECTION_WAIT_TIME_MS 1000 /* it is initialized in probe() */ static struct fsl_udc *udc_controller = NULL; @@ -1830,22 +1830,21 @@ static void fsl_udc_restart(struct fsl_udc *udc) } #endif + /* - * USB device controller interrupt handler + * Work thread function for handling the USB power sequence. + * + * This work thread is created to avoid the pre-emption from the ISR context. + * USB Power Rail is controlled based on the USB cable connection. + * USB Power rail function cannot be called from ISR as NvRmPmuSetVoltage() + * uses I2C driver, that waits on semaphore during the I2C transaction + * this will cause the pre-emption if called in ISR. */ -static irqreturn_t fsl_udc_irq(int irq, void *_udc) +static void fsl_udc_irq_work(struct work_struct* irq_work) { - struct fsl_udc *udc = _udc; - u32 irq_src; - irqreturn_t status = IRQ_NONE; - unsigned long flags; -#if defined(CONFIG_ARCH_TEGRA) + struct fsl_udc *udc = container_of (irq_work, struct fsl_udc, irq_work); u32 temp; -#endif - -#if defined(CONFIG_ARCH_TEGRA) - spin_lock_irqsave(&udc->lock, flags); /* check OTG tranceiver is available or not */ if (udc->transceiver) { if (udc->transceiver->state == OTG_STATE_B_PERIPHERAL) { @@ -1863,8 +1862,10 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) if (udc->vbus_active) { /* If cable disconnected, cancel any delayed work */ cancel_delayed_work(&udc->work); + spin_lock(&udc->lock); /* Reset all internal Queues and inform client driver */ reset_queues(udc); + spin_unlock(&udc->lock); /* stop the controller and turn off the clocks */ dr_controller_stop(udc); platform_udc_clk_suspend(); @@ -1876,17 +1877,12 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) regulator_set_current_limit(udc->vbus_regulator, 0, 0); } } - } else { - 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 (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) { if (temp & USB_SYS_VBUS_STATUS) { udc->vbus_active = 1; @@ -1899,7 +1895,9 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) } else { /* If cable disconnected, cancel any delayed work */ cancel_delayed_work(&udc->work); + spin_lock(&udc->lock); reset_queues(udc); + spin_unlock(&udc->lock); udc->vbus_active = 0; udc->usb_state = USB_STATE_DEFAULT; platform_udc_clk_suspend(); @@ -1909,9 +1907,55 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) } /* printk("USB cable dis-connected\n"); */ } - status = IRQ_HANDLED; } } +} + +/* + * USB device controller interrupt handler + */ +static irqreturn_t fsl_udc_irq(int irq, void *_udc) +{ + struct fsl_udc *udc = _udc; + u32 irq_src; + irqreturn_t status = IRQ_NONE; + unsigned long flags; +#if defined(CONFIG_ARCH_TEGRA) + u32 temp; +#endif + + +#if defined(CONFIG_ARCH_TEGRA) + 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) { + schedule_work(&udc->irq_work); + } + } else if (udc->transceiver->state == OTG_STATE_A_SUSPEND) { + if (udc->vbus_active) { + schedule_work(&udc->irq_work); + } else { + spin_unlock_irqrestore(&udc->lock, flags); + return IRQ_HANDLED; + } + } else { + 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 (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) { + schedule_work(&udc->irq_work); + } + status = IRQ_HANDLED; + } spin_unlock_irqrestore(&udc->lock, flags); #endif @@ -2648,6 +2692,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev) /* create a delayed work for detecting the USB charger */ INIT_DELAYED_WORK(&udc_controller->work, fsl_udc_charger_detection); + /* create a work for controlling the clocks to the Phy */ + INIT_WORK(&udc_controller->irq_work, fsl_udc_irq_work); + #if defined(CONFIG_ARCH_TEGRA) #ifdef CONFIG_USB_OTG_UTILS /* Get the OTG transceiver. If OTG is enabled then transceiver @@ -2737,8 +2784,20 @@ static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state) if (udc_controller->transceiver->state != OTG_STATE_B_PERIPHERAL) { /* we are not in device mode, return */ return 0; + } else { + udc_controller->transceiver->state = OTG_STATE_UNDEFINED; } } + if (udc_controller->vbus_active) + { + spin_lock(&udc_controller->lock); + /* Reset all internal Queues and inform client driver */ + reset_queues(udc_controller); + udc_controller->vbus_active = 0; + udc_controller->usb_state = USB_STATE_DEFAULT; + spin_unlock(&udc_controller->lock); + } + /* stop the controller and turn off the clocks */ dr_controller_stop(udc_controller); platform_udc_clk_suspend(); return 0; @@ -2751,12 +2810,22 @@ static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state) static int fsl_udc_resume(struct platform_device *pdev) { if (udc_controller->transceiver) { - if (udc_controller->transceiver->state != OTG_STATE_B_PERIPHERAL) { - /* we are not in device mode, return */ + if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_ID_PIN_STATUS)) { + /* If ID status is low means host is connected, return */ return 0; } + /* enable clock and check for VBUS */ + platform_udc_clk_resume(); + if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS)) { + /* if there is no VBUS then power down the clocks and return */ + platform_udc_clk_suspend(); + return 0; + } + } else { + /* enable the clocks to the controller */ + platform_udc_clk_resume(); } - platform_udc_clk_resume(); + #if defined(CONFIG_ARCH_TEGRA) fsl_udc_restart(udc_controller); #else diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index d2225d89ad66..5cdd6e983c8c 100755 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -447,6 +447,8 @@ struct ep_td_struct { #define USB_SYS_VBUS_WAKEUP_INT_ENABLE 0x100 #define USB_SYS_VBUS_WAKEUP_INT_STATUS 0x200 #define USB_SYS_VBUS_STATUS 0x400 +#define USB_SYS_ID_PIN_STATUS (0x4) + /*-------------------------------------------------------------------------*/ @@ -515,6 +517,7 @@ struct fsl_udc { u8 device_address; /* Device USB address */ struct regulator *vbus_regulator; /* regulator for drawing VBUS */ struct delayed_work work; /* delayed work for charger detection */ + struct work_struct irq_work; /* irq work for controling the usb power*/ }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 5c69667bd6ca..53301cb4342e 100755 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -54,22 +54,20 @@ * uses vfree and vmalloc functions which are not supposed to call from the * ISR context */ - static void tegra_ehci_busy_hint_work(struct work_struct* work) { - struct tegra_hcd_platform_data *pdata = - container_of(work, struct tegra_hcd_platform_data, work); - NvDdkUsbPhyIoctl_UsbBusyHintsOnOffInputArgs busyhint; - busyhint.OnOff = NV_TRUE; - - /* USB transfers will be done with in 1 sec, this need to be * - * fine tuned (if required). with safe limit set to 2 sec */ - busyhint.BoostDurationMs = 2000; - NvDdkUsbPhyIoctl( - pdata->hUsbPhy, - NvDdkUsbPhyIoctlType_UsbBusyHintsOnOff, - &busyhint, - NULL); + struct tegra_hcd_platform_data *pdata = + container_of(work, struct tegra_hcd_platform_data, work); + NvDdkUsbPhyIoctl_UsbBusyHintsOnOffInputArgs busyhint; + busyhint.OnOff = NV_TRUE; + + /* USB transfers will be done with in 1 sec, this need to be * + * fine tuned (if required). with safe limit set to 2 sec */ + busyhint.BoostDurationMs = 2000; + NvDdkUsbPhyIoctl(pdata->hUsbPhy, + NvDdkUsbPhyIoctlType_UsbBusyHintsOnOff, + &busyhint, + NULL); } static void tegra_ehci_power_up(struct usb_hcd *hcd) @@ -157,7 +155,6 @@ static int tegra_ehci_hub_control ( return retval; } -#ifdef CONFIG_USB_OTG_UTILS static void tegra_ehci_restart (struct usb_hcd *hcd) { unsigned int temp; @@ -190,7 +187,6 @@ static void tegra_ehci_restart (struct usb_hcd *hcd) /* Turn On Interrupts */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); } -#endif static void tegra_ehci_shutdown (struct usb_hcd *hcd) { @@ -203,6 +199,53 @@ static void tegra_ehci_shutdown (struct usb_hcd *hcd) tegra_ehci_power_down(hcd); } +/* + * Work thread function for handling the USB power sequence. + * + * This work thread is created to avoid the pre-emption from the ISR context. + * USB Power Rail and Vbus are controlled based on the USB cable connection. + * USB Power rail function and VBUS control function cannot be called from ISR + * as NvRmPmuSetVoltage() uses I2C driver, that waits on semaphore + * during the I2C transaction this will cause the pre-emption if called in ISR. + */ +static void tegra_ehci_irq_work(struct work_struct* irq_work) +{ + struct ehci_hcd *ehci = container_of(irq_work, struct ehci_hcd, irq_work); + struct usb_hcd *hcd = ehci_to_hcd(ehci); + struct tegra_hcd_platform_data *pdata; + u32 status; + + pdata = hcd->self.controller->platform_data; + +#ifdef CONFIG_USB_OTG_UTILS + if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG) + && ehci->transceiver) { + if (ehci->transceiver->state == OTG_STATE_A_HOST) { + if (!ehci->host_reinited) { + ehci->host_reinited = 1; + tegra_ehci_power_up(hcd); + tegra_ehci_restart(hcd); + } + } + } else +#endif + { + if (pdata->pUsbProperty->IdPinDetectionType == + NvOdmUsbIdPinType_CableId) { + /* read otgsc register for ID pin status change */ + status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); + /* Check pin status and enable/disable the power */ + if (status & TEGRA_USB_ID_PIN_STATUS) { + tegra_ehci_power_down(hcd); + } else { + tegra_ehci_power_up(hcd); + } + } + } +} + + + static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); @@ -218,9 +261,7 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) && ehci->transceiver) { if (ehci->transceiver->state == OTG_STATE_A_HOST) { if (!ehci->host_reinited) { - ehci->host_reinited = 1; - tegra_ehci_power_up(hcd); - tegra_ehci_restart(hcd); + schedule_work(&ehci->irq_work); } } else if (ehci->transceiver->state == OTG_STATE_A_SUSPEND) { if (!ehci->host_reinited) { @@ -242,12 +283,7 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) /* 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) { - tegra_ehci_power_down(hcd); - } else { - tegra_ehci_power_up(hcd); - } + schedule_work(&ehci->irq_work); } } } @@ -317,40 +353,45 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) return retval; } -#if defined(CONFIG_PM) + static int tegra_ehci_bus_suspend(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int err_status = 0; - -#ifdef CONFIG_USB_OTG_UTILS struct tegra_hcd_platform_data *pdata; + /* initialize the platform data pointer */ pdata = hcd->self.controller->platform_data; + +#ifdef CONFIG_USB_OTG_UTILS if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG) && ehci->transceiver) { if (ehci->transceiver->state != OTG_STATE_A_HOST) { /* we are not in host mode, return */ - return err_status; + return 0; } } #endif - if (ehci->host_resumed) { - err_status = ehci_bus_suspend(hcd); - if (!err_status) - tegra_ehci_power_down(hcd); + if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_Host) + && (pdata->pUsbProperty->IdPinDetectionType == + NvOdmUsbIdPinType_CableId)) { + u32 status; + /* check for ID pin status */ + status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); + /* If Id pin is high means host is not connected return */ + if (status & TEGRA_USB_ID_PIN_STATUS) { + return 0; + } } - return err_status; + + return ehci_bus_suspend(hcd); } static int tegra_ehci_bus_resume(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int err_status = 0; - u32 status; - struct tegra_hcd_platform_data *pdata; + /* initialize the platform data pointer */ pdata = hcd->self.controller->platform_data; @@ -359,67 +400,61 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd) && ehci->transceiver) { if (ehci->transceiver->state != OTG_STATE_A_HOST) { /* we are not in host mode, return */ - return err_status; + return 0; } } #endif - if (!ehci->host_resumed) { - tegra_ehci_power_up(hcd); - err_status = ehci_bus_resume(hcd); - } - - if ((pdata->pUsbProperty->UsbMode != NvOdmUsbModeType_OTG) + if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_Host) && (pdata->pUsbProperty->IdPinDetectionType == NvOdmUsbIdPinType_CableId)) { - /* read otgsc register for ID pin status */ + u32 status; + /* read ID pin status */ status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); - writel(status, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET)); - /* If no Id pin then disable the power */ + /* If Id pin is high no host then return */ if (status & TEGRA_USB_ID_PIN_STATUS) { - tegra_ehci_power_down(hcd); + return 0; } - } - return err_status; -} -#endif + } + return ehci_bus_resume(hcd); +} static int tegra_ehci_urb_enqueue( - struct usb_hcd *hcd, - struct urb *urb, - gfp_t mem_flags + struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags ) { - struct tegra_hcd_platform_data *pdata; - int xfertype; - int transfer_buffer_length; - - pdata = hcd->self.controller->platform_data; - - xfertype = usb_endpoint_type(&urb->ep->desc); - transfer_buffer_length = urb->transfer_buffer_length; - /* Turn on the USB busy hints */ - switch (xfertype) { - case USB_ENDPOINT_XFER_INT: - if (transfer_buffer_length < 255) { - /* Do nothing for interrupt buffers < 255 */ - } else { - // signal to set the busy hints - schedule_work(&pdata->work); - } - break; - case USB_ENDPOINT_XFER_ISOC: - case USB_ENDPOINT_XFER_BULK: - // signal to set the busy hints - schedule_work(&pdata->work); - break; - case USB_ENDPOINT_XFER_CONTROL: - default: - /* Do nothing special here */ - break; - } - - return ehci_urb_enqueue(hcd, urb, mem_flags); + struct tegra_hcd_platform_data *pdata; + int xfertype; + int transfer_buffer_length; + + pdata = hcd->self.controller->platform_data; + + xfertype = usb_endpoint_type(&urb->ep->desc); + transfer_buffer_length = urb->transfer_buffer_length; + /* Turn on the USB busy hints */ + switch (xfertype) { + case USB_ENDPOINT_XFER_INT: + if (transfer_buffer_length < 255) { + /* Do nothing for interrupt buffers < 255 */ + } else { + // signal to set the busy hints + schedule_work(&pdata->work); + } + break; + case USB_ENDPOINT_XFER_ISOC: + case USB_ENDPOINT_XFER_BULK: + // signal to set the busy hints + schedule_work(&pdata->work); + break; + case USB_ENDPOINT_XFER_CONTROL: + default: + /* Do nothing special here */ + break; + } + + return ehci_urb_enqueue(hcd, urb, mem_flags); } static const struct hc_driver tegra_ehci_hc_driver = { @@ -458,6 +493,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) int irq; unsigned int temp; static u64 dummy_mask = DMA_32BIT_MASK; + struct ehci_hcd *ehci; pdata = (struct tegra_hcd_platform_data *)pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "Cannot run without platform data\n"); @@ -531,6 +567,9 @@ static int tegra_ehci_probe(struct platform_device *pdev) set_irq_flags(irq, IRQF_VALID); + ehci = hcd_to_ehci(hcd); + INIT_WORK(&ehci->irq_work, tegra_ehci_irq_work); + e = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (e != 0) goto fail; @@ -538,7 +577,6 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS 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); @@ -587,6 +625,86 @@ fail: return e; } +#if defined(CONFIG_PM) +static int tegra_ehci_resume(struct platform_device * pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct tegra_hcd_platform_data *pdata; + u32 status; + + /* initialize the platform data pointer */ + pdata = hcd->self.controller->platform_data; + + /* read otgsc register for ID pin status */ + status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET); + +#ifdef CONFIG_USB_OTG_UTILS + if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG) + && ehci->transceiver) { + /* check if ID pin is high then no host return */ + if (status & TEGRA_USB_ID_PIN_STATUS) { + return 0; + } + else { + /* set HCD flags to start host ISR */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + ehci->host_reinited = 1; + ehci->transceiver->state = OTG_STATE_A_HOST; + } + } +#endif + + if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_Host) + && (pdata->pUsbProperty->IdPinDetectionType == + NvOdmUsbIdPinType_CableId)) { + /* If no Id pin then return */ + if (status & TEGRA_USB_ID_PIN_STATUS) { + return 0; + } + } + + if (!ehci->host_resumed) { + tegra_ehci_power_up(hcd); + tegra_ehci_restart(hcd); + } + + return 0; +} + +static int tegra_ehci_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + +#ifdef CONFIG_USB_OTG_UTILS + struct tegra_hcd_platform_data *pdata; + /* initialize the platform data pointer */ + pdata = hcd->self.controller->platform_data; + if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG) + && ehci->transceiver) { + if (ehci->transceiver->state != OTG_STATE_A_HOST) { + /* we are not in host mode, return */ + return 0; + } + else { + ehci->transceiver->state = OTG_STATE_UNDEFINED; + ehci->host_reinited = 0; + ehci_halt(ehci); + /* indicate hcd flags, that hardware is not accessable now */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + } + } +#endif + + if (ehci->host_resumed) { + tegra_ehci_power_down(hcd); + } + + return 0; +} +#endif + static int tegra_ehci_remove(struct platform_device *pdev) { struct tegra_hcd_platform_data *pdata = pdev->dev.platform_data; @@ -612,6 +730,10 @@ static struct platform_driver tegra_ehci_driver = { .probe = tegra_ehci_probe, .remove = tegra_ehci_remove, +#if defined(CONFIG_PM) + .suspend = tegra_ehci_suspend, + .resume = tegra_ehci_resume, +#endif .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "tegra-ehci", diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 1e4394d4f363..ed2ec5b5e47d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -135,6 +135,7 @@ struct ehci_hcd { /* one per controller */ unsigned controller_resets_phy:1; unsigned host_reinited:1; /* tegra */ unsigned host_resumed:1; /* tegra */ + struct work_struct irq_work; /* tegra irq work for power control*/ /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c index a17d8d90f44f..1f300308fb78 100755 --- a/drivers/usb/otg/tegra-otg.c +++ b/drivers/usb/otg/tegra-otg.c @@ -111,16 +111,16 @@ static int tegra_otg_set_peripheral(struct otg_transceiver *otg, 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)) { struct usb_hcd *hcd = (struct usb_hcd *)otg->host; + spin_lock_irqsave(&sg_tegra_otg->lock, flags); otg->state = OTG_STATE_A_HOST; /* set HCD flags to start host ISR */ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + spin_unlock_irqrestore(&sg_tegra_otg->lock, flags); NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_TRUE, 0); } - spin_unlock_irqrestore(&sg_tegra_otg->lock, flags); return 0; } @@ -179,7 +179,7 @@ static int __init tegra_otg_probe(struct platform_device *pdev) NvDdkUsbPhyOpen(s_hRmGlobal, instance, &sg_tegra_otg->usb_phy) ); NV_CHECK_ERROR_CLEANUP( - NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_TRUE, 0) + NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_FALSE, 0) ); sg_tegra_otg->instance = pdev->id; sg_tegra_otg->dev = &pdev->dev; @@ -224,7 +224,7 @@ static int __init tegra_otg_probe(struct platform_device *pdev) goto err_otg; } - NvDdkUsbPhyPowerDown(sg_tegra_otg->usb_phy, NV_TRUE, 0); + NvDdkUsbPhyPowerDown(sg_tegra_otg->usb_phy, NV_FALSE, 0); return 0; |