summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVenkat Moganty <vmoganty@nvidia.com>2010-04-29 20:44:29 +0530
committerYu-Huan Hsu <yhsu@nvidia.com>2010-05-02 17:58:43 -0700
commit1b8406f6f9556f8d205ebf01035350e587c11764 (patch)
tree4086f6d63860feb729938e6e44d79973981e632c
parent7eb7a42ddadfe5dbcb529e7d0e908dbb0c1c2b18 (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.h7
-rw-r--r--arch/arm/mach-tegra/nvddk/nvddk_usbphy.c298
-rw-r--r--arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h10
-rwxr-xr-xarch/arm/mach-tegra/nvodm/nvodm_services.c83
-rwxr-xr-xdrivers/usb/gadget/fsl_udc_core.c113
-rwxr-xr-xdrivers/usb/gadget/fsl_usb2_udc.h3
-rwxr-xr-xdrivers/usb/host/ehci-tegra.c296
-rw-r--r--drivers/usb/host/ehci.h1
-rwxr-xr-xdrivers/usb/otg/tegra-otg.c8
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;