diff options
Diffstat (limited to 'arch/arm/mach-tegra/nvrm/core/common/nvrm_pmu.c')
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/common/nvrm_pmu.c | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_pmu.c b/arch/arm/mach-tegra/nvrm/core/common/nvrm_pmu.c new file mode 100644 index 000000000000..405a7eeff267 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_pmu.c @@ -0,0 +1,608 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvrm_pmu.h" +#include "nvodm_pmu.h" +#include "nvassert.h" +#include "nvrm_interrupt.h" +#include "nvrm_moduleids.h" +#include "nvrm_pmu_private.h" +#include "nvrm_power_private.h" +#include "nvrm_clocks.h" +#include "nvrm_structure.h" +#include "nvodm_query_discovery.h" +#include "nvodm_query.h" + + +// TODO: after testing is completed, remove this macro +// "1" - keep PMU rails always On, "0" - allows to power Off PMU rails +#define PMU_RAILS_NEVER_OFF (0) + +// Retry count for voltage control API +#define VOLTAGE_CONTROL_RETRY_CNT (2) + +/* + * Combines RM PMU object data + */ +typedef struct NvRmPmuRec +{ + // RM PMU access mutex + NvOsMutexHandle hMutex; + + // ODM PMU device handle + NvOdmPmuDeviceHandle hOdmPmu; + + // PMU interrupt handle + NvOsInterruptHandle hInterrupt; + + // PMU ISR semaphore + NvOsSemaphoreHandle hSemaphore; + + // PMU interrupt thread + NvOsThreadHandle hThread; + + // PMU interrupt mask state + volatile NvBool IntrMasked; + + // PMU interrupt thread abort indicator + volatile NvBool AbortThread; + + // IO power rails level detect mask + NvU32 IoPwrDetectMask; + + // Unpowered IO rails mask + NvU32 NoIoPwrMask; +} NvRmPmu; + +// RM PMU object +static NvRmPmu s_Pmu; + +// PMU supported execution environment flag +static NvBool s_PmuSupportedEnv = NV_FALSE; + +/*****************************************************************************/ + +static void PmuIsr(void* args) +{ + NvRmPmu* pPmu = (NvRmPmu*)args; + NvOsSemaphoreSignal(pPmu->hSemaphore); +} + +static void PmuThread(void* args) +{ + NvRmPmu* pPmu = (NvRmPmu*)args; + + for (;;) + { + NvOsSemaphoreWait(pPmu->hSemaphore); + if (pPmu->AbortThread) + { + break; + } + + NvOsMutexLock(pPmu->hMutex); + if (pPmu->IntrMasked) + { + NvOsMutexUnlock(pPmu->hMutex); + continue; + } + NvOdmPmuInterruptHandler(pPmu->hOdmPmu); + NvOsMutexUnlock(pPmu->hMutex); + + if (pPmu->hInterrupt) + NvRmInterruptDone(pPmu->hInterrupt); + } +} + +static void PmuThreadTerminate(NvRmPmu* pPmu) +{ + /* + * Request thread abort, signal semaphore to make sure the thread is + * awaken and wait for its self-termination. Do nothing if invalid PMU + * structure + */ + if (pPmu) + { + if (pPmu->hSemaphore && pPmu->hThread) + { + pPmu->AbortThread = NV_TRUE; + NvOsSemaphoreSignal(pPmu->hSemaphore); + NvOsThreadJoin(pPmu->hThread); + } + pPmu->AbortThread = NV_FALSE; + } +} + +/*****************************************************************************/ + +NvError NvRmPrivPmuInit(NvRmDeviceHandle hRmDevice) +{ + NvError e; + ExecPlatform env; + NvOdmPmuProperty PmuProperty; + + NV_ASSERT(hRmDevice); + env = NvRmPrivGetExecPlatform(hRmDevice); + + NvOsMemset(&s_Pmu, 0, sizeof(NvRmPmu)); + s_PmuSupportedEnv = NV_FALSE; + + if (env == ExecPlatform_Soc) + { + // Set supported environment flag + s_PmuSupportedEnv = NV_TRUE; + + // Create the PMU mutex, semaphore, interrupt handler thread, + // register PMU interrupt, and get ODM PMU handle + NV_CHECK_ERROR_CLEANUP(NvOsMutexCreate(&s_Pmu.hMutex)); + NV_CHECK_ERROR_CLEANUP(NvOsSemaphoreCreate(&s_Pmu.hSemaphore, 0)); + + if (NvOdmQueryGetPmuProperty(&PmuProperty) && PmuProperty.IrqConnected) + { + if (hRmDevice->ChipId.Id >= 0x20) + NvRmPrivAp20SetPmuIrqPolarity( + hRmDevice, PmuProperty.IrqPolarity); + else + NV_ASSERT(PmuProperty.IrqPolarity == + NvOdmInterruptPolarity_Low); + { + NvOsInterruptHandler hPmuIsr = PmuIsr; + NvU32 PmuExtIrq = NvRmGetIrqForLogicalInterrupt( + hRmDevice, NVRM_MODULE_ID(NvRmPrivModuleID_PmuExt, 0), 0); + NV_CHECK_ERROR_CLEANUP(NvRmInterruptRegister(hRmDevice, 1, + &PmuExtIrq, &hPmuIsr, &s_Pmu, &s_Pmu.hInterrupt, NV_FALSE)); + } + } + + if(!NvOdmPmuDeviceOpen(&s_Pmu.hOdmPmu)) + { + e = NvError_NotInitialized; + goto fail; + } + NV_CHECK_ERROR_CLEANUP(NvOsThreadCreate(PmuThread, &s_Pmu, &s_Pmu.hThread)); + NvRmPrivIoPowerControlInit(hRmDevice); + NvRmPrivCoreVoltageInit(hRmDevice); + } + return NvSuccess; + +fail: + NvRmPrivPmuDeinit(hRmDevice); + return e; +} + +void NvRmPrivPmuInterruptEnable(NvRmDeviceHandle hRmDevice) +{ + if (s_Pmu.hInterrupt) + NvRmInterruptEnable(hRmDevice, s_Pmu.hInterrupt); +} + +void NvRmPrivPmuInterruptMask(NvRmDeviceHandle hRmDevice, NvBool mask) +{ + if (s_Pmu.hInterrupt) + { + + NvOsMutexLock(s_Pmu.hMutex); + s_Pmu.IntrMasked = mask; + NvOsMutexUnlock(s_Pmu.hMutex); + + if (!mask) + NvRmInterruptDone(s_Pmu.hInterrupt); + } +} + +void NvRmPrivPmuDeinit(NvRmDeviceHandle hRmDevice) +{ + if (s_PmuSupportedEnv == NV_FALSE) + return; + + PmuThreadTerminate(&s_Pmu); + NvOdmPmuDeviceClose(s_Pmu.hOdmPmu); + NvRmInterruptUnregister(hRmDevice, s_Pmu.hInterrupt); + NvOsSemaphoreDestroy(s_Pmu.hSemaphore); + NvOsMutexDestroy(s_Pmu.hMutex); + + NvOsMemset(&s_Pmu, 0, sizeof(NvRmPmu)); + s_PmuSupportedEnv = NV_FALSE; +} +/*****************************************************************************/ + +void NvRmPmuGetCapabilities( + NvRmDeviceHandle hDevice, + NvU32 vddId, + NvRmPmuVddRailCapabilities * pCapabilities ) +{ + NvOdmPmuVddRailCapabilities RailCap; + + if (!s_PmuSupportedEnv) + return; + + NvOdmPmuGetCapabilities(vddId, &RailCap); + pCapabilities->MaxMilliVolts = RailCap.MaxMilliVolts; + pCapabilities->MinMilliVolts = RailCap.MinMilliVolts; + pCapabilities->requestMilliVolts = RailCap.requestMilliVolts; + pCapabilities->RmProtected = RailCap.OdmProtected; + pCapabilities->StepMilliVolts = RailCap.StepMilliVolts; +} + +void NvRmPmuGetVoltage( + NvRmDeviceHandle hDevice, + NvU32 vddId, + NvU32 * pMilliVolts) +{ + NvU32 i; + if (!s_PmuSupportedEnv) + return; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + for (i = 0; i < VOLTAGE_CONTROL_RETRY_CNT; i++) + { + if (NvOdmPmuGetVoltage(s_Pmu.hOdmPmu, vddId, pMilliVolts)) + break; + } + NV_ASSERT(i < VOLTAGE_CONTROL_RETRY_CNT); + NvOsMutexUnlock(s_Pmu.hMutex); +} + +void NvRmPmuSetVoltage( + NvRmDeviceHandle hDevice, + NvU32 vddId, + NvU32 MilliVolts, + NvU32 * pSettleMicroSeconds) +{ + NvU32 i; + NvU32 t = NVRM_PWR_DET_DELAY_US; + NV_ASSERT(hDevice); + + if (pSettleMicroSeconds) + *pSettleMicroSeconds = 0; + + if (!s_PmuSupportedEnv) + return; + + // This API is blocked if diagnostic is in progress for any module + if (NvRmPrivIsDiagMode(NvRmModuleID_Invalid)) + return; + +#if PMU_RAILS_NEVER_OFF + if (MilliVolts == ODM_VOLTAGE_OFF) + return; +#endif + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + + // Set voltage and latch IO level sampling results + for (i = 0; i < VOLTAGE_CONTROL_RETRY_CNT; i++) + { + if (NvOdmPmuSetVoltage(s_Pmu.hOdmPmu, vddId, MilliVolts, pSettleMicroSeconds)) + break; + } + NV_ASSERT(i < VOLTAGE_CONTROL_RETRY_CNT); + + if (s_Pmu.IoPwrDetectMask || s_Pmu.NoIoPwrMask) + { + NV_ASSERT(MilliVolts != ODM_VOLTAGE_OFF); + if (pSettleMicroSeconds) + { + t += (*pSettleMicroSeconds); + *pSettleMicroSeconds = 0; // Don't wait twice + } + NvOsWaitUS(t); + + if (s_Pmu.IoPwrDetectMask) // Latch just powered IO rails + NvRmPrivIoPowerDetectLatch(hDevice); + if (s_Pmu.NoIoPwrMask) // Enable just powered IO rails + NvRmPrivIoPowerControl(hDevice, s_Pmu.NoIoPwrMask, NV_TRUE); + s_Pmu.IoPwrDetectMask = s_Pmu.NoIoPwrMask = 0; + } + NvOsMutexUnlock(s_Pmu.hMutex); +} + +void +NvRmPmuSetSocRailPowerState( + NvRmDeviceHandle hDevice, + NvU32 vddId, + NvBool Enable) +{ + NV_ASSERT(hDevice); + NvRmPrivSetSocRailPowerState( + hDevice, vddId, Enable, &s_Pmu.IoPwrDetectMask, &s_Pmu.NoIoPwrMask); +} + +/*****************************************************************************/ + +void NvRmPmuSetChargingCurrentLimit( + NvRmDeviceHandle hRmDevice, + NvRmPmuChargingPath ChargingPath, + NvU32 ChargingCurrentLimitMa, + NvU32 ChargerType) +{ + NvU32 i; + + if (!s_PmuSupportedEnv) + return; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + for (i = 0; i < VOLTAGE_CONTROL_RETRY_CNT; i++) + { + if (NvOdmPmuSetChargingCurrent( + s_Pmu.hOdmPmu, (NvOdmPmuChargingPath)ChargingPath, + ChargingCurrentLimitMa, ChargerType)) + break; + } + NV_ASSERT(i < VOLTAGE_CONTROL_RETRY_CNT); + NvOsMutexUnlock(s_Pmu.hMutex); +} + +/*****************************************************************************/ + +NvBool NvRmPmuGetAcLineStatus( + NvRmDeviceHandle hRmDevice, + NvRmPmuAcLineStatus * pStatus ) +{ + NvBool ReturnStatus = NV_FALSE; + + if (!s_PmuSupportedEnv) + return NV_FALSE; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + ReturnStatus = + NvOdmPmuGetAcLineStatus(s_Pmu.hOdmPmu, (NvOdmPmuAcLineStatus*)pStatus); + NvOsMutexUnlock(s_Pmu.hMutex); + return ReturnStatus; +} + + NvBool NvRmPmuGetBatteryStatus( + NvRmDeviceHandle hRmDevice, + NvRmPmuBatteryInstance batteryInst, + NvU8 * pStatus ) +{ + NvBool ReturnStatus = NV_FALSE; + + if (!s_PmuSupportedEnv) + return NV_FALSE; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + ReturnStatus = NvOdmPmuGetBatteryStatus( + s_Pmu.hOdmPmu, (NvOdmPmuBatteryInstance)batteryInst, pStatus); + NvOsMutexUnlock(s_Pmu.hMutex); + return ReturnStatus; +} + +/*****************************************************************************/ + +NvBool NvRmPmuGetBatteryData( + NvRmDeviceHandle hRmDevice, + NvRmPmuBatteryInstance batteryInst, + NvRmPmuBatteryData * pData ) +{ + NvOdmPmuBatteryData BatteryData; + + if (!s_PmuSupportedEnv) + return NV_FALSE; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + if (NvOdmPmuGetBatteryData( + s_Pmu.hOdmPmu, (NvOdmPmuBatteryInstance)batteryInst, &BatteryData)) + { + pData->batteryAverageCurrent = BatteryData.batteryAverageCurrent; + pData->batteryAverageInterval = BatteryData.batteryAverageInterval; + pData->batteryCurrent = BatteryData.batteryCurrent; + pData->batteryLifePercent = BatteryData.batteryLifePercent; + pData->batteryLifeTime = BatteryData.batteryLifeTime; + pData->batteryMahConsumed = BatteryData.batteryMahConsumed; + pData->batteryTemperature = BatteryData.batteryTemperature; + pData->batteryVoltage = BatteryData.batteryVoltage; + NvOsMutexUnlock(s_Pmu.hMutex); + return NV_TRUE; + } + NvOsMutexUnlock(s_Pmu.hMutex); + return NV_FALSE; +} + +void NvRmPmuGetBatteryFullLifeTime( + NvRmDeviceHandle hRmDevice, + NvRmPmuBatteryInstance batteryInst, + NvU32 * pLifeTime ) +{ + if (!s_PmuSupportedEnv) + return; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + NvOdmPmuGetBatteryFullLifeTime( + s_Pmu.hOdmPmu,(NvOdmPmuBatteryInstance)batteryInst, pLifeTime); + NvOsMutexUnlock(s_Pmu.hMutex); +} + +void NvRmPmuGetBatteryChemistry( + NvRmDeviceHandle hRmDevice, + NvRmPmuBatteryInstance batteryInst, + NvRmPmuBatteryChemistry * pChemistry ) +{ + if (!s_PmuSupportedEnv) + return; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + NvOdmPmuGetBatteryChemistry(s_Pmu.hOdmPmu, + (NvOdmPmuBatteryInstance)batteryInst, + (NvOdmPmuBatteryChemistry*)pChemistry); + NvOsMutexUnlock(s_Pmu.hMutex); +} + +/*****************************************************************************/ + +NvBool +NvRmPmuReadRtc( + NvRmDeviceHandle hRmDevice, + NvU32* pCount) +{ + NvBool ReturnStatus = NV_FALSE; + + if (!s_PmuSupportedEnv) + return NV_FALSE; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + ReturnStatus = NvOdmPmuReadRtc(s_Pmu.hOdmPmu, pCount); + NvOsMutexUnlock(s_Pmu.hMutex); + return ReturnStatus; +} + +NvBool +NvRmPmuWriteRtc( + NvRmDeviceHandle hRmDevice, + NvU32 Count) +{ + NvBool ReturnStatus = NV_FALSE; + + if (!s_PmuSupportedEnv) + return NV_FALSE; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + ReturnStatus = NvOdmPmuWriteRtc(s_Pmu.hOdmPmu, Count); + NvOsMutexUnlock(s_Pmu.hMutex); + return ReturnStatus; +} + +NvBool +NvRmPmuIsRtcInitialized( + NvRmDeviceHandle hRmDevice) +{ + NvBool ReturnStatus = NV_FALSE; + + if (!s_PmuSupportedEnv) + return NV_FALSE; + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + ReturnStatus = NvOdmPmuIsRtcInitialized(s_Pmu.hOdmPmu); + NvOsMutexUnlock(s_Pmu.hMutex); + return ReturnStatus; +} + +/*****************************************************************************/ + +NvBool +NvRmPrivDiagPmuSetVoltage( + NvRmDeviceHandle hDevice, + NvU32 vddId, + NvU32 MilliVolts, + NvU32 * pSettleMicroSeconds) +{ + NvU32 i; + + if (pSettleMicroSeconds) + *pSettleMicroSeconds = 0; + + NV_ASSERT(s_PmuSupportedEnv); + NV_ASSERT(NvRmPrivIsDiagMode(NvRmModuleID_Invalid)); + + NV_ASSERT(s_Pmu.hMutex); + NvOsMutexLock(s_Pmu.hMutex); + for (i = 0; i < VOLTAGE_CONTROL_RETRY_CNT; i++) + { + if (NvOdmPmuSetVoltage(s_Pmu.hOdmPmu, vddId, MilliVolts, pSettleMicroSeconds)) + break; + } + NvOsMutexUnlock(s_Pmu.hMutex); + + return (i < VOLTAGE_CONTROL_RETRY_CNT); +} + +/*****************************************************************************/ + +void +NvRmPrivPmuRailControl( + NvRmDeviceHandle hRmDevice, + NvU64 NvRailId, + NvBool TurnOn) +{ + NvU32 RailAddress, TimeUs; + const NvOdmPeripheralConnectivity* pPmuRail; + NvRmPmuVddRailCapabilities RailCapabilities = {0}; + + if (!s_PmuSupportedEnv) + return; + + pPmuRail = NvOdmPeripheralGetGuid(NvRailId); + + NV_ASSERT(hRmDevice); + NV_ASSERT(pPmuRail); + NV_ASSERT(pPmuRail->NumAddress); + + RailAddress = pPmuRail->AddressList[0].Address; + if (TurnOn) + { + NvRmPmuGetCapabilities(hRmDevice, RailAddress, &RailCapabilities); + NvRmPmuSetVoltage(hRmDevice, RailAddress, + RailCapabilities.requestMilliVolts, &TimeUs); + } + else + { + NvRmPmuSetVoltage( + hRmDevice, RailAddress, ODM_VOLTAGE_OFF, &TimeUs); + } + NvOsWaitUS(TimeUs); +} + +NvU32 +NvRmPrivPmuRailGetVoltage( + NvRmDeviceHandle hRmDevice, + NvU64 NvRailId) +{ + NvU32 RailAddress; + const NvOdmPeripheralConnectivity* pPmuRail; + NvU32 MilliVolts = NVRM_NO_PMU_DEFAULT_VOLTAGE; + + if (s_PmuSupportedEnv) + { + pPmuRail = NvOdmPeripheralGetGuid(NvRailId); + + NV_ASSERT(hRmDevice); + NV_ASSERT(pPmuRail); + NV_ASSERT(pPmuRail->NumAddress); + + RailAddress = pPmuRail->AddressList[0].Address; + NvRmPmuGetVoltage(hRmDevice, RailAddress, &MilliVolts); + } + return MilliVolts; +} + +/*****************************************************************************/ |