diff options
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c | 46 |
1 files changed, 43 insertions, 3 deletions
diff --git a/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c b/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c index 740c5527c7d8..ca9bce2f9270 100644 --- a/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c +++ b/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c @@ -520,6 +520,9 @@ static void Ap20EmcConfigInit(NvRmDeviceHandle hRmDevice) } } +// EMC module virtual base address to speed up timing update +static void* s_pEmcBaseReg = NULL; + static void Ap20EmcTimingSet( NvRmDeviceHandle hRmDevice, @@ -527,9 +530,6 @@ Ap20EmcTimingSet( { NvU32 i, a, d; - // EMC module virtual base address to speed up timing update - static void* s_pEmcBaseReg = NULL; - if (s_pEmcBaseReg == NULL) { NvRmModuleTable *tbl = NvRmPrivGetModuleTable(hRmDevice); @@ -547,6 +547,43 @@ Ap20EmcTimingSet( a = (((NvU32)(s_pEmcBaseReg)) + s_Ap20EmcConfig.pEmcTimingReg[i]); NV_WRITE32(a, d); } + + // Clear clock change interrupt status (write 1 to clear) + a = (((NvU32)(s_pEmcBaseReg)) + EMC_INTSTATUS_0); + d = NV_DRF_NUM(EMC, INTSTATUS, CLKCHANGE_COMPLETE_INT, 1); + NV_WRITE32(a, d); + + d = NV_READ32(a); // make sure writes are completed +} + +static void +Ap20EmcTimingSetFinish( + NvRmDeviceHandle hRmDevice, + const NvRmAp20EmcTimingConfig* pEmcConfig) +{ + NvU32 a, d; + NV_ASSERT(s_pEmcBaseReg); + + // After EMC clock change is completed, digital DLL should be restarted + // (provided it is enabled for current EMC frequency) + if (!NV_DRF_VAL(EMC, CFG_DIG_DLL, CFG_DLL_EN, pEmcConfig->EmcDigDll)) + return; + + a = (((NvU32)(s_pEmcBaseReg)) + EMC_INTSTATUS_0); + for (;;) + { + d = NV_DRF_VAL(EMC, INTSTATUS, CLKCHANGE_COMPLETE_INT, NV_READ32(a)); + if (d) + break; + } + + a = (((NvU32)(s_pEmcBaseReg)) + EMC_CFG_DIG_DLL_0); + NV_WRITE32(a, pEmcConfig->EmcDigDll); + d = NV_READ32(a); // make sure writes are completed + + a = (((NvU32)(s_pEmcBaseReg)) + EMC_TIMING_CONTROL_0); + d = NV_DRF_NUM(EMC, TIMING_CONTROL, TIMING_UPDATE, 1); + NV_WRITE32(a, d); d = NV_READ32(a); // make sure writes are completed } @@ -615,6 +652,7 @@ Ap20EmcSwitchToUndividedPllM0( NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0, reg); NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + Ap20EmcTimingSetFinish(hRmDevice, pEmcConfig); // Now set new divider value. Note that PLLM_UD bit is already set, so // the actual EMC frequency is not changed. Hence, no need to update EMC @@ -657,6 +695,7 @@ Ap20EmcSwitchFromUndividedPllM0( NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0, reg); NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + Ap20EmcTimingSetFinish(hRmDevice, pEmcConfig); // Update EMC state s_Ap20EmcConfig.UdPllM0 = NV_FALSE; @@ -696,6 +735,7 @@ Ap20EmcSwitchDividedSources( NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0, reg); NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + Ap20EmcTimingSetFinish(hRmDevice, pEmcConfig); // Update EMC state (undivided path status not changed) NV_ASSERT(!s_Ap20EmcConfig.UdPllM0); |