/** * @file * * mrndct.c * * Northbridge common DCT support for Recovery * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: (Proc/Recovery/Mem/NB) * @e \$Revision: 6781 $ @e \$Date: 2008-07-17 12:11:08 -0500 (Thu, 17 Jul 2008) $ * **/ /***************************************************************************** * * Copyright (c) 2011, 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 "OptionMemory.h" #include "PlatformMemoryConfiguration.h" #include "Ids.h" #include "cpuFamRegisters.h" #include "mm.h" #include "mn.h" #include "mt.h" #include "mru.h" #include "Filecode.h" #define FILECODE PROC_RECOVERY_MEM_NB_MRNDCT_FILECODE /*---------------------------------------------------------------------------- * DEFINITIONS AND MACROS * *---------------------------------------------------------------------------- */ #define RECDEF_DRAM_CONTROL_REG 0x320C2A06 #define RECDEF_DRAM_MRSREG 0x000400A4 #define RECDEF_DRAM_TIMING_LO 0x000A0092 #define RECDEF_DRAM_TIMING_HI 0xB6D218FF #define RECDEF_CSMASK_REG 0x00083FE0 #define RECDEF_DRAM_CONFIG_LO_REG 0x00000000 #define RECDEF_DRAM_CONFIG_HI_REG 0x1F48010B #define RECDEF_DRAM_BASE_REG 0x00000003 /*---------------------------------------------------------------------------- * TYPEDEFS AND STRUCTURES * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * PROTOTYPES OF LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ VOID STATIC MemRecTCtlOnDimmMirrorNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN BOOLEAN SetFlag ); VOID STATIC MemRecNSwapBitsNb ( IN OUT MEM_NB_BLOCK *NBPtr ); /*---------------------------------------------------------------------------- * EXPORTED FUNCTIONS * *---------------------------------------------------------------------------- */ /* -----------------------------------------------------------------------------*/ /** * * This function programs the memory controller with configuration parameters * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return TRUE - An Error value lower than AGESA_ERROR may have occurred * @return FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred */ BOOLEAN MemRecNAutoConfigNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { UINT8 Dimm; UINT8 Dct; UINT32 RegValue; UINT8 ChipSel; UINT32 CSBase; DCT_STRUCT *DCTPtr; CH_DEF_STRUCT *ChannelPtr; Dct = NBPtr->Dct; DCTPtr = NBPtr->DCTPtr; ChannelPtr = NBPtr->ChannelPtr; //Prepare variables for future usage. for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) { if ((ChannelPtr->ChDimmValid & (UINT8) 1 << Dimm) != 0) { DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2); if (((ChannelPtr->DimmDrPresent & (UINT8) 1 << Dimm) == 0) && ((ChannelPtr->DimmQrPresent & (UINT8) 1 << Dimm) == 0)) { continue; } else { DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2 + 1); } } } Dimm = NBPtr->DimmToBeUsed; //Temporarily set all CS Base/Limit registers (corresponding to Dimms exist on a channel) with 256MB size for WL training. CSBase = 0; for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) { if (DCTPtr->Timings.CsPresent & (UINT8) 1 << ChipSel) { CSBase &= (UINT32) ~0x08; //Clear OnDimmMirror bit. if (((ChipSel & 1) != 0) && ((ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0)) { CSBase |= (UINT32) 0x08; //Set OnDimmMirror bit. } MemRecNSetBitFieldNb (NBPtr, (BFCSBaseAddr0Reg + ChipSel), (CSBase | 0x01)); CSBase += 0x100000; if ((ChipSel & 1) == 0) { MemRecNSetBitFieldNb (NBPtr, (BFCSMask0Reg + (ChipSel >> 1)), RECDEF_CSMASK_REG); } } } MemRecNSetBitFieldNb (NBPtr, BFDramBaseReg0, RECDEF_DRAM_BASE_REG); MemRecNSetBitFieldNb (NBPtr, BFDramLimitReg0, 0x70000); // Disable the other DCT NBPtr->MemRecNSwitchDctNb (NBPtr, Dct ^ 0x01); MemRecNSetBitFieldNb (NBPtr, BFDisDramInterface, 1); NBPtr->MemRecNSwitchDctNb (NBPtr, Dct); if (Dct != 0) { // If DCT 1, set DctSelBase registers MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseAddrReg, 0x00000003); MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseOffsetReg, 0x00000000); } MemRecNSetBitFieldNb (NBPtr, BFDramBankAddrReg, 0x00001111); // Set timing registers MemRecNSetBitFieldNb (NBPtr, BFDramTimingLoReg, RECDEF_DRAM_TIMING_LO); MemRecNSetBitFieldNb (NBPtr, BFDramTimingHiReg, RECDEF_DRAM_TIMING_HI); MemRecNSetBitFieldNb (NBPtr, BFDramMRSReg, RECDEF_DRAM_MRSREG); MemRecNSetBitFieldNb (NBPtr, BFDramControlReg, RECDEF_DRAM_CONTROL_REG); // Set DRAM Config Low Register RegValue = RECDEF_DRAM_CONFIG_LO_REG; // Set x4Dimm based on DIMM type if ((NBPtr->ChannelPtr->Dimmx4Present & ((UINT8) 1 << Dimm)) != 0) { RegValue |= ((UINT32) 1 << (Dimm + 12)); } // If not registered, set unbuffered DIMM if (!(NBPtr->ChannelPtr->RegDimmPresent & ((UINT8) 1 << Dimm))) { RegValue |= ((UINT32) 1 << 16); } MemRecNSetBitFieldNb (NBPtr, BFDramConfigLoReg, RegValue); // Set DRAM Config High Register RegValue = RECDEF_DRAM_CONFIG_HI_REG; MemRecNSetBitFieldNb (NBPtr, BFDramConfigHiReg, RegValue); //====================================================================== // Build Dram Config Misc Register Value //====================================================================== // if ((NBPtr->ChannelPtr->RegDimmPresent != 0) && (NBPtr->ChannelPtr->TechType == DDR3_TECHNOLOGY)) { MemRecNSetBitFieldNb (NBPtr, BFSubMemclkRegDly, 1); } MemRecNSetBitFieldNb (NBPtr, BFOdtSwizzle, 1); return TRUE; } /* -----------------------------------------------------------------------------*/ /** * * 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_ERROR may have occurred * @return FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred */ BOOLEAN MemRecNPlatformSpecNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { UINT8 MaxDimmPerCH; UINT32 AddrTmgValue; UINT32 DrvStrValue; UINT8 *DimmsPerChPtr; CH_DEF_STRUCT *ChannelPtr; ChannelPtr = NBPtr->ChannelPtr; DimmsPerChPtr = MemRecFindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MAX_DIMMS, 0, ChannelPtr->ChannelID); if (DimmsPerChPtr != NULL) { MaxDimmPerCH = *DimmsPerChPtr; } else { MaxDimmPerCH = 2; } if (ChannelPtr->RegDimmPresent) { if (MaxDimmPerCH == 2) { DrvStrValue = 0x20223222; AddrTmgValue = 0; if ((ChannelPtr->Dimms == 1) && (ChannelPtr->DimmQrPresent == 0)) { DrvStrValue = 0x20113222; } } else if (MaxDimmPerCH == 3) { DrvStrValue = 0x20223222; AddrTmgValue = 0; if ((ChannelPtr->Dimms == 1) && (ChannelPtr->DimmQrPresent == 0)) { DrvStrValue = 0x20113222; } else if (ChannelPtr->Dimms == 3) { AddrTmgValue = 0x00380038; } } else { DrvStrValue = 0x20223222; AddrTmgValue = 0; if (ChannelPtr->Dimms >= 3) { AddrTmgValue = 0x002F0000; } else if (ChannelPtr->Dimms == 1) { DrvStrValue = 0x20113222; } } } else { if (ChannelPtr->Dimms == 2) { DrvStrValue = 0x20223323; AddrTmgValue = 0x00390039; } else { DrvStrValue = 0x20113222; AddrTmgValue = 0; if ((ChannelPtr->Dimms == 1) && (ChannelPtr->Loads >= 16)) { AddrTmgValue = 0x003B0000; } } } MemRecNSetBitFieldNb (NBPtr, BFODCControl, DrvStrValue); MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, AddrTmgValue); return TRUE; } /* -----------------------------------------------------------------------------*/ /** * * This function reads MemClkFreqVal bit to see if the DIMMs are present in this node. * If the DIMMs are present then set the DRAM Enable bit for this node. * * Setting dram init starts up the DCT state machine, initializes the * dram devices with MRS commands, and kicks off any * HW memory clear process that the chip is capable of. The sooner * that dram init is set for all nodes, the faster the memory system * initialization can complete. Thus, the init loop is unrolled into * two loops so as to start the processes for non BSP nodes sooner. * This procedure will not wait for the process to finish. Synchronization is * handled elsewhere. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemRecNStartupDCTNb ( 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. // ------- Done in InitPhyComp_Nb ------- // MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 1); MemRecUWait10ns (500, NBPtr->MemPtr); //MemRecNSetBitFieldNb (NBPtr, BFInitDram, 1); // HW Dram init AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader)); NBPtr->TechPtr->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. // MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 0); MemRecUWait10ns (75000, NBPtr->MemPtr); while (MemRecNGetBitFieldNb (NBPtr, BFDramEnabled) == 0); } /* -----------------------------------------------------------------------------*/ /** * * 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 MemRecNSetMaxLatencyNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN UINT16 MaxRcvEnDly ) { UINT16 SubTotal; AGESA_TESTPOINT (TpProcMemRcvrCalcLatency, &(NBPtr->MemPtr->StdHeader)); // Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs UINTs. SubTotal = 6 * 2; // If registered DIMMs are being used then add 1 MEMCLK to the sub-total. if (MemRecNGetBitFieldNb (NBPtr, BFUnBuffDimm) == 0) { SubTotal += 2; } // if (AddrCmdSetup || CsOdtSetup || CkeSetup) then K := K + 2; 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 += 8 - 5; // Add the maximum (worst case) delay value of DqsRcvEnGrossDelay // that exists across all DIMMs and byte lanes. // SubTotal += MaxRcvEnDly >> 5; // Add 5.5 to the sub-total. 5.5 represents part of the processor // specific constant delay value in the DRAM clock domain. // SubTotal += 5; // add 5.5 1/2MemClk // 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). // // Simplified formula: // SubTotal *= (Fn2xD4[NBFid]+4)/4 // SubTotal = SubTotal * ((UINT16) MemRecNGetBitFieldNb (NBPtr, BFNbFid) + 4); SubTotal /= 4; // Add 5 NCLKs to the sub-total. 5 represents part of the processor // specific constant value in the northbridge clock domain. // SubTotal += 5; // Program the F2x[1, 0]78[MaxRdLatency] register with the total delay value MemRecNSetBitFieldNb (NBPtr, BFMaxLatency, SubTotal); } /* -----------------------------------------------------------------------------*/ /** * * Set Dram ODT for mission mode and write leveling mode. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] OdtMode - Mission mode or write leveling mode * @param[in] ChipSelect - Chip select number * @param[in] TargetCS - Chip select number that is being trained * */ VOID MemRecNSetDramOdtNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN ODT_MODE OdtMode, IN UINT8 ChipSelect, IN UINT8 TargetCS ) { UINT8 Dimms; UINT8 DramTerm; UINT8 DramTermDyn; BOOLEAN IsDualRank; Dimms = NBPtr->ChannelPtr->Dimms; IsDualRank = (NBPtr->DCTPtr->Timings.CsPresent & (ChipSelect + 1)) ? TRUE : FALSE; if (OdtMode == WRITE_LEVELING_MODE) { DramTermDyn = 0; // Disabled if (ChipSelect & 1) { DramTerm = 0; // Disabled } else if (Dimms == 1) { DramTerm = 1; // 60 Ohms } else { DramTerm = 3; // 40 Ohms } if (ChipSelect == TargetCS) { if (Dimms == 2) { DramTerm = 2; // 120 Ohms } else if (IsDualRank) { DramTerm = 0; // Disabled } // Program WrLvOdt MemRecNSetBitFieldNb (NBPtr, BFWrLvOdt, 0x0F); } else if (ChipSelect == (TargetCS + 1)) { if (Dimms == 1) { DramTerm = 1; // 60 Ohms } } } else { // Dram nominal termination if (Dimms == 1) { DramTerm = 1; // 60 Ohms } else { DramTerm = 3; // 40 Ohms } // Dram dynamic termination if (Dimms < 2) { DramTermDyn = 0; // Disabled } else { DramTermDyn = 2; // 120 Ohms } } MemRecNSetBitFieldNb (NBPtr, BFDramTerm, DramTerm); MemRecNSetBitFieldNb (NBPtr, BFDramTermDyn, DramTermDyn); } /*---------------------------------------------------------------------------- * LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ /* -----------------------------------------------------------------------------*/ /** * * This function sends an MRS command * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemRecNSendMrsCmdNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { BOOLEAN ClearODM; ClearODM = FALSE; if (NBPtr->IsSupported[CheckClearOnDimmMirror]) { ClearODM = FALSE; if ((NBPtr->MCTPtr->LogicalCpuid.Revision & AMD_F10_C0) != 0) { if (NBPtr->IsSupported[CheckClearOnDimmMirror]) { if (MemRecNGetBitFieldNb (NBPtr, BFEnDramInit) == 0) { // For C0, if EnDramInit bit is cleared, ODM needs to be cleared before sending MRS MemRecTCtlOnDimmMirrorNb (NBPtr, FALSE); ClearODM = TRUE; } } } } MemRecNSwapBitsNb (NBPtr); IDS_HDT_CONSOLE ("\t\t\tCS%d MR%d %04x\n", (MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg) >> 20) & 0xF, (MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg) >> 16) & 0xF, (MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg) & 0xFFFF)); // 1.Set SendMrsCmd=1 MemRecNSetBitFieldNb (NBPtr, BFSendMrsCmd, 1); // 2.Wait for SendMrsCmd=0 while (MemRecNGetBitFieldNb (NBPtr, BFSendMrsCmd)) {} if (NBPtr->IsSupported[CheckClearOnDimmMirror]) { if (ClearODM) { // Restore ODM if necessary MemRecTCtlOnDimmMirrorNb (NBPtr, TRUE); } } } /* -----------------------------------------------------------------------------*/ /** * * This function sends the ZQCL command * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemRecNSendZQCmdNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { // 1.Program MrsAddress[10]=1 MemRecNSetBitFieldNb (NBPtr, BFMrsAddress, (UINT32) 1 << 10); // 2.Set SendZQCmd=1 MemRecNSetBitFieldNb (NBPtr, BFSendZQCmd, 1); // 3.Wait for SendZQCmd=0 while (MemRecNGetBitFieldNb (NBPtr, BFSendZQCmd)) {} // 4.Wait 512 MEMCLKs MemRecUWait10ns (128, NBPtr->MemPtr); // 512*2.5ns=1280, wait 1280ns } /* -----------------------------------------------------------------------------*/ /** * * This function disables/enables F2x[1, 0][5C:40][OnDimmMirror] * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] SetFlag - Enable or disable flag - TRUE - Enable, FALSE - DISABLE * */ VOID STATIC MemRecTCtlOnDimmMirrorNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN BOOLEAN SetFlag ) { UINT8 Chipsel; UINT32 CSBaseAddrReg; for (Chipsel = 0; Chipsel < MAX_CS_PER_CHANNEL; Chipsel += 2) { CSBaseAddrReg = MemRecNGetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel); if ((CSBaseAddrReg & 1) == 1) { if (SetFlag && ((NBPtr->ChannelPtr->DimmMirrorPresent & ((UINT8) 1 << (Chipsel >> 1))) != 0)) { CSBaseAddrReg |= ((UINT32) 1 << BFOnDimmMirror); } else { CSBaseAddrReg &= ~((UINT32) 1 << BFOnDimmMirror); } MemRecNSetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel, CSBaseAddrReg); } } } /* -----------------------------------------------------------------------------*/ /** * * * This function swaps bits for OnDimmMirror support * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID STATIC MemRecNSwapBitsNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { UINT8 ChipSel; UINT32 MRSReg; ChipSel = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFMrsChipSel); if ((ChipSel & 1) != 0) { MRSReg = MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg); if ((NBPtr->ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0) { MRSReg = (MRSReg & 0xFFFCFE07) | ((MRSReg&0x100A8) << 1) | ((MRSReg&0x20150) >> 1); MemRecNSetBitFieldNb (NBPtr, BFDramInitRegReg, MRSReg); } } }