blob: 2769e1cc01d35c2d88b6c430955defb32135c862 [file] [log] [blame]
/* $NoKeywords:$ */
/**
* @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: 48803 $ @e \$Date: 2011-03-10 20:18:28 -0700 (Thu, 10 Mar 2011) $
*
**/
/*
*****************************************************************************
*
* 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 "mrport.h"
#include "cpuFamRegisters.h"
#include "mm.h"
#include "mn.h"
#include "mt.h"
#include "mru.h"
#include "ma.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
*
*----------------------------------------------------------------------------
*/
/// Type of an entry for processing phy init compensation for client NB
typedef struct {
BIT_FIELD_NAME IndexBitField; ///< Bit field on which the value is decided
BIT_FIELD_NAME StartTargetBitField; ///< First bit field to be modified
BIT_FIELD_NAME EndTargetBitField; ///< Last bit field to be modified
UINT16 ExtraValue; ///< Extra value needed to be written to bit field
CONST UINT16 (*TxPrePN)[4]; ///< Pointer to slew rate table
} REC_PHY_COMP_INIT_CLIENTNB;
/*----------------------------------------------------------------------------
* 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
);
VOID
STATIC
MemRecNProgNbPstateDependentRegClientNb (
IN OUT MEM_NB_BLOCK *NBPtr
);
VOID
STATIC
MemRecNTrainPhyFenceNb (
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 p;
p = 0;
for (p = 0; p < MAX_PLATFORM_TYPES; p++) {
if (NBPtr->MemPtr->GetPlatformCfg[p] (NBPtr->MemPtr, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr) == AGESA_SUCCESS) {
MemRecNSetBitFieldNb (NBPtr, BFODCControl, NBPtr->ChannelPtr->DctOdcCtl);
MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, NBPtr->ChannelPtr->DctAddrTmg);
return TRUE;
}
}
return FALSE;
}
/* -----------------------------------------------------------------------------*/
/**
*
* 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 initializes the DRAM devices on all DCTs at the same time
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
*
*/
VOID
MemRecNStartupDCTClientNb (
IN OUT MEM_NB_BLOCK *NBPtr
)
{
IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", NBPtr->Dct);
// Program D18F2x[1,0]9C_x0000_000B = 80000000h. #109999.
MemRecNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x80000000);
// Program D18F2x[1,0]9C_x0D0F_E013[PllRegWaitTime] = 0118h. #193770.
MemRecNSetBitFieldNb (NBPtr, BFPllRegWaitTime, 0x118);
// Phy Voltage Level Programming
MemRecNPhyVoltageLevelNb (NBPtr);
// Run frequency change sequence
MemRecNSetBitFieldNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
MemRecNSetBitFieldNb (NBPtr, BFMemClkFreq, 6);
MemRecNProgNbPstateDependentRegClientNb (NBPtr);
MemRecNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 1);
MemRecNSetBitFieldNb (NBPtr, BFPllLockTime, 0x000F);
IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClkAlign=0\n");
IDS_HDT_CONSOLE (MEM_FLOW, "\tEnDramInit = 1 for DCT%d\n", NBPtr->Dct);
MemRecNSetBitFieldNb (NBPtr, BFDbeGskMemClkAlignMode, 0);
MemRecNSetBitFieldNb (NBPtr, BFEnDramInit, 1);
// Run DramInit sequence
AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
NBPtr->TechPtr->DramInit (NBPtr->TechPtr);
IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq: %d MHz\n", DDR800_FREQUENCY);
}
/* -----------------------------------------------------------------------------*/
/**
*
* 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 DramTerm;
UINT8 DramTermDyn;
DramTerm = NBPtr->ChannelPtr->Reserved[0];
DramTermDyn = NBPtr->ChannelPtr->Reserved[1];
if (OdtMode == WRITE_LEVELING_MODE) {
if (ChipSelect == TargetCS) {
DramTerm = DramTermDyn;
MemRecNSetBitFieldNb (NBPtr, BFWrLvOdt, NBPtr->ChannelPtr->PhyWLODT[TargetCS >> 1]);
}
}
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 (MEM_FLOW, "\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);
}
}
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* 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
*/
UINT32
MemRecNTotalSyncComponentsClientNb (
IN OUT MEM_NB_BLOCK *NBPtr
)
{
UINT32 T;
UINT32 P;
UINT32 AddrTmgCtl;
UINT32 MemClkPeriod;
AGESA_TESTPOINT (TpProcMemRcvrCalcLatency , &(NBPtr->MemPtr->StdHeader));
// P = P + ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16) where RdPtrInitMin = RdPtrInit
P = 0;
AddrTmgCtl = MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl);
if (((AddrTmgCtl >> 16) & 0x20) != (AddrTmgCtl & 0x20)) {
P += 1;
}
// IF (DbeGskMemClkAlignMode==01b || (DbeGskMemClkAlignMode==00b && !(AddrCmdSetup==CsOdtSetup==CkeSetup)))
// THEN P = P + 1
// IF (SlowAccessMode==1) THEN P = P + 2
// T = T + (0.5 * MemClkPeriod) - 786 ps
MemClkPeriod = 1000000 / DDR800_FREQUENCY;
T = MemClkPeriod / 2 - 768;
// 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 * 5; // Tcl = 6 clocks
// (DisCutThroughMode = 0), so P = P + 3
P += 3;
return ((P * MemClkPeriod + 1) / 2) + T;
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function programs the phy registers according to the desired phy VDDIO voltage level
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
*
*/
VOID
MemRecNPhyVoltageLevelNb (
IN OUT MEM_NB_BLOCK *NBPtr
)
{
BIT_FIELD_NAME BitField;
UINT16 Value;
UINT16 Mask;
Mask = 0xFFE7;
Value = (UINT16) NBPtr->RefPtr->DDR3Voltage << 3;
for (BitField = BFDataRxVioLvl; BitField <= BFCmpVioLvl; BitField++) {
if (BitField == BFCmpVioLvl) {
Mask = 0x3FFF;
Value <<= (14 - 3);
}
MemRecNSetBitFieldNb (NBPtr, BitField, ((MemRecNGetBitFieldNb (NBPtr, BitField) & Mask)) | Value);
}
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function executes prototypical Phy fence training function.
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
*
*/
VOID
MemRecNPhyFenceTrainingNb (
IN OUT MEM_NB_BLOCK *NBPtr
)
{
UINT8 FenceThresholdTxDll;
UINT8 FenceThresholdRxDll;
UINT8 FenceThresholdTxPad;
UINT16 Fence2Data;
// 1. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=10b.
// 2. Perform phy fence training. See 2.10.3.2.3.1 [Phy Fence Training].
// 3. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxDll].
MemRecNSetBitFieldNb (NBPtr, BFFenceTrSel, 2);
MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 30, 26, BFPhyFence);
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tFenceThresholdTxDll\n");
MemRecNTrainPhyFenceNb (NBPtr);
FenceThresholdTxDll = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFPhyFence);
// 4. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=001b.
MemRecNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x1000);
// 5. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=01b.
// 6. Perform phy fence training. See 2.10.3.2.3.1 [Phy Fence Training].
// 7. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdRxDll].
MemRecNSetBitFieldNb (NBPtr, BFFenceTrSel, 1);
MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 25, 21, BFPhyFence);
IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdRxDll\n");
MemRecNTrainPhyFenceNb (NBPtr);
FenceThresholdRxDll = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFPhyFence);
// 8. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=000b.
MemRecNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x0000);
// 9. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=11b.
// 10. Perform phy fence training. See 2.10.3.2.3.1 [Phy Fence Training].
// 11. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxPad].
MemRecNSetBitFieldNb (NBPtr, BFFenceTrSel, 3);
MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 20, 16, BFPhyFence);
IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdTxPad\n");
MemRecNTrainPhyFenceNb (NBPtr);
FenceThresholdTxPad = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFPhyFence);
// Program Fence2 threshold for Clk, Cmd, and Addr
if (FenceThresholdTxPad < 16) {
MemRecNSetBitFieldNb (NBPtr, BFClkFence2, FenceThresholdTxPad | 0x10);
MemRecNSetBitFieldNb (NBPtr, BFCmdFence2, FenceThresholdTxPad | 0x10);
MemRecNSetBitFieldNb (NBPtr, BFAddrFence2, FenceThresholdTxPad | 0x10);
} else {
MemRecNSetBitFieldNb (NBPtr, BFClkFence2, 0);
MemRecNSetBitFieldNb (NBPtr, BFCmdFence2, 0);
MemRecNSetBitFieldNb (NBPtr, BFAddrFence2, 0);
}
// Program Fence2 threshold for data
Fence2Data = 0;
if (FenceThresholdTxPad < 16) {
Fence2Data |= FenceThresholdTxPad | 0x10;
}
if (FenceThresholdRxDll < 16) {
Fence2Data |= (FenceThresholdRxDll | 0x10) << 10;
}
if (FenceThresholdTxDll < 16) {
Fence2Data |= (FenceThresholdTxDll | 0x10) << 5;
}
MemRecNSetBitFieldNb (NBPtr, BFDataFence2, Fence2Data);
// Reprogram F2x9C_04.
MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl));
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function executes Phy fence training
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
*
*/
VOID
STATIC
MemRecNTrainPhyFenceNb (
IN OUT MEM_NB_BLOCK *NBPtr
)
{
UINT8 Byte;
UINT16 Avg;
UINT8 PREvalue;
if (MemRecNGetBitFieldNb (NBPtr, BFDisDramInterface)) {
return;
}
// 1. BIOS first programs a seed value to the phase recovery
// engine registers.
//
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tSeeds: ");
for (Byte = 0; Byte < 9; Byte++) {
// This includes ECC as byte 8
MemRecNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19);
IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", 19);
}
IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tPhyFenceTrEn = 1");
// 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1.
MemRecNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1);
MemRecUWait10ns (5000, NBPtr->MemPtr);
// 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0.
MemRecNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 0);
// 5. BIOS reads the phase recovery engine registers
// F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52.
// 6. Calculate the average value of the fine delay and subtract 8.
//
Avg = 0;
for (Byte = 0; Byte < 9; Byte++) {
// This includes ECC as byte 8
PREvalue = (UINT8) (0x1F & MemRecNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte)));
Avg = Avg + ((UINT16) PREvalue);
}
Avg = ((Avg + 8) / 9); // round up
Avg -= 6;
// 7. Write the value to F2x[1, 0]9C_x0C[PhyFence].
MemRecNSetBitFieldNb (NBPtr, BFPhyFence, Avg);
// 8. BIOS rewrites F2x[1, 0]9C_x04, DRAM Address/Command Timing Control
// Register delays for both channels. This forces the phy to recompute
// the fence.
//
MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl));
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function calculates and programs NB P-state dependent registers
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
*
*/
VOID
STATIC
MemRecNProgNbPstateDependentRegClientNb (
IN OUT MEM_NB_BLOCK *NBPtr
)
{
UINT8 i;
UINT8 NclkFid;
UINT16 MemClkDid;
UINT8 PllMult;
UINT8 NclkDiv;
UINT8 RdPtrInit;
UINT32 NclkPeriod;
UINT32 MemClkPeriod;
INT32 PartialSum2x;
INT32 PartialSumSlotI2x;
INT32 RdPtrInitRmdr2x;
NclkFid = (UINT8) (MemRecNGetBitFieldNb (NBPtr, BFMainPllOpFreqId) + 0x10);
MemClkDid = 2; //BKDG recommended value for DDR800
PllMult = 16; //BKDG recommended value for DDR800
NclkDiv = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFNbPs0NclkDiv);
NclkPeriod = (2500 * NclkDiv) / NclkFid;
MemClkPeriod = 1000000 / DDR800_FREQUENCY;
NBPtr->NBClkFreq = ((UINT32) NclkFid * 400) / NclkDiv;
IDS_HDT_CONSOLE (MEM_FLOW, "\n\tNB P%d Freq: %dMHz\n", 0, NBPtr->NBClkFreq);
IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClk Freq: %dMHz\n", DDR800_FREQUENCY);
// 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 = NBPtr->FreqChangeParam->RdPtrInitLower667;
MemRecNSetBitFieldNb (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].
MemRecNSetBitFieldNb (NBPtr, BFDbeGskFifoNumerator, NclkFid * MemClkDid * 16);
MemRecNSetBitFieldNb (NBPtr, BFDbeGskFifoDenominator, PllMult * NclkDiv);
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoNumerator: %d\n", NclkFid * MemClkDid * 16);
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoDenominator: %d\n", PllMult * NclkDiv);
// 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)
PartialSum2x = NBPtr->FreqChangeParam->NclkPeriodMul2x * NclkPeriod;
PartialSum2x += NBPtr->FreqChangeParam->MemClkPeriodMul2x * MemClkPeriod;
PartialSum2x += 520 * 2;
RdPtrInitRmdr2x = ((NBPtr->FreqChangeParam->SyncTimeMul4x * MemClkPeriod) / 2) - 2 * (NBPtr->FreqChangeParam->TDataPropLower800 + 520);
RdPtrInitRmdr2x %= MemClkPeriod;
PartialSum2x -= RdPtrInitRmdr2x;
PartialSum2x = (PartialSum2x + MemClkPeriod - 1) / MemClkPeriod; // round-up here
PartialSum2x -= 2 * 5; //Tcwl + 5
if ((MemRecNGetBitFieldNb (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;
if (i == 0) {
PartialSumSlotI2x += 2;
}
if (PartialSumSlotI2x > 0) {
MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 0);
MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, (PartialSumSlotI2x + 1) / 2);
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: %d\n", i, (PartialSumSlotI2x + 1) / 2);
} else {
MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 1);
PartialSumSlotI2x = ((-PartialSumSlotI2x) * MemClkPeriod) / (2 * NclkPeriod);
MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, PartialSumSlotI2x);
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: -%d\n", i, PartialSumSlotI2x);
}
}
// Program ProcOdtAdv
MemRecNSetBitFieldNb (NBPtr, BFProcOdtAdv, 0);
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function reads cache lines continuously using TCB CPG engine
*
* @param[in,out] NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] Buffer - Array of bytes to be filled with data read from DRAM
* @param[in] Address - System Address [47:16]
* @param[in] ClCount - Number of cache lines
*
*/
VOID
MemRecNContReadPatternClientNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN UINT8 Buffer[],
IN UINT32 Address,
IN UINT16 ClCount
)
{
// 1. Program D18F2x1C0[RdDramTrainMode]=1.
MemRecNSetBitFieldNb (NBPtr, BFRdDramTrainMode, 1);
// 2. Program D18F2x1C0[TrainLength] to the appropriate number of cache lines.
MemRecNSetBitFieldNb (NBPtr, BFTrainLength, ClCount);
// 3. Program the DRAM training address as follows:
MemRecNSetBitFieldNb (NBPtr, BFWrTrainAdrPtrLo, (Address >> 6));
// 4. Program D18F2x1D0[WrTrainBufAddr]=000h
MemRecNSetBitFieldNb (NBPtr, BFWrTrainBufAddr, 0);
// 5. Program D18F2x1C0[RdTrainGo]=1.
MemRecNSetBitFieldNb (NBPtr, BFRdTrainGo, 1);
// 6. Wait for D18F2x1C0[RdTrainGo]=0.
while (MemRecNGetBitFieldNb (NBPtr, BFRdTrainGo) != 0) {}
// 7. Read D18F2x1E8[TrainCmpSts] and D18F2x1E8[TrainCmpSts2].
// 8. Program D18F2x1C0[RdDramTrainMode]=0.
MemRecNSetBitFieldNb (NBPtr, BFRdDramTrainMode, 0);
}
/* -----------------------------------------------------------------------------*/
/**
*
* This is function sets the platform specific settings for the systems with UDIMMs configuration
*
* @param[in,out] *MemData Pointer to MEM_DATA_STRUCTURE
* @param[in] SocketID Socket number
* @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
*
* @return AGESA_SUCCESS
* @return CurrentChannel->DctAddrTmg Address Command Timing Settings for specified channel
* @return CurrentChannel->DctOdcCtl Drive Strength settings for specified channel
* @return CurrentChannel->Reserved[0] Dram Term for specified channel
* @return CurrentChannel->Reserved[1] Dynamic Dram Term for specified channel
* @return CurrentChannel->PhyWLODT[0] WL ODT for DIMM0
* @return CurrentChannel->PhyWLODT[1] WL ODT for DIMM1
* @return CurrentChannel->PhyWLODT[2] WL ODT for DIMM2
* @return CurrentChannel->PhyWLODT[3] WL ODT for DIMM3
*
*/
AGESA_STATUS
MemRecNGetPsCfgUDIMM3Nb (
IN OUT MEM_DATA_STRUCT *MemData,
IN UINT8 SocketID,
IN OUT CH_DEF_STRUCT *CurrentChannel
)
{
UINT32 AddrTmgCTL;
UINT32 DctOdcCtl;
UINT8 Dimms;
UINT8 MaxDimmPerCH;
UINT8 DramTerm;
UINT8 DramTermDyn;
if ((CurrentChannel->RegDimmPresent != 0) || (CurrentChannel->SODimmPresent != 0)) {
return AGESA_UNSUPPORTED;
}
Dimms = CurrentChannel->Dimms;
MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
if (MaxDimmPerCH == 1) {
return AGESA_UNSUPPORTED;
} else {
DctOdcCtl = 0x20223323;
AddrTmgCTL = 0x00390039;
if (Dimms == 1) {
DctOdcCtl = 0x20113222;
AddrTmgCTL = 0x00390039;
if (CurrentChannel->Loads == 16) {
AddrTmgCTL = 0x003B0000;
}
}
}
CurrentChannel->DctAddrTmg = AddrTmgCTL;
CurrentChannel->DctOdcCtl = DctOdcCtl;
// ODT
if (Dimms == 1) {
DramTerm = 1; // 60 ohms
DramTermDyn = 0; // Disable
if ((MaxDimmPerCH == 3) && (CurrentChannel->DimmDrPresent != 0)) {
DramTermDyn = 1; // 60 ohms
}
} else {
DramTerm = 3; // 40 ohms
DramTermDyn = 2; // 120 ohms
}
CurrentChannel->Reserved[0] = DramTerm;
CurrentChannel->Reserved[1] = DramTermDyn;
// WL ODT
if (Dimms == 1) {
CurrentChannel->PhyWLODT[0] = 0;
CurrentChannel->PhyWLODT[1] = (CurrentChannel->DimmDrPresent != 0) ? 8 : 2;
} else {
CurrentChannel->PhyWLODT[0] = 3;
CurrentChannel->PhyWLODT[1] = 3;
}
CurrentChannel->PhyWLODT[2] = 0;
CurrentChannel->PhyWLODT[3] = 0;
return AGESA_SUCCESS;
}
/* -----------------------------------------------------------------------------*/
/**
*
* This is function sets the platform specific settings for the systems with SODIMMs configuration
*
* @param[in,out] *MemData Pointer to MEM_DATA_STRUCTURE
* @param[in] SocketID Socket number
* @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
*
* @return AGESA_SUCCESS
* @return CurrentChannel->DctAddrTmg Address Command Timing Settings for specified channel
* @return CurrentChannel->DctOdcCtl Drive Strength settings for specified channel
* @return CurrentChannel->Reserved[0] Dram Term for specified channel
* @return CurrentChannel->Reserved[1] Dynamic Dram Term for specified channel
* @return CurrentChannel->PhyWLODT[0] WL ODT for DIMM0
* @return CurrentChannel->PhyWLODT[1] WL ODT for DIMM1
* @return CurrentChannel->PhyWLODT[2] WL ODT for DIMM2
* @return CurrentChannel->PhyWLODT[3] WL ODT for DIMM3
*
*/
AGESA_STATUS
MemRecNGetPsCfgSODIMM3Nb (
IN OUT MEM_DATA_STRUCT *MemData,
IN UINT8 SocketID,
IN OUT CH_DEF_STRUCT *CurrentChannel
)
{
UINT32 AddrTmgCTL;
UINT32 DctOdcCtl;
UINT8 MaxDimmPerCH;
UINT8 Dimms;
UINT8 DramTerm;
UINT8 DramTermDyn;
if (CurrentChannel->SODimmPresent != CurrentChannel->ChDimmValid) {
return AGESA_UNSUPPORTED;
}
Dimms = CurrentChannel->Dimms;
MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
if (MaxDimmPerCH == 1) {
DctOdcCtl = 0x00113222;
AddrTmgCTL = 0;
} else {
DctOdcCtl = 0x00223323;
AddrTmgCTL = 0x00000039;
if (Dimms == 1) {
DctOdcCtl = 0x00113222;
AddrTmgCTL = 0;
}
}
CurrentChannel->DctAddrTmg = AddrTmgCTL;
CurrentChannel->DctOdcCtl = DctOdcCtl;
// ODT
if (Dimms == 1) {
DramTerm = 2; // 120 ohms
DramTermDyn = 0; // Disable
if (MaxDimmPerCH == 2) {
DramTerm = 1; // 60 ohms
}
} else {
DramTerm = 3; // 40 ohms
DramTermDyn = 2; // 120 ohms
}
CurrentChannel->Reserved[0] = DramTerm;
CurrentChannel->Reserved[1] = DramTermDyn;
// WL ODT
if (Dimms == 1) {
if (MaxDimmPerCH == 1) {
CurrentChannel->PhyWLODT[0] = (CurrentChannel->DimmDrPresent != 0) ? 4 : 1;
CurrentChannel->PhyWLODT[1] = 0;
} else {
CurrentChannel->PhyWLODT[0] = 0;
CurrentChannel->PhyWLODT[1] = (CurrentChannel->DimmDrPresent != 0) ? 8 : 2;
}
} else {
CurrentChannel->PhyWLODT[0] = 3;
CurrentChannel->PhyWLODT[1] = 3;
}
CurrentChannel->PhyWLODT[2] = 0;
CurrentChannel->PhyWLODT[3] = 0;
return AGESA_SUCCESS;
}
/* -----------------------------------------------------------------------------*/
/**
*
* This is function sets the platform specific settings for the systems with RDIMMs configuration
*
* @param[in,out] *MemData Pointer to MEM_DATA_STRUCTURE
* @param[in] SocketID Socket number
* @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
*
* @return AGESA_SUCCESS
* @return CurrentChannel->DctAddrTmg Address Command Timing Settings for specified channel
* @return CurrentChannel->DctOdcCtl Drive Strength settings for specified channel
* @return CurrentChannel->Reserved[0] Dram Term for specified channel
* @return CurrentChannel->Reserved[1] Dynamic Dram Term for specified channel
* @return CurrentChannel->PhyWLODT[0] WL ODT for DIMM0
* @return CurrentChannel->PhyWLODT[1] WL ODT for DIMM1
* @return CurrentChannel->PhyWLODT[2] WL ODT for DIMM2
* @return CurrentChannel->PhyWLODT[3] WL ODT for DIMM3
*
*/
AGESA_STATUS
MemRecNGetPsCfgRDIMM3Nb (
IN OUT MEM_DATA_STRUCT *MemData,
IN UINT8 SocketID,
IN OUT CH_DEF_STRUCT *CurrentChannel
)
{
STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg2DIMMsWlODT[] = {
{SR_DIMM0, {0x01, 0x00, 0x00, 0x00}, 1},
{DR_DIMM0, {0x04, 0x00, 0x00, 0x00}, 1},
{QR_DIMM0, {0x05, 0x00, 0x00, 0x00}, 1},
{SR_DIMM1, {0x00, 0x02, 0x00, 0x00}, 1},
{DR_DIMM1, {0x00, 0x08, 0x00, 0x00}, 1},
{QR_DIMM1, {0x00, 0x0A, 0x00, 0x00}, 1},
{SR_DIMM0 + DR_DIMM0 + SR_DIMM1 + DR_DIMM1, {0x03, 0x03, 0x00, 0x00}, 2},
{SR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
{DR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
{QR_DIMM0 + SR_DIMM1, {0x03, 0x07, 0x06, 0x00}, 2},
{QR_DIMM0 + DR_DIMM1, {0x03, 0x07, 0x06, 0x00}, 2},
{QR_DIMM0 + QR_DIMM1, {0x0B, 0x07, 0x0E, 0x0D}, 2}
};
STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg3DIMMsWlODT[] = {
{SR_DIMM2 + DR_DIMM2, {0x00, 0x00, 0x04, 0x00}, 1},
{SR_DIMM0 + DR_DIMM0, {0x01, 0x02, 0x00, 0x00}, 1},
{SR_DIMM0 + DR_DIMM0 + SR_DIMM2 + DR_DIMM2, {0x05, 0x00, 0x05, 0x00}, 2},
{SR_DIMM0 + DR_DIMM0 + SR_DIMM1 + DR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x07, 0x07, 0x07, 0x00}, 3},
{QR_DIMM1, {0x00, 0x0A, 0x00, 0x0A}, 1},
{QR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x00, 0x06, 0x0E, 0x0C}, 2},
{SR_DIMM0 + DR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
{SR_DIMM0 + DR_DIMM0 + QR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x0F, 0x07, 0x0F, 0x0D}, 3}
};
STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg4DIMMsWlODT[] = {
{ANY_DIMM3, {0x00, 0x00, 0x00, 0x08}, 1},
{ANY_DIMM2 + ANY_DIMM3, {0x00, 0x00, 0x0C, 0x0C}, 2},
{ANY_DIMM1 + ANY_DIMM2 + ANY_DIMM3, {0x00, 0x0E, 0x0E, 0x0E}, 3},
{ANY_DIMM0 + ANY_DIMM1 + ANY_DIMM2 + ANY_DIMM3, {0x0F, 0x0F, 0x0F, 0x0F}, 4}
};
UINT8 i;
UINT8 j;
UINT8 Dimms;
UINT8 DimmQrPresent;
UINT32 AddrTmgCTL;
UINT32 DctOdcCtl;
UINT8 PhyWLODT[4];
UINT8 DramTerm;
UINT8 DramTermDyn;
UINT16 DIMMRankType;
UINT16 _DIMMRankType_;
UINT8 DimmTpMatch;
UINT8 MaxDimmPerCH;
UINT8 PSCfgWlODTSize;
CONST ADV_R_PSCFG_WL_ODT_ENTRY *PSCfgWlODTPtr;
if (CurrentChannel->RegDimmPresent != CurrentChannel->ChDimmValid) {
return AGESA_UNSUPPORTED;
}
DIMMRankType = MemRecNGetPsRankType (CurrentChannel);
MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
Dimms = CurrentChannel->Dimms;
PSCfgWlODTPtr = RecPSCfg2DIMMsWlODT;
PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg2DIMMsWlODT);
PhyWLODT[0] = PhyWLODT[1] = PhyWLODT[2] = PhyWLODT[3] = 0xFF;
DimmQrPresent = CurrentChannel->DimmQrPresent;
if (MaxDimmPerCH == 4) {
AddrTmgCTL = (Dimms > 2) ? 0x002F0000 : 0;
DctOdcCtl = (Dimms == 1) ? 0x20113222 : 0x20223222;
PSCfgWlODTPtr = RecPSCfg4DIMMsWlODT;
PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg4DIMMsWlODT);
} else if (MaxDimmPerCH == 3) {
AddrTmgCTL = 0;
DctOdcCtl = 0x20223222;
if (Dimms == 3) {
AddrTmgCTL = 0x00380038;
DctOdcCtl = 0x20113222;
}
if (Dimms == 1) {
DctOdcCtl = 0x20113222;
}
PSCfgWlODTPtr = RecPSCfg3DIMMsWlODT;
PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg3DIMMsWlODT);
} else if (MaxDimmPerCH == 2) {
AddrTmgCTL = 0;
DctOdcCtl = 0x20223222;
if ((Dimms == 1) && (DimmQrPresent == 0)) {
DctOdcCtl = 0x20113222;
}
} else {
AddrTmgCTL = 0;
DctOdcCtl = (DimmQrPresent == 0) ? 0x20113222 : 0x20223222;
}
CurrentChannel->DctAddrTmg = AddrTmgCTL;
CurrentChannel->DctOdcCtl = DctOdcCtl;
// ODT
if (Dimms == 1) {
DramTerm = 1; // 60 ohms
DramTermDyn = 0; // Disable
if (DimmQrPresent != 0) {
DramTermDyn = 2; // 120 ohms
}
} else {
DramTerm = 3; // 40 ohms
DramTermDyn = 2; // 120 ohms
if (DimmQrPresent != 0) {
DramTerm = 1; // 60 ohms
}
}
CurrentChannel->Reserved[0] = DramTerm;
CurrentChannel->Reserved[1] = DramTermDyn;
// WL ODT
for (i = 0; i < PSCfgWlODTSize; i++, PSCfgWlODTPtr++) {
if (Dimms != PSCfgWlODTPtr->Dimms) {
continue;
}
DimmTpMatch = 0;
_DIMMRankType_ = DIMMRankType & PSCfgWlODTPtr->DIMMRankType;
for (j = 0; j < MAX_DIMMS_PER_CHANNEL; j++) {
if ((_DIMMRankType_ & (UINT16) 0x0F << (j << 2)) != 0) {
DimmTpMatch++;
}
}
if (DimmTpMatch == PSCfgWlODTPtr->Dimms) {
PhyWLODT[0] = PSCfgWlODTPtr->PhyWrLvOdt[0];
PhyWLODT[1] = PSCfgWlODTPtr->PhyWrLvOdt[1];
PhyWLODT[2] = PSCfgWlODTPtr->PhyWrLvOdt[2];
PhyWLODT[3] = PSCfgWlODTPtr->PhyWrLvOdt[3];
break;
}
}
CurrentChannel->PhyWLODT[0] = PhyWLODT[0];
CurrentChannel->PhyWLODT[1] = PhyWLODT[1];
CurrentChannel->PhyWLODT[2] = PhyWLODT[2];
CurrentChannel->PhyWLODT[3] = PhyWLODT[3];
return AGESA_SUCCESS;
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function returns the max dimms for a given memory channel on a given
* processor. It first searches the platform override table for the max dimms
* value. If it is not provided, the AGESA default value is returned. The target
* socket must be a valid present socket.
*
* @param[in] PlatformMemoryConfiguration - Platform config table
* @param[in] SocketID - ID of the processor that owns the channel
* @param[in] ChannelID - Channel to get max dimms for
*
*
* @return UINT8 - Max Number of Dimms for that channel
*/
UINT8
RecGetMaxDimmsPerChannel (
IN PSO_TABLE *PlatformMemoryConfiguration,
IN UINT8 SocketID,
IN UINT8 ChannelID
)
{
UINT8 *DimmsPerChPtr;
UINT8 MaxDimmPerCH;
DimmsPerChPtr = MemRecFindPSOverrideEntry (PlatformMemoryConfiguration, PSO_MAX_DIMMS, SocketID, ChannelID);
if (DimmsPerChPtr != NULL) {
MaxDimmPerCH = *DimmsPerChPtr;
} else {
MaxDimmPerCH = 2;
}
return MaxDimmPerCH;
}
/* -----------------------------------------------------------------------------*/
/**
*
* This is the default return function of the ARDK block. The function always
* returns AGESA_UNSUPPORTED
*
* @param[in,out] *MemData Pointer to MEM_DATA_STRUCTURE
* @param[in] SocketID Socket number
* @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
*
* @return AGESA_UNSUPPORTED AGESA status indicating that default is unsupported
*
*/
AGESA_STATUS
MemRecNGetPsCfgDef (
IN OUT MEM_DATA_STRUCT *MemData,
IN UINT8 SocketID,
IN OUT CH_DEF_STRUCT *CurrentChannel
)
{
return AGESA_UNSUPPORTED;
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function returns the rank type map of a channel.
*
* @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
*
* @return UINT16 - The map of rank type.
*
*/
UINT16
MemRecNGetPsRankType (
IN CH_DEF_STRUCT *CurrentChannel
)
{
UINT8 i;
UINT16 DIMMRankType;
DIMMRankType = 0;
for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i++) {
if ((CurrentChannel->DimmQrPresent & (UINT8) 1 << i) != 0) {
if (i < 2) {
DIMMRankType |= (UINT16) 4 << (i << 2);
}
} else if ((CurrentChannel->DimmDrPresent & (UINT8) 1 << i) != 0) {
DIMMRankType |= (UINT16) 2 << (i << 2);
} else if ((CurrentChannel->DimmSRPresent & (UINT8) 1 << i) != 0) {
DIMMRankType |= (UINT16) 1 << (i << 2);
}
}
return DIMMRankType;
}
UINT32
MemRecNcmnGetSetTrainDlyClientNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN UINT8 IsSet,
IN TRN_DLY_TYPE TrnDly,
IN DRBN DrbnVar,
IN UINT16 Field
)
{
UINT16 Index;
UINT16 Offset;
UINT32 Value;
UINT32 Address;
UINT8 Dimm;
UINT8 Byte;
Dimm = DRBN_DIMM (DrbnVar);
Byte = DRBN_BYTE (DrbnVar);
ASSERT (Dimm < 2);
ASSERT (Byte <= ECC_DLY);
if ((Byte > 7)) {
// LN and ON do not support ECC delay, so:
if (IsSet) {
// On write, ignore
return 0;
} else {
// On read, redirect to byte 0 to correct fence averaging
Byte = 0;
}
}
switch (TrnDly) {
case AccessRcvEnDly:
Index = 0x10;
break;
case AccessWrDqsDly:
Index = 0x30;
break;
case AccessWrDatDly:
Index = 0x01;
break;
case AccessRdDqsDly:
Index = 0x05;
break;
case AccessPhRecDly:
Index = 0x50;
break;
default:
Index = 0;
IDS_ERROR_TRAP;
}
switch (TrnDly) {
case AccessRcvEnDly:
case AccessWrDqsDly:
Index += (Dimm * 3);
if (Byte & 0x04) {
// if byte 4,5,6,7
Index += 0x10;
}
if (Byte & 0x02) {
// if byte 2,3,6,7
Index++;
}
Offset = 16 * (Byte % 2);
break;
case AccessRdDqsDly:
case AccessWrDatDly:
Index += (Dimm * 0x100);
// break is not being used here because AccessRdDqsDly and AccessWrDatDly also need
// to run AccessPhRecDly sequence.
case AccessPhRecDly:
Index += (Byte / 4);
Offset = 8 * (Byte % 4);
break;
default:
Offset = 0;
IDS_ERROR_TRAP;
}
Address = Index;
MemRecNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
Value = MemRecNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
if (IsSet) {
if (TrnDly == AccessPhRecDly) {
Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
}
Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
MemRecNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
Address |= DCT_ACCESS_WRITE;
MemRecNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
if (TrnDly == AccessPhRecDly) {
NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
}
// Gross WrDatDly and WrDqsDly cannot be larger than 4
ASSERT (((TrnDly == AccessWrDatDly) || (TrnDly == AccessWrDqsDly)) ? (NBPtr->IsSupported[WLNegativeDelay] || (Field < 0xA0)) : TRUE);
} else {
Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF);
}
return Value;
}