7 * Technology SPD supporting functions for DDR2
9 * @xrefitem bom "File Content Label" "Release Content"
11 * @e sub-project: (Mem/Tech/DDR2)
12 * @e \$Revision: 56279 $ @e \$Date: 2011-07-11 13:11:28 -0600 (Mon, 11 Jul 2011) $
15 /*****************************************************************************
17 * Copyright (C) 2012 Advanced Micro Devices, Inc.
18 * All rights reserved.
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions are met:
22 * * Redistributions of source code must retain the above copyright
23 * notice, this list of conditions and the following disclaimer.
24 * * Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
27 * * Neither the name of Advanced Micro Devices, Inc. nor the names of
28 * its contributors may be used to endorse or promote products derived
29 * from this software without specific prior written permission.
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
35 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 * ***************************************************************************
47 *----------------------------------------------------------------------------
50 *----------------------------------------------------------------------------
56 #include "AdvancedApi.h"
67 #include "GeneralServices.h"
72 #define FILECODE PROC_MEM_TECH_DDR2_MTSPD2_FILECODE
74 /*----------------------------------------------------------------------------
75 * DEFINITIONS AND MACROS
77 *----------------------------------------------------------------------------
80 /*----------------------------------------------------------------------------
81 * TYPEDEFS AND STRUCTURES
83 *----------------------------------------------------------------------------
86 /*----------------------------------------------------------------------------
87 * PROTOTYPES OF LOCAL FUNCTIONS
89 *----------------------------------------------------------------------------
95 IN OUT MEM_TECH_BLOCK *TechPtr
101 IN OUT MEM_TECH_BLOCK *TechPtr,
109 IN OUT MEM_TECH_BLOCK *TechPtr,
127 /*----------------------------------------------------------------------------
130 *----------------------------------------------------------------------------
133 extern BUILD_OPT_CFG UserOptions;
135 /* -----------------------------------------------------------------------------*/
138 * This function sets the DRAM mode
140 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
142 * @return TRUE - indicates that the DRAM mode is set to DDR2
147 IN OUT MEM_TECH_BLOCK *TechPtr
150 TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFLegacyBiosMode, 0);
154 /* -----------------------------------------------------------------------------*/
157 * This function determines if DIMMs are present. It checks checksum and interrogates the SPDs
159 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
161 * @return TRUE - indicates that a FATAL error has not occurred
162 * @return FALSE - indicates that a FATAL error has occurred
167 IN OUT MEM_TECH_BLOCK *TechPtr
171 MEM_PARAMETER_STRUCT *RefPtr;
174 CH_DEF_STRUCT *ChannelPtr;
189 NBPtr = TechPtr->NBPtr;
190 RefPtr = NBPtr->RefPtr;
191 MCTPtr = NBPtr->MCTPtr;
193 SPDCtrl = UserOptions.CfgIgnoreSpdChecksum;
195 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
196 NBPtr->SwitchDCT (NBPtr, Dct);
197 DCTPtr = NBPtr->DCTPtr;
198 for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) {
199 NBPtr->SwitchChannel (NBPtr, Channel);
200 ChannelPtr = NBPtr->ChannelPtr;
201 ChannelPtr->DimmQrPresent = 0;
203 // Get the maximum number of DIMMs
204 DimmSlots = GetMaxDimmsPerChannel (RefPtr->PlatformMemoryConfiguration,
206 NBPtr->GetSocketRelativeChannel (NBPtr, Dct, Channel)
208 MaxDimms = MAX_DIMMS_PER_CHANNEL;
209 for (i = 0; i < MaxDimms; i++) {
210 // Bitmask representing dimm #i.
211 DimmMask = (UINT16)1 << i;
213 if ((ChannelPtr->DimmQrPresent & DimmMask) || (i < DimmSlots)) {
214 if (MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferPtr, i)) {
215 MCTPtr->DimmPresent |= DimmMask;
217 // Start by computing checksum for this SPD
219 for (ByteNum = 0; ByteNum < SPD_CHECKSUM; ByteNum++) {
220 Checksum = Checksum + (UINT16) SpdBufferPtr[ByteNum];
222 // Check for valid checksum value
223 AGESA_TESTPOINT (TpProcMemSPDChecking, &(NBPtr->MemPtr->StdHeader));
225 if (SpdBufferPtr[SPD_TYPE] == JED_DDR2_SDRAM) {
226 ChannelPtr->ChDimmValid |= DimmMask;
227 MCTPtr->DimmValid |= DimmMask;
229 // Current socket is set up to only support DDR2 dimms.
232 if ((SpdBufferPtr[SPD_CHECKSUM] != (UINT8)Checksum) && !SPDCtrl) {
234 // if NV_SPDCHK_RESTRT is set to 0,
235 // cannot ignore faulty SPD checksum
237 // Indicate checksum error
238 ChannelPtr->DimmSpdCse |= DimmMask;
239 PutEventLog (AGESA_ERROR, MEM_ERROR_CHECKSUM_NV_SPDCHK_RESTRT_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
240 SetMemError (AGESA_ERROR, MCTPtr);
243 // Check module type information.
244 if (SpdBufferPtr[SPD_DIMM_TYPE] & JED_REG_ADC_MSK) {
245 ChannelPtr->RegDimmPresent |= DimmMask;
246 MCTPtr->RegDimmPresent |= DimmMask;
249 if (SpdBufferPtr[SPD_DIMM_TYPE] & JED_SODIMM) {
250 ChannelPtr->SODimmPresent |= DimmMask;
253 // Check error correction type
254 if (SpdBufferPtr[SPD_EDC_TYPE] & JED_ECC) {
255 MCTPtr->DimmEccPresent |= DimmMask; // Dimm has ECC
257 if (SpdBufferPtr[SPD_EDC_TYPE] & JED_ADRC_PAR) {
258 MCTPtr->DimmParPresent |= DimmMask; // Dimm has parity
261 // Get the Dimm width data
262 Devwidth = SpdBufferPtr[SPD_DEV_WIDTH] & 0xFE;
264 ChannelPtr->Dimmx4Present |= DimmMask; // Dimm has parity
265 } else if (Devwidth == 8) {
266 ChannelPtr->Dimmx8Present |= DimmMask; // Dimm has parity
267 } else if (Devwidth == 16) {
268 ChannelPtr->Dimmx16Present |= DimmMask; // Dimm has parity
271 // Determine the page size.
272 // page_size = 2^COLBITS * Devwidth/8
274 Value16 = (((UINT16)1 << SpdBufferPtr[SPD_COL_SZ]) * Devwidth) / 8;
275 if (!(Value16 >> 11)) {
276 DCTPtr->Timings.DIMM1KPage |= DimmMask;
279 // Check for 'analysis probe installed'
280 if (SpdBufferPtr[SPD_ATTRIB] & JED_PROBE_MSK) {
281 MCTPtr->Status[SbDiagClks] = TRUE;
284 // Determine the geometry of the DIMM module
285 if (SpdBufferPtr[SPD_DM_BANKS] & SP_DPL_BIT) {
286 ChannelPtr->DimmPlPresent |= DimmMask; // Dimm is planar
289 // specify the number of ranks
290 Value8 = (SpdBufferPtr[SPD_DM_BANKS] & 0x07) + 1;
292 if (ChannelPtr->DimmQrPresent == 0) {
293 // if any DIMMs are QR,
294 // we have to make two passes through DIMMs
296 MaxDimms = MaxDimms << 1;
300 ChannelPtr->DimmQrPresent |= DimmMask;
301 ChannelPtr->DimmQrPresent |= (DimmMask << 2);
304 } else if (Value8 == 2) {
305 ChannelPtr->DimmDrPresent |= DimmMask; // Dual rank dimms
308 // Calculate bus loading per Channel
309 if (Devwidth == 16) {
311 } else if (Devwidth == 4) {
314 // double Addr bus load value for dual rank DIMMs
316 Devwidth = Devwidth << 1;
319 ChannelPtr->Ranks = ChannelPtr->Ranks + Value8;
320 ChannelPtr->Loads = ChannelPtr->Loads + Devwidth;
323 // Now examine the dimm packaging dates
324 Value8 = SpdBufferPtr[SPD_MAN_DATE_YR];
325 if (Value8 < M_YEAR_06) {
326 ChannelPtr->DimmYr06 |= DimmMask; // Built before end of 2006
327 ChannelPtr->DimmWk2406 |= DimmMask; // Built before end of week 24,2006
328 } else if (Value8 == M_YEAR_06) {
329 ChannelPtr->DimmYr06 |= DimmMask; // Built before end of 2006
330 if (SpdBufferPtr[SPD_MAN_DATE_WK] <= M_WEEK_24) {
331 ChannelPtr->DimmWk2406 |= DimmMask; // Built before end of week 24,2006
339 DCTPtr->Timings.DctDimmValid = ChannelPtr->ChDimmValid;
340 DCTPtr->Timings.DimmSpdCse = ChannelPtr->DimmSpdCse;
341 DCTPtr->Timings.DimmQrPresent = ChannelPtr->DimmQrPresent;
342 DCTPtr->Timings.DimmDrPresent = ChannelPtr->DimmDrPresent;
343 DCTPtr->Timings.Dimmx4Present = ChannelPtr->Dimmx4Present;
344 DCTPtr->Timings.Dimmx8Present = ChannelPtr->Dimmx8Present;
345 DCTPtr->Timings.Dimmx16Present = ChannelPtr->Dimmx16Present;
347 if ((Channel != 1) || (Dct != 1)) {
348 MCTPtr->DimmPresent <<= 8;
349 MCTPtr->DimmValid <<= 8;
350 MCTPtr->RegDimmPresent <<= 8;
351 MCTPtr->DimmEccPresent <<= 8;
352 MCTPtr->DimmParPresent <<= 8;
358 // If we have DIMMs, some further general characteristics checking
359 if (MCTPtr->DimmValid) {
360 // If there are registered dimms, all the dimms must be registered
361 if (MCTPtr->RegDimmPresent == MCTPtr->DimmValid) {
362 // All dimms registered
363 MCTPtr->Status[SbRegistered] = TRUE;
364 } else if (MCTPtr->RegDimmPresent) {
365 // We have an illegal DIMM mismatch
366 PutEventLog (AGESA_FATAL, MEM_ERROR_MODULE_TYPE_MISMATCH_DIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
367 SetMemError (AGESA_FATAL, MCTPtr);
370 // check the ECC capability of the DIMMs
371 if (MCTPtr->DimmEccPresent == MCTPtr->DimmValid) {
372 MCTPtr->Status[SbEccDimms] = TRUE; // All dimms ECC capable
375 // check the parity capability of the DIMMs
376 if (MCTPtr->DimmParPresent == MCTPtr->DimmValid) {
377 MCTPtr->Status[SbParDimms] = TRUE; // All dimms parity capable
382 NBPtr->SwitchDCT (NBPtr, 0);
383 NBPtr->SwitchChannel (NBPtr, 0);
384 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
388 /* -----------------------------------------------------------------------------*/
391 * This function finds the best T and CL primary timing parameter pair, per Mfg.,for the given
392 * set of DIMMs, and store into DIE_STRUCT(.Speed and .Casl).
393 * See "Global relationship between index values and item values" for definition of
394 * CAS latency index (j) and Frequency index (k).
396 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
398 * @return TRUE - indicates that a FATAL error has not occurred
399 * @return FALSE - indicates that a FATAL error has occurred
403 MemTSPDGetTargetSpeed2 (
404 IN OUT MEM_TECH_BLOCK *TechPtr
407 CONST UINT16 SpeedCvt[] = {
425 CH_DEF_STRUCT *ChannelPtr;
427 NBPtr = TechPtr->NBPtr;
428 MCTPtr = TechPtr->NBPtr->MCTPtr;
433 // For DDR2, run SyncTargetSpeed first to get frequency limit into DCTPtr->Timings.Speed
434 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
435 NBPtr->SwitchDCT (NBPtr, Dct);
436 NBPtr->DCTPtr->Timings.TargetSpeed = 16; // initialized with big number
438 NBPtr->SyncTargetSpeed (NBPtr);
440 // Find target frequency and Tcl
441 for (k = K_MAX; k >= K_MIN; k--) {
442 for (j = J_MIN; j <= J_MAX; j++) {
443 if (MemTSysCapability2 (TechPtr, k, j)) {
445 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
446 NBPtr->SwitchDCT (NBPtr, Dct);
447 for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) {
448 NBPtr->SwitchChannel (NBPtr, Channel);
449 ChannelPtr = NBPtr->ChannelPtr;
450 for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i++) {
451 if (ChannelPtr->ChDimmValid & ((UINT8)1 << i)) {
452 if (!MemTDimmSupports2 (TechPtr, k, j, i)) {
454 Dct = NBPtr->DctCount;
455 Channel = NBPtr->ChannelCount;
475 // Failsafe values, running in minimum mode
476 PutEventLog (AGESA_FATAL, MEM_ERROR_MISMATCH_DIMM_CLOCKS, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
477 PutEventLog (AGESA_FATAL, MEM_ERROR_MINIMUM_MODE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
478 SetMemError (AGESA_ERROR, MCTPtr);
484 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
485 NBPtr->SwitchDCT (NBPtr, Dct);
486 DCTPtr = NBPtr->DCTPtr;
487 DCTPtr->Timings.TargetSpeed = SpeedCvt[T1min - 1];
490 // Ensure the target speed can be applied to all channels of the current node
491 NBPtr->SyncTargetSpeed (NBPtr);
493 // Set the start-up frequency
494 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
495 NBPtr->SwitchDCT (NBPtr, Dct);
496 DCTPtr = NBPtr->DCTPtr;
497 DCTPtr->Timings.Speed = DCTPtr->Timings.TargetSpeed;
498 DCTPtr->Timings.CasL = CL1min + 2; // Convert to clocks
501 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
504 /* -----------------------------------------------------------------------------*/
507 * This function check the symmetry of DIMM pairs (DIMM on Channel A matching with
508 * DIMM on Channel B), the overall DIMM population, and determine the width mode:
509 * 64-bit, 64-bit muxed, 128-bit.
511 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
513 * @return TRUE - indicates that a FATAL error has not occurred
514 * @return FALSE - indicates that a FATAL error has occurred
519 IN OUT MEM_TECH_BLOCK *TechPtr
522 UINT8 *SpdBufferAPtr;
523 UINT8 *SpdBufferBPtr;
531 NBPtr = TechPtr->NBPtr;
532 MCTPtr = NBPtr->MCTPtr;
533 DCTPtr = NBPtr->DCTPtr;
535 UngangMode = UserOptions.CfgMemoryModeUnganged;
536 IDS_OPTION_HOOK (IDS_GANGING_MODE, &UngangMode, &(NBPtr->MemPtr->StdHeader));
538 // Check symmetry of channel A and channel B dimms for 128-bit mode
541 AGESA_TESTPOINT (TpProcMemModeChecking, &(NBPtr->MemPtr->StdHeader));
543 if (MCTPtr->DctData[0].Timings.DctDimmValid == MCTPtr->DctData[1].Timings.DctDimmValid) {
544 for (; i < MAX_DIMMS_PER_CHANNEL; i++) {
545 DimmMask = (UINT16)1 << i;
546 if (DCTPtr->Timings.DctDimmValid & DimmMask) {
547 NBPtr->SwitchDCT (NBPtr, 0);
548 MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferAPtr, i);
549 NBPtr->SwitchDCT (NBPtr, 1);
550 MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferBPtr, i);
552 if ((SpdBufferAPtr[SPD_ROW_SZ]&0x1F) != (SpdBufferBPtr[SPD_ROW_SZ]&0x1F)) {
556 if ((SpdBufferAPtr[SPD_COL_SZ]&0x1F) != (SpdBufferBPtr[SPD_COL_SZ]&0x1F)) {
560 if (SpdBufferAPtr[SPD_BANK_SZ] != SpdBufferBPtr[SPD_BANK_SZ]) {
564 if ((SpdBufferAPtr[SPD_DEV_WIDTH]&0x7F) != (SpdBufferBPtr[SPD_DEV_WIDTH]&0x7F)) {
568 if ((SpdBufferAPtr[SPD_DM_BANKS]&0x07) != (SpdBufferBPtr[SPD_DM_BANKS]&0x07)) {
574 if (i < MAX_DIMMS_PER_CHANNEL) {
575 PutEventLog (AGESA_ALERT, MEM_ALERT_ORG_MISMATCH_DIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
576 SetMemError (AGESA_ALERT, MCTPtr);
577 } else if (!UngangMode) {
578 NBPtr->Ganged = TRUE;
579 MCTPtr->GangedMode = TRUE;
580 MCTPtr->Status[Sb128bitmode] = TRUE;
581 NBPtr->SetBitField (NBPtr, BFDctGangEn, 1);
584 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
588 /* -----------------------------------------------------------------------------*/
591 * Initialize DCT Timing registers as per DIMM SPD.
592 * For primary timing (T, CL) use best case T value.
593 * For secondary timing params., use most aggressive settings
597 * There are three components to determining "maximum frequency": SPD component,
598 * Bus load component, and "Preset" max frequency component.
599 * The SPD component is a function of the min cycle time specified by each DIMM,
600 * and the interaction of cycle times from all DIMMs in conjunction with CAS
601 * latency. The SPD component only applies when user timing mode is 'Auto'.
603 * The Bus load component is a limiting factor determined by electrical
604 * characteristics on the bus as a result of varying number of device loads. The
605 * Bus load component is specific to each platform but may also be a function of
606 * other factors. The bus load component only applies when user timing mode is
609 * The Preset component is subdivided into three items and is the minimum of
610 * the set: Silicon revision, user limit setting when user timing mode is 'Auto' and
611 * memclock mode is 'Limit', OEM build specification of the maximum frequency.
612 * The Preset component only applies when user timing mode is 'Auto'.
615 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
617 * @return TRUE - indicates that a FATAL error has not occurred
618 * @return FALSE - indicates that a FATAL error has occurred
623 IN OUT MEM_TECH_BLOCK *TechPtr
626 CONST UINT8 SpdIndexes[] = {
636 CONST UINT8 Multiples[] = {10, 10, 10, 40, 40, 10, 10, 10};
638 CONST UINT8 Tab1KTfawTK[] = {8, 10, 13, 14, 0, 20};
639 CONST UINT8 Tab2KTfawTK[] = {10, 14, 17, 18, 0, 24};
640 CONST UINT8 TabDefTrcK[] = {0x41, 0x3C, 0x3C, 0x3A, 0, 0x3A};
642 UINT8 MiniMaxTmg[GET_SIZE_OF (SpdIndexes)];
643 UINT8 MiniMaxTrfc[4];
656 UINT16 *StatDimmTmgPtr;
660 NBPtr = TechPtr->NBPtr;
661 MCTPtr = NBPtr->MCTPtr;
662 DCTPtr = NBPtr->DCTPtr;
664 // initialize mini-max arrays
665 for (j = 0; j < GET_SIZE_OF (MiniMaxTmg); j++) {
668 for (j = 0; j < GET_SIZE_OF (MiniMaxTrfc); j++) {
672 // ======================================================================
673 // Get primary timing (CAS Latency and Cycle Time)
674 // ======================================================================
675 // Get OEM specific load variant max
678 //======================================================================
679 // Gather all DIMM mini-max values for cycle timing data
680 //======================================================================
683 for (i = 0; i < (MAX_CS_PER_CHANNEL / 2); i++) {
684 if (DCTPtr->Timings.DctDimmValid & DimmMask) {
685 MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferPtr, i);
686 for (j = 0; j < GET_SIZE_OF (SpdIndexes); j++) {
687 Value8 = SpdBufferPtr[SpdIndexes[j]];
688 if (SpdIndexes[j] == SPD_TRC) {
689 if (Value8 == 0 || Value8 == 0xFF) {
690 PutEventLog (AGESA_WARNING, MEM_WARNING_NO_SPDTRC_FOUND, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, i, &NBPtr->MemPtr->StdHeader);
691 SetMemError (AGESA_WARNING, MCTPtr);
692 Value8 = TabDefTrcK[(DCTPtr->Timings.Speed / 66) - 3];
695 if (MiniMaxTmg[j] < Value8) {
696 MiniMaxTmg[j] = Value8;
700 // get Trfc0 - Trfc3 values
701 Value8 = SpdBufferPtr[SPD_BANK_SZ];
702 Temp8 = (Value8 << 3) | (Value8 >> 5);
703 Value8 = SpdBufferPtr[SPD_DEV_WIDTH];
704 ASSERT (LibAmdBitScanReverse ((UINT32)Value8) <= 4);
705 Temp8 >>= 4 - LibAmdBitScanReverse ((UINT32)Value8);
706 Value8 = LibAmdBitScanReverse ((UINT32)Temp8);
707 if (MiniMaxTrfc[i] < Value8) {
708 MiniMaxTrfc[i] = Value8;
714 // ======================================================================
715 // Convert DRAM CycleTiming values and store into DCT structure
716 // ======================================================================
718 Tk40 = 40000 / DCTPtr->Timings.Speed;
719 if (DCTPtr->Timings.Speed == DDR1066_FREQUENCY) {
725 // 1. All secondary time values given in SPDs are in binary with UINTs of ns.
726 // 2. Some time values are scaled by four, in order to have least count of 0.25 ns
727 // (more accuracy). JEDEC SPD spec. shows which ones are x1 and x4.
728 // 3. Internally to this SW, cycle time, Tk, is scaled by 10 to affect a
729 // least count of 0.1 ns (more accuracy).
730 // 4. SPD values not scaled are multiplied by 10 and then divided by 10T to find
731 // equivalent minimum number of bus clocks (a remainder causes round-up of clocks).
732 // 5. SPD values that are prescaled by 4 are multiplied by 10 and then divided by 40T to find
733 // equivalent minimum number of bus clocks (a remainder causes round-up of clocks).
735 StatDimmTmgPtr = &DCTPtr->Timings.DIMMTrcd;
736 StatTmgPtr = &DCTPtr->Timings.Trcd;
737 for (j = 0; j < GET_SIZE_OF (SpdIndexes); j++) {
738 Value16 = (UINT16)MiniMaxTmg[j] * Multiples[j];
739 StatDimmTmgPtr[j] = Value16;
741 MiniMaxTmg[j] = (UINT8) ((Value16 + Tk40 - 1) / Tk40);
742 if (SpdIndexes[j] == SPD_TRTP) {
743 MiniMaxTmg[j] = (DCTPtr->Timings.Speed <= DDR533_FREQUENCY) ? 2 : 3; // based on BL of 32 bytes
746 StatTmgPtr[j] = MiniMaxTmg[j];
748 DCTPtr->Timings.Trfc0 = MiniMaxTrfc[0];
749 DCTPtr->Timings.Trfc1 = MiniMaxTrfc[1];
750 DCTPtr->Timings.Trfc2 = MiniMaxTrfc[2];
751 DCTPtr->Timings.Trfc3 = MiniMaxTrfc[3];
753 DCTPtr->Timings.CasL = MemTSPDGetTCL2 (TechPtr);
755 if (DCTPtr->Timings.DIMM1KPage) {
756 DCTPtr->Timings.Tfaw = Tab1KTfawTK[(DCTPtr->Timings.Speed / 66) - 3];
758 DCTPtr->Timings.Tfaw = Tab2KTfawTK[(DCTPtr->Timings.Speed / 66) - 3];
761 DCTPtr->Timings.Tfaw >>= 1;
764 //======================================================================
765 // Program DRAM Timing values
766 //======================================================================
768 NBPtr->ProgramCycTimings (NBPtr);
770 MemFInitTableDrive (NBPtr, MTAfterAutoCycTiming);
772 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
775 /* -----------------------------------------------------------------------------*/
778 * This function sets the bank addressing, program Mask values and build a chip-select population map.
779 * This routine programs PCI 0:24N:2x80 config register.
780 * This routine programs PCI 0:24N:2x60,64,68,6C config registers (CS Mask 0-3)
782 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
784 * @return TRUE - indicates that a FATAL error has not occurred
785 * @return FALSE - indicates that a FATAL error has occurred
790 IN OUT MEM_TECH_BLOCK *TechPtr
811 NBPtr = TechPtr->NBPtr;
812 MCTPtr = NBPtr->MCTPtr;
813 DCTPtr = NBPtr->DCTPtr;
818 for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel += 2) {
819 DimmID = ChipSel >> 1;
821 DimmQRDR = (DCTPtr->Timings.DimmQrPresent) | (DCTPtr->Timings.DimmDrPresent);
822 if (DCTPtr->Timings.DimmSpdCse & (UINT16) 1 << DimmID) {
823 CSSpdCSE |= (UINT16) ((DimmQRDR & (UINT16) 1 << DimmID) ? 3 : 1) << ChipSel;
825 if ((DCTPtr->Timings.DimmExclude & ((UINT16) 1 << DimmID)) != 0) {
826 CSExclude |= (UINT16) ((DimmQRDR & (UINT16) 1 << DimmID) ? 3: 1) << ChipSel;
829 if (DCTPtr->Timings.DctDimmValid & ((UINT16)1 << DimmID)) {
830 MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferPtr, DimmID);
832 // Get the basic data
833 Rows = SpdBufferPtr[SPD_ROW_SZ] & 0x1F;
834 Cols = SpdBufferPtr[SPD_COL_SZ] & 0x1F;
835 Banks = SpdBufferPtr[SPD_L_BANKS];
836 Ranks = (SpdBufferPtr[SPD_DM_BANKS] & 0x07) + 1;
838 // Configure the bank encoding
839 Value8 = (Cols - 9) << 3;
840 Value8 |= (Banks == 8) ? 4 : 0;
841 Value8 |= (Rows - 13);
843 for (i = 0; i < 12; i++) {
844 if (Value8 == MemTGetBankAddr2 (i)) {
850 BankAddrReg |= ((UINT32)i << (ChipSel << 1));
852 // Mask value=(2pow(rows+cols+banks+3)-1)>>8,
853 // or 2pow(rows+cols+banks-5)-1
855 Value8 = Rows + Cols;
856 Value8 -= (Banks == 8) ? 2:3;
857 if (MCTPtr->Status[Sb128bitmode]) {
860 CsMask = ((UINT32)1 << Value8) - 1;
861 DCTPtr->Timings.CsPresent |= (UINT16)1 << ChipSel;
864 DCTPtr->Timings.CsPresent |= (UINT16)1 << (ChipSel + 1);
867 // Update the DRAM CS Mask for this chipselect
868 NBPtr->SetBitField (NBPtr, BFCSMask0Reg + (ChipSel >> 1), (CsMask & NBPtr->CsRegMsk));
872 // For ranks that need to be excluded, the loading of this rank should be considered
873 // in timing, so need to set CsPresent before setting CsTestFail
874 if ((CSSpdCSE != 0) || (CSExclude != 0)) {
875 if (!NBPtr->MemPtr->ErrorHandling (MCTPtr, NBPtr->Dct, (CSSpdCSE | CSExclude), &NBPtr->MemPtr->StdHeader)) {
880 // If there are no chip selects, we have an error situation.
881 if (DCTPtr->Timings.CsPresent == 0) {
882 PutEventLog (AGESA_ERROR, MEM_ERROR_NO_CHIPSELECT, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
883 SetMemError (AGESA_ERROR, MCTPtr);
886 NBPtr->SetBitField (NBPtr, BFDramBankAddrReg, BankAddrReg);
888 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
891 /* -----------------------------------------------------------------------------*/
894 * This function returns the low bit that will be swapped to enable CS interleaving
896 * @param[in] BankEnc - AddrMap Bank encoding from F2x80
897 * @param[in] *LowBit - pointer to low bit
898 * @param[in] *HiBit - pointer hight bit
903 MemTGetCSIntLvAddr2 (
909 CONST UINT8 ArrCodesLo[] = {6, 7, 7, 8, 8, 8, 8, 8, 9, 9, 8, 9};
910 CONST UINT8 ArrCodesHi[] = {19, 20, 21, 21, 21, 22, 22, 23, 23, 24, 24, 25};
911 ASSERT (BankEnc < GET_SIZE_OF (ArrCodesLo));
912 ASSERT (BankEnc < GET_SIZE_OF (ArrCodesHi));
913 // return ArrCodes[BankEnc];
914 *LowBit = ArrCodesLo[BankEnc];
915 *HiBit = ArrCodesHi[BankEnc];
918 /*----------------------------------------------------------------------------
921 *----------------------------------------------------------------------------
923 /* -----------------------------------------------------------------------------*/
926 * This function returns the CAS latency of the current frequency.
928 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
930 * @return CAS Latency
935 IN OUT MEM_TECH_BLOCK *TechPtr
938 return TechPtr->NBPtr->DCTPtr->Timings.CasL;
941 /* -----------------------------------------------------------------------------*/
944 * Get max frequency from OEM platform definition, from
945 * any user override (limiting) of max frequency, and
946 * from any Si Revision Specific information. Return
947 * the least of these three in DIE_STRUCT.PresetmaxFreq.
949 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
950 * @param[in] k - Frequency index
951 * @param[in] j - CAS Latency index
953 * @return TRUE - (k << 8) | j
960 IN OUT MEM_TECH_BLOCK *TechPtr,
965 if ((k > TechPtr->NBPtr->DCTPtr->Timings.TargetSpeed) || (j > J_MAX)) {
969 return TRUE; //(k << 8) | j;
973 /* -----------------------------------------------------------------------------*/
976 * Determine whether dimm(b,i) supports CL(j) and F(k)
978 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
979 * @param[in] k - Frequency index
980 * @param[in] j - CAS Latency index
981 * @param[in] i - DIMM number
983 * @return TRUE - DIMM supports
984 * @return FALSE - DIMM does not support
990 IN OUT MEM_TECH_BLOCK *TechPtr,
996 CONST UINT8 SpdBytesForCL[3] = { 9, 23, 25}; // SPD bytes for CL X, CL X-.5, and CL X-1
1002 UINT8 *SpdBufferPtr;
1003 MEM_NB_BLOCK *NBPtr;
1005 NBPtr = TechPtr->NBPtr;
1007 MemTGetDimmSpdBuffer2 (TechPtr, &SpdBufferPtr, i);
1008 CLj = (UINT8) 1 << (j + 2);
1009 CLi = SpdBufferPtr[SPD_CAS_LAT];
1012 // If this dimm supports the desired CAS latency...
1013 // Determine the SPD location of the dimm speed UINT8 appropriate
1014 // to the CAS latency indicated by Table_CL2_j.
1016 T1 = LibAmdBitScanReverse ((UINT32)CLj);
1017 T2 = LibAmdBitScanReverse ((UINT32)CLi);
1018 ASSERT ((T2 - T1) < 3);
1019 CLi = SpdBufferPtr[SpdBytesForCL[(T2 - T1)]];
1020 Tk = MemTGetTk2 (k);
1022 PutEventLog (AGESA_FATAL, MEM_ERROR_NO_CYC_TIME, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
1023 SetMemError (AGESA_WARNING, NBPtr->MCTPtr);
1024 } else if (Tk >= CLi) {
1031 /* -----------------------------------------------------------------------------*/
1034 * This function returns the cycle time
1036 * @param[in] k - CAS Latency index
1038 * @return Tk as specified by JEDEC SPD byte 9.
1047 CONST UINT8 TableTK[] = {0x00, 0x50, 0x3D, 0x30, 0x25, 0x18};
1048 ASSERT (k < GET_SIZE_OF (TableTK));
1052 /* -----------------------------------------------------------------------------*/
1055 * This function returns the encoded value of bank address.
1057 * @param[in] k value
1059 * @return RRRBCC, where CC is the number of Columns minus 9,
1060 * RRR is the number of Rows minus 12, and B is the number of banks
1070 CONST UINT8 TabBankAddr[] = {
1071 0x00, 0x08, 0x09, 0x10, 0x0C, 0x0D,
1072 0x11, 0x0E, 0x15, 0x16, 0x0F, 0x17
1074 ASSERT (k < GET_SIZE_OF (TabBankAddr));
1075 return TabBankAddr[k];
1078 /* -----------------------------------------------------------------------------*/
1081 * This function returns a pointer to the SPD Buffer of a specific dimm on
1082 * the current channel.
1084 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
1085 * @param[in,out] **SpdBuffer - Pointer to a pointer to a UINT8 Buffer
1086 * @param[in] Dimm - Dimm number
1089 * @return BOOLEAN - Value of DimmPresent
1090 * TRUE = Dimm is present, pointer is valid
1091 * FALSE = Dimm is not present, pointer has not been modified.
1095 MemTGetDimmSpdBuffer2 (
1096 IN OUT MEM_TECH_BLOCK *TechPtr,
1097 IN OUT UINT8 **SpdBuffer,
1101 CH_DEF_STRUCT *ChannelPtr;
1102 SPD_DEF_STRUCT *SPDPtr;
1103 BOOLEAN DimmPresent;
1105 DimmPresent = FALSE;
1106 ChannelPtr = TechPtr->NBPtr->ChannelPtr;
1107 ASSERT (Dimm < (sizeof (ChannelPtr->DimmSpdPtr) / sizeof (ChannelPtr->DimmSpdPtr[0])))
1108 SPDPtr = ChannelPtr->DimmSpdPtr[Dimm];
1111 if (SPDPtr != NULL) {
1112 DimmPresent = SPDPtr->DimmPresent;
1114 *SpdBuffer = SPDPtr->Data;