/* $NoKeywords:$ */ /** * @file * * mtspd2.c * * Technology SPD supporting functions for DDR2 * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: (Mem/Tech/DDR2) * @e \$Revision: 56279 $ @e \$Date: 2011-07-11 13:11:28 -0600 (Mon, 11 Jul 2011) $ * **/ /***************************************************************************** * * Copyright (C) 2012 Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Advanced Micro Devices, Inc. nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *************************************************************************** * */ /* *---------------------------------------------------------------------------- * MODULES USED * *---------------------------------------------------------------------------- */ #include "AGESA.h" #include "AdvancedApi.h" #include "amdlib.h" #include "Ids.h" #include "mport.h" #include "mm.h" #include "mn.h" #include "mt.h" #include "mu.h" #include "mt2.h" #include "mtspd2.h" #include "mftds.h" #include "GeneralServices.h" #include "Filecode.h" CODE_GROUP (G1_PEICC) RDATA_GROUP (G2_PEI) #define FILECODE PROC_MEM_TECH_DDR2_MTSPD2_FILECODE /*---------------------------------------------------------------------------- * DEFINITIONS AND MACROS * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * TYPEDEFS AND STRUCTURES * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * PROTOTYPES OF LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ UINT8 STATIC MemTSPDGetTCL2 ( IN OUT MEM_TECH_BLOCK *TechPtr ); BOOLEAN STATIC MemTSysCapability2 ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 k, IN UINT16 j ); BOOLEAN STATIC MemTDimmSupports2 ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 k, IN UINT8 j, IN UINT8 i ); UINT8 STATIC MemTGetTk2 ( IN UINT8 k ); UINT8 STATIC MemTGetBankAddr2 ( IN UINT8 k ); /*---------------------------------------------------------------------------- * 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 DDR2 */ BOOLEAN MemTSetDramMode2 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFLegacyBiosMode, 0); 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 MemTDIMMPresence2 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { UINT8 *SpdBufferPtr; MEM_PARAMETER_STRUCT *RefPtr; DIE_STRUCT *MCTPtr; DCT_STRUCT *DCTPtr; CH_DEF_STRUCT *ChannelPtr; MEM_NB_BLOCK *NBPtr; UINT16 Checksum; UINT16 Value16; UINT8 Dct; UINT8 Channel; UINT8 i; UINT8 ByteNum; UINT8 Devwidth; UINT8 Value8; UINT8 MaxDimms; UINT8 DimmSlots; UINT16 DimmMask; BOOLEAN SPDCtrl; 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 DimmSlots = GetMaxDimmsPerChannel (RefPtr->PlatformMemoryConfiguration, MCTPtr->SocketId, NBPtr->GetSocketRelativeChannel (NBPtr, Dct, Channel) ); MaxDimms = MAX_DIMMS_PER_CHANNEL; for (i = 0; i < MaxDimms; i++) { // Bitmask representing dimm #i. DimmMask = (UINT16)1 << i; if ((ChannelPtr->DimmQrPresent & DimmMask) || (i < DimmSlots)) { if (MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferPtr, i)) { MCTPtr->DimmPresent |= DimmMask; // Start by computing checksum for this SPD Checksum = 0; for (ByteNum = 0; ByteNum < SPD_CHECKSUM; ByteNum++) { Checksum = Checksum + (UINT16) SpdBufferPtr[ByteNum]; } // Check for valid checksum value AGESA_TESTPOINT (TpProcMemSPDChecking, &(NBPtr->MemPtr->StdHeader)); if (SpdBufferPtr[SPD_TYPE] == JED_DDR2_SDRAM) { ChannelPtr->ChDimmValid |= DimmMask; MCTPtr->DimmValid |= DimmMask; } else { // Current socket is set up to only support DDR2 dimms. IDS_ERROR_TRAP; } if ((SpdBufferPtr[SPD_CHECKSUM] != (UINT8)Checksum) && !SPDCtrl) { // // if 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_REG_ADC_MSK) { ChannelPtr->RegDimmPresent |= DimmMask; MCTPtr->RegDimmPresent |= DimmMask; } if (SpdBufferPtr[SPD_DIMM_TYPE] & JED_SODIMM) { ChannelPtr->SODimmPresent |= DimmMask; } // Check error correction type if (SpdBufferPtr[SPD_EDC_TYPE] & JED_ECC) { MCTPtr->DimmEccPresent |= DimmMask; // Dimm has ECC } if (SpdBufferPtr[SPD_EDC_TYPE] & JED_ADRC_PAR) { MCTPtr->DimmParPresent |= DimmMask; // Dimm has parity } // Get the Dimm width data Devwidth = SpdBufferPtr[SPD_DEV_WIDTH] & 0xFE; if (Devwidth == 4) { ChannelPtr->Dimmx4Present |= DimmMask; // Dimm has parity } else if (Devwidth == 8) { ChannelPtr->Dimmx8Present |= DimmMask; // Dimm has parity } else if (Devwidth == 16) { ChannelPtr->Dimmx16Present |= DimmMask; // Dimm has parity } // Determine the page size. // page_size = 2^COLBITS * Devwidth/8 // Value16 = (((UINT16)1 << SpdBufferPtr[SPD_COL_SZ]) * Devwidth) / 8; if (!(Value16 >> 11)) { DCTPtr->Timings.DIMM1KPage |= DimmMask; } // Check for 'analysis probe installed' if (SpdBufferPtr[SPD_ATTRIB] & JED_PROBE_MSK) { MCTPtr->Status[SbDiagClks] = TRUE; } // Determine the geometry of the DIMM module if (SpdBufferPtr[SPD_DM_BANKS] & SP_DPL_BIT) { ChannelPtr->DimmPlPresent |= DimmMask; // Dimm is planar } // specify the number of ranks Value8 = (SpdBufferPtr[SPD_DM_BANKS] & 0x07) + 1; if (Value8 > 2) { if (ChannelPtr->DimmQrPresent == 0) { // if any DIMMs are QR, // we have to make two passes through DIMMs // MaxDimms = MaxDimms << 1; } if (i < DimmSlots) { ChannelPtr->DimmQrPresent |= DimmMask; ChannelPtr->DimmQrPresent |= (DimmMask << 2); } Value8 = 2; } else if (Value8 == 2) { ChannelPtr->DimmDrPresent |= DimmMask; // Dual 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 if (Value8 == 2) { Devwidth = Devwidth << 1; } ChannelPtr->Ranks = ChannelPtr->Ranks + Value8; ChannelPtr->Loads = ChannelPtr->Loads + Devwidth; ChannelPtr->Dimms++; // Now examine the dimm packaging dates Value8 = SpdBufferPtr[SPD_MAN_DATE_YR]; if (Value8 < M_YEAR_06) { ChannelPtr->DimmYr06 |= DimmMask; // Built before end of 2006 ChannelPtr->DimmWk2406 |= DimmMask; // Built before end of week 24,2006 } else if (Value8 == M_YEAR_06) { ChannelPtr->DimmYr06 |= DimmMask; // Built before end of 2006 if (SpdBufferPtr[SPD_MAN_DATE_WK] <= M_WEEK_24) { ChannelPtr->DimmWk2406 |= DimmMask; // Built before end of week 24,2006 } } } // if DIMM present } // Quadrank } // Dimm loop if (Channel == 0) { DCTPtr->Timings.DctDimmValid = ChannelPtr->ChDimmValid; DCTPtr->Timings.DimmSpdCse = ChannelPtr->DimmSpdCse; DCTPtr->Timings.DimmQrPresent = ChannelPtr->DimmQrPresent; DCTPtr->Timings.DimmDrPresent = ChannelPtr->DimmDrPresent; 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->DimmEccPresent <<= 8; MCTPtr->DimmParPresent <<= 8; } } // Channel loop } // DCT loop // If we have DIMMs, some further general characteristics checking if (MCTPtr->DimmValid) { // If there are registered dimms, all the dimms must be registered if (MCTPtr->RegDimmPresent == MCTPtr->DimmValid) { // All dimms registered MCTPtr->Status[SbRegistered] = TRUE; } else if (MCTPtr->RegDimmPresent) { // 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 } // check the parity capability of the DIMMs if (MCTPtr->DimmParPresent == MCTPtr->DimmValid) { MCTPtr->Status[SbParDimms] = TRUE; // All dimms parity capable } } else { } NBPtr->SwitchDCT (NBPtr, 0); NBPtr->SwitchChannel (NBPtr, 0); return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL); } /* -----------------------------------------------------------------------------*/ /** * * This function finds the best T and CL primary timing parameter pair, per Mfg.,for the given * set of DIMMs, and store into DIE_STRUCT(.Speed and .Casl). * See "Global relationship between index values and item values" for definition of * CAS latency index (j) and Frequency index (k). * * @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 MemTSPDGetTargetSpeed2 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { CONST UINT16 SpeedCvt[] = { DDR400_FREQUENCY, DDR533_FREQUENCY, DDR667_FREQUENCY, DDR800_FREQUENCY, DDR1066_FREQUENCY }; INT8 i; INT8 j; INT8 k; INT8 Dct; INT8 Channel; UINT8 T1min; UINT8 CL1min; BOOLEAN IsSupported; MEM_NB_BLOCK *NBPtr; DIE_STRUCT *MCTPtr; DCT_STRUCT *DCTPtr; CH_DEF_STRUCT *ChannelPtr; NBPtr = TechPtr->NBPtr; MCTPtr = TechPtr->NBPtr->MCTPtr; CL1min = 0xFF; T1min = 0xFF; // For DDR2, run SyncTargetSpeed first to get frequency limit into DCTPtr->Timings.Speed for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { NBPtr->SwitchDCT (NBPtr, Dct); NBPtr->DCTPtr->Timings.TargetSpeed = 16; // initialized with big number } NBPtr->SyncTargetSpeed (NBPtr); // Find target frequency and Tcl for (k = K_MAX; k >= K_MIN; k--) { for (j = J_MIN; j <= J_MAX; j++) { if (MemTSysCapability2 (TechPtr, k, j)) { IsSupported = TRUE; for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { NBPtr->SwitchDCT (NBPtr, Dct); for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) { NBPtr->SwitchChannel (NBPtr, Channel); ChannelPtr = NBPtr->ChannelPtr; for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i++) { if (ChannelPtr->ChDimmValid & ((UINT8)1 << i)) { if (!MemTDimmSupports2 (TechPtr, k, j, i)) { IsSupported = FALSE; Dct = NBPtr->DctCount; Channel = NBPtr->ChannelCount; break; } } } } } if (IsSupported) { T1min = k; CL1min = j; // Kill the loops... k = K_MIN - 1; j = J_MAX + 1; } } } } if (T1min == 0xFF) { // Failsafe values, running in minimum mode PutEventLog (AGESA_FATAL, MEM_ERROR_MISMATCH_DIMM_CLOCKS, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); PutEventLog (AGESA_FATAL, MEM_ERROR_MINIMUM_MODE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); SetMemError (AGESA_ERROR, MCTPtr); T1min = T_DEF; CL1min = CL_DEF; } for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { NBPtr->SwitchDCT (NBPtr, Dct); DCTPtr = NBPtr->DCTPtr; DCTPtr->Timings.TargetSpeed = SpeedCvt[T1min - 1]; } // 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); DCTPtr = NBPtr->DCTPtr; DCTPtr->Timings.Speed = DCTPtr->Timings.TargetSpeed; DCTPtr->Timings.CasL = CL1min + 2; // Convert to clocks } return (BOOLEAN) (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 MemTSPDCalcWidth2 ( 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; 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 (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) { NBPtr->SwitchDCT (NBPtr, 0); MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferAPtr, i); NBPtr->SwitchDCT (NBPtr, 1); MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferBPtr, i); if ((SpdBufferAPtr[SPD_ROW_SZ]&0x1F) != (SpdBufferBPtr[SPD_ROW_SZ]&0x1F)) { break; } if ((SpdBufferAPtr[SPD_COL_SZ]&0x1F) != (SpdBufferBPtr[SPD_COL_SZ]&0x1F)) { break; } if (SpdBufferAPtr[SPD_BANK_SZ] != SpdBufferBPtr[SPD_BANK_SZ]) { break; } if ((SpdBufferAPtr[SPD_DEV_WIDTH]&0x7F) != (SpdBufferBPtr[SPD_DEV_WIDTH]&0x7F)) { break; } if ((SpdBufferAPtr[SPD_DM_BANKS]&0x07) != (SpdBufferBPtr[SPD_DM_BANKS]&0x07)) { 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 if (!UngangMode) { 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 MemTAutoCycTiming2 ( 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 }; CONST UINT8 Multiples[] = {10, 10, 10, 40, 40, 10, 10, 10}; CONST UINT8 Tab1KTfawTK[] = {8, 10, 13, 14, 0, 20}; CONST UINT8 Tab2KTfawTK[] = {10, 14, 17, 18, 0, 24}; CONST UINT8 TabDefTrcK[] = {0x41, 0x3C, 0x3C, 0x3A, 0, 0x3A}; UINT8 MiniMaxTmg[GET_SIZE_OF (SpdIndexes)]; UINT8 MiniMaxTrfc[4]; DIE_STRUCT *MCTPtr; DCT_STRUCT *DCTPtr; MEM_NB_BLOCK *NBPtr; UINT16 DimmMask; UINT16 Value16; UINT16 Tk40; UINT8 i; UINT8 j; UINT8 Value8; UINT8 Temp8; UINT8 *StatTmgPtr; UINT16 *StatDimmTmgPtr; BOOLEAN Is1066; UINT8 *SpdBufferPtr; 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) { MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferPtr, i); for (j = 0; j < GET_SIZE_OF (SpdIndexes); j++) { Value8 = SpdBufferPtr[SpdIndexes[j]]; if (SpdIndexes[j] == SPD_TRC) { if (Value8 == 0 || Value8 == 0xFF) { PutEventLog (AGESA_WARNING, MEM_WARNING_NO_SPDTRC_FOUND, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, i, &NBPtr->MemPtr->StdHeader); SetMemError (AGESA_WARNING, MCTPtr); Value8 = TabDefTrcK[(DCTPtr->Timings.Speed / 66) - 3]; } } if (MiniMaxTmg[j] < Value8) { MiniMaxTmg[j] = Value8; } } // get Trfc0 - Trfc3 values Value8 = SpdBufferPtr[SPD_BANK_SZ]; Temp8 = (Value8 << 3) | (Value8 >> 5); Value8 = SpdBufferPtr[SPD_DEV_WIDTH]; ASSERT (LibAmdBitScanReverse ((UINT32)Value8) <= 4); Temp8 >>= 4 - LibAmdBitScanReverse ((UINT32)Value8); Value8 = LibAmdBitScanReverse ((UINT32)Temp8); if (MiniMaxTrfc[i] < Value8) { MiniMaxTrfc[i] = Value8; } } DimmMask <<= 1; } // ====================================================================== // Convert DRAM CycleTiming values and store into DCT structure // ====================================================================== // Tk40 = 40000 / DCTPtr->Timings.Speed; if (DCTPtr->Timings.Speed == DDR1066_FREQUENCY) { Is1066 = TRUE; } else { Is1066 = FALSE; } // Notes: // 1. All secondary time values given in SPDs are in binary with UINTs of ns. // 2. Some time values are scaled by four, in order to have least count of 0.25 ns // (more accuracy). JEDEC SPD spec. shows which ones are x1 and x4. // 3. Internally to this SW, cycle time, Tk, is scaled by 10 to affect a // least count of 0.1 ns (more accuracy). // 4. SPD values not scaled are multiplied by 10 and then divided by 10T to find // equivalent minimum number of bus clocks (a remainder causes round-up of clocks). // 5. SPD values that are prescaled by 4 are multiplied by 10 and then divided by 40T to find // equivalent minimum number of bus clocks (a remainder causes round-up of clocks). // StatDimmTmgPtr = &DCTPtr->Timings.DIMMTrcd; StatTmgPtr = &DCTPtr->Timings.Trcd; for (j = 0; j < GET_SIZE_OF (SpdIndexes); j++) { Value16 = (UINT16)MiniMaxTmg[j] * Multiples[j]; StatDimmTmgPtr[j] = Value16; MiniMaxTmg[j] = (UINT8) ((Value16 + Tk40 - 1) / Tk40); if (SpdIndexes[j] == SPD_TRTP) { MiniMaxTmg[j] = (DCTPtr->Timings.Speed <= DDR533_FREQUENCY) ? 2 : 3; // based on BL of 32 bytes } StatTmgPtr[j] = 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 = MemTSPDGetTCL2 (TechPtr); if (DCTPtr->Timings.DIMM1KPage) { DCTPtr->Timings.Tfaw = Tab1KTfawTK[(DCTPtr->Timings.Speed / 66) - 3]; } else { DCTPtr->Timings.Tfaw = Tab2KTfawTK[(DCTPtr->Timings.Speed / 66) - 3]; } if (Is1066) { DCTPtr->Timings.Tfaw >>= 1; } //====================================================================== // 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 MemTSPDSetBanks2 ( 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) { 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)) { MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferPtr, DimmID); // Get the basic data Rows = SpdBufferPtr[SPD_ROW_SZ] & 0x1F; Cols = SpdBufferPtr[SPD_COL_SZ] & 0x1F; Banks = SpdBufferPtr[SPD_L_BANKS]; Ranks = (SpdBufferPtr[SPD_DM_BANKS] & 0x07) + 1; // Configure the bank encoding Value8 = (Cols - 9) << 3; Value8 |= (Banks == 8) ? 4 : 0; Value8 |= (Rows - 13); for (i = 0; i < 12; i++) { if (Value8 == MemTGetBankAddr2 (i)) { break; } } if (i < 12) { BankAddrReg |= ((UINT32)i << (ChipSel << 1)); // Mask value=(2pow(rows+cols+banks+3)-1)>>8, // or 2pow(rows+cols+banks-5)-1 // Value8 = Rows + Cols; Value8 -= (Banks == 8) ? 2:3; if (MCTPtr->Status[Sb128bitmode]) { Value8++; } CsMask = ((UINT32)1 << Value8) - 1; DCTPtr->Timings.CsPresent |= (UINT16)1 << ChipSel; if (Ranks >= 2) { DCTPtr->Timings.CsPresent |= (UINT16)1 << (ChipSel + 1); } // Update the DRAM CS Mask for this chipselect NBPtr->SetBitField (NBPtr, BFCSMask0Reg + (ChipSel >> 1), (CsMask & NBPtr->CsRegMsk)); } } } // 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)) { if (!NBPtr->MemPtr->ErrorHandling (MCTPtr, NBPtr->Dct, (CSSpdCSE | CSExclude), &NBPtr->MemPtr->StdHeader)) { ASSERT (FALSE); } } // 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 MemTGetCSIntLvAddr2 ( IN UINT8 BankEnc, OUT UINT8 *LowBit, OUT UINT8 *HiBit ) { CONST UINT8 ArrCodesLo[] = {6, 7, 7, 8, 8, 8, 8, 8, 9, 9, 8, 9}; CONST UINT8 ArrCodesHi[] = {19, 20, 21, 21, 21, 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 returns the CAS latency of the current frequency. * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * * @return CAS Latency */ UINT8 STATIC MemTSPDGetTCL2 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { return TechPtr->NBPtr->DCTPtr->Timings.CasL; } /* -----------------------------------------------------------------------------*/ /** * * Get max frequency from OEM platform definition, from * any user override (limiting) of max frequency, and * from any Si Revision Specific information. Return * the least of these three in DIE_STRUCT.PresetmaxFreq. * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * @param[in] k - Frequency index * @param[in] j - CAS Latency index * * @return TRUE - (k << 8) | j * @return FALSE - 0 */ BOOLEAN STATIC MemTSysCapability2 ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 k, IN UINT16 j ) { if ((k > TechPtr->NBPtr->DCTPtr->Timings.TargetSpeed) || (j > J_MAX)) { return FALSE; } return TRUE; //(k << 8) | j; } /* -----------------------------------------------------------------------------*/ /** * * Determine whether dimm(b,i) supports CL(j) and F(k) * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * @param[in] k - Frequency index * @param[in] j - CAS Latency index * @param[in] i - DIMM number * * @return TRUE - DIMM supports * @return FALSE - DIMM does not support */ BOOLEAN STATIC MemTDimmSupports2 ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 k, IN UINT8 j, IN UINT8 i ) { CONST UINT8 SpdBytesForCL[3] = { 9, 23, 25}; // SPD bytes for CL X, CL X-.5, and CL X-1 UINT8 CLj; UINT8 CLi; UINT8 T1; UINT8 T2; UINT8 Tk; UINT8 *SpdBufferPtr; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferPtr, i); CLj = (UINT8) 1 << (j + 2); CLi = SpdBufferPtr[SPD_CAS_LAT]; if (CLj & CLi) { // If this dimm supports the desired CAS latency... // Determine the SPD location of the dimm speed UINT8 appropriate // to the CAS latency indicated by Table_CL2_j. // T1 = LibAmdBitScanReverse ((UINT32)CLj); T2 = LibAmdBitScanReverse ((UINT32)CLi); ASSERT ((T2 - T1) < 3); CLi = SpdBufferPtr[SpdBytesForCL[(T2 - T1)]]; Tk = MemTGetTk2 (k); if (CLi == 0) { PutEventLog (AGESA_FATAL, MEM_ERROR_NO_CYC_TIME, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); SetMemError (AGESA_WARNING, NBPtr->MCTPtr); } else if (Tk >= CLi) { return TRUE; } } return FALSE; } /* -----------------------------------------------------------------------------*/ /** * * This function returns the cycle time * * @param[in] k - CAS Latency index * * @return Tk as specified by JEDEC SPD byte 9. */ UINT8 STATIC MemTGetTk2 ( IN UINT8 k ) { CONST UINT8 TableTK[] = {0x00, 0x50, 0x3D, 0x30, 0x25, 0x18}; ASSERT (k < GET_SIZE_OF (TableTK)); return TableTK[k]; } /* -----------------------------------------------------------------------------*/ /** * * This function returns the encoded value of bank address. * * @param[in] k value * * @return 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. */ UINT8 STATIC MemTGetBankAddr2 ( IN UINT8 k ) { CONST UINT8 TabBankAddr[] = { 0x00, 0x08, 0x09, 0x10, 0x0C, 0x0D, 0x11, 0x0E, 0x15, 0x16, 0x0F, 0x17 }; ASSERT (k < GET_SIZE_OF (TabBankAddr)); return TabBankAddr[k]; } /* -----------------------------------------------------------------------------*/ /** * * 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 MemTGetDimmSpdBuffer2 ( 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; }