summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c')
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c568
1 files changed, 568 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c
new file mode 100644
index 000000000000..32ec996f6c9b
--- /dev/null
+++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c
@@ -0,0 +1,568 @@
+/*
+ * 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.
+ *
+ */
+
+/**
+ * @file
+ * @brief <b>nVIDIA Driver Development Kit:
+ * Power Resource manager </b>
+ *
+ * @b Description: Implements the interface of the NvRM Power.
+ *
+ */
+
+#include "nvrm_power_private.h"
+#include "nvrm_pmu.h"
+#include "nvrm_pmu_private.h"
+#include "nvassert.h"
+#include "nvrm_drf.h"
+#include "nvrm_hwintf.h"
+#include "nvodm_query_discovery.h"
+#include "ap15rm_private.h"
+#include "ap15rm_clocks.h"
+#include "ap15/arapbpm.h"
+#include "ap15/project_relocation_table.h"
+
+// TODO: Always Disable before check-in
+// Module debug: 0=disable, 1=enable
+#define NVRM_ENABLE_PRINTF (0)
+
+#if (NV_DEBUG && NVRM_ENABLE_PRINTF)
+#define NVRM_POWER_PRINTF(x) NvOsDebugPrintf x
+#else
+#define NVRM_POWER_PRINTF(x)
+#endif
+
+
+#if !NV_OAL
+/*****************************************************************************/
+/*****************************************************************************/
+
+#define NV_POWER_GATE_TD (1)
+// TODO: check PCIE voltage control calls before enabling
+#define NV_POWER_GATE_PCIE (0)
+// TODO: check VDE/BSEV/NSEA voltage control calls before enabling
+#define NV_POWER_GATE_VDE (0)
+// TODO: check MPE voltage control calls before enabling
+#define NV_POWER_GATE_MPE (0)
+
+// Power Group -to- Power Gating Ids mapping
+static const NvU32* s_PowerGroupIds = NULL;
+
+/*****************************************************************************/
+
+static NvBool IsPowerGateSupported(NvU32 PowerGroup)
+{
+ // 1st check h/w support capabilities
+ NV_ASSERT(s_PowerGroupIds);
+ if (s_PowerGroupIds[PowerGroup] == NV_POWERGROUP_INVALID)
+ return NV_FALSE;
+
+ // now check s/w support
+ switch (PowerGroup)
+ {
+ case NV_POWERGROUP_TD:
+ return NV_POWER_GATE_TD;
+ case NV_POWERGROUP_PCIE:
+ return NV_POWER_GATE_PCIE;
+ case NV_POWERGROUP_VDE:
+ return NV_POWER_GATE_VDE;
+ case NV_POWERGROUP_MPE:
+ return NV_POWER_GATE_MPE;
+ default:
+ return NV_FALSE;
+ }
+}
+
+static void PowerGroupResetControl(
+ NvRmDeviceHandle hRmDeviceHandle,
+ NvU32 PowerGroup,
+ NvBool Assert)
+{
+ switch (PowerGroup)
+ {
+ case NV_POWERGROUP_TD:
+ NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_3D, Assert);
+ break;
+ case NV_POWERGROUP_PCIE:
+ if (Assert) // Keep PHY in reset - let driver to take it out
+ NvRmModuleResetWithHold(
+ hRmDeviceHandle, NvRmPrivModuleID_PcieXclk, Assert);
+ NvRmModuleResetWithHold(
+ hRmDeviceHandle, NvRmPrivModuleID_Pcie, Assert);
+ NvRmModuleResetWithHold(
+ hRmDeviceHandle, NvRmPrivModuleID_Afi, Assert);
+ break;
+ case NV_POWERGROUP_VDE:
+ NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_Vde, Assert);
+ break;
+ case NV_POWERGROUP_MPE:
+ NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_Mpe, Assert);
+ break;
+ default:
+ break;
+ }
+}
+
+static void PowerGroupClockControl(
+ NvRmDeviceHandle hRmDeviceHandle,
+ NvU32 PowerGroup,
+ NvBool Enable)
+{
+ ModuleClockState ClockState =
+ Enable ? ModuleClockState_Enable : ModuleClockState_Disable;
+
+ switch (PowerGroup)
+ {
+ case NV_POWERGROUP_TD:
+ NvRmPrivEnableModuleClock(
+ hRmDeviceHandle, NvRmModuleID_3D, ClockState);
+ break;
+ case NV_POWERGROUP_PCIE:
+ NvRmPrivEnableModuleClock(
+ hRmDeviceHandle, NvRmPrivModuleID_Pcie, ClockState);
+ break;
+ case NV_POWERGROUP_VDE:
+ NvRmPrivEnableModuleClock(
+ hRmDeviceHandle, NvRmModuleID_Vde, ClockState);
+ break;
+ case NV_POWERGROUP_MPE:
+ NvRmPrivEnableModuleClock(
+ hRmDeviceHandle, NvRmModuleID_Mpe, ClockState);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+NvRmPrivPowerGroupControl(
+ NvRmDeviceHandle hRmDeviceHandle,
+ NvU32 PowerGroup,
+ NvBool Enable)
+{
+ NvU32 reg, Id, Mask, Status;
+ NVRM_POWER_PRINTF(("%s Power Group %d\n",
+ (Enable ? "Enable" : "Disable"), PowerGroup));
+
+ // Do nothing if not SoC platform
+ NV_ASSERT(hRmDeviceHandle);
+ if (NvRmPrivGetExecPlatform(hRmDeviceHandle) != ExecPlatform_Soc)
+ return;
+
+ // Do nothing if power gating is not supported for this group
+ if (PowerGroup >= NV_POWERGROUP_MAX)
+ return; // "virtual" groups are always On
+ if (!IsPowerGateSupported(PowerGroup))
+ return;
+
+ // Do nothing if power group is already in requested state
+ NV_ASSERT(s_PowerGroupIds[PowerGroup] != NV_POWERGROUP_INVALID);
+ Id = s_PowerGroupIds[PowerGroup];
+ Mask = (0x1 << Id);
+ Status = Mask & NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0,
+ APBDEV_PMC_PWRGATE_STATUS_0);
+ if (Enable == (Status != 0x0))
+ return;
+
+ /*
+ * Gating procedure:
+ * - assert resets to all modules in power group
+ * - toggle power gate
+ *
+ * Ungating procedure
+ * - assert resets to all modules in power group (redundunt)
+ * - toggle power gate
+ * - enable clocks to all modules in power group
+ * - reset propagation delay
+ * - remove clamping
+ * - de-assert reset to all modules in power group
+ * - disable clocks to all modules in power group
+ *
+ * Special note on toggle timers( shared with OAL which does CPU power
+ * gating): per convention with OAL default settings are never changed.
+ */
+ PowerGroupResetControl(hRmDeviceHandle, PowerGroup, NV_TRUE);
+
+ reg = NV_DRF_DEF(APBDEV_PMC, PWRGATE_TOGGLE, START, ENABLE) | Id;
+ NV_REGW(hRmDeviceHandle, NvRmModuleID_Pmif, 0,
+ APBDEV_PMC_PWRGATE_TOGGLE_0, reg);
+ for (;;)
+ {
+ reg = NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0,
+ APBDEV_PMC_PWRGATE_STATUS_0);
+ if (Status != (reg & Mask))
+ break;
+ }
+ if (Enable)
+ {
+ PowerGroupClockControl(hRmDeviceHandle, PowerGroup, NV_TRUE);
+ NvOsWaitUS(NVRM_RESET_DELAY);
+
+ // PCIE and VDE clamping masks are swapped relatively to
+ // partition Ids (bug 602975)
+ if (PowerGroup == NV_POWERGROUP_PCIE)
+ Mask = 0x1 << s_PowerGroupIds[NV_POWERGROUP_VDE];
+ else if (PowerGroup == NV_POWERGROUP_VDE)
+ Mask = 0x1 << s_PowerGroupIds[NV_POWERGROUP_PCIE];
+
+ NV_REGW(hRmDeviceHandle, NvRmModuleID_Pmif, 0,
+ APBDEV_PMC_REMOVE_CLAMPING_CMD_0, Mask);
+ for (;;)
+ {
+ reg = NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0,
+ APBDEV_PMC_REMOVE_CLAMPING_CMD_0);
+ if (reg == 0)
+ break;
+ }
+ PowerGroupResetControl(hRmDeviceHandle, PowerGroup, NV_FALSE);
+ PowerGroupClockControl(hRmDeviceHandle, PowerGroup, NV_FALSE);
+ }
+}
+
+NvRmMilliVolts
+NvRmPrivPowerGroupGetVoltage(
+ NvRmDeviceHandle hRmDeviceHandle,
+ NvU32 PowerGroup)
+{
+ NvRmMilliVolts Voltage = NvRmVoltsUnspecified;
+ if (PowerGroup >= NV_POWERGROUP_MAX)
+ return Voltage; // "virtual" groups are always On
+
+ // Do not check non-gated power group - it is On by definition
+ if (s_PowerGroupIds[PowerGroup] != NV_POWERGROUP_INVALID)
+ {
+ NvU32 reg = NV_REGR(
+ hRmDeviceHandle, NvRmModuleID_Pmif, 0, APBDEV_PMC_PWRGATE_STATUS_0);
+ if ((reg & (0x1 << s_PowerGroupIds[PowerGroup])) == 0x0)
+ {
+ // Specified power group is gated
+ Voltage = NvRmVoltsOff;
+ }
+ }
+ return Voltage;
+}
+
+void NvRmPrivPowerGroupControlInit(NvRmDeviceHandle hRmDeviceHandle)
+{
+ NvU32 i, Size;
+
+ // Init chip specific power group map
+ if ((hRmDeviceHandle->ChipId.Id == 0x15) ||
+ (hRmDeviceHandle->ChipId.Id == 0x16))
+ {
+ NvRmPrivAp15PowerGroupTableInit(&s_PowerGroupIds, &Size);
+ }
+ else if (hRmDeviceHandle->ChipId.Id == 0x20)
+ {
+ NvRmPrivAp20PowerGroupTableInit(&s_PowerGroupIds, &Size);
+ }
+ else
+ {
+ NV_ASSERT(!"Unsupported chip ID");
+ }
+ NV_ASSERT(Size == NV_POWERGROUP_MAX);
+
+ // Power gate supported partitions
+ for (i = 0; i < NV_POWERGROUP_MAX; i++)
+ NvRmPrivPowerGroupControl(hRmDeviceHandle, i, NV_FALSE);
+}
+
+#endif // !NV_OAL
+/*****************************************************************************/
+/*****************************************************************************/
+
+#define NV_NO_IOPOWER_CONTROL (1)
+#define NV_RAIL_ADDR_INVALID ((NvU32)-1)
+
+typedef struct IoPowerDetectInfoRec
+{
+ // SoC Power rail GUID
+ NvU64 PowerRailId;
+
+ // IO Power rail disable bit mask
+ NvU32 DisableRailMask;
+
+ // IO Power Detect cell enable bit mask
+ NvU32 EnablePwrDetMask;
+
+ // PMU Rail Address
+ NvU32 PmuRailAddress;
+
+} IoPowerDetectInfo;
+
+static IoPowerDetectInfo s_IoPowerDetectMap[] =
+{
+ {NV_VDD_SYS_ODM_ID, (0x1 << 0), (0x1 << 0), 0},
+ {NV_VDD_NAND_ODM_ID, (0x1 << 1), (0x1 << 1), 0},
+ {NV_VDD_UART_ODM_ID, (0x1 << 2), (0x1 << 2), 0},
+ {NV_VDD_BB_ODM_ID, (0x1 << 3), (0x1 << 3), 0},
+
+ {NV_VDD_VI_ODM_ID, (0x1 << 4), (0x1 << 4), 0},
+ {NV_VDD_AUD_ODM_ID, (0x1 << 5), (0x1 << 5), 0},
+ {NV_VDD_LCD_ODM_ID, (0x1 << 6), (0x1 << 6), 0},
+ {NV_VDD_DDR_ODM_ID, (0x1 << 7), (0x1 << 7), 0},
+
+ {NV_VDD_SDIO_ODM_ID, (0x1 << 8), (0x1 << 8), 0},
+ {NV_VDD_MIPI_ODM_ID, (0x1 << 9), (0x0), 0} // No detect cell
+};
+
+static void IoPowerMapRail(
+ NvU32 PmuRailAddress,
+ NvU32* pIoPwrDetectMask,
+ NvU32* pNoIoPwrMask)
+{
+ NvU32 i;
+ *pIoPwrDetectMask = 0;
+ *pNoIoPwrMask = 0;
+
+ // Find all power detect cells and controls on this IO rail
+ for (i = 0; i < NV_ARRAY_SIZE(s_IoPowerDetectMap); i++)
+ {
+ if (s_IoPowerDetectMap[i].PmuRailAddress == PmuRailAddress)
+ {
+ *pIoPwrDetectMask |= s_IoPowerDetectMap[i].EnablePwrDetMask;
+ *pNoIoPwrMask |= s_IoPowerDetectMap[i].DisableRailMask;
+ }
+ }
+}
+
+void NvRmPrivIoPowerControlInit(NvRmDeviceHandle hRmDeviceHandle)
+{
+ NvU32 i, v;
+ NvU32 NoIoPwrMask = 0;
+ const NvOdmPeripheralConnectivity* pPmuRail = NULL;
+
+ if (NvRmPrivGetExecPlatform(hRmDeviceHandle) != ExecPlatform_Soc)
+ {
+ // Invalidate IO Power detect map if not SoC platform
+ for (i = 0; i < NV_ARRAY_SIZE(s_IoPowerDetectMap); i++)
+ s_IoPowerDetectMap[i].PmuRailAddress = NV_RAIL_ADDR_INVALID;
+ return;
+ }
+
+ for (i = 0; i < NV_ARRAY_SIZE(s_IoPowerDetectMap); i++)
+ {
+ // Fill in PMU rail addresses in IO Power detect map
+ pPmuRail = NvOdmPeripheralGetGuid(s_IoPowerDetectMap[i].PowerRailId);
+ NV_ASSERT(pPmuRail && pPmuRail->NumAddress);
+ s_IoPowerDetectMap[i].PmuRailAddress = pPmuRail->AddressList[0].Address;
+
+ // Find all unpowered rails
+ v = NvRmPrivPmuRailGetVoltage(
+ hRmDeviceHandle, s_IoPowerDetectMap[i].PowerRailId);
+ if (v == ODM_VOLTAGE_OFF)
+ NoIoPwrMask |= s_IoPowerDetectMap[i].DisableRailMask;
+ }
+
+ // Latch already powered IO rails
+ NvRmPrivIoPowerDetectLatch(hRmDeviceHandle);
+
+ // Disable IO pads for unpowered rails
+ if (NoIoPwrMask)
+ NvRmPrivIoPowerControl(hRmDeviceHandle, NoIoPwrMask, NV_FALSE);
+}
+
+void NvRmPrivIoPowerDetectStart(
+ NvRmDeviceHandle hRmDeviceHandle,
+ NvU32 PwrDetMask)
+{
+// (1) Enable specified power detect cell
+ NV_REGW(hRmDeviceHandle,
+ NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_0, PwrDetMask);
+
+// (2-3) Set power detect latches for enabled cells to safe "1" (high) value
+ if ((hRmDeviceHandle->ChipId.Id == 0x15) ||
+ (hRmDeviceHandle->ChipId.Id == 0x16))
+ {
+ // On AP15/AP16 set/clear reset bit in PMC scratch0
+ NvRmPrivAp15IoPowerDetectReset(hRmDeviceHandle);
+
+ // For AP15 A01 chip the above reset does nothing, therefore
+ // need to set latch "pass-thru" before transition
+ if ((hRmDeviceHandle->ChipId.Id == 0x15) &&
+ (hRmDeviceHandle->ChipId.Major == 0x01) &&
+ (hRmDeviceHandle->ChipId.Minor == 0x01))
+ {
+ NvOsWaitUS(NVRM_PWR_DET_DELAY_US);
+ NV_REGW(hRmDeviceHandle,
+ NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_LATCH_0, 1);
+ }
+ }
+ else
+ {
+ // On AP20+ reset high values directly
+ NvRmPrivAp20IoPowerDetectReset(hRmDeviceHandle);
+ }
+}
+//
+// (4) Power rail OFF -> ON transition and stabilization
+//
+void NvRmPrivIoPowerDetectLatch(NvRmDeviceHandle hRmDeviceHandle)
+{
+// (5) Set latch "pass-thru"
+// (6) Latch results
+// (7) Disable all power detect cells
+ NV_REGW(hRmDeviceHandle,
+ NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_LATCH_0, 1);
+ NV_REGW(hRmDeviceHandle,
+ NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_LATCH_0, 0);
+ NV_REGW(hRmDeviceHandle,
+ NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_0, 0);
+}
+
+void NvRmPrivIoPowerControl(
+ NvRmDeviceHandle hRmDeviceHandle,
+ NvU32 NoIoPwrMask,
+ NvBool Enable)
+{
+ NvU32 reg = NV_REGR(
+ hRmDeviceHandle, NvRmModuleID_Pmif, 0, APBDEV_PMC_NO_IOPOWER_0);
+ reg = Enable ? (reg & (~NoIoPwrMask)) : (reg | NoIoPwrMask);
+
+#if NV_NO_IOPOWER_CONTROL
+ NV_REGW(hRmDeviceHandle,
+ NvRmModuleID_Pmif, 0, APBDEV_PMC_NO_IOPOWER_0, reg);
+#endif
+}
+
+void
+NvRmPrivSetSocRailPowerState(
+ NvRmDeviceHandle hRmDeviceHandle,
+ NvU32 PmuRailAddress,
+ NvBool Enable,
+ NvU32* pIoPwrDetectMask,
+ NvU32* pNoIoPwrMask)
+{
+ IoPowerMapRail(PmuRailAddress, pIoPwrDetectMask, pNoIoPwrMask);
+ if ((*pIoPwrDetectMask == 0) && (*pNoIoPwrMask == 0))
+ return; // Exit if not mapped rail
+
+ if (Enable)
+ {
+ // On/Off transition: activate power detect cells and keep control
+ // masks so that the results can be latched and IO pads enabled after
+ // the transition is completed
+ if (*pIoPwrDetectMask != 0)
+ NvRmPrivIoPowerDetectStart(hRmDeviceHandle, *pIoPwrDetectMask);
+ }
+ else
+ {
+ // Off/On transition: disable IO pads, and clear control masks,
+ // as no action is required after the transition is completed
+ if (*pNoIoPwrMask != 0)
+ NvRmPrivIoPowerControl(hRmDeviceHandle, *pNoIoPwrMask, NV_FALSE);
+ *pIoPwrDetectMask = *pNoIoPwrMask = 0;
+ }
+}
+
+/*****************************************************************************/
+
+void NvRmPrivCoreVoltageInit(NvRmDeviceHandle hRmDevice)
+{
+ NvU32 CoreRailAddress, RtcRailAddress, CpuRailAddress;
+ const NvOdmPeripheralConnectivity* pPmuRail;
+ NvRmMilliVolts CurrentCoreMv = 0;
+ NvRmMilliVolts CurrentRtcMv = 0;
+ NvRmMilliVolts NominalCoreMv = NvRmPrivGetNominalMV(hRmDevice);
+
+ NV_ASSERT(hRmDevice);
+
+ if (NvRmPrivGetExecPlatform(hRmDevice) != ExecPlatform_Soc)
+ {
+ return;
+ }
+
+ pPmuRail = NvOdmPeripheralGetGuid(NV_VDD_CORE_ODM_ID);
+ NV_ASSERT(pPmuRail);
+ NV_ASSERT(pPmuRail->NumAddress);
+ CoreRailAddress = pPmuRail->AddressList[0].Address;
+
+ pPmuRail = NvOdmPeripheralGetGuid(NV_VDD_RTC_ODM_ID);
+ NV_ASSERT(pPmuRail);
+ NV_ASSERT(pPmuRail->NumAddress);
+ RtcRailAddress = pPmuRail->AddressList[0].Address;
+
+ // This function is called during PMU initialization when current (= boot)
+ // core voltage is expected to be within one safe step from nominal, and
+ // RTC voltage must be within one safe step from the core. Set nominal
+ // voltage (bump PMU ref count), if the above conditions are true.
+ NvRmPmuGetVoltage(hRmDevice, CoreRailAddress, &CurrentCoreMv);
+ NvRmPmuGetVoltage(hRmDevice, RtcRailAddress, &CurrentRtcMv);
+ if((CurrentCoreMv > (NominalCoreMv + NVRM_SAFE_VOLTAGE_STEP_MV)) ||
+ ((CurrentCoreMv + NVRM_SAFE_VOLTAGE_STEP_MV) < NominalCoreMv))
+ {
+ NV_ASSERT(!"Unexpected initial core voltage");
+ return;
+ }
+ if((CurrentRtcMv > (CurrentCoreMv + NVRM_SAFE_VOLTAGE_STEP_MV)) ||
+ ((CurrentRtcMv + NVRM_SAFE_VOLTAGE_STEP_MV) < CurrentCoreMv))
+ {
+ NV_ASSERT(!"Unexpected initial RTC voltage");
+ return;
+ }
+ NvRmPmuSetVoltage(hRmDevice, RtcRailAddress, NominalCoreMv, NULL);
+ NvRmPmuSetVoltage(hRmDevice, CoreRailAddress, NominalCoreMv, NULL);
+
+ // If the platform has dedicated CPU voltage rail, make sure it is set to
+ // nominal level first. Similar to the core, CPU boot voltage is expected
+ // to be within one safe step from nominal.
+ if (NvRmPrivIsCpuRailDedicated(hRmDevice))
+ {
+ NvRmMilliVolts CurrentCpuMv = 0;
+ NvRmMilliVolts NominalCpuMv = NvRmPrivModuleVscaleGetMV(
+ hRmDevice, NvRmModuleID_Cpu, NvRmFreqMaximum);
+
+ pPmuRail = NvOdmPeripheralGetGuid(NV_VDD_CPU_ODM_ID);
+ NV_ASSERT(pPmuRail);
+ NV_ASSERT(pPmuRail->NumAddress);
+ CpuRailAddress = pPmuRail->AddressList[0].Address;
+
+ NvRmPmuGetVoltage(hRmDevice, CpuRailAddress, &CurrentCpuMv);
+ if((CurrentCpuMv > (NominalCpuMv + NVRM_SAFE_VOLTAGE_STEP_MV)) ||
+ ((CurrentCpuMv + NVRM_SAFE_VOLTAGE_STEP_MV) < NominalCpuMv))
+ {
+ NV_ASSERT(!"Unexpected initial CPU voltage");
+ return;
+ }
+ NvRmPmuSetVoltage(hRmDevice, CpuRailAddress, NominalCpuMv, NULL);
+ }
+
+ // Always On System I/O, DDR IO and RX DDR (if exist) - set nominal,
+ // bump ref count
+ NvRmPrivPmuRailControl(hRmDevice, NV_VDD_SYS_ODM_ID, NV_TRUE);
+ NvRmPrivPmuRailControl(hRmDevice, NV_VDD_DDR_ODM_ID, NV_TRUE);
+ if (NvOdmPeripheralGetGuid(NV_VDD_DDR_RX_ODM_ID))
+ NvRmPrivPmuRailControl(hRmDevice, NV_VDD_DDR_RX_ODM_ID, NV_TRUE);
+}
+
+/*****************************************************************************/
+