| /* $NoKeywords:$ */ |
| /** |
| * @file |
| * |
| * mndct.c |
| * |
| * Common Northbridge DCT support |
| * |
| * @xrefitem bom "File Content Label" "Release Content" |
| * @e project: AGESA |
| * @e sub-project: (Mem/NB) |
| * @e \$Revision: 64574 $ @e \$Date: 2012-01-25 01:01:51 -0600 (Wed, 25 Jan 2012) $ |
| * |
| **/ |
| /***************************************************************************** |
| * |
| * Copyright (c) 2008 - 2012, Advanced Micro Devices, Inc. |
| * 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 Advanced Micro Devices, Inc. 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 ADVANCED MICRO DEVICES, INC. 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. |
| * *************************************************************************** |
| * |
| */ |
| |
| |
| /* |
| *---------------------------------------------------------------------------- |
| * MODULES USED |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| |
| |
| #include "AGESA.h" |
| #include "amdlib.h" |
| #include "Ids.h" |
| #include "mport.h" |
| #include "mm.h" |
| #include "mn.h" |
| #include "mt.h" |
| #include "mu.h" |
| #include "mftds.h" |
| #include "merrhdl.h" |
| #include "cpuFamilyTranslation.h" |
| #include "OptionMemory.h" |
| #include "PlatformMemoryConfiguration.h" |
| #include "Filecode.h" |
| CODE_GROUP (G1_PEICC) |
| RDATA_GROUP (G1_PEICC) |
| |
| #define FILECODE PROC_MEM_NB_MNDCT_FILECODE |
| /*---------------------------------------------------------------------------- |
| * DEFINITIONS AND MACROS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| #define UNUSED_CLK 4 |
| |
| /*---------------------------------------------------------------------------- |
| * TYPEDEFS AND STRUCTURES |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| /*---------------------------------------------------------------------------- |
| * PROTOTYPES OF LOCAL FUNCTIONS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| VOID |
| STATIC |
| MemNAfterStitchMemNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ); |
| |
| UINT8 |
| MemNGet1KTFawTkNb ( |
| IN UINT8 k |
| ); |
| |
| UINT8 |
| MemNGet2KTFawTkNb ( |
| IN UINT8 k |
| ); |
| |
| VOID |
| STATIC |
| MemNQuarterMemClk2NClkNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT UINT16 *SubTotalPtr |
| ); |
| |
| /*---------------------------------------------------------------------------- |
| * EXPORTED FUNCTIONS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| extern BUILD_OPT_CFG UserOptions; |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function combines all the memory into a contiguous map. |
| * Requires that Mask values for each bank be programmed first and that |
| * the chip-select population indicator is correctly set. |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return TRUE - An Error value lower than AGESA_FATAL may have occurred |
| * @return FALSE - An Error value greater than or equal to AGESA_FATAL may have occurred |
| */ |
| |
| BOOLEAN |
| MemNStitchMemoryNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| BOOLEAN DSpareEn; |
| UINT32 NxtCSBase; |
| UINT32 CurCSBase; |
| UINT32 CsSize; |
| UINT32 BiggestBank; |
| UINT8 p; |
| UINT8 q; |
| UINT8 BiggestDimm; |
| MEM_PARAMETER_STRUCT *RefPtr; |
| DIE_STRUCT *MCTPtr; |
| DCT_STRUCT *DCTPtr; |
| RefPtr = NBPtr->RefPtr; |
| MCTPtr = NBPtr->MCTPtr; |
| DCTPtr = NBPtr->DCTPtr; |
| DSpareEn = FALSE; |
| if (NBPtr->IsSupported[SetSpareEn]) { |
| DSpareEn = FALSE; |
| if (RefPtr->GStatus[GsbEnDIMMSpareNW]) { |
| DSpareEn = TRUE; |
| } |
| } |
| |
| DCTPtr->Timings.CsEnabled = 0; |
| NxtCSBase = 0; |
| for (p = 0; p < MAX_CS_PER_CHANNEL; p++) { |
| BiggestBank = 0; |
| BiggestDimm = 0; |
| for (q = 0; q < MAX_CS_PER_CHANNEL; q++) { |
| if (((DCTPtr->Timings.CsPresent & ~DCTPtr->Timings.CsTestFail) & ((UINT16)1 << q)) != 0) { |
| if ((MemNGetBitFieldNb (NBPtr, BFCSBaseAddr0Reg + q) & 7) == 0) { |
| // (CSEnable|Spare==1)bank is not enabled yet |
| CsSize = MemNGetBitFieldNb (NBPtr, BFCSMask0Reg + (q >> 1)); |
| if (CsSize != 0) { |
| CsSize += ((UINT32)1 << 19); |
| CsSize &= 0xFFF80000; |
| } |
| if (CsSize > BiggestBank) { |
| BiggestBank = CsSize; |
| BiggestDimm = q; |
| } |
| } |
| } |
| } |
| |
| if (BiggestBank != 0) { |
| CurCSBase = NxtCSBase; |
| if (NBPtr->IsSupported[CheckSpareEn]) { |
| if (DSpareEn) { |
| CurCSBase = ((UINT32)1 << BFSpare); |
| DSpareEn = FALSE; |
| } else { |
| CurCSBase |= ((UINT32)1 << BFCSEnable); |
| NxtCSBase += BiggestBank; |
| } |
| } else { |
| CurCSBase |= ((UINT32)1 << BFCSEnable); |
| NxtCSBase += BiggestBank; |
| } |
| if ((BiggestDimm & 1) != 0) { |
| if (!(MCTPtr->Status[SbLrdimms])) { |
| // For LRDIMMS, On Dimm Mirroring is enabled after SDI |
| if ((DCTPtr->Timings.DimmMirrorPresent & (1 << (BiggestDimm >> 1))) != 0) { |
| CurCSBase |= ((UINT32)1 << BFOnDimmMirror); |
| } |
| } |
| } |
| MemNSetBitFieldNb (NBPtr, BFCSBaseAddr0Reg + BiggestDimm, CurCSBase); |
| DCTPtr->Timings.CsEnabled |= (1 << BiggestDimm); |
| } |
| if ((DCTPtr->Timings.CsTestFail & ((UINT16)1 << p)) != 0) { |
| IDS_HDT_CONSOLE (MEM_FLOW, "Node %d Dct %d exclude CS %d\n", NBPtr->Node, NBPtr->Dct, p); |
| MemNSetBitFieldNb (NBPtr, (BFCSBaseAddr0Reg + p), (UINT32)1 << BFTestFail); |
| } |
| } |
| |
| if (NxtCSBase != 0) { |
| DCTPtr->Timings.DctMemSize = NxtCSBase >> 8; // Scale base address from [39:8] to [47:16] |
| MemNAfterStitchMemNb (NBPtr); |
| } |
| |
| return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets platform specific config/timing values from the interface layer and |
| * programs them into DCT. |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return TRUE - An Error value lower than AGESA_FATAL may have occurred |
| * @return FALSE - An Error value greater than or equal to AGESA_FATAL may have occurred |
| */ |
| |
| BOOLEAN |
| MemNPlatformSpecNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| CONST BIT_FIELD_NAME ChipletPDRegs[] = { |
| BFPhyClkConfig0, |
| BFPhyClkConfig3, |
| BFPhyClkConfig1, |
| BFPhyClkConfig2 |
| }; |
| CONST UINT8 ChipletPDClkDisMap[][2] = { |
| //F2[1, 0]x9C_x0D0F2030 -> F2x[1, 0]88[MemClkDis[1:0]] |
| {0, 1}, |
| //F2[1, 0]x9C_x0D0F2330 -> F2x[1, 0]88[MemClkDis[7:6]] |
| {6, 7}, |
| //F2x09C_x0D0F2130 -> F2x88[MemClkDis[5:4]] |
| {4, 5}, |
| //F2x09C_x0D0F2230 -> F2x88[MemClkDis[3:2]] |
| {2, 3}, |
| //F2x19C_x0D0F2130 -> F2x188[MemClkDis[5:2]] |
| {2, 5}, |
| //F2x19C_x0D0F2230 -> F2x188[MemClkDis[4:3]] |
| {3, 4} |
| }; |
| |
| UINT8 MemClkDis; |
| UINT8 i; |
| UINT8 MemoryAllClocks; |
| UINT8 *MemClkDisMap; |
| UINT16 CsPresent; |
| UINT8 RegIndex; |
| UINT8 Cs1; |
| UINT8 Cs2; |
| |
| if (!MemNGetPlatformCfgNb (NBPtr)) { |
| IDS_ERROR_TRAP; |
| } |
| |
| if (!NBPtr->PsPtr->MemPDoPs (NBPtr)) { |
| IDS_ERROR_TRAP; |
| } |
| MemNProgramPlatformSpecNb (NBPtr); |
| |
| MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_ODT, ALL_DIMMS); |
| |
| if (NBPtr->MCTPtr->GangedMode) { |
| MemNSwitchDCTNb (NBPtr, 1); |
| if (!MemNGetPlatformCfgNb (NBPtr)) { |
| IDS_ERROR_TRAP; |
| } |
| MemNProgramPlatformSpecNb (NBPtr); |
| MemNSwitchDCTNb (NBPtr, 0); |
| } |
| |
| //====================================================================== |
| // Disable unused MemClk to save power |
| //====================================================================== |
| // |
| MemClkDis = 0; |
| MemoryAllClocks = UserOptions.CfgMemoryAllClocksOn; |
| IDS_OPTION_HOOK (IDS_ALL_MEMORY_CLOCK, &MemoryAllClocks, &(NBPtr->MemPtr->StdHeader)); |
| if (!MemoryAllClocks) { |
| // Special Jedec SPD diagnostic bit - "enable all clocks" |
| if (!NBPtr->MCTPtr->Status[SbDiagClks]) { |
| MemClkDisMap = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MEMCLK_DIS, NBPtr->MCTPtr->SocketId, MemNGetSocketRelativeChannelNb (NBPtr, NBPtr->Dct, 0), 0, |
| &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader)); |
| if (MemClkDisMap == NULL) { |
| MemClkDisMap = NBPtr->ChannelPtr->MemClkDisMap; |
| } |
| |
| // Turn off the unused CS clocks |
| CsPresent = NBPtr->DCTPtr->Timings.CsPresent; |
| |
| if (NBPtr->IsSupported[CheckMemClkCSPresent]) { |
| if (NBPtr->ChannelPtr->RegDimmPresent != 0) { |
| // All DDR3 RDIMM use only one MEMCLOCK from edge finger to the register |
| // regardless of how many Ranks are on the DIMM (Single, Dual or Quad) |
| CsPresent = (CsPresent | (CsPresent >> 1)) & 0x5555; |
| } |
| } |
| for (i = 0; i < 8; i++) { |
| if ((CsPresent & MemClkDisMap[i]) == 0) { |
| MemClkDis |= (UINT8) (1 << i); |
| } |
| } |
| //Chiplet power down |
| for (RegIndex = 0; RegIndex < GET_SIZE_OF (ChipletPDRegs); RegIndex++) { |
| if ((NBPtr->Dct == 1) && (RegIndex >= 2)) { |
| Cs1 = MemClkDisMap[ChipletPDClkDisMap[RegIndex + 2][0]]; |
| Cs2 = MemClkDisMap[ChipletPDClkDisMap[RegIndex + 2][1]]; |
| } else { |
| Cs1 = MemClkDisMap[ChipletPDClkDisMap[RegIndex][0]]; |
| Cs2 = MemClkDisMap[ChipletPDClkDisMap[RegIndex][1]]; |
| } |
| if ((CsPresent & (UINT16) (Cs1 | Cs2)) == 0) { |
| MemNSetBitFieldNb (NBPtr, ChipletPDRegs[RegIndex], (MemNGetBitFieldNb (NBPtr, ChipletPDRegs[RegIndex]) | 0x10)); |
| } |
| } |
| } |
| } |
| MemNSetBitFieldNb (NBPtr, BFMemClkDis, MemClkDis); |
| |
| AGESA_TESTPOINT (TPProcMemPhyCompensation, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->MemNInitPhyComp (NBPtr); |
| |
| MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_SLEWRATE, ALL_DIMMS); |
| |
| // Program DramTerm for DDR2 |
| if ((MemNGetBitFieldNb (NBPtr, BFDdr3Mode)) == 0) { |
| MemNSetBitFieldNb (NBPtr, BFDramTerm, NBPtr->PsPtr->DramTerm); |
| } else { |
| // Dynamic Dynamic DramTerm for DDR3 |
| // Dram Term for DDR3 may vary based on chip selects |
| MemNSetBitFieldNb (NBPtr, BFDramTermDyn, NBPtr->PsPtr->DynamicDramTerm); |
| } |
| |
| MemFInitTableDrive (NBPtr, MTAfterPlatformSpec); |
| |
| return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets platform specific config/timing values from the interface layer and |
| * programs them into DCT. |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return TRUE - An Error value lower than AGESA_FATAL may have occurred |
| * @return FALSE - An Error value greater than or equal to AGESA_FATAL may have occurred |
| */ |
| |
| BOOLEAN |
| MemNPlatformSpecUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 MemClkDis; |
| UINT8 i; |
| UINT8 MemoryAllClocks; |
| UINT8 *MemClkDisMap; |
| UINT16 CsPresent; |
| |
| if (!MemNGetPlatformCfgNb (NBPtr)) { |
| IDS_ERROR_TRAP; |
| } |
| |
| if (!NBPtr->PsPtr->MemPDoPs (NBPtr)) { |
| IDS_HDT_CONSOLE (MEM_FLOW, "\tDisable DCT%d due to unsupported DIMM configuration\n", NBPtr->Dct); |
| if (!NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, EXCLUDE_ALL_CHIPSEL, &NBPtr->MemPtr->StdHeader)) { |
| ASSERT (FALSE); |
| } |
| NBPtr->DisableDCT (NBPtr); |
| } else { |
| |
| MemNProgramPlatformSpecNb (NBPtr); |
| MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_ODT, ALL_DIMMS); |
| |
| //====================================================================== |
| // Disable unused MemClk to save power |
| //====================================================================== |
| // |
| MemClkDis = 0; |
| MemoryAllClocks = UserOptions.CfgMemoryAllClocksOn; |
| IDS_OPTION_HOOK (IDS_ALL_MEMORY_CLOCK, &MemoryAllClocks, &(NBPtr->MemPtr->StdHeader)); |
| if (!MemoryAllClocks) { |
| // Special Jedec SPD diagnostic bit - "enable all clocks" |
| if (!NBPtr->MCTPtr->Status[SbDiagClks]) { |
| MemClkDisMap = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MEMCLK_DIS, NBPtr->MCTPtr->SocketId, NBPtr->Dct, 0, |
| &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader)); |
| if (MemClkDisMap == NULL) { |
| MemClkDisMap = NBPtr->ChannelPtr->MemClkDisMap; |
| } |
| |
| // Turn off unused clocks |
| CsPresent = NBPtr->DCTPtr->Timings.CsPresent; |
| |
| for (i = 0; i < 8; i++) { |
| if ((CsPresent & MemClkDisMap[i]) == 0) { |
| MemClkDis |= (UINT8) (1 << i); |
| } |
| } |
| |
| // Turn off unused chiplets |
| for (i = 0; i < 3; i++) { |
| if (((MemClkDis >> (i * 2)) & 0x3) == 0x3) { |
| MemNSetBitFieldNb (NBPtr, BFPhyClkConfig0 + i, 0x0010); |
| } |
| } |
| } |
| } |
| MemNSetBitFieldNb (NBPtr, BFMemClkDis, MemClkDis); |
| MemFInitTableDrive (NBPtr, MTAfterPlatformSpec); |
| } |
| |
| return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function disables the DCT and mem clock |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNDisableDCTNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| MemNSetBitFieldNb (NBPtr, BFCKETri, 0x03); |
| MemNSetBitFieldNb (NBPtr, BFODTTri, 0x0F); |
| MemNSetBitFieldNb (NBPtr, BFChipSelTri, 0xFF); |
| |
| // To maximize power savings when DisDramInterface=1b, |
| // all of the MemClkDis bits should also be set. |
| // |
| MemNSetBitFieldNb (NBPtr, BFMemClkDis, 0xFF); |
| MemNSetBitFieldNb (NBPtr, BFDisDramInterface, 1); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function disables the DCT and mem clock for client NB |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNDisableDCTClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| MemNSetBitFieldNb (NBPtr, BFCKETri, 0x03); |
| MemNSetBitFieldNb (NBPtr, BFODTTri, 0x0F); |
| MemNSetBitFieldNb (NBPtr, BFChipSelTri, 0xFF); |
| |
| //Wait for 24 MEMCLKs |
| MemNWaitXMemClksNb (NBPtr, 24); |
| |
| // To maximize power savings when DisDramInterface=1b, |
| // all of the MemClkDis bits should also be set. |
| // |
| MemNSetBitFieldNb (NBPtr, BFMemClkDis, 0xFF); |
| |
| MemNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x80800000); |
| |
| MemNSetBitFieldNb (NBPtr, BFDisDramInterface, 1); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function disables the DCT and mem clock for UNB |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNDisableDCTUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| MemNSetBitFieldNb (NBPtr, BFExtendedParityEn, 0); |
| MemNSetBitFieldNb (NBPtr, BFParEn, 0); |
| MemNSetBitFieldNb (NBPtr, BFCKETri, 0x0F); |
| |
| //Wait for 24 MEMCLKs |
| MemNWaitXMemClksNb (NBPtr, 24); |
| |
| // To maximize power savings when DisDramInterface=1b, |
| // all of the MemClkDis bits should also be set. |
| // |
| MemNSetBitFieldNb (NBPtr, BFMemClkDis, 0xFF); |
| |
| MemNSetBitFieldNb (NBPtr, BFDisDramInterface, 1); |
| |
| if (NBPtr->Dct == 0) { |
| MemNSetBitFieldNb (NBPtr, BFPhyPSMasterChannel, 0x100); |
| } |
| |
| // If channel is disabled after dram init, set DisDllShutdownSR |
| if (MemNGetBitFieldNb (NBPtr, BFDramEnabled) == 1) { |
| MemNSetBitFieldNb (NBPtr, BFDisDllShutdownSR, 1); |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function initializes the DRAM devices on all DCTs at the same time |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNStartupDCTNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| // 1. Ensure F2x[1, 0]9C_x08[DisAutoComp] = 1. |
| // 2. BIOS waits 5 us for the disabling of the compensation engine to complete. |
| // DisAutoComp is still being set since InitPhyComp |
| |
| if (NBPtr->MCTPtr->NodeMemSize != 0) { |
| // Init MemClk frequency |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 1); |
| |
| |
| AGESA_TESTPOINT (TpProcMemBeforeDramInit, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->MemNBeforeDramInitNb (NBPtr); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq: %d MHz\n", NBPtr->DCTPtr->Timings.Speed); |
| AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->FeatPtr->DramInit (NBPtr->TechPtr); |
| } |
| |
| // 7. Program F2x[1, 0]9C_x08[DisAutoComp] = 0. |
| // 8. BIOS must wait 750 us for the phy compensation engine |
| // to reinitialize. |
| // DisAutoComp will be cleared after DramEnabled turns to 1 |
| |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function initializes the DRAM devices on all DCTs at the same time |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNStartupDCTUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 Dct; |
| UINT16 FinalPllLockTime; |
| |
| if (NBPtr->MCTPtr->NodeMemSize != 0) { |
| // Update NB frequency for startup DDR speed |
| NBPtr->ChangeNbFrequency (NBPtr); |
| |
| if (!NBPtr->IsSupported[ForcePhyToM0]) { |
| MemNBrdcstSetNb (NBPtr, BFDramPhyStatusReg, 0x80000000); |
| |
| MemNBrdcstSetNb (NBPtr, BFPllRegWaitTime, 0x118); |
| } |
| |
| // Phy Voltage Level Programming |
| MemNPhyVoltageLevelNb (NBPtr); |
| |
| // Run frequency change sequence |
| MemNBrdcstSetNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault); |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreq, NBPtr->GetMemClkFreqId (NBPtr, NBPtr->DCTPtr->Timings.Speed)); |
| NBPtr->FamilySpecificHook[SetSkewMemClk] (NBPtr, NULL); |
| NBPtr->ProgramNbPsDependentRegs (NBPtr); |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if ((NBPtr->DCTPtr->Timings.DctMemSize != 0)) { |
| MemNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 1); |
| MemNPollBitFieldNb (NBPtr, BFFreqChgInProg, 0, PCI_ACCESS_TIMEOUT, FALSE); |
| } |
| } |
| FinalPllLockTime = 0xF; |
| NBPtr->FamilySpecificHook[AfterMemClkFreqVal] (NBPtr, &FinalPllLockTime); |
| if (!NBPtr->IsSupported[CsrPhyPllPdEn]) { |
| // IF (D18F2x[1,0]9C_x0D0F_E00A[CsrPhySrPllPdMode]==0) THEN program |
| // D18F2x[1,0]9C_x0D0F_E006[PllLockTime] = 0Fh |
| MemNBrdcstSetNb (NBPtr, BFPllLockTime, FinalPllLockTime); |
| } |
| |
| NBPtr->FamilySpecificHook[BeforePhyFenceTraining] (NBPtr, NBPtr); |
| |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); |
| |
| // Phy fence programming |
| AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->PhyFenceTraining (NBPtr); |
| |
| // Phy compensation initialization |
| AGESA_TESTPOINT (TPProcMemPhyCompensation, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->MemNInitPhyComp (NBPtr); |
| MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_SLEWRATE, ALL_DIMMS); |
| } |
| } |
| |
| AGESA_TESTPOINT (TpProcMemBeforeDramInit, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->MemNBeforeDramInitNb (NBPtr); |
| |
| AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader)); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq: %d MHz\n", NBPtr->DCTPtr->Timings.Speed); |
| NBPtr->FeatPtr->DramInit (NBPtr->TechPtr); |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * MemNChangeFrequencyHy: |
| * |
| * This function change MemClk frequency to the value that is specified by DCTPtr->Timings.Speed |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNChangeFrequencyNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| MEM_TECH_BLOCK *TechPtr; |
| UINT8 Dct; |
| UINT8 ChipSel; |
| |
| TechPtr = NBPtr->TechPtr; |
| if (NBPtr->IsSupported[CheckDisDllShutdownSR] && !(NBPtr->IsSupported[SetDllShutDown])) { |
| // #107421 |
| MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 1); |
| } |
| |
| //Program F2x[1,0]90[EnterSelfRefresh]=1. |
| //Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0. |
| MemNBrdcstSetNb (NBPtr, BFEnterSelfRef, 1); |
| MemNPollBitFieldNb (NBPtr, BFEnterSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE); |
| |
| //Program F2x9C_x08[DisAutoComp]=1 |
| MemNSwitchDCTNb (NBPtr, 0); |
| MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 1); |
| |
| //Program F2x[1, 0]94[MemClkFreqVal] = 0. |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 0); |
| |
| //Program F2x[1, 0]94[MemClkFreq] to specify the target MEMCLK frequency. |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreq, NBPtr->GetMemClkFreqId (NBPtr, NBPtr->DCTPtr->Timings.Speed)); |
| |
| IDS_OPTION_HOOK (IDS_BEFORE_MEM_FREQ_CHG, NBPtr, &(NBPtr->MemPtr->StdHeader)); |
| //Program F2x[1, 0]94[MemClkFreqVal] = 1. |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 1); |
| |
| //Wait until F2x[1, 0]94[FreqChgInProg]=0. |
| MemNPollBitFieldNb (NBPtr, BFFreqChgInProg, 0, PCI_ACCESS_TIMEOUT, TRUE); |
| |
| if (NBPtr->IsSupported[CheckPhyFenceTraining]) { |
| //Perform Phy Fence retraining after frequency changed |
| AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader)); |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); |
| AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader)); |
| MemNPhyFenceTrainingNb (NBPtr); |
| } |
| } |
| } |
| |
| //Program F2x9C_x08[DisAutoComp]=0 |
| MemNSwitchDCTNb (NBPtr, 0); |
| MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 0); |
| |
| //Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs. |
| //Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. |
| MemNBrdcstSetNb (NBPtr, BFExitSelfRef, 1); |
| MemNPollBitFieldNb (NBPtr, BFExitSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE); |
| |
| if (NBPtr->MCTPtr->Status[SbRegistered]) { |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| TechPtr->FreqChgCtrlWrd (TechPtr); |
| } |
| } |
| } |
| |
| //wait for 500 MCLKs after ExitSelfRef, 500*2.5ns=1250ns |
| MemNWaitXMemClksNb (NBPtr, 500); |
| |
| if (NBPtr->IsSupported[CheckDisDllShutdownSR] && !(NBPtr->IsSupported[SetDllShutDown])) { |
| // #107421 |
| MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 0); |
| } |
| |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| |
| //9.Configure the DCT to send initialization MR commands: |
| // BIOS must reprogram Twr, Tcwl, and Tcl based on the new MEMCLK frequency. |
| // Program F2x[1, 0]7C similar to step #2 in Pass 1 above for the new Dimm values. |
| TechPtr->AutoCycTiming (TechPtr); |
| if (!MemNPlatformSpecNb (NBPtr)) { |
| IDS_ERROR_TRAP; |
| } |
| |
| for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) { |
| if (NBPtr->IsSupported[CheckGetMCTSysAddr]) { |
| if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) { |
| // if chip select present |
| TechPtr->SendAllMRCmds (TechPtr, ChipSel); |
| // NOTE: wait 512 clocks for DLL-relock |
| MemUWait10ns (50000, NBPtr->MemPtr); // wait 500us |
| } |
| } |
| if (NBPtr->IsSupported[CheckSendAllMRCmds]) { |
| if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) { |
| |
| // if chip select present |
| TechPtr->SendAllMRCmds (TechPtr, ChipSel); |
| } |
| } |
| } |
| if ((NBPtr->DCTPtr->Timings.Speed == DDR1600_FREQUENCY) && (NBPtr->IsSupported[CheckDllSpeedUp])) { |
| MemNSetBitFieldNb (NBPtr, BFPhy0x0D080F11, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D080F11) | 0x2000)); |
| MemNSetBitFieldNb (NBPtr, BFPhy0x0D080F10, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D080F10) | 0x2000)); |
| MemNSetBitFieldNb (NBPtr, BFPhy0x0D088F30, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D088F30) | 0x2000)); |
| MemNSetBitFieldNb (NBPtr, BFPhy0x0D08C030, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D08C030) | 0x2000)); |
| if (Dct == 0) { |
| MemNSetBitFieldNb (NBPtr, BFPhy0x0D082F30, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D082F30) | 0x2000)); |
| } |
| // NOTE: wait 512 clocks for DLL-relock |
| MemUWait10ns (50000, NBPtr->MemPtr); // wait 500us |
| } |
| } |
| } |
| // Re-enable phy compensation since it had been disabled during InitPhyComp |
| MemNSwitchDCTNb (NBPtr, 0); |
| MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 0); |
| |
| MemFInitTableDrive (NBPtr, MTAfterFreqChg); |
| } |
| |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function ramp up frequency the next level if it have not reached |
| * its TargetSpeed yet. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return TRUE - No fatal error occurs. |
| * @return FALSE - Fatal error occurs. |
| */ |
| |
| BOOLEAN |
| MemNRampUpFrequencyNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| CONST UINT16 FreqList[] = { |
| DDR400_FREQUENCY, |
| DDR533_FREQUENCY, |
| DDR667_FREQUENCY, |
| DDR800_FREQUENCY, |
| DDR1066_FREQUENCY, |
| DDR1333_FREQUENCY, |
| DDR1600_FREQUENCY, |
| DDR1866_FREQUENCY, |
| DDR2133_FREQUENCY |
| }; |
| UINT8 Dct; |
| UINT8 i; |
| UINT16 NewSpeed; |
| DIE_STRUCT *MCTPtr; |
| |
| MCTPtr = NBPtr->MCTPtr; |
| |
| // Do not change frequency when it is already at TargetSpeed |
| if (NBPtr->DCTPtr->Timings.Speed == NBPtr->DCTPtr->Timings.TargetSpeed) { |
| return TRUE; |
| } |
| |
| // Find the next supported frequency level |
| NewSpeed = NBPtr->DCTPtr->Timings.TargetSpeed; |
| for (i = 0; i < (GET_SIZE_OF (FreqList) - 1); i++) { |
| if (NBPtr->DCTPtr->Timings.Speed == FreqList[i]) { |
| NewSpeed = FreqList[i + 1]; |
| break; |
| } |
| } |
| ASSERT (i < (GET_SIZE_OF (FreqList) - 1)); |
| ASSERT (NewSpeed <= NBPtr->DCTPtr->Timings.TargetSpeed); |
| |
| // BIOS must program both DCTs to the same frequency. |
| IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq changed: %d MHz", NBPtr->DCTPtr->Timings.Speed); |
| for (Dct = 0; Dct < MCTPtr->DctCount; Dct++) { |
| NBPtr->SwitchDCT (NBPtr, Dct); |
| NBPtr->DCTPtr->Timings.Speed = NewSpeed; |
| } |
| IDS_HDT_CONSOLE (MEM_FLOW, " -> %d MHz", NewSpeed); |
| |
| NBPtr->ChangeFrequency (NBPtr); |
| |
| return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function ramp up frequency to target frequency |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return TRUE - No fatal error occurs. |
| * @return FALSE - Fatal error occurs. |
| */ |
| |
| BOOLEAN |
| MemNRampUpFrequencyUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 Dct; |
| DIE_STRUCT *MCTPtr; |
| |
| MCTPtr = NBPtr->MCTPtr; |
| |
| // Do not change frequency when it is already at TargetSpeed |
| if (NBPtr->DCTPtr->Timings.Speed == NBPtr->DCTPtr->Timings.TargetSpeed) { |
| return TRUE; |
| } |
| |
| // BIOS must program both DCTs to the same frequency. |
| IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq changed: %d MHz", NBPtr->DCTPtr->Timings.Speed); |
| for (Dct = 0; Dct < MCTPtr->DctCount; Dct++) { |
| NBPtr->SwitchDCT (NBPtr, Dct); |
| NBPtr->DCTPtr->Timings.Speed = NBPtr->DCTPtr->Timings.TargetSpeed; |
| } |
| IDS_HDT_CONSOLE (MEM_FLOW, " -> %d MHz", NBPtr->DCTPtr->Timings.TargetSpeed); |
| |
| NBPtr->ChangeFrequency (NBPtr); |
| |
| return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function uses calculated values from DCT.Timings structure to |
| * program its registers. |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNProgramCycTimingsNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| CONST CTENTRY TmgAdjTab[] = { |
| // BitField, Min, Max, Bias, Ratio_x2 |
| {BFTcl, 4, 12, 4, 2}, |
| {BFTrcd, 5, 12, 5, 2}, |
| {BFTrp, 5, 12, 5, 2}, |
| {BFTrtp, 4, 7, 4, 2}, |
| {BFTras, 15, 30, 15, 2}, |
| {BFTrc, 11, 42, 11, 2}, |
| {BFTwrDDR3, 5, 12, 4, 2}, |
| {BFTrrd, 4, 7, 4, 2}, |
| {BFTwtr, 4, 7, 4, 2}, |
| {BFFourActWindow, 16, 32, 14, 1} |
| }; |
| |
| DCT_STRUCT *DCTPtr; |
| UINT8 *MiniMaxTmg; |
| UINT8 *MiniMaxTrfc; |
| UINT8 Value8; |
| UINT8 j; |
| BIT_FIELD_NAME BitField; |
| |
| DCTPtr = NBPtr->DCTPtr; |
| |
| //====================================================================== |
| // Program turnaround timings to their max during DRAM init and training |
| //====================================================================== |
| // |
| MemNSetBitFieldNb (NBPtr, BFNonSPD, 0x28FF); |
| |
| MemNSetBitFieldNb (NBPtr, BFNonSPDHi, 0x2A); |
| |
| //====================================================================== |
| // Program DRAM Timing values |
| //====================================================================== |
| // |
| MiniMaxTmg = &DCTPtr->Timings.CasL; |
| for (j = 0; j < GET_SIZE_OF (TmgAdjTab); j++) { |
| BitField = TmgAdjTab[j].BitField; |
| |
| if (MiniMaxTmg[j] < TmgAdjTab[j].Min) { |
| MiniMaxTmg[j] = TmgAdjTab[j].Min; |
| } else if (MiniMaxTmg[j] > TmgAdjTab[j].Max) { |
| MiniMaxTmg[j] = TmgAdjTab[j].Max; |
| } |
| |
| Value8 = (UINT8) MiniMaxTmg[j]; |
| |
| if (BitField == BFTwrDDR3) { |
| Value8 = (Value8 == 10) ? 9 : (Value8 >= 11) ? 10 : Value8; |
| } else if (BitField == BFTrtp) { |
| Value8 = (DCTPtr->Timings.Speed <= DDR1066_FREQUENCY) ? 4 : (DCTPtr->Timings.Speed == DDR1333_FREQUENCY) ? 5 : 6; |
| } |
| |
| Value8 = Value8 - TmgAdjTab[j].Bias; |
| Value8 = (Value8 * TmgAdjTab[j].Ratio_x2) >> 1; |
| |
| ASSERT ((BitField == BFTcl ) ? (Value8 <= 8) : |
| (BitField == BFTrcd) ? (Value8 <= 7) : |
| (BitField == BFTrp ) ? (Value8 <= 7) : |
| (BitField == BFTrtp) ? (Value8 <= 3) : |
| (BitField == BFTras) ? (Value8 <= 15) : |
| (BitField == BFTrc ) ? (Value8 <= 31) : |
| (BitField == BFTrrd) ? (Value8 <= 3) : |
| (BitField == BFTwtr) ? (Value8 <= 3) : |
| (BitField == BFTwrDDR3) ? ((Value8 >= 1) && (Value8 <= 6)) : |
| (BitField == BFFourActWindow) ? ((Value8 >= 1) && (Value8 <= 9)) : FALSE); |
| MemNSetBitFieldNb (NBPtr, BitField, Value8); |
| } |
| |
| MiniMaxTrfc = &DCTPtr->Timings.Trfc0; |
| for (j = 0; j < 4; j++) { |
| ASSERT (MiniMaxTrfc[j] <= 4); |
| MemNSetBitFieldNb (NBPtr, BFTrfc0 + j, MiniMaxTrfc[j]); |
| } |
| |
| MemNSetBitFieldNb (NBPtr, BFTcwl, ((DCTPtr->Timings.Speed >= DDR800_FREQUENCY) ? |
| (NBPtr->GetMemClkFreqId (NBPtr, DCTPtr->Timings.Speed) - 3) : 0)); |
| |
| MemNSetBitFieldNb (NBPtr, BFTref, 2); // 7.8 us |
| |
| //====================================================================== |
| // DRAM MRS Register, set ODT |
| //====================================================================== |
| // |
| // DrvImpCtrl: drive impedance control.01b(34 ohm driver; Ron34 = Rzq/7) |
| MemNSetBitFieldNb (NBPtr, BFDrvImpCtrl, 1); |
| |
| // burst length control |
| if (NBPtr->MCTPtr->Status[Sb128bitmode]) { |
| MemNSetBitFieldNb (NBPtr, BFBurstCtrl, 2); |
| } |
| |
| // ASR=1, auto self refresh; SRT=0 |
| MemNSetBitFieldNb (NBPtr, BFASR, 1); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function uses calculated values from DCT.Timings structure to |
| * program its registers. |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNProgramCycTimingsClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| CONST CTENTRY TmgAdjTab[] = { |
| // BitField, Min, Max, Bias, Ratio_x2 |
| {BFTcl, 5, 14, 4, 2}, |
| {BFTrcd, 5, 14, 5, 2}, |
| {BFTrp, 5, 14, 5, 2}, |
| {BFTrtp, 4, 8, 4, 2}, |
| {BFTras, 15, 36, 15, 2}, |
| {BFTrc, 20, 49, 11, 2}, |
| {BFTwrDDR3, 5, 16, 4, 2}, |
| {BFTrrd, 4, 8, 4, 2}, |
| {BFTwtr, 4, 8, 4, 2}, |
| {BFFourActWindow, 16, 40, 14, 1} |
| }; |
| |
| DCT_STRUCT *DCTPtr; |
| UINT8 *MiniMaxTmg; |
| UINT8 *MiniMaxTrfc; |
| UINT8 Value8; |
| UINT8 j; |
| UINT8 Tcwl; |
| UINT8 Trcd; |
| INT32 TCK_ps; |
| BIT_FIELD_NAME BitField; |
| |
| DCTPtr = NBPtr->DCTPtr; |
| |
| //====================================================================== |
| // Program DRAM Timing values |
| //====================================================================== |
| // |
| MiniMaxTmg = &DCTPtr->Timings.CasL; |
| for (j = 0; j < GET_SIZE_OF (TmgAdjTab); j++) { |
| BitField = TmgAdjTab[j].BitField; |
| |
| if (MiniMaxTmg[j] < TmgAdjTab[j].Min) { |
| MiniMaxTmg[j] = TmgAdjTab[j].Min; |
| } else if (MiniMaxTmg[j] > TmgAdjTab[j].Max) { |
| MiniMaxTmg[j] = TmgAdjTab[j].Max; |
| } |
| |
| Value8 = (UINT8) MiniMaxTmg[j]; |
| |
| if (BitField == BFTwrDDR3) { |
| if (NBPtr->IsSupported[AdjustTwr]) { |
| Value8 ++; |
| } |
| Value8 = (Value8 >= 10) ? (((Value8 + 1) / 2) + 4) : Value8; |
| } |
| |
| if ((BitField == BFTrc) && NBPtr->IsSupported[AdjustTrc]) { |
| Value8 -= 5; |
| } |
| |
| Value8 = Value8 - TmgAdjTab[j].Bias; |
| Value8 = (Value8 * TmgAdjTab[j].Ratio_x2) >> 1; |
| |
| ASSERT ((BitField == BFTcl ) ? ((Value8 >= 1) && (Value8 <= 10)) : |
| (BitField == BFTrcd) ? (Value8 <= 9) : |
| (BitField == BFTrp ) ? (Value8 <= 9) : |
| (BitField == BFTrtp) ? (Value8 <= 4) : |
| (BitField == BFTras) ? (Value8 <= 21) : |
| (BitField == BFTrc ) ? (NBPtr->IsSupported[AdjustTrc] ? ((Value8 >= 4) && (Value8 <= 38)) : ((Value8 >= 9) && (Value8 <= 38))) : |
| (BitField == BFTrrd) ? (Value8 <= 4) : |
| (BitField == BFTwtr) ? (Value8 <= 4) : |
| (BitField == BFTwrDDR3) ? (Value8 <= 7) : |
| (BitField == BFFourActWindow) ? ((Value8 >= 1) && (Value8 <= 13)) : FALSE); |
| MemNSetBitFieldNb (NBPtr, BitField, Value8); |
| } |
| |
| MiniMaxTrfc = &DCTPtr->Timings.Trfc0; |
| for (j = 0; j < 4; j++) { |
| ASSERT (MiniMaxTrfc[j] <= 5); |
| MemNSetBitFieldNb (NBPtr, BFTrfc0 + j, MiniMaxTrfc[j]); |
| } |
| |
| Tcwl = (UINT8) (DCTPtr->Timings.Speed / 133) + 2; |
| MemNSetBitFieldNb (NBPtr, BFTcwl, ((Tcwl > 5) ? (Tcwl - 5) : 0)); |
| |
| MemNSetBitFieldNb (NBPtr, BFTref, 2); // Tref = 7.8 us |
| |
| // Skid buffer can only be programmed once before Dram init |
| if (NBPtr->DCTPtr->Timings.Speed == DDR800_FREQUENCY) { |
| TCK_ps = 1000500 / DCTPtr->Timings.TargetSpeed; |
| Trcd = (UINT8) ((((1000 / 40) * (UINT32)DCTPtr->Timings.DIMMTrcd) + TCK_ps - 1) / TCK_ps); |
| MemNSetBitFieldNb (NBPtr, BFDbeSkidBufDis, (Trcd > 10) ? 0 : 1); |
| } |
| |
| MemNSetBitFieldNb (NBPtr, BFRdOdtTrnOnDly, (DCTPtr->Timings.CasL > Tcwl) ? (DCTPtr->Timings.CasL - Tcwl) : 0); |
| |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function uses calculated values from DCT.Timings structure to |
| * program its registers for UNB |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNProgramCycTimingsUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| CONST CTENTRY TmgAdjTab[] = { |
| // BitField, Min, Max, Bias, Ratio_x2 |
| {BFTcl, 5, 14, 0, 2}, |
| {BFTrcd, 2, 19, 0, 2}, |
| {BFTrp, 2, 19, 0, 2}, |
| {BFTrtp, 4, 10, 0, 2}, |
| {BFTras, 8, 40, 0, 2}, |
| {BFTrc, 10, 56, 0, 2}, |
| {BFTwrDDR3, 5, 16, 0, 2}, |
| {BFTrrd, 4, 9, 0, 2}, |
| {BFTwtr, 4, 9, 0, 2}, |
| {BFFourActWindow, 6, 42, 0, 2} |
| }; |
| |
| DCT_STRUCT *DCTPtr; |
| UINT8 *MiniMaxTmg; |
| UINT8 *MiniMaxTrfc; |
| UINT8 Value8; |
| UINT8 j; |
| UINT8 Tcwl; |
| UINT8 RdOdtTrnOnDly; |
| BIT_FIELD_NAME BitField; |
| |
| DCTPtr = NBPtr->DCTPtr; |
| |
| //====================================================================== |
| // Program DRAM Timing values |
| //====================================================================== |
| // |
| MiniMaxTmg = &DCTPtr->Timings.CasL; |
| for (j = 0; j < GET_SIZE_OF (TmgAdjTab); j++) { |
| BitField = TmgAdjTab[j].BitField; |
| |
| if (BitField == BFTrp) { |
| if (NBPtr->IsSupported[AdjustTrp]) { |
| MiniMaxTmg[j] ++; |
| if (MiniMaxTmg[j] < 5) { |
| MiniMaxTmg[j] = 5; |
| } |
| } |
| } |
| |
| if (MiniMaxTmg[j] < TmgAdjTab[j].Min) { |
| MiniMaxTmg[j] = TmgAdjTab[j].Min; |
| } else if (MiniMaxTmg[j] > TmgAdjTab[j].Max) { |
| MiniMaxTmg[j] = TmgAdjTab[j].Max; |
| } |
| |
| Value8 = (UINT8) MiniMaxTmg[j]; |
| |
| if (BitField == BFTwrDDR3) { |
| if ((Value8 > 8) && ((Value8 & 1) != 0)) { |
| ASSERT (FALSE); |
| } |
| } |
| |
| MemNSetBitFieldNb (NBPtr, BitField, Value8); |
| } |
| |
| MiniMaxTrfc = &DCTPtr->Timings.Trfc0; |
| for (j = 0; j < 4; j++) { |
| if ((NBPtr->DCTPtr->Timings.DctDimmValid & (1 << j)) != 0) { |
| ASSERT (MiniMaxTrfc[j] <= 4); |
| MemNSetBitFieldNb (NBPtr, BFTrfc0 + j, MiniMaxTrfc[j]); |
| } |
| } |
| |
| Tcwl = (UINT8) (DCTPtr->Timings.Speed / 133) + 2; |
| Tcwl = (Tcwl > 5) ? Tcwl : 5; |
| MemNSetBitFieldNb (NBPtr, BFTcwl, Tcwl); |
| |
| MemNSetBitFieldNb (NBPtr, BFTref, 2); // 7.8 us |
| |
| RdOdtTrnOnDly = (DCTPtr->Timings.CasL > Tcwl) ? (DCTPtr->Timings.CasL - Tcwl) : 0; |
| MemNSetBitFieldNb (NBPtr, BFRdOdtTrnOnDly, RdOdtTrnOnDly); |
| NBPtr->FamilySpecificHook[ProgOdtControl] (NBPtr, NULL); |
| |
| // |
| // Program Tmod |
| // |
| MemNSetBitFieldNb (NBPtr, BFTmod, (DCTPtr->Timings.Speed < DDR1866_FREQUENCY) ? 0x0C : |
| (DCTPtr->Timings.Speed > DDR1866_FREQUENCY) ? 0x10 : 0x0E); |
| // |
| // Program Tzqcs and Tzqoper |
| // |
| // Tzqcs max(64nCK, 80ns) |
| MemNSetBitFieldNb (NBPtr, BFTzqcs, MIN (6, (MAX (64, MemUnsToMemClk (NBPtr->DCTPtr->Timings.Speed, 80)) + 15) / 16)); |
| // Tzqoper max(256nCK, 320ns) |
| MemNSetBitFieldNb (NBPtr, BFTzqoper, MIN (0xC, (MAX (256, MemUnsToMemClk (NBPtr->DCTPtr->Timings.Speed, 320)) + 31) / 32)); |
| |
| // Program power management timing |
| MemNDramPowerMngTimingNb (NBPtr); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets platform specific settings for the current channel |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return TRUE - All platform types defined have initialized successfully |
| * @return FALSE - At least one of the platform types gave not been initialized successfully |
| */ |
| |
| BOOLEAN |
| MemNGetPlatformCfgNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 p; |
| |
| for (p = 0; p < MAX_PLATFORM_TYPES; p++) { |
| ASSERT (NBPtr->MemPtr->GetPlatformCfg[p] != NULL); |
| if (NBPtr->MemPtr->GetPlatformCfg[p] (NBPtr->MemPtr, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr) == AGESA_SUCCESS) { |
| break; |
| } |
| } |
| return (p < MAX_PLATFORM_TYPES); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function retrieves the Max latency parameters |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @param[in] *MinDlyPtr - Pointer to variable to store the Minimum Delay value |
| * @param[in] *MaxDlyPtr - Pointer to variable to store the Maximum Delay value |
| * @param[in] *DlyBiasPtr - Pointer to variable to store Delay Bias value |
| * @param[in] MaxRcvEnDly - Maximum receiver enable delay value |
| */ |
| |
| VOID |
| MemNGetMaxLatParamsNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT16 MaxRcvEnDly, |
| IN OUT UINT16 *MinDlyPtr, |
| IN OUT UINT16 *MaxDlyPtr, |
| IN OUT UINT16 *DlyBiasPtr |
| ) |
| { |
| *MinDlyPtr = (MemNTotalSyncComponentsNb (NBPtr) + (MaxRcvEnDly >> 5)) * 2; |
| MemNQuarterMemClk2NClkNb (NBPtr, MinDlyPtr); |
| |
| *MaxDlyPtr = 0x3FF; |
| |
| *DlyBiasPtr = 4; |
| MemNQuarterMemClk2NClkNb (NBPtr, DlyBiasPtr); // 1 MEMCLK Margin |
| |
| *DlyBiasPtr += 1; // add 1 NCLK |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function sets the maximum round-trip latency in the system from the processor to the DRAM |
| * devices and back. |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] MaxRcvEnDly - Maximum receiver enable delay value |
| * |
| */ |
| |
| VOID |
| MemNSetMaxLatencyNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT16 MaxRcvEnDly |
| ) |
| { |
| UINT16 SubTotal; |
| |
| AGESA_TESTPOINT (TpProcMemRcvrCalcLatency, &(NBPtr->MemPtr->StdHeader)); |
| |
| SubTotal = 0xC8; // init value for MaxRdLat used in training |
| |
| |
| if (MaxRcvEnDly != 0xFFFF) { |
| // Get all sync components BKDG steps 1-5 |
| SubTotal = MemNTotalSyncComponentsNb (NBPtr); |
| |
| // Add the maximum (worst case) delay value of DqsRcvEnGrossDelay |
| // that exists across all DIMMs and byte lanes. |
| // |
| SubTotal += MaxRcvEnDly >> 5; |
| |
| |
| // Add 14.5 to the sub-total. 14.5 represents part of the processor |
| // specific constant delay value in the DRAM clock domain. |
| // |
| SubTotal <<= 1; // scale 1/2 MemClk to 1/4 MemClk |
| SubTotal += 29; // add 14.5 1/2 MemClk |
| |
| // Convert the sub-total (in 1/2 MEMCLKs) to northbridge clocks (NCLKs) |
| // as follows (assuming DDR400 and assuming that no P-state or link speed |
| // changes have occurred). |
| // |
| MemNQuarterMemClk2NClkNb (NBPtr, &SubTotal); |
| |
| // Add 2 NCLKs to the sub-total. 2 represents part of the processor |
| // specific constant value in the northbridge clock domain. |
| // |
| SubTotal += 2; |
| } |
| |
| NBPtr->DCTPtr->Timings.MaxRdLat = SubTotal; |
| // Program the F2x[1, 0]78[MaxRdLatency] register with the total delay value |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\tMaxRdLat: %03x\n", SubTotal); |
| MemNSetBitFieldNb (NBPtr, BFMaxLatency, SubTotal); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function sends the ZQCL command |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNSendZQCmdNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| // 1.Program MrsAddress[10]=1 |
| MemNSetBitFieldNb (NBPtr, BFMrsAddress, (UINT32)1 << 10); |
| |
| // 2.Set SendZQCmd=1 |
| MemNSetBitFieldNb (NBPtr, BFSendZQCmd, 1); |
| |
| // 3.Wait for SendZQCmd=0 |
| MemNPollBitFieldNb (NBPtr, BFSendZQCmd, 0, PCI_ACCESS_TIMEOUT, FALSE); |
| |
| // 4.Wait 512 MEMCLKs |
| MemNWaitXMemClksNb (NBPtr, 512); |
| } |
| |
| |
| /*---------------------------------------------------------------------------- |
| * LOCAL FUNCTIONS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function is used to create the DRAM map |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| */ |
| |
| VOID |
| STATIC |
| MemNAfterStitchMemNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| if (NBPtr->MCTPtr->GangedMode) { |
| NBPtr->MCTPtr->NodeMemSize = NBPtr->DCTPtr->Timings.DctMemSize; |
| NBPtr->MCTPtr->NodeSysLimit = NBPtr->MCTPtr->NodeMemSize - 1; |
| NBPtr->MCTPtr->DctData[1].Timings.CsPresent = NBPtr->DCTPtr->Timings.CsPresent; |
| NBPtr->MCTPtr->DctData[1].Timings.CsEnabled = NBPtr->DCTPtr->Timings.CsEnabled; |
| NBPtr->MCTPtr->DctData[1].Timings.DctMemSize = NBPtr->DCTPtr->Timings.DctMemSize; |
| } else { |
| // In unganged mode, add DCT0 and DCT1 to NodeMemSize |
| NBPtr->MCTPtr->NodeMemSize += NBPtr->DCTPtr->Timings.DctMemSize; |
| NBPtr->MCTPtr->NodeSysLimit = NBPtr->MCTPtr->NodeMemSize - 1; |
| } |
| } |
| |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function Return the binary value of tfaw associated with |
| * the index k |
| * |
| * @param[in] k value |
| * |
| * @return F[k], in Binary MHz. |
| */ |
| |
| UINT8 |
| MemNGet1KTFawTkNb ( |
| IN UINT8 k |
| ) |
| { |
| CONST UINT8 Tab1KTfawTK[] = {0, 8, 10, 13, 14, 19}; |
| ASSERT (k <= 5); |
| return Tab1KTfawTK[k]; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function Return the binary value of the 2KTFaw associated with |
| * the index k |
| * |
| * @param[in] k value |
| * |
| * @return 2KTFaw converted based on k. |
| */ |
| |
| UINT8 |
| MemNGet2KTFawTkNb ( |
| IN UINT8 k |
| ) |
| { |
| CONST UINT8 Tab2KTfawTK[] = {0, 10, 14, 17, 18, 24}; |
| ASSERT (k <= 5); |
| return Tab2KTfawTK[k]; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function converts the sub-total (in 1/4 MEMCLKs) to northbridge clocks (NCLKs) |
| * (assuming DDR400 and assuming that no P-state or link speed |
| * changes have occurred). |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] *SubTotalPtr - pointer to Sub-Total |
| */ |
| |
| VOID |
| STATIC |
| MemNQuarterMemClk2NClkNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT UINT16 *SubTotalPtr |
| ) |
| { |
| UINT32 NBFreq; |
| UINT32 MemFreq; |
| |
| // Multiply SubTotal by NB COF |
| NBFreq = (MemNGetBitFieldNb (NBPtr, BFNbFid) + 4) * 200; |
| // Divide SubTotal by 4 times current MemClk frequency |
| MemFreq = NBPtr->DCTPtr->Timings.Speed * 4; |
| *SubTotalPtr = (UINT16) (((NBFreq * (*SubTotalPtr)) + MemFreq - 1) / MemFreq); // round up |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets the total of sync components for Max Read Latency calculation |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return Total in 1/2 MEMCLKs |
| */ |
| |
| UINT16 |
| MemNTotalSyncComponentsNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT16 SubTotal; |
| |
| // Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs UINTs. |
| SubTotal = (UINT16) MemNGetBitFieldNb (NBPtr, BFTcl) + 1; |
| if ((MemNGetBitFieldNb (NBPtr, BFDdr3Mode)) != 0) { |
| SubTotal += 3; |
| } |
| SubTotal *= 2; |
| |
| // If registered DIMMs are being used then add 1 MEMCLK to the sub-total. |
| if ((MemNGetBitFieldNb (NBPtr, BFUnBuffDimm)) == 0) { |
| SubTotal += 2; |
| } |
| |
| // If (F2x[1, 0]9C_x04[AddrCmdSetup] and F2x[1, 0]9C_x04[CsOdtSetup] and F2x[1, 0]9C_x04[Cke-Setup] = 0) then K = K + 1 |
| // If (F2x[1, 0]9C_x04[AddrCmdSetup] or F2x[1, 0]9C_x04[CsOdtSetup] or F2x[1, 0]9C_x04[CkeSetup] = 1) then K = K + 2 |
| if ((MemNGetBitFieldNb (NBPtr, BFAddrTmgControl) & 0x0202020) == 0) { |
| SubTotal += 1; |
| } else { |
| SubTotal += 2; |
| } |
| |
| // If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs, |
| // then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total. |
| // |
| SubTotal = SubTotal + (8 - (UINT16) MemNGetBitFieldNb (NBPtr, BFRdPtrInit)); |
| |
| return SubTotal; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function swaps bits for OnDimmMirror support |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNSwapBitsNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 ChipSel; |
| UINT32 MRSReg; |
| |
| ChipSel = (UINT8) MemNGetBitFieldNb (NBPtr, BFMrsChipSel); |
| if ((ChipSel & 1) != 0) { |
| MRSReg = MemNGetBitFieldNb (NBPtr, BFDramInitRegReg); |
| if ((NBPtr->DCTPtr->Timings.DimmMirrorPresent & (1 << (ChipSel >> 1))) != 0) { |
| MRSReg = (MRSReg & 0xFFFCFE07) | ((MRSReg&0x100A8) << 1) | ((MRSReg&0x20150) >> 1); |
| MemNSetBitFieldNb (NBPtr, BFDramInitRegReg, MRSReg); |
| } |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function swaps bits for OnDimmMirror support for Unb |
| * |
| * Dimm Mirroring Requires that, during MRS command cycles, the following |
| * bits are swapped by software |
| * |
| * A3 -> A4 A7 -> A8 |
| * A4 -> A3 BA0 -> BA1 |
| * A5 -> A6 BA1 -> BA0 |
| * A6 -> A5 |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNSwapBitsUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 ChipSel; |
| UINT32 MRSBank; |
| UINT32 MRSAddr; |
| |
| ChipSel = (UINT8) MemNGetBitFieldNb (NBPtr, BFMrsChipSel); |
| if ((ChipSel & 1) != 0) { |
| if ((NBPtr->DCTPtr->Timings.DimmMirrorPresent & (1 << (ChipSel >> 1))) != 0) { |
| MRSBank = MemNGetBitFieldNb (NBPtr, BFMrsBank); |
| MRSAddr = MemNGetBitFieldNb (NBPtr, BFMrsAddress); |
| |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS%d MR%d %05x swapped to ->", |
| (ChipSel & 0x7), |
| (MRSBank & 0x7), |
| (MRSAddr & 0x3FFFF)); |
| // |
| // Swap Mrs Bank bits 0 with 1 |
| MRSBank = (MRSBank & 0x0100) | ((MRSBank & 0x01) << 1) | ((MRSBank & 0x02) >> 1); |
| // |
| // Swap Mrs Address bits 3 with 4, 5 with 6, and 7 with 8 |
| MRSAddr = (MRSAddr & 0x03FE07) | ((MRSAddr&0x000A8) << 1) | ((MRSAddr&0x00150) >> 1); |
| MemNSetBitFieldNb (NBPtr, BFMrsBank, MRSBank); |
| MemNSetBitFieldNb (NBPtr, BFMrsAddress, MRSAddr); |
| } |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Programs Address/command timings, driver strengths, and tri-state fields. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| VOID |
| MemNProgramPlatformSpecNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| CONST UINT8 PinType[3] = {PSO_CKE_TRI, PSO_ODT_TRI, PSO_CS_TRI}; |
| CONST UINT8 TabSize[3] = { 2, 4, 8}; |
| CONST BIT_FIELD_NAME BitField[3] = { BFCKETri, BFODTTri, BFChipSelTri}; |
| UINT8 *TabPtr; |
| UINT8 i; |
| UINT8 k; |
| UINT8 Value; |
| //=================================================================== |
| // Tristate unused CKE, ODT and chip select to save power |
| //=================================================================== |
| // |
| TabPtr = NULL; |
| for (k = 0; k < sizeof (PinType); k++) { |
| if (NBPtr->IsSupported[CheckFindPSOverideWithSocket]) { |
| TabPtr = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PinType[k], NBPtr->MCTPtr->SocketId, MemNGetSocketRelativeChannelNb (NBPtr, NBPtr->Dct, 0), 0, |
| &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader)); |
| } |
| if (NBPtr->IsSupported[CheckFindPSDct]) { |
| TabPtr = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PinType[k], NBPtr->MCTPtr->SocketId, NBPtr->Dct, 0, |
| &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader)); |
| } |
| if (TabPtr == NULL) { |
| switch (k) { |
| case 0: |
| TabPtr = NBPtr->ChannelPtr->CKETriMap; |
| break; |
| case 1: |
| TabPtr = NBPtr->ChannelPtr->ODTTriMap; |
| break; |
| case 2: |
| TabPtr = NBPtr->ChannelPtr->ChipSelTriMap; |
| break; |
| default: |
| IDS_ERROR_TRAP; |
| } |
| } |
| ASSERT (TabPtr != NULL); |
| |
| Value = 0; |
| for (i = 0; i < TabSize[k]; i++) { |
| if ((NBPtr->DCTPtr->Timings.CsPresent & TabPtr[i]) == 0) { |
| Value |= (UINT8) (1 << i); |
| } |
| } |
| |
| if (k == PSO_CS_TRI) { |
| NBPtr->FamilySpecificHook[BeforeSetCsTri] (NBPtr, &Value); |
| } |
| |
| ASSERT (k < GET_SIZE_OF (BitField)); |
| MemNSetBitFieldNb (NBPtr, BitField[k], Value); |
| } |
| NBPtr->MemNBeforePlatformSpecNb (NBPtr); |
| |
| //=================================================================== |
| // Program Address/Command timings and driver strength |
| //=================================================================== |
| // |
| MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_ADDRTMG, ALL_DIMMS); |
| MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_ODCCONTROL, ALL_DIMMS); |
| |
| MemNSetBitFieldNb (NBPtr, BFSlowAccessMode, (NBPtr->ChannelPtr->SlowMode) ? 1 : 0); |
| MemNSetBitFieldNb (NBPtr, BFODCControl, NBPtr->ChannelPtr->DctOdcCtl); |
| MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, NBPtr->ChannelPtr->DctAddrTmg); |
| NBPtr->FamilySpecificHook[SetDqsODT] (NBPtr, NBPtr); |
| |
| if (NBPtr->IsSupported[CheckODTControls]) { |
| MemNSetBitFieldNb (NBPtr, BFPhyRODTCSLow, NBPtr->ChannelPtr->PhyRODTCSLow); |
| MemNSetBitFieldNb (NBPtr, BFPhyRODTCSHigh, NBPtr->ChannelPtr->PhyRODTCSHigh); |
| MemNSetBitFieldNb (NBPtr, BFPhyWODTCSLow, NBPtr->ChannelPtr->PhyWODTCSLow); |
| MemNSetBitFieldNb (NBPtr, BFPhyWODTCSHigh, NBPtr->ChannelPtr->PhyWODTCSHigh); |
| } |
| } |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets the Trdrd value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return Trdrd value |
| */ |
| |
| UINT8 |
| MemNGetTrdrdNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| DCT_STRUCT *DCTPtr; |
| INT8 Cgdd; |
| |
| DCTPtr = NBPtr->DCTPtr; |
| |
| // BIOS calculates Trdrd (in MEMCLKs) = CGDD / 2 + 3 clocks and programs F2x[1, 0]8C[Trdrd] with the |
| // converted field value. BIOS rounds fractional values down. |
| // The Critical Gross Delay Difference (CGDD) for Trdrd on any given byte lane is the largest F2x[1, |
| // 0]9C_x[3:0][2B:10][DqsRcvEnGrossDelay] delay of any DIMM minus the F2x[1, |
| // 0]9C_x[3:0][2B:10][DqsRcvEnGrossDelay] delay of any other DIMM. |
| |
| Cgdd = MemNGetOptimalCGDDNb (NBPtr, AccessRcvEnDly, AccessRcvEnDly); |
| DCTPtr->Timings.Trdrd = (Cgdd / 2) + 3; |
| |
| // Transfer clk to reg definition, 2T is 00b, etc. |
| DCTPtr->Timings.Trdrd -= 2; |
| if (DCTPtr->Timings.Trdrd > 8) { |
| DCTPtr->Timings.Trdrd = 8; |
| } |
| |
| return DCTPtr->Timings.Trdrd; |
| } |
| |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets the Twrwr value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return Twrwr value |
| */ |
| |
| UINT8 |
| MemNGetTwrwrNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| DCT_STRUCT *DCTPtr; |
| INT8 Cgdd; |
| |
| DCTPtr = NBPtr->DCTPtr; |
| |
| // Twrwr (in MEMCLKs) = CGDD / 2 + 3 clocks and programs F2x[1, 0]8C[Twrwr] with the |
| // converted field value. BIOS rounds fractional values down. |
| // On any given byte lane, the largest F2x[1, 0]9C_x[3:0][A, 7, 6, 0][2:1]:F2x[1, 0]9C_x[3:0][A, 7, 6, |
| // 0]3[WrDatGrossDlyByte] delay of any DIMM minus the F2x[1, 0]9C_x[3:0][A, 7, 6, 0][2:1]:F2x[1, |
| // 0]9C_x[3:0][A, 7, 6, 0]3[WrDatGrossDlyByte] delay of any other DIMM is equal to the Critical Gross |
| // Delay Difference (CGDD) for Twrwr. |
| |
| Cgdd = MemNGetOptimalCGDDNb (NBPtr, AccessWrDatDly, AccessWrDatDly); |
| DCTPtr->Timings.Twrwr = (Cgdd / 2) + 3; |
| NBPtr->TechPtr->AdjustTwrwr (NBPtr->TechPtr); |
| |
| return DCTPtr->Timings.Twrwr; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets the Twrrd value. BIOS calculates Twrrd (in MEMCLKs) = CGDD / 2 - LD + 3 clocks and programs |
| * F2x[1, 0]8C[Twrrd] with the converted field value. BIOS rounds fractional |
| * values down. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return Value to be programmed to Twrrd field |
| * pDCT->Timings.Twrrd updated |
| */ |
| |
| UINT8 |
| MemNGetTwrrdNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| INT8 Cgdd; |
| INT8 Ld; |
| INT8 Twrrd; |
| DCT_STRUCT *DCTPtr; |
| |
| DCTPtr = NBPtr->DCTPtr; |
| |
| // |
| // For DDR3, BIOS calculates the latency difference (Ld) as equal to read CAS latency minus write CAS |
| // latency, in MEMCLKs (see F2x[1, 0]88[Tcl] and F2x[1, 0]84[Tcwl]) which can be a negative or positive |
| // value. |
| // For DDR2, LD is always one clock (For DDR2, Tcwl is always Tcl minus 1). |
| // |
| Ld = NBPtr->TechPtr->GetLD (NBPtr->TechPtr); |
| |
| // On any given byte lane, the largest WrDatGrossDlyByte delay of any DIMM |
| // minus the DqsRcvEnGrossDelay delay of any other DIMM is |
| // equal to the Critical Gross Delay Difference (CGDD) for Twrrd. |
| Cgdd = MemNGetOptimalCGDDNb (NBPtr, AccessWrDatDly, AccessRcvEnDly); |
| Twrrd = (Cgdd / 2) - Ld + 3; |
| DCTPtr->Timings.Twrrd = (UINT8) ((Twrrd >= 0) ? Twrrd : 0); |
| NBPtr->TechPtr->AdjustTwrrd (NBPtr->TechPtr); |
| |
| return DCTPtr->Timings.Twrrd; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets the TrwtTO value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return pDCT->Timings.TrwtTO updated |
| */ |
| |
| UINT8 |
| MemNGetTrwtTONb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| INT8 Cgdd; |
| INT8 Ld; |
| INT8 TrwtTO; |
| DCT_STRUCT *DCTPtr; |
| |
| DCTPtr = NBPtr->DCTPtr; |
| // |
| // For DDR3, BIOS calculates the latency difference (Ld) as equal to read CAS latency minus write CAS |
| // latency, in MEMCLKs (see F2x[1, 0]88[Tcl] and F2x[1, 0]84[Tcwl]) which can be a negative or positive |
| // value. |
| // For DDR2, LD is always one clock (For DDR2, Tcwl is always Tcl minus 1). |
| // |
| Ld = NBPtr->TechPtr->GetLD (NBPtr->TechPtr); |
| |
| // On any byte lane, the largest DqsRcvEnGrossDelay delay of any DIMM minus |
| // the WrDatGrossDlyByte delay of any other DIMM is equal to the Critical Gross |
| // Delay Difference (CGDD) for TrwtTO. |
| Cgdd = MemNGetOptimalCGDDNb (NBPtr, AccessRcvEnDly, AccessWrDatDly); |
| TrwtTO = (Cgdd / 2) + Ld + 3; |
| TrwtTO -= 2; |
| DCTPtr->Timings.TrwtTO = (UINT8) ((TrwtTO > 1) ? TrwtTO : 1); |
| |
| return DCTPtr->Timings.TrwtTO; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets the TrwtWB value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return TrwtWB value |
| */ |
| UINT8 |
| MemNGetTrwtWBNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| DCT_STRUCT *DCTPtr; |
| |
| DCTPtr = NBPtr->DCTPtr; |
| |
| // TrwtWB ensures read-to-write data-bus turnaround. |
| // This value should be one more than the programmed TrwtTO. |
| return DCTPtr->Timings.TrwtWB = DCTPtr->Timings.TrwtTO; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function converts MemClk frequency in MHz to MemClkFreq value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] Speed - MemClk frequency in MHz |
| * |
| * @return MemClkFreq value |
| */ |
| UINT8 |
| MemNGetMemClkFreqIdNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT16 Speed |
| ) |
| { |
| return (UINT8) ((Speed < DDR800_FREQUENCY) ? ((Speed / 66) - 3) : (Speed / 133)); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function enables swapping interleaved region feature. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] Base - Swap interleaved region base [47:27] |
| * @param[in] Limit - Swap interleaved region limit [47:27] |
| * |
| */ |
| VOID |
| MemNEnableSwapIntlvRgnNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT32 Base, |
| IN UINT32 Limit |
| ) |
| { |
| UINT32 Size; |
| UINT32 SizeOfAlign; |
| |
| // Swapped interleaving region must be below 16G |
| if (Limit < (1 << (34 - 27))) { |
| // Adjust Base and Size to meet : |
| // 1. The size of the swapped region must be less than or equal to the alignment of F2x10C[IntLvRegionBase]. |
| // 2. Entire UMA region is swapped with interleaving region. |
| Size = Limit - Base; |
| SizeOfAlign = (UINT32) 1 << LibAmdBitScanForward (Base); |
| while (SizeOfAlign <= Size) { |
| // In case of SizeOfAlign <= Size, UmaBase -= 128MB, SizeOfIntlvrgn += 128MB. |
| Base -= 1; |
| Size += 1; |
| SizeOfAlign = (UINT32) 1 << LibAmdBitScanForward (Base); |
| } |
| MemNSetBitFieldNb (NBPtr, BFIntLvRgnBaseAddr, Base); |
| MemNSetBitFieldNb (NBPtr, BFIntLvRgnLmtAddr, (Limit - 1)); |
| MemNSetBitFieldNb (NBPtr, BFIntLvRgnSize, Size); |
| MemNSetBitFieldNb (NBPtr, BFIntLvRgnSwapEn, 1); |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function converts MemClk frequency in MHz to MemClkFreq value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] Speed - MemClk frequency in MHz |
| * |
| * @return MemClkFreq value |
| */ |
| UINT8 |
| MemNGetMemClkFreqIdClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT16 Speed |
| ) |
| { |
| return (UINT8) ((Speed > DDR400_FREQUENCY) ? ((Speed / 33) - 6) : ((Speed == DDR400_FREQUENCY) ? 2 : (Speed / 55))); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function converts MemClk frequency in MHz to MemClkFreq value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] Speed - MemClk frequency in MHz |
| * |
| * @return MemClkFreq value |
| */ |
| UINT8 |
| MemNGetMemClkFreqIdUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT16 Speed |
| ) |
| { |
| return (UINT8) ((Speed > DDR400_FREQUENCY) ? ((Speed / 33) - 6) : ((Speed == DDR400_FREQUENCY) ? 2 : (Speed / 55))); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function converts MemClkFreq Id value to MemClk frequency in MHz |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] FreqId - FreqId from Register |
| * |
| * @return MemClk frequency in MHz |
| */ |
| UINT16 |
| MemNGetMemClkFreqUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT8 FreqId |
| ) |
| { |
| UINT16 MemClkFreq; |
| if (FreqId > 2) { |
| MemClkFreq = (FreqId == 14) ? 667 : (300 + ((FreqId - 3) * 33) + (FreqId - 3) / 3); |
| } else if (FreqId == 2) { |
| MemClkFreq = 200; |
| } else { |
| MemClkFreq = 50 + (50 * FreqId); |
| } |
| return MemClkFreq; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function change MemClk frequency to the value that is specified by DCTPtr->Timings.Speed |
| * for client NB. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNChangeFrequencyClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| MEM_TECH_BLOCK *TechPtr; |
| UINT8 Dct; |
| UINT8 ChipSel; |
| UINT16 FinalPllLockTime; |
| BOOLEAN FrequencyChangeSuccess; |
| UINT64 OrgMMIOCfgBase; |
| UINT64 NewMMIOCfgBase; |
| |
| TechPtr = NBPtr->TechPtr; |
| |
| // Disable MMIO to prevent speculative DRAM reads during self refresh |
| LibAmdMsrRead (MSR_MMIO_Cfg_Base, &OrgMMIOCfgBase, &(NBPtr->MemPtr->StdHeader)); |
| NewMMIOCfgBase = OrgMMIOCfgBase & (~(BIT0)); |
| LibAmdMsrWrite (MSR_MMIO_Cfg_Base, &NewMMIOCfgBase, &(NBPtr->MemPtr->StdHeader)); |
| |
| MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 1); |
| |
| //Program F2x[1,0]90[EnterSelfRefresh]=1. |
| //Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0. |
| MemNBrdcstSetNb (NBPtr, BFEnterSelfRef, 1); |
| MemNPollBitFieldNb (NBPtr, BFEnterSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE); |
| |
| if (NBPtr->ChangeNbFrequency (NBPtr)) { |
| // Reprogram Twr, Tcwl, and Tcl based on the new MEMCLK frequency. |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| TechPtr->AutoCycTiming (TechPtr); |
| if (!MemNPlatformSpecUnb (NBPtr)) { |
| IDS_ERROR_TRAP; |
| } |
| } |
| } |
| |
| // 1. Program PllLockTime to Family-specific value |
| MemNBrdcstSetNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault); |
| |
| // 2. Program D18F2x[1,0]94[MemClkFreqVal] = 0. |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 0); |
| |
| // 3. Program D18F2x[1,0]94[MemClkFreq] to the desired DRAM frequency. |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreq, NBPtr->GetMemClkFreqId (NBPtr, NBPtr->DCTPtr->Timings.Speed)); |
| |
| // 4. Program D18F2x[1,0]F4_x30[DbeGskFifoNumerator] and D18F2x[1,0]F4_x31[DbeGskFifoDenominator]. |
| // 5. Program D18F2x[1,0]F4_x32[DataTxFifoSchedDlyNegSlot1, DataTxFifoSchedDlySlot1, |
| // DataTxFifoSchedDlyNegSlot0, DataTxFifoSchedDlySlot0]. See 2.10.3.2.2.1 [DCT Transmit Fifo Schedule |
| // Delay Programming]. |
| // 6. D18F2x[1,0]78[RdPtrInit] = IF (D18F2x[1,0]94[MemClkFreq] >= 667 MHz) THEN 7 ELSE 8 ENDIF (Llano) |
| // THEN 2 ELSE 3 ENDIF (Ontario) |
| NBPtr->ProgramNbPsDependentRegs (NBPtr); |
| |
| NBPtr->FamilySpecificHook[BeforeMemClkFreqVal] (NBPtr, NBPtr); |
| IDS_OPTION_HOOK (IDS_BEFORE_MEM_FREQ_CHG, NBPtr, &(NBPtr->MemPtr->StdHeader)); |
| // 7. Program D18F2x[1,0]94[MemClkFreqVal] = 1. |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 1); |
| MemNPollBitFieldNb (NBPtr, BFFreqChgInProg, 0, PCI_ACCESS_TIMEOUT, TRUE); |
| FinalPllLockTime = 0xF; |
| NBPtr->FamilySpecificHook[AfterMemClkFreqVal] (NBPtr, &FinalPllLockTime); |
| |
| // 8. IF (D18F2x[1,0]9C_x0D0F_E00A[CsrPhySrPllPdMode]==0) THEN program |
| // D18F2x[1,0]9C_x0D0F_E006[PllLockTime] = 0Fh. |
| if (!NBPtr->IsSupported[CsrPhyPllPdEn]) { |
| MemNBrdcstSetNb (NBPtr, BFPllLockTime, FinalPllLockTime); |
| } |
| |
| FrequencyChangeSuccess = TRUE; |
| } else { |
| // If NB frequency cannot be updated, use the current speed as the target speed |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| NBPtr->DCTPtr->Timings.Speed = NBPtr->TechPtr->PrevSpeed; |
| NBPtr->DCTPtr->Timings.TargetSpeed = NBPtr->TechPtr->PrevSpeed; |
| } |
| FrequencyChangeSuccess = FALSE; |
| } |
| |
| //Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs. |
| //Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. |
| MemNBrdcstSetNb (NBPtr, BFExitSelfRef, 1); |
| MemNPollBitFieldNb (NBPtr, BFExitSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE); |
| MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 0); |
| |
| if (FrequencyChangeSuccess) { |
| NBPtr->FamilySpecificHook[AfterMemClkFreqChg] (NBPtr, NULL); |
| |
| // Perform Phy Fence training and Phy comp init after frequency change |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); |
| |
| // Phy fence programming |
| AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->PhyFenceTraining (NBPtr); |
| |
| // Phy compensation initialization |
| AGESA_TESTPOINT (TPProcMemPhyCompensation, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->MemNInitPhyComp (NBPtr); |
| MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_SLEWRATE, ALL_DIMMS); |
| } |
| } |
| |
| //====================================================================== |
| // Calculate and program DRAM Timings at new frequency |
| //====================================================================== |
| // |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) { |
| if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) { |
| // if chip select present |
| if (!(TechPtr->TechnologySpecificHook[LrdimmSendAllMRCmds] (TechPtr, &ChipSel))) { |
| TechPtr->SendAllMRCmds (TechPtr, ChipSel); |
| } |
| } |
| } |
| // Wait 512 clocks for DLL-relock |
| MemNWaitXMemClksNb (NBPtr, 512); |
| } |
| } |
| } |
| |
| // Restore MMIO setting |
| LibAmdMsrWrite (MSR_MMIO_Cfg_Base, &OrgMMIOCfgBase, &(NBPtr->MemPtr->StdHeader)); |
| |
| MemFInitTableDrive (NBPtr, MTAfterFreqChg); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function change MemClk frequency to the value that is specified by DCTPtr->Timings.Speed |
| * for UNB. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNChangeFrequencyUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| MEM_TECH_BLOCK *TechPtr; |
| UINT8 Dct; |
| UINT8 ChipSel; |
| UINT16 FinalPllLockTime; |
| BOOLEAN FrequencyChangeSuccess; |
| UINT64 OrgMMIOCfgBase; |
| UINT64 NewMMIOCfgBase; |
| |
| TechPtr = NBPtr->TechPtr; |
| |
| // Disable MMIO to prevent speculative DRAM reads during self refresh |
| LibAmdMsrRead (MSR_MMIO_Cfg_Base, &OrgMMIOCfgBase, &(NBPtr->MemPtr->StdHeader)); |
| NewMMIOCfgBase = OrgMMIOCfgBase & (~(BIT0)); |
| LibAmdMsrWrite (MSR_MMIO_Cfg_Base, &NewMMIOCfgBase, &(NBPtr->MemPtr->StdHeader)); |
| |
| MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 1); |
| |
| //Program F2x[1,0]90[EnterSelfRefresh]=1. |
| //Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0. |
| MemNBrdcstSetNb (NBPtr, BFEnterSelfRef, 1); |
| MemNPollBitFieldNb (NBPtr, BFEnterSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE); |
| |
| if (NBPtr->ChangeNbFrequency (NBPtr)) { |
| // Reprogram Twr, Tcwl, and Tcl based on the new MEMCLK frequency. |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| TechPtr->AutoCycTiming (TechPtr); |
| if (!MemNPlatformSpecUnb (NBPtr)) { |
| IDS_ERROR_TRAP; |
| } |
| } |
| } |
| |
| // 1. Program PllLockTime to Family-specific value |
| MemNBrdcstSetNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault); |
| |
| // 2. Program D18F2x[1,0]94[MemClkFreqVal] = 0. |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 0); |
| |
| // 3. Program D18F2x[1,0]94[MemClkFreq] to the desired DRAM frequency. |
| MemNBrdcstSetNb (NBPtr, BFMemClkFreq, NBPtr->GetMemClkFreqId (NBPtr, NBPtr->DCTPtr->Timings.Speed)); |
| |
| // 4. Program D18F2x[1,0]F4_x30[DbeGskFifoNumerator] and D18F2x[1,0]F4_x31[DbeGskFifoDenominator]. |
| // 5. Program D18F2x[1,0]F4_x32[DataTxFifoSchedDlyNegSlot1, DataTxFifoSchedDlySlot1, |
| // DataTxFifoSchedDlyNegSlot0, DataTxFifoSchedDlySlot0]. See 2.10.3.2.2.1 [DCT Transmit Fifo Schedule |
| // Delay Programming]. |
| // 6. D18F2x[1,0]78[RdPtrInit] = IF (D18F2x[1,0]94[MemClkFreq] >= 667 MHz) THEN 7 ELSE 8 ENDIF (Llano) |
| // THEN 2 ELSE 3 ENDIF (Ontario) |
| NBPtr->ProgramNbPsDependentRegs (NBPtr); |
| |
| IDS_OPTION_HOOK (IDS_BEFORE_MEM_FREQ_CHG, NBPtr, &(NBPtr->MemPtr->StdHeader)); |
| // 7. Program D18F2x[1,0]94[MemClkFreqVal] = 1. |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if ((NBPtr->DCTPtr->Timings.DctMemSize != 0)) { |
| MemNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 1); |
| MemNPollBitFieldNb (NBPtr, BFFreqChgInProg, 0, PCI_ACCESS_TIMEOUT, FALSE); |
| } |
| } |
| FinalPllLockTime = 0xF; |
| NBPtr->FamilySpecificHook[AfterMemClkFreqVal] (NBPtr, &FinalPllLockTime); |
| |
| // 8. IF (D18F2x[1,0]9C_x0D0F_E00A[CsrPhySrPllPdMode]==0) THEN program |
| // D18F2x[1,0]9C_x0D0F_E006[PllLockTime] = 0Fh. |
| if (!NBPtr->IsSupported[CsrPhyPllPdEn]) { |
| MemNBrdcstSetNb (NBPtr, BFPllLockTime, FinalPllLockTime); |
| } |
| |
| FrequencyChangeSuccess = TRUE; |
| } else { |
| // If NB frequency cannot be updated, use the current speed as the target speed |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| NBPtr->DCTPtr->Timings.Speed = NBPtr->TechPtr->PrevSpeed; |
| NBPtr->DCTPtr->Timings.TargetSpeed = NBPtr->TechPtr->PrevSpeed; |
| } |
| FrequencyChangeSuccess = FALSE; |
| } |
| |
| if (FrequencyChangeSuccess) { |
| // Perform Phy Fence training and Phy comp init after frequency change |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); |
| |
| // Phy fence programming |
| AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->PhyFenceTraining (NBPtr); |
| |
| // Phy compensation initialization |
| AGESA_TESTPOINT (TPProcMemPhyCompensation, &(NBPtr->MemPtr->StdHeader)); |
| NBPtr->MemNInitPhyComp (NBPtr); |
| MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_SLEWRATE, ALL_DIMMS); |
| } |
| } |
| } |
| |
| //Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs. |
| //Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. |
| MemNBrdcstSetNb (NBPtr, BFExitSelfRef, 1); |
| MemNPollBitFieldNb (NBPtr, BFExitSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE); |
| if (NBPtr->IsSupported[SetDllShutDown]) { |
| MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 0); |
| } |
| |
| if (FrequencyChangeSuccess) { |
| NBPtr->FamilySpecificHook[AfterMemClkFreqChg] (NBPtr, NULL); |
| |
| //====================================================================== |
| // Calculate and program DRAM Timings at new frequency |
| //====================================================================== |
| // |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) { |
| if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) { |
| // if chip select present |
| if (!(TechPtr->TechnologySpecificHook[LrdimmSendAllMRCmds] (TechPtr, &ChipSel))) { |
| TechPtr->SendAllMRCmds (TechPtr, ChipSel); |
| } |
| } |
| } |
| // Wait 512 clocks for DLL-relock |
| MemNWaitXMemClksNb (NBPtr, 512); |
| } |
| } |
| } |
| |
| // Restore MMIO setting |
| LibAmdMsrWrite (MSR_MMIO_Cfg_Base, &OrgMMIOCfgBase, &(NBPtr->MemPtr->StdHeader)); |
| |
| MemFInitTableDrive (NBPtr, MTAfterFreqChg); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| CONST UINT8 PllDivTab[] = {0, 0, 0, 2, 3, 3, 2, 3}; |
| CONST UINT8 PllMultTab[] = {0, 0, 0, 16, 32, 40, 32, 56}; |
| |
| /** |
| * |
| * This function calculates and programs NB P-state dependent registers |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNProgramNbPstateDependentRegistersClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 i; |
| UINT8 Dct; |
| UINT8 NclkFid; |
| UINT16 MemClkDid; |
| UINT8 PllMult; |
| UINT8 NclkDiv; |
| UINT8 RdPtrInitMin; |
| UINT8 RdPtrInit; |
| UINT32 NclkPeriod; |
| UINT32 MemClkPeriod; |
| INT32 PartialSum2x; |
| INT32 PartialSumSlotI2x; |
| INT32 RdPtrInitRmdr2x; |
| INT32 TDataProp; |
| UINT8 NbPstate; |
| UINT8 SlowMode; |
| UINT32 CalcNclkDiv; |
| UINT32 DbeGskFifoNumeratorVal; |
| UINT32 DbeGskFifoDenominatorVal; |
| |
| CalcNclkDiv = 0; |
| NclkFid = (UINT8) (MemNGetBitFieldNb (NBPtr, BFMainPllOpFreqId) + 0x10); // NclkFid is in 100MHz |
| |
| MemClkDid = PllDivTab[NBPtr->DCTPtr->Timings.Speed / 133]; |
| NBPtr->FamilySpecificHook[OverridePllDiv] (NBPtr, &MemClkDid); |
| PllMult = PllMultTab[NBPtr->DCTPtr->Timings.Speed / 133]; |
| NBPtr->FamilySpecificHook[OverridePllMult] (NBPtr, &PllMult); |
| |
| if (NBPtr->NbFreqChgState == 2) { |
| MemNSetBitFieldNb (NBPtr, BFNbPsCsrAccSel, 1); |
| MemNSetBitFieldNb (NBPtr, BFNbPsDbgEn, 1); |
| NclkDiv = (UINT8) MemNGetBitFieldNb (NBPtr, BFNbPs1NclkDiv); |
| // Divisors less than 8 are undefined. Maybe the CPU does not support NB P-states. |
| if (NclkDiv < 8) { |
| // Set a dummy divisor to prevent divide by zero exception below. |
| NclkDiv = 8; |
| } |
| NbPstate = 1; |
| } else { |
| NclkDiv = (UINT8) MemNGetBitFieldNb (NBPtr, BFNbPs0NclkDiv); |
| NbPstate = 0; |
| } |
| |
| if (NclkDiv >= 0x60) { |
| CalcNclkDiv = (NclkDiv - 0x40) * 10000; |
| } else if (NclkDiv >= 0x40) { |
| CalcNclkDiv = ((NclkDiv - 0x40) * 5000) + 160000; |
| } else { |
| CalcNclkDiv = 2500 * NclkDiv; |
| } |
| |
| NclkPeriod = CalcNclkDiv / NclkFid; // (1,000,000 * 0.25 * NclkDiv) / (NclkFid * 100MHz) = ps |
| MemClkPeriod = 1000000 / NBPtr->DCTPtr->Timings.Speed; |
| NBPtr->NBClkFreq = ((UINT32) NclkFid * 1000000) / CalcNclkDiv; |
| |
| IDS_HDT_CONSOLE (MEM_FLOW, "\n\tNB P%d Freq: %dMHz\n", NbPstate, NBPtr->NBClkFreq); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClk Freq: %dMHz\n", NBPtr->DCTPtr->Timings.Speed); |
| // D18F2x[1,0]78[RdPtrInit] = IF (D18F2x[1,0]94[MemClkFreq] >= 667 MHz) THEN 7 ELSE 8 ENDIF (Llano) |
| // THEN 2 ELSE 3 ENDIF (Ontario) |
| RdPtrInit = RdPtrInitMin = (NBPtr->DCTPtr->Timings.Speed >= DDR1333_FREQUENCY) ? NBPtr->FreqChangeParam->RdPtrInit667orHigher : NBPtr->FreqChangeParam->RdPtrInitLower667; |
| NBPtr->FamilySpecificHook[AdjustRdPtrInit] (NBPtr, &RdPtrInit); |
| MemNBrdcstSetNb (NBPtr, BFRdPtrInit, RdPtrInit); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\tRdPtr: %d\n", RdPtrInit); |
| |
| // Program D18F2x[1,0]F4_x30[DbeGskFifoNumerator] and D18F2x[1,0]F4_x31[DbeGskFifoDenominator]. |
| DbeGskFifoNumeratorVal = NclkFid * MemClkDid * 16; |
| DbeGskFifoDenominatorVal = NclkDiv * PllMult; |
| if (NclkDiv >= 0x40) { |
| DbeGskFifoNumeratorVal = NclkFid * MemClkDid * 4; |
| DbeGskFifoDenominatorVal = CalcNclkDiv * PllMult / 10000; |
| } |
| MemNBrdcstSetNb (NBPtr, BFDbeGskFifoNumerator, DbeGskFifoNumeratorVal); |
| MemNBrdcstSetNb (NBPtr, BFDbeGskFifoDenominator, DbeGskFifoDenominatorVal); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoNumerator: %d\n", DbeGskFifoNumeratorVal); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoDenominator: %d\n", DbeGskFifoDenominatorVal); |
| |
| // Program D18F2x[1,0]F4_x32[DataTxFifoSchedDlyNegSlot1, DataTxFifoSchedDlySlot1, |
| // DataTxFifoSchedDlyNegSlot0, DataTxFifoSchedDlySlot0]. |
| // PartialSum = ((7 * NclkPeriod) + (1.5 * MemClkPeriod) + 520ps)*MemClkFrequency - tCWL - |
| // CmdSetup - PtrSeparation - 1. (Llano) |
| // PartialSum = ((5 * NclkPeriod) + MemClkPeriod) + 520ps)*MemClkFrequency - tCWL - |
| // CmdSetup - PtrSeparation - 1. (Ontario) |
| for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| PartialSum2x = NBPtr->FreqChangeParam->NclkPeriodMul2x * NclkPeriod; |
| PartialSum2x += NBPtr->FreqChangeParam->MemClkPeriodMul2x * MemClkPeriod; |
| PartialSum2x += 520 * 2; |
| |
| // PtrSeparation = ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16)/2 + RdPtrInitRmdr |
| // If (D18F2x[1,0]94[MemClkFreq] >= 800 MHz) |
| // then RdPtrInitRmdr = (((4.5 * MemClkPeriod) - 990ps) MOD MemClkPeriod)/MemClkPeriod |
| // else RdPtrInitRmdr = (((4.5 * MemClkPeriod) - 1466ps) MOD MemClkPeriod)/MemClkPeriod |
| TDataProp = (NBPtr->DCTPtr->Timings.Speed >= DDR1600_FREQUENCY) ? |
| NBPtr->FreqChangeParam->TDataProp800orHigher : NBPtr->FreqChangeParam->TDataPropLower800; |
| RdPtrInitRmdr2x = ((NBPtr->FreqChangeParam->SyncTimeMul4x * MemClkPeriod) / 2) - 2 * (TDataProp + 520); |
| RdPtrInitRmdr2x %= MemClkPeriod; |
| PartialSum2x -= ((16 + RdPtrInitMin - RdPtrInit) % 16) * MemClkPeriod + RdPtrInitRmdr2x; |
| |
| // Convert PartialSum2x to PCLK |
| PartialSum2x = (PartialSum2x + MemClkPeriod - 1) / MemClkPeriod; // round-up here |
| PartialSum2x -= 2 * (MemNGetBitFieldNb (NBPtr, BFTcwl) + 5); |
| if ((MemNGetBitFieldNb (NBPtr, BFAddrTmgControl) & 0x0202020) == 0) { |
| PartialSum2x -= 1; |
| } else { |
| PartialSum2x -= 2; |
| } |
| PartialSum2x -= 2; |
| |
| // If PartialSumSlotN is positive: |
| // DataTxFifoSchedDlySlotN=CEIL(PartialSumSlotN). |
| // DataTxFifoSchedDlyNegSlotN=0. |
| // Else if PartialSumSlotN is negative: |
| // DataTxFifoSchedDlySlotN=ABS(CEIL(PartialSumSlotN*MemClkPeriod/NclkPeriod)). |
| // DataTxFifoSchedDlyNegSlotN=1. |
| for (i = 0; i < 2; i++) { |
| PartialSumSlotI2x = PartialSum2x; |
| SlowMode = (UINT8) MemNGetBitFieldNb (NBPtr, BFSlowAccessMode); |
| if ((i == 0) && (SlowMode == 0)) { |
| PartialSumSlotI2x += 2; |
| } |
| if (NBPtr->IsSupported[SchedDlySlot1Extra] && (i == 1) && (SlowMode != 0)) { |
| PartialSumSlotI2x -= 2; |
| } |
| if (PartialSumSlotI2x > 0) { |
| MemNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 0); |
| MemNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, (PartialSumSlotI2x + 1) / 2); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: %d\n", i, (PartialSumSlotI2x + 1) / 2); |
| } else { |
| MemNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 1); |
| PartialSumSlotI2x = ((-PartialSumSlotI2x) * MemClkPeriod) / (2 * NclkPeriod); |
| MemNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, PartialSumSlotI2x); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: -%d\n", i, PartialSumSlotI2x); |
| } |
| } |
| |
| // Set ProcOdtAdv |
| if ((NBPtr->DCTPtr->Timings.Speed <= DDR1333_FREQUENCY) && |
| ((!(NBPtr->IsSupported[EnProcOdtAdvForUDIMM])) || (NBPtr->ChannelPtr->SODimmPresent != 0))) { |
| MemNSetBitFieldNb (NBPtr, BFProcOdtAdv, 0); |
| } else { |
| MemNSetBitFieldNb (NBPtr, BFProcOdtAdv, 0x4000); |
| } |
| } |
| } |
| |
| MemFInitTableDrive (NBPtr, MTAfterNbPstateChange); |
| if (NBPtr->NbFreqChgState == 2) { |
| MemNSetBitFieldNb (NBPtr, BFNbPsDbgEn, 0); |
| MemNSetBitFieldNb (NBPtr, BFNbPsCsrAccSel, 0); |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets the total of sync components for Max Read Latency calculation |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return Total in ps |
| */ |
| |
| UINT32 |
| MemNTotalSyncComponentsClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT32 P; |
| UINT32 T; |
| UINT8 RdPtrInitMin; |
| UINT8 RdPtrInit; |
| UINT32 AddrTmgCtl; |
| UINT8 DbeGskMemClkAlignMode; |
| UINT32 MemClkPeriod; |
| |
| // P = P + ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16) |
| RdPtrInitMin = (NBPtr->DCTPtr->Timings.Speed >= DDR1333_FREQUENCY) ? NBPtr->FreqChangeParam->RdPtrInit667orHigher : NBPtr->FreqChangeParam->RdPtrInitLower667; |
| RdPtrInit = (UINT8) MemNGetBitFieldNb (NBPtr, BFRdPtrInit); |
| P = (16 + RdPtrInitMin - RdPtrInit) % 16; |
| |
| // IF (AddrCmdSetup != CkeSetup) THEN P = P + 1 |
| AddrTmgCtl = MemNGetBitFieldNb (NBPtr, BFAddrTmgControl); |
| if (((AddrTmgCtl >> 16) & 0x20) != (AddrTmgCtl & 0x20)) { |
| P += 1; |
| } |
| |
| // IF (DbeGskMemClkAlignMode==01b || (DbeGskMemClkAlignMode==00b && !(AddrCmdSetup==CsOdtSetup==CkeSetup))) |
| // THEN P = P + 1 |
| DbeGskMemClkAlignMode = (UINT8) MemNGetBitFieldNb (NBPtr, BFDbeGskMemClkAlignMode); |
| if ((DbeGskMemClkAlignMode == 1) || ((DbeGskMemClkAlignMode == 0) && |
| !((((AddrTmgCtl >> 16) & 0x20) == (AddrTmgCtl & 0x20)) && (((AddrTmgCtl >> 8) & 0x20) == (AddrTmgCtl & 0x20))))) { |
| P += 1; |
| } |
| |
| // IF (SlowAccessMode==1) THEN P = P + 2 |
| if (MemNGetBitFieldNb (NBPtr, BFSlowAccessMode) == 1) { |
| P += 2; |
| } |
| |
| // P = P + 2 |
| P += 2; |
| T = 0; |
| |
| // If (AddrCmdSetup==0 && CsOdtSetup==0 && CkeSetup==0) |
| // then P = P + 1 |
| // else P = P + 2 |
| if ((AddrTmgCtl & 0x0202020) == 0) { |
| P += 1; |
| } else { |
| P += 2; |
| } |
| |
| // P = P + (2 * (D18F2x[1,0]88[Tcl] clocks - 1)) |
| P += 2 * (NBPtr->DCTPtr->Timings.CasL - 1); |
| |
| // If (DisCutThroughMode==0) |
| // then P = P + 3 |
| // else P = P + 7 |
| if (MemNGetBitFieldNb (NBPtr, BFDisCutThroughMode) == 0) { |
| P += 3; |
| } else { |
| P += 7; |
| } |
| |
| MemClkPeriod = 1000000 / NBPtr->DCTPtr->Timings.Speed; |
| return (((P * MemClkPeriod + 1) / 2) + T); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function sets up phy power saving for client NB |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| VOID |
| MemNPhyPowerSavingClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| // 4. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]13[DllDisEarlyU] = 1b. |
| // 5. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]13[DllDisEarlyL] = 1b. |
| // 6. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]13[7:4] = 1010b. |
| MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13Bit0to7, 0xA3); |
| // 7. Program D18F2x[1,0]9C_x0D0F_812F[7, 5, 0] = {1b, 1b, 1b} to disable unused PAR and A[17:16] pins. |
| MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 0xA1); |
| // 8. Program D18F2x[1,0]9C_x0D0F_C000[LowPowerDrvStrengthEn] = 1. |
| if (!NBPtr->FamilySpecificHook[DisLowPwrDrvStr] (NBPtr, NULL)) { |
| MemNSetBitFieldNb (NBPtr, BFLowPowerDrvStrengthEn, 0x100); |
| } |
| // 9. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]10[EnRxPadStandby]= IF (D18F2x[1,0]94[MemClkFreq] <= |
| // 800 MHz) THEN 1 ELSE 0 ENDIF. |
| MemNSetBitFieldNb (NBPtr, BFEnRxPadStandby, (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) ? 0x1000 : 0); |
| // 10. Program D18F2x[1,0]9C_x0000_000D as follows: |
| // TxMaxDurDllNoLock/RxMaxDurDllNoLock = 7h. |
| MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 7); |
| MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 7); |
| // TxCPUpdPeriod/RxCPUpdPeriod = 011b. |
| MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 3); |
| MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 3); |
| // TxDLLWakeupTime/RxDLLWakeupTime = 11b. |
| MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 3); |
| MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 3); |
| |
| IDS_OPTION_HOOK (IDS_PHY_DLL_STANDBY_CTRL, NBPtr, &NBPtr->MemPtr->StdHeader); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function sets up phy power saving for UNB |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| VOID |
| MemNPhyPowerSavingUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT16 MixedX4AndX8Dimms; |
| |
| // 4. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyU] = 1b. |
| // 5. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyL] = 1b. |
| MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13, MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) | 3); |
| // 6. D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][RxDqsUDllPowerDown] = (D18F2x90_dct[1:0][X4Dimm]!=0). |
| MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13, MemNGetBitFieldNb (NBPtr, BFX4Dimm) == 0 ? (MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) | 0x80) : (MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) & 0xFF7F)); |
| // 7. D18F2x9C_x0D0F_812F_dct[1:0][PARTri] = ~D18F2x90_dct[1:0][ParEn]. |
| MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFParEn) == 0 ? (MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 1) : (MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) & 0xFFFE)); |
| // 8. D18F2x9C_x0D0F_812F_dct[1:0][Add17Tri, Add16Tri] = {1b, 1b} |
| MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 0xA0); |
| // 9. IF (D18F2x94_dct[1:0][MemClkFreq] <= 800 MHz && ~(mixed channel of x4 and x8 DIMMs)) THEN |
| // Program D18F2x9C_x0D0F_0[F,8:0]10_dct[1:0][EnRxPadStandby] = 1. |
| // ELSE |
| // Program D18F2x9C_x0D0F_0[F,8:0]10_dct[1:0][EnRxPadStandby] = 0. |
| // ENDIF. |
| MixedX4AndX8Dimms = NBPtr->DCTPtr->Timings.Dimmx4Present != 0 && NBPtr->DCTPtr->Timings.Dimmx8Present != 0; |
| MemNSetBitFieldNb (NBPtr, BFEnRxPadStandby, (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) && !MixedX4AndX8Dimms ? 0x1000 : 0); |
| // 10. IF (~(mixed channel of x4 and x8 DIMMs)) THEN |
| if (MixedX4AndX8Dimms == FALSE) { |
| // Program D18F2x9C_x0000_000D_dct[1:0] as follows: |
| // TxMaxDurDllNoLock = RxMaxDurDllNoLock = 7h. |
| MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 7); |
| MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 7); |
| // TxCPUpdPeriod = RxCPUpdPeriod = 011b. |
| MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 3); |
| MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 3); |
| // TxDLLWakeupTime = RxDLLWakeupTime = 11b. |
| MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 3); |
| MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 3); |
| } else { |
| // ELSE |
| // Program D18F2x9C_x0000_000D_dct[1:0][TxMaxDurDllNoLock, RxMaxDurDllNoLock, TxCPUpdPeriod, |
| // RxCPUpdPeriod, TxDLLWakeupTime, RxDLLWakeupTime] = {0, 0, 0, 0, 0, 0}. |
| MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 0); |
| MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 0); |
| MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 0); |
| MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 0); |
| MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 0); |
| MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 0); |
| } |
| // 11. Program D18F2x9C_x0D0F_0[F,8:0]30_dct[1:0][PwrDn] to disable unused ECC byte lane. |
| if (NBPtr->IsSupported[CheckEccDLLPwrDnConfig]) { |
| if (!NBPtr->MCTPtr->Status[SbEccDimms]) { |
| MemNSetBitFieldNb (NBPtr, BFEccDLLPwrDnConf, 0x0010); |
| } |
| } |
| |
| // 12. Program D18F2x9C_x0D0F_0[F,8:0]04_dct[1:0][TriDM] = IF (LRDIMM & (D18F2x90_dct[1:0][X4Dimm] == 0)) THEN 1 ELSE 0. |
| if (NBPtr->MCTPtr->Status[SbLrdimms]) { |
| MemNSetBitFieldNb (NBPtr, BFDataByteDMConf, (MemNGetBitFieldNb (NBPtr, BFX4Dimm) == 0) ? 0x2000 : 0); |
| } |
| |
| IDS_OPTION_HOOK (IDS_PHY_DLL_STANDBY_CTRL, NBPtr, &NBPtr->MemPtr->StdHeader); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function overrides the ASR and SRT value in MRS command |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| VOID |
| MemNSetASRSRTNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT32 MrsAddress; |
| UINT8 Dimm; |
| UINT8 *SpdBufferPtr; |
| BOOLEAN ASREn; |
| BOOLEAN SRTEn; |
| |
| // Look for MR2 |
| if (NBPtr->GetBitField (NBPtr, BFMrsBank) == 2) { |
| MrsAddress = NBPtr->GetBitField (NBPtr, BFMrsAddress); |
| // Clear A6(ASR) and A7(SRT) |
| MrsAddress &= (UINT32) ~0xC0; |
| if ((NBPtr->ChannelPtr->RegDimmPresent) || (NBPtr->ChannelPtr->LrDimmPresent)) { |
| // For registered dimm and LR dimm, MRS command is sent to all chipselects. |
| // So different ASR/SRT setting can be sent to each chip select. |
| Dimm = (UINT8) (NBPtr->GetBitField (NBPtr, BFMrsChipSel) >> 1); |
| // Make sure we access SPD of the second logical dimm of QR dimm correctly |
| if ((Dimm >= 2) && ((NBPtr->ChannelPtr->DimmQrPresent & (UINT8) (1 << Dimm)) != 0)) { |
| Dimm -= 2; |
| } |
| if (NBPtr->TechPtr->GetDimmSpdBuffer (NBPtr->TechPtr, &SpdBufferPtr, Dimm)) { |
| // Bit 2 is ASR |
| if (SpdBufferPtr[THERMAL_OPT] & 0x4) { |
| // when ASR is 1, set SRT to 0 |
| MrsAddress |= 0x40; |
| } else { |
| // Set SRT based on bit on of thermal byte |
| MrsAddress |= ((SpdBufferPtr[THERMAL_OPT] & 1) << 7); |
| } |
| } |
| } else { |
| // Udimm and unbuffered dimm, MSR command will be broadcasted during Dram Init. |
| // ASR/SRT value needs to be leveled across the DCT. Only if all dimms on the DCT |
| // support ASR or SRT can ASR or SRT be enabled. |
| ASREn = TRUE; |
| SRTEn = TRUE; |
| for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm ++) { |
| if (NBPtr->TechPtr->GetDimmSpdBuffer (NBPtr->TechPtr, &SpdBufferPtr, Dimm)) { |
| // Bit 2 is ASR |
| if ((SpdBufferPtr[THERMAL_OPT] & 0x4) == 0) { |
| // when any dimm in the DCT does not support ASR, disable ASR for the DCT |
| ASREn = FALSE; |
| // When any dimm does not have SRT with a value of 1, set SRT to 0 for the DCT |
| if ((SpdBufferPtr[THERMAL_OPT] & 1) == 0) { |
| SRTEn = FALSE; |
| } |
| } |
| } |
| } |
| if (ASREn) { |
| MrsAddress |= 0x40; |
| } else { |
| MrsAddress |= (UINT8) SRTEn << 7; |
| } |
| } |
| |
| NBPtr->SetBitField (NBPtr, BFMrsAddress, MrsAddress); |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function changes NB frequency as below: |
| * NBP0-DDR800 -> NBP0-DDR1066 -> ... -> NBP0-DDRTarget -> NBP1-DDRTarget -> NBP0-DDRTarget |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| BOOLEAN |
| MemNChangeNbFrequencyNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| BOOLEAN Status; |
| |
| Status = FALSE; |
| |
| // State machine to change NB frequency and NB Pstate |
| switch (NBPtr->NbFreqChgState) { |
| case 0: |
| // Starting up by not changing NB P state, but only updating NB frequency based on current MemClk frequency |
| Status = NBPtr->ChangeNbFrequencyWrap (NBPtr, 0); |
| ASSERT (Status); |
| |
| if (NBPtr->DCTPtr->Timings.Speed == NBPtr->DCTPtr->Timings.TargetSpeed) { |
| // When MemClk has been ramped up to its max, transition to next state, which changes NBPstate to P1 |
| NBPtr->NbFreqChgState = 1; |
| IDS_OPTION_HOOK (IDS_NB_PSTATE_DIDVID, NBPtr, &(NBPtr->MemPtr->StdHeader)); |
| } |
| break; |
| |
| case 1: |
| // Clear ForceCasToSlot0 after MaxRdLatency training is completed for NB-P0 |
| MemNBrdcstSetNb (NBPtr, BFForceCasToSlot0, 0); |
| |
| // Next state would be to change NBPstate back to P0 |
| NBPtr->NbFreqChgState = 2; |
| |
| // Update NB freq dependent registers |
| NBPtr->ProgramNbPsDependentRegs (NBPtr); |
| |
| // Change NB P-State to NBP1 for MaxRdLat training |
| if (NBPtr->ChangeNbFrequencyWrap (NBPtr, 1)) { |
| // Enable cut through mode for NB P1 |
| MemNBrdcstSetNb (NBPtr, BFDisCutThroughMode, 0); |
| |
| // Return TRUE to repeat MaxRdLat training |
| Status = TRUE; |
| |
| } else { |
| // If transition to NB-P1 fails, transition to exit state machine |
| NBPtr->NbFreqChgState = 3; |
| } |
| break; |
| |
| case 2: |
| // Clear ForceCasToSlot0 after MaxRdLatency training is completed for NB-P1 |
| MemNBrdcstSetNb (NBPtr, BFForceCasToSlot0, 0); |
| |
| // Change NB P-State back to NBP0 |
| Status = NBPtr->ChangeNbFrequencyWrap (NBPtr, 0); |
| ASSERT (Status); |
| |
| // Return FALSE to get out of MaxRdLat training loop |
| Status = FALSE; |
| |
| // Exit state machine |
| NBPtr->NbFreqChgState = 3; |
| break; |
| |
| default: |
| break; |
| } |
| |
| return Status; |
| } |
| |
| /*----------------------------------------------------------------------------- |
| * |
| * |
| * This function programs registers before phy fence training for CNB |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] OptParam - Optional parameter |
| * |
| * @return TRUE |
| * ---------------------------------------------------------------------------- |
| */ |
| BOOLEAN |
| MemNBeforePhyFenceTrainingClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *OptParam |
| ) |
| { |
| IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClkAlign=0\n"); |
| MemNBrdcstSetNb (NBPtr, BFDbeGskMemClkAlignMode, 0); |
| |
| IDS_HDT_CONSOLE (MEM_FLOW, "\tEnDramInit = 1 for both DCTs\n"); |
| MemNBrdcstSetNb (NBPtr, BFEnDramInit, 1); |
| |
| return TRUE; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function changes NB frequency foras below: |
| * NBP0-DDR800 -> NBP0-DDR1066 -> ... -> NBP0-DDRTarget -> NBP1-DDRTarget -> NBP2-DDRTarget -> NBP3-DDRTarget -> NBP0-DDRTarget |
| * |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| BOOLEAN |
| MemNChangeNbFrequencyUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| BOOLEAN Status; |
| |
| Status = FALSE; |
| |
| // State machine to change NB frequency and NB Pstate |
| switch (NBPtr->NbFreqChgState) { |
| case 0: |
| // Do not change NB Pstate, just to save initial NB Pstate value |
| Status = NBPtr->ChangeNbFrequencyWrap (NBPtr, 0); |
| if (NBPtr->DCTPtr->Timings.Speed == NBPtr->DCTPtr->Timings.TargetSpeed) { |
| // When MemClk has been ramped up to its max, transition to next state, which changes NBPstate to P1 |
| NBPtr->NbFreqChgState = 1; |
| IDS_OPTION_HOOK (IDS_NB_PSTATE_DIDVID, NBPtr, &(NBPtr->MemPtr->StdHeader)); |
| } |
| break; |
| |
| case 1: |
| case 2: |
| case 3: |
| // Change NB P-State to NBP1 for MaxRdLat training |
| if (NBPtr->ChangeNbFrequencyWrap (NBPtr, NBPtr->NbFreqChgState)) { |
| // Next state is to try all NBPstates |
| NBPtr->NbFreqChgState++; |
| |
| // Return TRUE to repeat MaxRdLat training |
| Status = TRUE; |
| } else { |
| // If transition to any NBPs fails, transition to exit state machine |
| NBPtr->NbFreqChgState = 4; |
| } |
| break; |
| |
| case 4: |
| // Change NB P-State back to NBP0 |
| Status = NBPtr->ChangeNbFrequencyWrap (NBPtr, 0); |
| ASSERT (Status); |
| |
| // Return FALSE to get out of MaxRdLat training loop |
| Status = FALSE; |
| |
| // Exit state machine |
| NBPtr->NbFreqChgState = 5; |
| break; |
| |
| default: |
| break; |
| } |
| |
| return Status; |
| } |
| |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets "Dram Term" value from data structure |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] ChipSel - Targeted chipsel |
| * |
| * @return Dram Term value |
| */ |
| UINT8 |
| MemNGetDramTermNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT8 ChipSel |
| ) |
| { |
| UINT8 DramTerm; |
| |
| if ((NBPtr->ChannelPtr->DimmQrPresent & ((UINT16) (1 << (ChipSel >> 1)))) != 0) { |
| DramTerm = NBPtr->PsPtr->QR_DramTerm; |
| } else { |
| DramTerm = NBPtr->PsPtr->DramTerm; |
| } |
| |
| return DramTerm; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets "Dram Term" value from data structure for Unb |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] ChipSel - Targeted chipsel |
| * |
| * @return Dram Term value |
| */ |
| UINT8 |
| MemNGetDramTermTblDrvNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT8 ChipSel |
| ) |
| { |
| UINT8 RttNom; |
| RttNom = NBPtr->PsPtr->RttNom[ChipSel]; |
| IDS_OPTION_HOOK (IDS_MEM_DRAM_TERM, &RttNom, &NBPtr->MemPtr->StdHeader); |
| return RttNom; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets "Dynamic Dram Term" value from data structure |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] ChipSel - Targeted chipsel |
| * |
| * @return Dynamic Dram Term value |
| */ |
| UINT8 |
| MemNGetDynDramTermNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT8 ChipSel |
| ) |
| { |
| return (NBPtr->PsPtr->DynamicDramTerm); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets "Dynamic Dram Term" value from data structure |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] ChipSel - Targeted chipsel |
| * |
| * @return Dynamic Dram Term value |
| */ |
| UINT8 |
| MemNGetDynDramTermTblDrvNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT8 ChipSel |
| ) |
| { |
| UINT8 RttWr; |
| RttWr = NBPtr->PsPtr->RttWr[ChipSel]; |
| IDS_OPTION_HOOK (IDS_MEM_DYN_DRAM_TERM, &RttWr, &NBPtr->MemPtr->StdHeader); |
| return RttWr; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function returns MR0[CL] value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return MR0[CL] value |
| */ |
| UINT32 |
| MemNGetMR0CLNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 Tcl; |
| UINT32 Value32; |
| |
| Tcl = (UINT8) MemNGetBitFieldNb (NBPtr, BFTcl); |
| Value32 = (UINT32) ((Tcl < 8) ? (Tcl << 4) : (((Tcl - 8) << 4) | 4)); |
| |
| return Value32; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function returns MR0[WR] value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return MR0[WR] value |
| */ |
| UINT32 |
| MemNGetMR0WRNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT32 Value32; |
| |
| Value32 = MemNGetBitFieldNb (NBPtr, BFTwrDDR3) << 9; |
| |
| return Value32; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function returns MR0[WR] value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return MR0[WR] value |
| */ |
| UINT32 |
| MemNGetMR0WRTblDrvNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| return (UINT32) (NBPtr->PsPtr->MR0WR << 9); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function returns MR2[CWL] value |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return MR0[CWL] value |
| */ |
| UINT32 |
| MemNGetMR2CWLNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT32 Value32; |
| |
| Value32 = MemNGetBitFieldNb (NBPtr, BFTcwl) << 3; |
| |
| return Value32; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function returns MR2[CWL] value for UNB |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return MR0[CWL] value |
| */ |
| UINT32 |
| MemNGetMR2CWLUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT32 Value32; |
| |
| Value32 = (MemNGetBitFieldNb (NBPtr, BFTcwl) - 5) << 3; |
| |
| return Value32; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function sets Txp and Txpdll |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return none |
| */ |
| VOID |
| MemNSetTxpNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| CONST UINT8 Txp[] = {0xFF, 0xFF, 3, 3, 4, 4, 5, 6, 7}; |
| CONST UINT8 Txpdll[] = {0xFF, 0xFF, 0xA, 0xA, 0xD, 0x10, 0x14, 0x17, 0x1A}; |
| UINT8 i; |
| UINT8 TxpVal; |
| UINT8 TxpdllVal; |
| UINT16 Speed; |
| |
| Speed = NBPtr->DCTPtr->Timings.Speed; |
| i = (UINT8) ((Speed < DDR800_FREQUENCY) ? ((Speed / 66) - 3) : (Speed / 133)); |
| ASSERT (i < sizeof (Txp)); |
| ASSERT (i < sizeof (Txpdll)); |
| |
| TxpdllVal = Txpdll[i]; |
| |
| if ((NBPtr->MCTPtr->Status[SbLrdimms] || NBPtr->MCTPtr->Status[SbRegistered]) && |
| ((NBPtr->DCTPtr->Timings.Speed == DDR667_FREQUENCY) || (NBPtr->DCTPtr->Timings.Speed == DDR800_FREQUENCY)) && |
| (NBPtr->RefPtr->DDR3Voltage == VOLT1_25)) { |
| TxpVal = 4; |
| } else { |
| TxpVal = Txp[i]; |
| } |
| |
| if (TxpVal != 0xFF) { |
| MemNSetBitFieldNb (NBPtr, BFTxp, TxpVal); |
| } |
| if (TxpdllVal != 0xFF) { |
| NBPtr->FamilySpecificHook[AdjustTxpdll] (NBPtr, &TxpdllVal); |
| MemNSetBitFieldNb (NBPtr, BFTxpdll, TxpdllVal); |
| } |
| } |
| |
| /*----------------------------------------------------------------------------- |
| * |
| * |
| * This function adjust value of Txpdll to encoded value. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] OptParam - Optional parameter |
| * |
| * @return TRUE |
| * ---------------------------------------------------------------------------- |
| */ |
| BOOLEAN |
| MemNAdjustTxpdllClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *OptParam |
| ) |
| { |
| *(UINT8 *) OptParam -= 10; |
| return TRUE; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function is a wrapper to handle or switch NB Pstate for UNB |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in] *NBPstate - NB Pstate |
| * |
| * @return TRUE - Succeed |
| * @return FALSE - Fail |
| */ |
| |
| BOOLEAN |
| MemNChangeNbFrequencyWrapUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN UINT32 NBPstate |
| ) |
| { |
| UINT8 TargetNbPs; |
| UINT32 FreqNumeratorInMHz; |
| UINT32 FreqDivisor; |
| UINT32 VoltageInuV; |
| UINT8 NbPstateMaxVal; |
| CPU_SPECIFIC_SERVICES *FamilySpecificServices; |
| |
| if (NBPtr->NbFreqChgState == 0) { |
| // While in state 0, keep NB Pstate at the highest supported |
| TargetNbPs = 0; |
| if (NBPtr->NbPsCtlReg == 0) { |
| // Save NbPsCtl register on the first run |
| NBPtr->NbPsCtlReg = MemNGetBitFieldNb (NBPtr, BFNbPstateCtlReg); |
| } else { |
| // Do not need to switch NB Pstate again if it is already at highest |
| return TRUE; |
| } |
| } else if (NBPtr->NbFreqChgState < 4) { |
| // While in other states, go to the next lower NB Pstate |
| TargetNbPs = (UINT8) MemNGetBitFieldNb (NBPtr, BFCurNbPstate) + 1; |
| if (TargetNbPs == 1) { |
| // Set up intermediate NBPstate |
| NbPstateMaxVal = (UINT8) MemNGetBitFieldNb (NBPtr, BFNbPstateMaxVal); |
| MemNSetBitFieldNb (NBPtr, BFNbPsSel, NbPstateMaxVal); |
| GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, &NBPtr->MemPtr->StdHeader); |
| if (FamilySpecificServices->GetNbPstateInfo (FamilySpecificServices, |
| NBPtr->MemPtr->PlatFormConfig, |
| &NBPtr->PciAddr, |
| (UINT32) NbPstateMaxVal, |
| &FreqNumeratorInMHz, |
| &FreqDivisor, |
| &VoltageInuV, |
| &(NBPtr->MemPtr->StdHeader))) { |
| // Get NCLK speed for intermediate NBPstate |
| NBPtr->NBClkFreq = FreqNumeratorInMHz / FreqDivisor; |
| NBPtr->ProgramNbPsDependentRegs (NBPtr); |
| } else { |
| ASSERT (FALSE); |
| } |
| } |
| } else { |
| // When done with training, release NB Pstate force by restoring NbPsCtl register |
| NBPtr->FamilySpecificHook[ReleaseNbPstate] (NBPtr, NBPtr); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\tRelease NB Pstate force\n"); |
| return TRUE; |
| } |
| |
| // Make sure target NB Pstate is enabled, else find next enabled NB Pstate |
| GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, &NBPtr->MemPtr->StdHeader); |
| for (; TargetNbPs < 4; TargetNbPs++) { |
| if (FamilySpecificServices->GetNbPstateInfo (FamilySpecificServices, |
| NBPtr->MemPtr->PlatFormConfig, |
| &NBPtr->PciAddr, |
| (UINT32) TargetNbPs, |
| &FreqNumeratorInMHz, |
| &FreqDivisor, |
| &VoltageInuV, |
| &(NBPtr->MemPtr->StdHeader))) { |
| // Record NCLK speed |
| NBPtr->NBClkFreq = FreqNumeratorInMHz / FreqDivisor; |
| break; |
| } |
| } |
| |
| if (TargetNbPs < 4) { |
| IDS_HDT_CONSOLE (MEM_FLOW, "\tNB P%d: %dMHz\n", TargetNbPs, NBPtr->NBClkFreq); |
| |
| // 1.Program the configuration registers which contain multiple internal copies for each NB P-state. See |
| // D18F1x10C[NbPsSel]. |
| MemNSetBitFieldNb (NBPtr, BFNbPsSel, TargetNbPs); |
| |
| // Check to see if NB P-states have been disabled. @todo This should only be needed for |
| // bring up, but must be included in any releases that occur before NB P-state operation |
| // has been debugged/fixed. |
| if ((NBPtr->NbPsCtlReg & 0x00000003) != 0) { |
| NbPstateMaxVal = (UINT8) MemNGetBitFieldNb (NBPtr, BFNbPstateMaxVal); |
| // Set up RdPtrInit before transit to target NBPstate |
| if ((TargetNbPs > 0) && (TargetNbPs != NbPstateMaxVal)) { |
| NBPtr->ProgramNbPsDependentRegs (NBPtr); |
| } |
| |
| // If current NBPstate is already in NBPstateLo, do not do transition to NBPstateLo. |
| if ((TargetNbPs != 0) || (MemNGetBitFieldNb (NBPtr, BFNbPstateLo) != MemNGetBitFieldNb (NBPtr, BFCurNbPstate))) { |
| // 2.Program D18F5x170 to transition the NB P-state: |
| // NbPstateLo = NbPstateMaxVal. (HW requires an intermediate transition to low) |
| // SwNbPstateLoDis = NbPstateDisOnP0 = NbPstateThreshold = 0. |
| MemNSetBitFieldNb (NBPtr, BFNbPstateLo, NbPstateMaxVal); |
| MemNSetBitFieldNb (NBPtr, BFNbPstateCtlReg, MemNGetBitFieldNb (NBPtr, BFNbPstateCtlReg) & 0xFFFF91FF); |
| |
| // 3.Wait for D18F5x174[CurNbPstate] to equal NbPstateLo. |
| MemNPollBitFieldNb (NBPtr, BFCurNbPstate, NbPstateMaxVal, PCI_ACCESS_TIMEOUT, FALSE); |
| } |
| // 4.Program D18F5x170 to force the NB P-state: |
| // NbPstateHi = target NB P-state. |
| // SwNbPstateLoDis = 1 (triggers the transition) |
| MemNSetBitFieldNb (NBPtr, BFNbPstateHi, TargetNbPs); |
| MemNSetBitFieldNb (NBPtr, BFSwNbPstateLoDis, 1); |
| |
| // 5.Wait for D18F5x174[CurNbPstate] to equal the target NB P-state. |
| MemNPollBitFieldNb (NBPtr, BFCurNbPstate, TargetNbPs, PCI_ACCESS_TIMEOUT, FALSE); |
| } |
| |
| // When NB frequency change succeeds, TSC rate may have changed. |
| // We need to update TSC rate |
| FamilySpecificServices->GetTscRate (FamilySpecificServices, &NBPtr->MemPtr->TscRate, &NBPtr->MemPtr->StdHeader); |
| // Switch MemPstate context if the current MemPstate does not sync with MemPstate context |
| if (MemNGetBitFieldNb (NBPtr, BFCurMemPstate) != MemNGetBitFieldNb (NBPtr, BFMemPsSel)) { |
| MemNChangeMemPStateContextNb (NBPtr, MemNGetBitFieldNb (NBPtr, BFCurMemPstate)); |
| } |
| } else { |
| // Cannot find a supported NB Pstate to switch to |
| // Release NB Pstate force by restoring NbPsCtl register |
| NBPtr->FamilySpecificHook[ReleaseNbPstate] (NBPtr, NBPtr); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\tRelease NB Pstate force\n"); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function sends an MRS command for Unb |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNSendMrsCmdUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 MrsBank; |
| UINT16 MrsBuffer; |
| UINT8 MrsChipSel; |
| |
| MemNSetASRSRTNb (NBPtr); |
| MemNSwapBitsUnb (NBPtr); |
| |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS%d MR%d %05x\n", |
| (MemNGetBitFieldNb (NBPtr, BFMrsChipSel) & 0x7), |
| (MemNGetBitFieldNb (NBPtr, BFMrsBank) & 0x7), |
| (MemNGetBitFieldNb (NBPtr, BFMrsAddress) & 0x3FFFF)); |
| |
| // 1.Set SendMrsCmd=1 |
| MemNSetBitFieldNb (NBPtr, BFSendMrsCmd, 1); |
| |
| // 2.Wait for SendMrsCmd=0 |
| MemNPollBitFieldNb (NBPtr, BFSendMrsCmd, 0, PCI_ACCESS_TIMEOUT, FALSE); |
| |
| // Send MRS buffer if memory pstate is supported and enabled |
| if (NBPtr->MemPstateStage != 0) { |
| MrsChipSel = (UINT8) (MemNGetBitFieldNb (NBPtr, BFMrsChipSel) & 0x7); |
| // Only user even rank MRS to set MRS buffer |
| if ((MrsChipSel & 1) == 0) { |
| MrsBank = (UINT8) (MemNGetBitFieldNb (NBPtr, BFMrsBank) & 0x7); |
| MrsBuffer = (UINT16) (MemNGetBitFieldNb (NBPtr, BFMrsAddress) & 0xFFFF); |
| if (MrsBank == 0) { |
| MrsBuffer &= 0xFEFF; |
| MemNSetBitFieldNb (NBPtr, BFMxMr0, MrsBuffer); |
| } else if (MrsBank == 1) { |
| MemNSetBitFieldNb (NBPtr, BFMxMr1, MrsBuffer); |
| } else if (MrsBank == 2) { |
| MemNSetBitFieldNb (NBPtr, BFMxMr2, MrsBuffer); |
| } |
| } |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function returns MR0[CL] value with table driven support |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return MR0[CL] value |
| */ |
| UINT32 |
| MemNGetMR0CLTblDrvNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| return (UINT32) ((NBPtr->PsPtr->MR0CL31 << 4) | (NBPtr->PsPtr->MR0CL0 << 2)); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function performs MaxRdLat training for slot 1 |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] TestAddrRJ16 - Test address |
| * |
| * @return TRUE |
| * ---------------------------------------------------------------------------- |
| */ |
| BOOLEAN |
| MemNSlot1MaxRdLatTrainClientNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *TestAddrRJ16 |
| ) |
| { |
| UINT8 DummyBuffer[8]; |
| UINT16 MaxLatDly; |
| UINT8 i; |
| |
| // Perform slot1 specific training: |
| // A.Program D18F2x[1,0]78[SlotSel]=1. Force read CAS to fifo slot1 for training. |
| // B.Program D18F2x[1,0]78[MaxRdLatency] = TrainedMaxRdLatency. Set to last slot0 value that passed. |
| // C.Read the DIMM test addresses. |
| // D.Compare the values read against the pattern written. |
| |
| IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tTrain Slot 1: \n"); |
| MemNSetBitFieldNb (NBPtr, BFSlotSel, 1); |
| |
| MaxLatDly = (UINT16) (MemNGetBitFieldNb (NBPtr, BFMaxLatency) + 1); // Add 1 to get back to the last passing value |
| MemNSetBitFieldNb (NBPtr, BFMaxLatency, MaxLatDly); |
| |
| for (i = 0; i < 100; i++) { |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDly %3x", MaxLatDly); |
| |
| NBPtr->ReadPattern (NBPtr, DummyBuffer, *(UINT32*)TestAddrRJ16, 6); |
| |
| if (NBPtr->CompareTestPattern (NBPtr, DummyBuffer, DummyBuffer, 6 * 64) == 0xFFFF) { |
| IDS_HDT_CONSOLE (MEM_FLOW, " P"); |
| break; |
| } |
| IDS_HDT_CONSOLE (MEM_FLOW, "\n"); |
| } |
| |
| if (i < 100) { |
| MemNSetBitFieldNb (NBPtr, BFSlot1ExtraClkEn, 0); |
| } else { |
| MemNSetBitFieldNb (NBPtr, BFSlot1ExtraClkEn, 1); |
| } |
| |
| MemNSetBitFieldNb (NBPtr, BFMaxSkipErrTrain, 0); |
| |
| return TRUE; |
| } |
| |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function programs dram power management timing related registers |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return none |
| * ---------------------------------------------------------------------------- |
| */ |
| VOID |
| MemNDramPowerMngTimingNb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| STATIC CONST UINT8 Tckesr[] = {4, 4, 4, 5, 5, 6, 7, 2, 2}; |
| UINT8 Tck; |
| |
| // These timings are based on DDR3 spec |
| // Tcksrx = max(5 nCK, 10 ns) |
| Tck = (UINT8) MAX (5, (MemUnsToMemClk (NBPtr->DCTPtr->Timings.Speed, 10))); |
| MemNSetBitFieldNb (NBPtr, BFTcksrx, MIN (0xE, MAX (Tck, 2))); |
| |
| // Tcksre = max(5 nCK, 10 ns) |
| MemNSetBitFieldNb (NBPtr, BFTcksre, MIN (0x27, MAX (Tck, 5))); |
| |
| // Tckesr = tCKE(min) + 1 nCK |
| // tCKE(min) |
| // DDR-667 7.5ns = 3nCk max(3nCK, 7.5ns) + 1 = 3nCK + 1nCK = 4nCK |
| // DDR-800 7.5ns = 3nCk max(3nCK, 7.5ns) + 1 = 3nCK + 1nCK = 4nCK |
| // DDR-1066 5.625ns = 3nCK max(3nCK, 5.625ns) + 1 = 3nCL + 1nCK = 4nCK |
| // DDR-1333 5.625ns = 4nCK max(3nCK, 4nCK) + 1 = 4nCK + 1nCK = 5nCK |
| // DDR-1600 5ns = 4nCK max(3nCK, 4nCK) + 1 = 4nCK + 1nCK = 5nCK |
| // DDR-1866 5ns = 5nCK max(3nCK, 5nCK) + 1 = 5nCK + 1nCK = 6nCK |
| // DDR-2133 5ns = 6nCK max(3nCK, 6nCK) + 1 = 6nCK + 1nCK = 7nCK |
| ASSERT (((NBPtr->DCTPtr->Timings.Speed / 133) >= 2) && ((NBPtr->DCTPtr->Timings.Speed / 133) <= 10)); |
| MemNSetBitFieldNb (NBPtr, BFTckesr, Tckesr[(NBPtr->DCTPtr->Timings.Speed / 133) - 2]); |
| |
| // Tpd = tCKE(min) |
| MemNSetBitFieldNb (NBPtr, BFTpd, Tckesr[(NBPtr->DCTPtr->Timings.Speed / 133) - 2] - 1); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * The function resets Rcv Fifo |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * @param[in] Dummy - Dummy parameter |
| * |
| */ |
| |
| VOID |
| MemTResetRcvFifoUnb ( |
| IN OUT struct _MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 Dummy |
| ) |
| { |
| // Program D18F2x9C_x0000_0050_dct[1:0]=00000000h |
| MemNSetBitFieldNb (TechPtr->NBPtr, BFRstRcvFifo, 0); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function gets the memory width |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| * @return Memory width |
| */ |
| |
| UINT32 |
| MemNGetMemoryWidthUnb ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| DIE_STRUCT *MCTPtr; |
| MEM_SHARED_DATA *SharedPtr; |
| |
| MCTPtr = NBPtr->MCTPtr; |
| SharedPtr = NBPtr->SharedPtr; |
| |
| return 64 + ((SharedPtr->AllECC && MCTPtr->Status[SbEccDimms]) ? 8 : 0); |
| } |