aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/amd/agesa/Proc/Mem/Tech/DDR3/mtspd3.c
diff options
context:
space:
mode:
authorFrank Vibrans <frank.vibrans@amd.com>2011-02-14 18:30:54 +0000
committerMarc Jones <marc.jones@amd.com>2011-02-14 18:30:54 +0000
commit2b4c831b4d16b55a7abdea20bce82cccd168232c (patch)
tree95a35c737d16119f1dfa9c1c9d7700710d8a04f7 /src/vendorcode/amd/agesa/Proc/Mem/Tech/DDR3/mtspd3.c
parent74ad66cdc143e04f976ba21e538e02b20362d7e6 (diff)
Add AMD Agesa and AMD CIMx SB800 code. Patch 1 of 8.
This code currently generates many warnings that are functionally benign. These are being addressed, but the wheels of bureaucracy turn slowly. This drop supports AMD cpu families 10h and 14h. Only Family 14h is used as an example in this set of patches. Other cpu families are supported by the infrastructure, but their specific support is not included herein. This patch is functionally independent of the other patches in this set. Signed-off-by: Frank Vibrans <frank.vibrans@amd.com> Acked-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Acked-by: Marc Jones <marcj303@gmail.com> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@6344 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/vendorcode/amd/agesa/Proc/Mem/Tech/DDR3/mtspd3.c')
-rw-r--r--src/vendorcode/amd/agesa/Proc/Mem/Tech/DDR3/mtspd3.c1153
1 files changed, 1153 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/Proc/Mem/Tech/DDR3/mtspd3.c b/src/vendorcode/amd/agesa/Proc/Mem/Tech/DDR3/mtspd3.c
new file mode 100644
index 0000000000..c2f93724f8
--- /dev/null
+++ b/src/vendorcode/amd/agesa/Proc/Mem/Tech/DDR3/mtspd3.c
@@ -0,0 +1,1153 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mtspd3.c
+ *
+ * Technology SPD supporting functions for DDR3
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/Tech/DDR3)
+ * @e \$Revision: 36054 $ @e \$Date: 2010-08-10 05:26:12 +0800 (Tue, 10 Aug 2010) $
+ *
+ **/
+/*
+ *****************************************************************************
+ *
+ * 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 "AdvancedApi.h"
+#include "Ids.h"
+#include "mport.h"
+#include "mm.h"
+#include "mn.h"
+#include "mt.h"
+#include "mt3.h"
+#include "mu.h"
+#include "mtspd3.h"
+#include "mftds.h"
+#include "GeneralServices.h"
+#include "Filecode.h"
+CODE_GROUP (G1_PEICC)
+RDATA_GROUP (G1_PEICC)
+
+#define FILECODE PROC_MEM_TECH_DDR3_MTSPD3_FILECODE
+
+/*----------------------------------------------------------------------------
+ * DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * TYPEDEFS AND STRUCTURES
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+BOOLEAN
+STATIC
+MemTCRCCheck3 (
+ IN OUT UINT8 *SPDPtr
+ );
+
+UINT8
+STATIC
+MemTSPDGetTCL3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ );
+
+BOOLEAN
+STATIC
+MemTCheckBankAddr3 (
+ IN UINT8 Encode,
+ OUT UINT8 *Index
+ );
+
+/*----------------------------------------------------------------------------
+ * EXPORTED FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+extern BUILD_OPT_CFG UserOptions;
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function sets the DRAM mode
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - indicates that the DRAM mode is set to DDR3
+ */
+
+BOOLEAN
+MemTSetDramMode3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFLegacyBiosMode, 0);
+ TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFDdr3Mode, 1);
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function determines if DIMMs are present. It checks checksum and interrogates the SPDs
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - indicates that a FATAL error has not occurred
+ * @return FALSE - indicates that a FATAL error has occurred
+ */
+
+BOOLEAN
+MemTDIMMPresence3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ UINT8 Dct;
+ UINT8 Channel;
+ UINT8 i;
+ MEM_PARAMETER_STRUCT *RefPtr;
+ UINT8 *SpdBufferPtr;
+ DIE_STRUCT *MCTPtr;
+ DCT_STRUCT *DCTPtr;
+ CH_DEF_STRUCT *ChannelPtr;
+ MEM_NB_BLOCK *NBPtr;
+ BOOLEAN SPDCtrl;
+ UINT8 Devwidth;
+ UINT8 MaxDimms;
+ UINT8 Value8;
+ UINT16 DimmMask;
+
+ NBPtr = TechPtr->NBPtr;
+ RefPtr = NBPtr->RefPtr;
+ MCTPtr = NBPtr->MCTPtr;
+
+ SPDCtrl = UserOptions.CfgIgnoreSpdChecksum;
+ for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
+ NBPtr->SwitchDCT (NBPtr, Dct);
+ DCTPtr = NBPtr->DCTPtr;
+ for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) {
+ NBPtr->SwitchChannel (NBPtr, Channel);
+ ChannelPtr = NBPtr->ChannelPtr;
+ ChannelPtr->DimmQrPresent = 0;
+ //
+ // Get the maximum number of DIMMs
+ //
+ MaxDimms = MAX_DIMMS_PER_CHANNEL;
+ for (i = 0; i < MaxDimms; i++) {
+ // Bitmask representing dimm #i.
+ DimmMask = (UINT16)1 << i;
+ //
+ if (MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, i)) {
+ MCTPtr->DimmPresent |= DimmMask;
+ //
+ // Check for valid checksum value
+ //
+ AGESA_TESTPOINT (TpProcMemSPDChecking, &(NBPtr->MemPtr->StdHeader));
+ if (SpdBufferPtr[SPD_TYPE] == JED_DDR3SDRAM) {
+ ChannelPtr->ChDimmValid |= DimmMask;
+ MCTPtr->DimmValid |= DimmMask;
+ } else {
+ // Current socket is set up to only support DDR3 dimms.
+ IDS_ERROR_TRAP;
+ }
+ if (!MemTCRCCheck3 (SpdBufferPtr) && !SPDCtrl) {
+ //
+ // NV_SPDCHK_RESTRT is set to 0,
+ // cannot ignore faulty SPD checksum
+ //
+ // Indicate checksum error
+ ChannelPtr->DimmSpdCse |= DimmMask;
+ PutEventLog (AGESA_ERROR, MEM_ERROR_CHECKSUM_NV_SPDCHK_RESTRT_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ SetMemError (AGESA_ERROR, MCTPtr);
+ }
+ //
+ // Check module type information.
+ //
+ if (SpdBufferPtr[SPD_DIMM_TYPE] == JED_LRDIMM) {
+ //
+ // LRDIMMS
+ //
+ ChannelPtr->LrDimmPresent |= DimmMask;
+ MCTPtr->LrDimmPresent |= DimmMask;
+ if (!UserOptions.CfgMemoryLRDimmCapable) {
+ PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_LRDIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ IDS_ERROR_TRAP;
+ }
+ }
+ if (SpdBufferPtr[SPD_DIMM_TYPE] == JED_RDIMM || SpdBufferPtr[SPD_DIMM_TYPE] == JED_MINIRDIMM) {
+ //
+ // RDIMMS
+ //
+ ChannelPtr->RegDimmPresent |= DimmMask;
+ MCTPtr->RegDimmPresent |= DimmMask;
+ if (!UserOptions.CfgMemoryRDimmCapable) {
+ PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_RDIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ IDS_ERROR_TRAP;
+ }
+ }
+ if ((SpdBufferPtr[SPD_DIMM_TYPE] == JED_UDIMM) && !UserOptions.CfgMemoryUDimmCapable) {
+ PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_UDIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ IDS_ERROR_TRAP;
+ }
+ if (SpdBufferPtr[SPD_DIMM_TYPE] == JED_SODIMM) {
+ ChannelPtr->SODimmPresent |= DimmMask;
+ if (!UserOptions.CfgMemorySODimmCapable) {
+ PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_SODIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ IDS_ERROR_TRAP;
+ }
+ }
+ //
+ // Check error correction type
+ //
+ if ((SpdBufferPtr[SPD_ECCBITS] & JED_ECC) != 0) {
+ MCTPtr->DimmEccPresent |= DimmMask; // Dimm has ECC
+ }
+ //
+ // Get the Dimm width data
+ //
+ Devwidth = SpdBufferPtr[SPD_DEV_WIDTH] & 0x7;
+ switch (Devwidth) {
+ case 0:
+ ChannelPtr->Dimmx4Present |= DimmMask;
+ Devwidth = 4;
+ break;
+ case 1:
+ ChannelPtr->Dimmx8Present |= DimmMask;
+ Devwidth = 8;
+ break;
+ case 2:
+ ChannelPtr->Dimmx16Present |= DimmMask;
+ Devwidth = 16;
+ break;
+ default:
+ IDS_ERROR_TRAP;
+ }
+ //
+ // Check for 'analysis probe installed'
+ // if (SpdBufferPtr[SPD_ATTRIB] & JED_PROBE_MSK)
+ //
+ // Determine the geometry of the DIMM module
+ // if (SpdBufferPtr[SPD_DM_BANKS] & SP_DPL_BIT)
+ //
+ // specify the number of ranks
+ //
+ Value8 = ((SpdBufferPtr[SPD_RANKS] >> 3) & 0x07) + 1;
+ if (Value8 == 5) {
+ // Octal Rank
+ Value8 = 8;
+ }
+ //
+ // For LRDIMMS we will assume that if there are at least 4 Physical ranks, then it Could be used
+ // as a QR RDIMM with a rank Mux of x1 and therefore all four CS will be used. So an 8R LRDIMM will
+ // be marked as a QR even if Rank multiplication allows it to use only 2 logical ranks.
+ //
+ if (ChannelPtr->LrDimmPresent |= DimmMask) {
+ //
+ // LRDIMM Physical Ranks
+ //
+ ChannelPtr->LrdimmPhysicalRanks[i] = Value8;
+ }
+ if (Value8 > 2) {
+ if (!UserOptions.CfgMemoryQuadRankCapable) {
+ PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_QRDIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ }
+ //
+ // Mark this Dimm as Quad Rank
+ //
+ ChannelPtr->DimmQrPresent |= DimmMask;
+ Value8 = 2;
+ } else if (Value8 == 2) {
+ ChannelPtr->DimmDrPresent |= DimmMask; // Dual rank dimms
+ } else {
+ ChannelPtr->DimmSRPresent |= DimmMask; // Single rank dimms
+ }
+ //
+ // Calculate bus loading per Channel
+ if (Devwidth == 16) {
+ Devwidth = 4;
+ } else if (Devwidth == 4) {
+ Devwidth = 16;
+ }
+ //
+ // Double Addr bus load value for dual rank DIMMs (Unless LRDIMM)
+ //
+ if ( ((ChannelPtr->LrDimmPresent |= DimmMask) == 0) && (Value8 == 2) ) {
+ Devwidth = Devwidth << 1;
+ }
+ //
+ ChannelPtr->Ranks = ChannelPtr->Ranks + Value8;
+ ChannelPtr->Loads = ChannelPtr->Loads + Devwidth;
+ if ((i < 2) || ((ChannelPtr->DimmQrPresent & DimmMask) == 0)) {
+ ChannelPtr->Dimms++;
+ }
+ //
+ // Check address mirror support for Unbuffered Dimms or LRDimms
+ //
+ if ((ChannelPtr->RegDimmPresent & DimmMask) == 0) {
+ if ((SpdBufferPtr[SPD_ADDRMAP] & 1) != 0) {
+ ChannelPtr->DimmMirrorPresent |= DimmMask;
+ }
+ }
+ //
+ // Get byte62: Reference Raw Card information
+ //
+ ChannelPtr->RefRawCard[i] = SpdBufferPtr[SPD_RAWCARD] & 0x1F;
+ //
+ // Get control word values for RC3, RC4 and RC5
+ //
+ ChannelPtr->CtrlWrd03[i] = SpdBufferPtr[SPD_CTLWRD03] >> 4;
+ ChannelPtr->CtrlWrd04[i] = SpdBufferPtr[SPD_CTLWRD04] & 0x0F;
+ ChannelPtr->CtrlWrd05[i] = SpdBufferPtr[SPD_CTLWRD05] >> 4;
+ //
+ // Temporarily store info. of SPD byte 63 into CtrlWrd02(s),
+ // and they will be used late to calculate real RC2 and RC8 value
+ //
+ ChannelPtr->CtrlWrd02[i] = SpdBufferPtr[SPD_ADDRMAP] & 0x03;
+ //
+ // Copy the number of registers to the Ps Block to persist across frequency changes
+ //
+ NBPtr->PsPtr->NumOfReg[i] = SpdBufferPtr[SPD_ADDRMAP] & 0x03;
+ //
+ } // if DIMM present
+ } // Dimm loop
+
+ if (Channel == 0) {
+ DCTPtr->Timings.DctDimmValid = ChannelPtr->ChDimmValid;
+ DCTPtr->Timings.DimmMirrorPresent = ChannelPtr->DimmMirrorPresent;
+ DCTPtr->Timings.DimmSpdCse = ChannelPtr->DimmSpdCse;
+ DCTPtr->Timings.DimmQrPresent = ChannelPtr->DimmQrPresent;
+ DCTPtr->Timings.DimmDrPresent = ChannelPtr->DimmDrPresent;
+ DCTPtr->Timings.DimmSRPresent = ChannelPtr->DimmSRPresent;
+ DCTPtr->Timings.Dimmx4Present = ChannelPtr->Dimmx4Present;
+ DCTPtr->Timings.Dimmx8Present = ChannelPtr->Dimmx8Present;
+ DCTPtr->Timings.Dimmx16Present = ChannelPtr->Dimmx16Present;
+ }
+ if ((Channel != 1) || (Dct != 1)) {
+ MCTPtr->DimmPresent <<= 8;
+ MCTPtr->DimmValid <<= 8;
+ MCTPtr->RegDimmPresent <<= 8;
+ MCTPtr->LrDimmPresent <<= 8;
+ MCTPtr->DimmEccPresent <<= 8;
+ MCTPtr->DimmParPresent <<= 8;
+ }
+ } // Channel loop
+ } // DCT loop
+
+ // If we have DIMMs, some further general characteristics checking
+ if (MCTPtr->DimmValid != 0) {
+ // If there are registered dimms, all the dimms must be registered
+ if (MCTPtr->RegDimmPresent == MCTPtr->DimmValid) {
+ // All dimms registered
+ MCTPtr->Status[SbRegistered] = TRUE;
+ MCTPtr->Status[SbParDimms] = TRUE; // All DDR3 RDIMMs are parity capable
+ TechPtr->SetDqsEccTmgs = MemTSetDQSEccTmgsRDdr3; // Change the function pointer for DQS ECC timing
+ } else if (MCTPtr->RegDimmPresent != 0) {
+ // We have an illegal DIMM mismatch
+ PutEventLog (AGESA_FATAL, MEM_ERROR_MODULE_TYPE_MISMATCH_DIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ SetMemError (AGESA_FATAL, MCTPtr);
+ }
+ // If there are LrDimms, all the dimms must be LrDimms
+ if (MCTPtr->LrDimmPresent == MCTPtr->DimmValid) {
+ // All dimms LRDIMMs
+ MCTPtr->Status[SbLrdimms] = TRUE;
+ } else if (MCTPtr->LrDimmPresent != 0) {
+ // We have an illegal DIMM mismatch
+ PutEventLog (AGESA_FATAL, MEM_ERROR_MODULE_TYPE_MISMATCH_DIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ SetMemError (AGESA_FATAL, MCTPtr);
+ }
+
+ // check the ECC capability of the DIMMs
+ if (MCTPtr->DimmEccPresent == MCTPtr->DimmValid) {
+ MCTPtr->Status[SbEccDimms] = TRUE; // All dimms ECC capable
+ }
+ } else {
+ }
+
+ NBPtr->SwitchDCT (NBPtr, 0);
+ NBPtr->SwitchChannel (NBPtr, 0);
+ return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function finds the maximum frequency that each channel is capable to run at.
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - indicates that a FATAL error has not occurred
+ * @return FALSE - indicates that a FATAL error has occurred
+ */
+
+BOOLEAN
+MemTSPDGetTargetSpeed3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ UINT8 *SpdBufferPtr;
+ UINT8 Dimm;
+ UINT8 Dct;
+ UINT8 Channel;
+ INT32 MTB_ps;
+ INT32 FTB_ps;
+ INT32 TCKmin_ps;
+ INT32 Value32;
+ MEM_NB_BLOCK *NBPtr;
+ DCT_STRUCT *DCTPtr;
+ CH_DEF_STRUCT *ChannelPtr;
+
+ NBPtr = TechPtr->NBPtr;
+ for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
+ NBPtr->SwitchDCT (NBPtr, Dct);
+ DCTPtr = NBPtr->DCTPtr;
+ TCKmin_ps = 0;
+ for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) {
+ NBPtr->SwitchChannel (NBPtr, Channel);
+ ChannelPtr = NBPtr->ChannelPtr;
+ for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
+ if ((ChannelPtr->ChDimmValid & ((UINT8)1 << Dimm)) != 0) {
+ MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, Dimm);
+
+ // Determine tCKmin(all) which is the largest tCKmin
+ // value for all modules on the memory Channel (SPD byte 12).
+ //
+ MTB_ps = ((INT32) SpdBufferPtr[SPD_DIVIDENT] * 1000) / SpdBufferPtr[SPD_DIVISOR];
+ FTB_ps = (SpdBufferPtr[SPD_FTB] >> 4) / (SpdBufferPtr[SPD_FTB] & 0xF);
+ Value32 = (MTB_ps * SpdBufferPtr[SPD_TCK]) + (FTB_ps * (INT8) SpdBufferPtr[SPD_TCK_FTB]) ;
+ if (TCKmin_ps < Value32) {
+ TCKmin_ps = Value32;
+ }
+ }
+ }
+ }
+ if (TCKmin_ps <= 1071) {
+ DCTPtr->Timings.TargetSpeed = DDR1866_FREQUENCY;
+ } else if (TCKmin_ps <= 1250) {
+ DCTPtr->Timings.TargetSpeed = DDR1600_FREQUENCY;
+ } else if (TCKmin_ps <= 1500) {
+ DCTPtr->Timings.TargetSpeed = DDR1333_FREQUENCY;
+ } else if (TCKmin_ps <= 1875) {
+ DCTPtr->Timings.TargetSpeed = DDR1066_FREQUENCY;
+ } else if (TCKmin_ps <= 2500) {
+ DCTPtr->Timings.TargetSpeed = DDR800_FREQUENCY;
+ } else {
+ DCTPtr->Timings.TargetSpeed = DDR667_FREQUENCY;
+ }
+ }
+
+ // Ensure the target speed can be applied to all channels of the current node
+ NBPtr->SyncTargetSpeed (NBPtr);
+
+ // Set the start-up frequency
+ for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
+ NBPtr->SwitchDCT (NBPtr, Dct);
+ NBPtr->DCTPtr->Timings.Speed = TechPtr->NBPtr->StartupSpeed;
+ }
+ return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function check the symmetry of DIMM pairs (DIMM on Channel A matching with
+ * DIMM on Channel B), the overall DIMM population, and determine the width mode:
+ * 64-bit, 64-bit muxed, 128-bit.
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - indicates that a FATAL error has not occurred
+ * @return FALSE - indicates that a FATAL error has occurred
+ */
+
+BOOLEAN
+MemTSPDCalcWidth3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ UINT8 *SpdBufferAPtr;
+ UINT8 *SpdBufferBPtr;
+ MEM_NB_BLOCK *NBPtr;
+ DIE_STRUCT *MCTPtr;
+ DCT_STRUCT *DCTPtr;
+ UINT8 i;
+ UINT16 DimmMask;
+ UINT8 UngangMode;
+
+ NBPtr = TechPtr->NBPtr;
+ MCTPtr = NBPtr->MCTPtr;
+ DCTPtr = NBPtr->DCTPtr;
+ UngangMode = UserOptions.CfgMemoryModeUnganged;
+ // Does not support ganged mode for DDR3 dimms
+ ASSERT (UngangMode);
+ IDS_OPTION_HOOK (IDS_GANGING_MODE, &UngangMode, &(NBPtr->MemPtr->StdHeader));
+
+ // Check symmetry of channel A and channel B dimms for 128-bit mode
+ // capability.
+ //
+ AGESA_TESTPOINT (TpProcMemModeChecking, &(NBPtr->MemPtr->StdHeader));
+ i = 0;
+ if (!UngangMode) {
+ if (MCTPtr->DctData[0].Timings.DctDimmValid == MCTPtr->DctData[1].Timings.DctDimmValid) {
+ for (; i < MAX_DIMMS_PER_CHANNEL; i++) {
+ DimmMask = (UINT16)1 << i;
+ if ((DCTPtr->Timings.DctDimmValid & DimmMask) != 0) {
+ NBPtr->SwitchDCT (NBPtr, 0);
+ MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferAPtr, i);
+ NBPtr->SwitchDCT (NBPtr, 1);
+ MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferBPtr, i);
+ // compare rows and columns
+ if ((SpdBufferAPtr[SPD_ROW_SZ]&0x3F) != (SpdBufferBPtr[SPD_ROW_SZ]&0x3F)) {
+ break;
+ }
+ if ((SpdBufferAPtr[SPD_DENSITY]&0x0F) != (SpdBufferBPtr[SPD_DENSITY]&0x0F)) {
+ break;
+ }
+ // compare ranks and devwidth
+ if ((SpdBufferAPtr[SPD_DEV_WIDTH]&0x7F) != (SpdBufferBPtr[SPD_DEV_WIDTH]&0x7F)) {
+ break;
+ }
+ }
+ }
+ }
+ if (i < MAX_DIMMS_PER_CHANNEL) {
+ PutEventLog (AGESA_ALERT, MEM_ALERT_ORG_MISMATCH_DIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ SetMemError (AGESA_ALERT, MCTPtr);
+ } else {
+ NBPtr->Ganged = TRUE;
+ MCTPtr->GangedMode = TRUE;
+ MCTPtr->Status[Sb128bitmode] = TRUE;
+ NBPtr->SetBitField (NBPtr, BFDctGangEn, 1);
+ }
+ }
+
+ return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Initialize DCT Timing registers as per DIMM SPD.
+ * For primary timing (T, CL) use best case T value.
+ * For secondary timing params., use most aggressive settings
+ * of slowest DIMM.
+ *
+ * Note:
+ * There are three components to determining "maximum frequency": SPD component,
+ * Bus load component, and "Preset" max frequency component.
+ * The SPD component is a function of the min cycle time specified by each DIMM,
+ * and the interaction of cycle times from all DIMMs in conjunction with CAS
+ * latency. The SPD component only applies when user timing mode is 'Auto'.
+ *
+ * The Bus load component is a limiting factor determined by electrical
+ * characteristics on the bus as a result of varying number of device loads. The
+ * Bus load component is specific to each platform but may also be a function of
+ * other factors. The bus load component only applies when user timing mode is
+ * ' Auto'.
+ *
+ * The Preset component is subdivided into three items and is the minimum of
+ * the set: Silicon revision, user limit setting when user timing mode is 'Auto' and
+ * memclock mode is 'Limit', OEM build specification of the maximum frequency.
+ * The Preset component only applies when user timing mode is 'Auto'.
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - indicates that a FATAL error has not occurred
+ * @return FALSE - indicates that a FATAL error has occurred
+ */
+
+BOOLEAN
+MemTAutoCycTiming3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ CONST UINT8 SpdIndexes[] = {
+ SPD_TRCD,
+ SPD_TRP,
+ SPD_TRTP,
+ SPD_TRAS,
+ SPD_TRC,
+ SPD_TWR,
+ SPD_TRRD,
+ SPD_TWTR,
+ SPD_TFAW
+ };
+
+ CONST UINT8 SpdFTBIndexes[] = {
+ SPD_TRCD_FTB,
+ SPD_TRP_FTB,
+ 0,
+ 0,
+ SPD_TRC_FTB,
+ 0,
+ 0,
+ 0,
+ 0
+ };
+
+ UINT8 *SpdBufferPtr;
+ INT32 MiniMaxTmg[GET_SIZE_OF (SpdIndexes)];
+ UINT8 MiniMaxTrfc[4];
+
+ DIE_STRUCT *MCTPtr;
+ DCT_STRUCT *DCTPtr;
+ MEM_NB_BLOCK *NBPtr;
+ UINT16 DimmMask;
+ INT32 Value32;
+ INT32 MTB_ps;
+ INT32 FTB_ps;
+ INT32 TCK_ps;
+ UINT8 i;
+ UINT8 j;
+ UINT8 Value8;
+ UINT8 *StatTmgPtr;
+ UINT16 *StatDimmTmgPtr;
+
+ NBPtr = TechPtr->NBPtr;
+ MCTPtr = NBPtr->MCTPtr;
+ DCTPtr = NBPtr->DCTPtr;
+ // initialize mini-max arrays
+ for (j = 0; j < GET_SIZE_OF (MiniMaxTmg); j++) {
+ MiniMaxTmg[j] = 0;
+ }
+ for (j = 0; j < GET_SIZE_OF (MiniMaxTrfc); j++) {
+ MiniMaxTrfc[j] = 0;
+ }
+
+ // ======================================================================
+ // Get primary timing (CAS Latency and Cycle Time)
+ // ======================================================================
+ // Get OEM specific load variant max
+ //
+
+ //======================================================================
+ // Gather all DIMM mini-max values for cycle timing data
+ //======================================================================
+ //
+ DimmMask = 1;
+ for (i = 0; i < (MAX_CS_PER_CHANNEL / 2); i++) {
+ if ((DCTPtr->Timings.DctDimmValid & DimmMask) != 0) {
+ MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, i);
+ MTB_ps = ((INT32) SpdBufferPtr[SPD_DIVIDENT] * 1000) / SpdBufferPtr[SPD_DIVISOR];
+ FTB_ps = (SpdBufferPtr[SPD_FTB] >> 4) / (SpdBufferPtr[SPD_FTB] & 0xF);
+
+ for (j = 0; j < GET_SIZE_OF (SpdIndexes); j++) {
+ Value32 = (UINT16)SpdBufferPtr[SpdIndexes[j]];
+ if (SpdIndexes[j] == SPD_TRC) {
+ Value32 |= ((UINT16)SpdBufferPtr[SPD_UPPER_TRC] & 0xF0) << 4;
+ } else if (SpdIndexes[j] == SPD_TRAS) {
+ Value32 |= ((UINT16)SpdBufferPtr[SPD_UPPER_TRAS] & 0x0F) << 8;
+ } else if (SpdIndexes[j] == SPD_TFAW) {
+ Value32 |= ((UINT16)SpdBufferPtr[SPD_UPPER_TFAW] & 0x0F) << 8;
+ }
+
+ Value32 *= MTB_ps;
+ if (SpdFTBIndexes[j] != 0) {
+ Value32 += (FTB_ps * (INT8) SpdBufferPtr[SpdFTBIndexes[j]]) ;
+ }
+ if (MiniMaxTmg[j] < Value32) {
+ MiniMaxTmg[j] = Value32;
+ }
+ }
+
+ // get Trfc0 - Trfc3 values
+ Value8 = SpdBufferPtr[SPD_DENSITY] & 0x0F;
+ if (MiniMaxTrfc[i] < Value8) {
+ MiniMaxTrfc[i] = Value8;
+ }
+ }
+ DimmMask <<= 1;
+ }
+
+ // ======================================================================
+ // Convert DRAM CycleTiming values and store into DCT structure
+ // ======================================================================
+ //
+ TCK_ps = 1000500 / DCTPtr->Timings.Speed;
+
+ StatDimmTmgPtr = &DCTPtr->Timings.DIMMTrcd;
+ StatTmgPtr = &DCTPtr->Timings.Trcd;
+ for (j = 0; j < GET_SIZE_OF (SpdIndexes); j++) {
+ Value32 = MiniMaxTmg[j];
+
+ MiniMaxTmg[j] = (MiniMaxTmg[j] + TCK_ps - 1) / TCK_ps;
+
+ StatDimmTmgPtr[j] = (UINT16) (Value32 / (1000 / 40));
+ StatTmgPtr[j] = (UINT8) MiniMaxTmg[j];
+ }
+ DCTPtr->Timings.Trfc0 = MiniMaxTrfc[0];
+ DCTPtr->Timings.Trfc1 = MiniMaxTrfc[1];
+ DCTPtr->Timings.Trfc2 = MiniMaxTrfc[2];
+ DCTPtr->Timings.Trfc3 = MiniMaxTrfc[3];
+
+ DCTPtr->Timings.CasL = MemTSPDGetTCL3 (TechPtr);
+
+ //======================================================================
+ // Program DRAM Timing values
+ //======================================================================
+ //
+ NBPtr->ProgramCycTimings (NBPtr);
+
+ MemFInitTableDrive (NBPtr, MTAfterAutoCycTiming);
+
+ return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function sets the bank addressing, program Mask values and build a chip-select population map.
+ * This routine programs PCI 0:24N:2x80 config register.
+ * This routine programs PCI 0:24N:2x60,64,68,6C config registers (CS Mask 0-3)
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - indicates that a FATAL error has not occurred
+ * @return FALSE - indicates that a FATAL error has occurred
+ */
+
+BOOLEAN
+MemTSPDSetBanks3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ UINT8 *SpdBufferPtr;
+ UINT8 i;
+ UINT8 ChipSel;
+ UINT8 DimmID;
+ UINT8 Value8;
+ UINT8 Rows;
+ UINT8 Cols;
+ UINT8 Ranks;
+ UINT8 Banks;
+ UINT32 BankAddrReg;
+ UINT32 CsMask;
+ UINT16 CSSpdCSE;
+ UINT16 CSExclude;
+ UINT16 DimmQRDR;
+ DIE_STRUCT *MCTPtr;
+ DCT_STRUCT *DCTPtr;
+ MEM_NB_BLOCK *NBPtr;
+
+ NBPtr = TechPtr->NBPtr;
+ MCTPtr = NBPtr->MCTPtr;
+ DCTPtr = NBPtr->DCTPtr;
+ BankAddrReg = 0;
+ CSSpdCSE = 0;
+ CSExclude = 0;
+
+ for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel += 2) {
+ DimmID = ChipSel >> 1;
+
+ DimmQRDR = (DCTPtr->Timings.DimmQrPresent) | (DCTPtr->Timings.DimmDrPresent);
+ if ((DCTPtr->Timings.DimmSpdCse & ((UINT16) 1 << DimmID)) != 0) {
+ CSSpdCSE |= (UINT16) ((DimmQRDR & (UINT16) 1 << DimmID) ? 3 : 1) << ChipSel;
+ }
+ if ((DCTPtr->Timings.DimmExclude & ((UINT16) 1 << DimmID)) != 0) {
+ CSExclude |= (UINT16) ((DimmQRDR & (UINT16) 1 << DimmID) ? 3: 1) << ChipSel;
+ }
+
+ if ((DCTPtr->Timings.DctDimmValid & ((UINT16)1 << DimmID)) != 0) {
+ MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, DimmID);
+
+ // Get the basic data
+ Rows = (SpdBufferPtr[SPD_ROW_SZ] >> 3) & 0x7;
+ Cols = SpdBufferPtr[SPD_COL_SZ] & 0x7;
+ Banks = (SpdBufferPtr[SPD_L_BANKS] >> 4) & 0x7;
+ Ranks = ((SpdBufferPtr[SPD_RANKS] >> 3) & 0x07) + 1;
+ if (Ranks == 5) {
+ Ranks = 8;
+ }
+
+ //
+ // Configure the bank encoding
+ // Use a 6-bit key into a lookup table.
+ // Key (index) = RRRBCC, where CC is the number of Columns minus 9,
+ // RRR is the number of Rows minus 12, and B is the number of banks
+ // minus 3.
+ //
+ Value8 = Cols;
+ Value8 |= (Banks == 1) ? 4 : 0;
+ Value8 |= Rows << 3;
+
+ if (MemTCheckBankAddr3 (Value8, &i)) {
+ BankAddrReg |= ((UINT32)i << (ChipSel << 1));
+
+ // Mask value=(2pow(rows+cols+banks+3)-1)>>8,
+ // or 2pow(rows+cols+banks-5)-1
+ //
+ Value8 = (Rows + 12) + (Cols + 9) + (Banks + 3) + 3 - 8;
+ if (MCTPtr->Status[Sb128bitmode]) {
+ Value8++;
+ }
+
+ DCTPtr->Timings.CsPresent |= (UINT16)1 << ChipSel;
+
+ if (Ranks >= 2) {
+ DCTPtr->Timings.CsPresent |= (UINT16)1 << (ChipSel + 1);
+ }
+ //
+ // Determine LRDIMM Rank Multiplication
+ //
+ if (TechPtr->TechnologySpecificHook[LrdimmRankMultiplication] (TechPtr, &DimmID)) {
+ //
+ // Increase the CS Size by the rank multiplication factor
+ //
+ Value8 += ((NBPtr->ChannelPtr->LrDimmRankMult[DimmID]) >> 1);
+ }
+
+ CsMask = ((UINT32)1 << Value8) - 1;
+ // Update the DRAM CS Mask for this chipselect
+ NBPtr->SetBitField (NBPtr, BFCSMask0Reg + (ChipSel >> 1), (CsMask & 0x1FF83FE0));
+ } else {
+ // Dimm is not supported, as no address mapping is found.
+ DCTPtr->Timings.CsPresent |= (UINT16)1 << ChipSel;
+ DCTPtr->Timings.CsTestFail |= (UINT16)1 << ChipSel;
+ if (Ranks >= 2) {
+ DCTPtr->Timings.CsPresent |= (UINT16)1 << (ChipSel + 1);
+ DCTPtr->Timings.CsTestFail |= (UINT16)1 << (ChipSel + 1);
+ }
+ PutEventLog (AGESA_ERROR, MEM_ERROR_NO_ADDRESS_MAPPING, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, DimmID, &NBPtr->MemPtr->StdHeader);
+ SetMemError (AGESA_ERROR, MCTPtr);
+ }
+ }
+ }
+ // For ranks that need to be excluded, the loading of this rank should be considered
+ // in timing, so need to set CsPresent before setting CsTestFail
+ if ((CSSpdCSE != 0) || (CSExclude != 0)) {
+ NBPtr->MemPtr->ErrorHandling (MCTPtr, NBPtr->Dct, (CSSpdCSE | CSExclude), &NBPtr->MemPtr->StdHeader);
+ }
+
+ // If there are no chip selects, we have an error situation.
+ if (DCTPtr->Timings.CsPresent == 0) {
+ PutEventLog (AGESA_ERROR, MEM_ERROR_NO_CHIPSELECT, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ SetMemError (AGESA_ERROR, MCTPtr);
+ }
+
+ NBPtr->SetBitField (NBPtr, BFDramBankAddrReg, BankAddrReg);
+
+ return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function returns the low bit that will be swapped to enable CS interleaving
+ *
+ * @param[in] BankEnc - AddrMap Bank encoding from F2x80
+ * @param[in] *LowBit - pointer to low bit
+ * @param[in] *HiBit - pointer hight bit
+ *
+ */
+
+VOID
+MemTGetCSIntLvAddr3 (
+ IN UINT8 BankEnc,
+ OUT UINT8 *LowBit,
+ OUT UINT8 *HiBit
+ )
+{
+ CONST UINT8 ArrCodesLo[] = {0, 8, 8, 0, 0, 8, 9, 8, 9, 9, 8, 9};
+ CONST UINT8 ArrCodesHi[] = {0, 20, 21, 0, 0, 22, 22, 23, 23, 24, 24, 25};
+ ASSERT (BankEnc < GET_SIZE_OF (ArrCodesLo));
+ ASSERT (BankEnc < GET_SIZE_OF (ArrCodesHi));
+ // return ArrCodes[BankEnc];
+ *LowBit = ArrCodesLo[BankEnc];
+ *HiBit = ArrCodesHi[BankEnc];
+}
+
+/*----------------------------------------------------------------------------
+ * LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function determines if the checksum is correct
+ *
+ * @param[in] *SPDPtr - Pointer to SPD data
+ *
+ * @return TRUE - CRC check passes
+ * @return FALSE - CRC check fails
+ */
+
+BOOLEAN
+STATIC
+MemTCRCCheck3 (
+ IN OUT UINT8 *SPDPtr
+ )
+{
+ UINT16 Crc;
+ INT16 i;
+ INT16 j;
+ INT16 Count;
+
+ if (SPDPtr[SPD_TYPE] == JED_DDR3SDRAM) {
+ Count = (SPDPtr[SPD_BYTE_USED] & 0x80) ? 117 : 126;
+ Crc = 0;
+ for (j = 0; j < Count; j++) {
+ Crc = Crc ^ ((UINT16)SPDPtr[j] << 8);
+ for (i = 0; i < 8; i++) {
+ if (Crc & 0x8000) {
+ Crc = (Crc << 1) ^ 0x1021;
+ } else {
+ Crc = (Crc << 1);
+ }
+ }
+ }
+ if (*(UINT16 *) (SPDPtr + 126) == Crc) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function returns the CAS latency of the current frequency (DCTPtr->Timings.Speed).
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return CAS Latency
+ */
+
+UINT8
+STATIC
+MemTSPDGetTCL3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ UINT8 *SpdBufferPtr;
+ UINT8 CLdesired;
+ UINT8 CLactual;
+ UINT8 Dimm;
+ UINT8 Channel;
+ UINT16 CASLat;
+ UINT16 Mask16;
+ INT32 MTB_ps;
+ INT32 FTB_ps;
+ INT32 TAAmin_ps;
+ INT32 TCKproposed_ps;
+ INT32 Value32;
+ BOOLEAN CltFail;
+ MEM_NB_BLOCK *NBPtr;
+ DCT_STRUCT *DCTPtr;
+ CH_DEF_STRUCT *ChannelPtr;
+
+ NBPtr = TechPtr->NBPtr;
+ DCTPtr = NBPtr->DCTPtr;
+
+ CASLat = 0xFFFF;
+ TAAmin_ps = 0;
+ CltFail = FALSE;
+
+ for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) {
+ NBPtr->SwitchChannel (NBPtr, Channel);
+ ChannelPtr = NBPtr->ChannelPtr;
+ for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
+ if ((ChannelPtr->ChDimmValid & ((UINT8)1 << Dimm)) != 0) {
+ MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, Dimm);
+
+ // Step 1: Determine the common set of supported CAS Latency
+ // values for all modules on the memory Channel using the CAS
+ // Latencies Supported in SPD bytes 14 and 15.
+ //
+ CASLat &= ((UINT16)SpdBufferPtr[SPD_CASHI] << 8) | SpdBufferPtr[SPD_CASLO];
+
+ // Step 2: Determine tAAmin(all) which is the largest tAAmin
+ // value for all modules on the memory Channel (SPD byte 16).
+ //
+ MTB_ps = ((INT32) SpdBufferPtr[SPD_DIVIDENT] * 1000) / SpdBufferPtr[SPD_DIVISOR];
+ FTB_ps = (SpdBufferPtr[SPD_FTB] >> 4) / (SpdBufferPtr[SPD_FTB] & 0xF);
+ Value32 = (MTB_ps * SpdBufferPtr[SPD_TAA]) + (FTB_ps * (INT8) SpdBufferPtr[SPD_TAA_FTB]) ;
+ if (TAAmin_ps < Value32) {
+ TAAmin_ps = Value32;
+ }
+
+ // Step 3: Determine tCKmin(all) which is the largest tCKmin
+ // value for all modules on the memory Channel (SPD byte 12).
+ // * This step has been done in SPDGetTargetSpeed
+ }
+ }
+ }
+
+ TCKproposed_ps = 1000500 / DCTPtr->Timings.Speed;
+
+ // Step 4: For a proposed tCK value (tCKproposed) between tCKmin(all) and tCKmax,
+ // determine the desired CAS Latency. If tCKproposed is not a standard JEDEC
+ // value (2.5, 1.875, 1.5, or 1.25 ns) then tCKproposed must be adjusted to the
+ // next lower standard tCK value for calculating CLdesired.
+ // CLdesired = ceiling ( tAAmin(all) / tCKproposed )
+ // where tAAmin is defined in Byte 16. The ceiling function requires that the
+ // quotient be rounded up always.
+ //
+ CLdesired = (UINT8) ((TAAmin_ps + TCKproposed_ps - 1) / TCKproposed_ps);
+
+ // Step 5: Choose an actual CAS Latency (CLactual) that is greater than or equal
+ // to CLdesired and is supported by all modules on the memory Channel as
+ // determined in step 1. If no such value exists, choose a higher tCKproposed
+ // value and repeat steps 4 and 5 until a solution is found.
+ //
+ CLactual = 4;
+ for (Mask16 = 1; Mask16 < 0x8000; Mask16 <<= 1) {
+ if (CASLat & Mask16) {
+ if (CLdesired <= CLactual) {
+ break;
+ }
+ }
+ CLactual++;
+ }
+ if (Mask16 == 0x8000) {
+ CltFail = TRUE;
+ }
+
+ // Step 6: Once the calculation of CLactual is completed, the BIOS must also
+ // verify that this CAS Latency value does not exceed tAAmax, which is 20 ns
+ // for all DDR3 speed grades, by multiplying CLactual times tCKproposed. If
+ // not, choose a lower CL value and repeat steps 5 and 6 until a solution is found.
+ //
+ if ((TCKproposed_ps * CLactual) > 20000) {
+ CltFail = TRUE;
+ }
+
+ if (!CltFail) {
+ DCTPtr->Timings.CasL = CLactual;
+ } else {
+ // Fail to find supported Tcl, use 6 clocks since it is required for all DDR3 speed bin.
+ DCTPtr->Timings.CasL = 6;
+ }
+
+ return DCTPtr->Timings.CasL;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function returns the encoded value of bank address.
+ *
+ * @param[in] Encode - RRRBCC, where CC is the number of Columns minus 9,
+ * RRR is the number of Rows minus 12, and B is the number of banks
+ * minus 3.
+ * @param[out] *Index - index in bank address table
+ * @return TRUE - encoded value is found.
+ * FALSE - encoded value is not found.
+ */
+
+BOOLEAN
+STATIC
+MemTCheckBankAddr3 (
+ IN UINT8 Encode,
+ OUT UINT8 *Index
+ )
+{
+ UINT8 i;
+ CONST UINT8 TabBankAddr[] = {
+ 0x3F, 0x01, 0x09, 0x3F, 0x3F, 0x11,
+ 0x0A, 0x19, 0x12, 0x1A, 0x21, 0x22
+ };
+
+ for (i = 0; i < GET_SIZE_OF (TabBankAddr); i++) {
+ if (Encode == TabBankAddr[i]) {
+ *Index = i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function returns a pointer to the SPD Buffer of a specific dimm on
+ * the current channel.
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ * @param[in,out] **SpdBuffer - Pointer to a pointer to a UINT8 Buffer
+ * @param[in] Dimm - Dimm number
+ *
+ *
+ * @return BOOLEAN - Value of DimmPresent
+ * TRUE = Dimm is present, pointer is valid
+ * FALSE = Dimm is not present, pointer has not been modified.
+ */
+
+BOOLEAN
+MemTGetDimmSpdBuffer3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN OUT UINT8 **SpdBuffer,
+ IN UINT8 Dimm
+ )
+{
+ CH_DEF_STRUCT *ChannelPtr;
+ SPD_DEF_STRUCT *SPDPtr;
+ BOOLEAN DimmPresent;
+
+ DimmPresent = FALSE;
+ ChannelPtr = TechPtr->NBPtr->ChannelPtr;
+ ASSERT (Dimm < (sizeof (ChannelPtr->DimmSpdPtr) / sizeof (ChannelPtr->DimmSpdPtr[0])))
+ SPDPtr = ChannelPtr->DimmSpdPtr[Dimm];
+
+
+ if (SPDPtr != NULL) {
+ DimmPresent = SPDPtr->DimmPresent;
+ if (DimmPresent) {
+ *SpdBuffer = SPDPtr->Data;
+ }
+ }
+ return DimmPresent;
+}
+