blob: dc6fa62ac992d0f8194497d7a5330a72e709521c [file] [log] [blame]
zbao7d94cf92012-07-02 14:19:14 +08001/* $NoKeywords:$ */
2/**
3 * @file
4 *
5 * mtspd3.c
6 *
7 * Technology SPD supporting functions for DDR3
8 *
9 * @xrefitem bom "File Content Label" "Release Content"
10 * @e project: AGESA
11 * @e sub-project: (Mem/Tech/DDR3)
12 * @e \$Revision: 63425 $ @e \$Date: 2011-12-22 11:24:10 -0600 (Thu, 22 Dec 2011) $
13 *
14 **/
15/*****************************************************************************
16*
Siyuan Wang641f00c2013-06-08 11:50:55 +080017 * Copyright (c) 2008 - 2012, Advanced Micro Devices, Inc.
18 * All rights reserved.
19 *
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.
30 *
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.
zbao7d94cf92012-07-02 14:19:14 +080041* ***************************************************************************
42*
43*/
44
45/*
46 *----------------------------------------------------------------------------
47 * MODULES USED
48 *
49 *----------------------------------------------------------------------------
50 */
51
52
53
54#include "AGESA.h"
55#include "AdvancedApi.h"
56#include "Ids.h"
57#include "mport.h"
58#include "mm.h"
59#include "mn.h"
60#include "mt.h"
61#include "mt3.h"
62#include "mu.h"
63#include "mtspd3.h"
64#include "mftds.h"
65#include "GeneralServices.h"
66#include "Filecode.h"
67CODE_GROUP (G1_PEICC)
68RDATA_GROUP (G1_PEICC)
69
70#define FILECODE PROC_MEM_TECH_DDR3_MTSPD3_FILECODE
71
72/*----------------------------------------------------------------------------
73 * DEFINITIONS AND MACROS
74 *
75 *----------------------------------------------------------------------------
76 */
77
78/*----------------------------------------------------------------------------
79 * TYPEDEFS AND STRUCTURES
80 *
81 *----------------------------------------------------------------------------
82 */
83
84/*----------------------------------------------------------------------------
85 * PROTOTYPES OF LOCAL FUNCTIONS
86 *
87 *----------------------------------------------------------------------------
88 */
89BOOLEAN
90STATIC
91MemTCRCCheck3 (
92 IN OUT UINT8 *SPDPtr
93 );
94
95UINT8
96STATIC
97MemTSPDGetTCL3 (
98 IN OUT MEM_TECH_BLOCK *TechPtr
99 );
100
101BOOLEAN
102STATIC
103MemTCheckBankAddr3 (
104 IN UINT8 Encode,
105 OUT UINT8 *Index
106 );
107
108/*----------------------------------------------------------------------------
109 * EXPORTED FUNCTIONS
110 *
111 *----------------------------------------------------------------------------
112 */
113
114extern BUILD_OPT_CFG UserOptions;
115
116/* -----------------------------------------------------------------------------*/
117/**
118 *
119 * This function sets the DRAM mode
120 *
121 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
122 *
123 * @return TRUE - indicates that the DRAM mode is set to DDR3
124 */
125
126BOOLEAN
127MemTSetDramMode3 (
128 IN OUT MEM_TECH_BLOCK *TechPtr
129 )
130{
131 TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFLegacyBiosMode, 0);
132 TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFDdr3Mode, 1);
133 return TRUE;
134}
135
136/* -----------------------------------------------------------------------------*/
137/**
138 *
139 * This function determines if DIMMs are present. It checks checksum and interrogates the SPDs
140 *
141 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
142 *
143 * @return TRUE - indicates that a FATAL error has not occurred
144 * @return FALSE - indicates that a FATAL error has occurred
145 */
146
147BOOLEAN
148MemTDIMMPresence3 (
149 IN OUT MEM_TECH_BLOCK *TechPtr
150 )
151{
152 UINT8 Dct;
153 UINT8 Channel;
154 UINT8 i;
155 MEM_PARAMETER_STRUCT *RefPtr;
Kyösti Mälkkiae296352016-04-19 07:17:35 +0300156 UINT8 *SpdBufferPtr = NULL;
zbao7d94cf92012-07-02 14:19:14 +0800157 DIE_STRUCT *MCTPtr;
158 DCT_STRUCT *DCTPtr;
159 CH_DEF_STRUCT *ChannelPtr;
160 MEM_NB_BLOCK *NBPtr;
161 BOOLEAN SPDCtrl;
162 UINT8 Devwidth;
163 UINT8 MaxDimms;
164 UINT8 NumDimmslots;
165 UINT8 Value8;
166 UINT16 DimmMask;
167 UINT32 DimmValidMask;
168
169 NBPtr = TechPtr->NBPtr;
170 RefPtr = NBPtr->RefPtr;
171 MCTPtr = NBPtr->MCTPtr;
172
173 SPDCtrl = UserOptions.CfgIgnoreSpdChecksum;
174 DimmValidMask = 0;
175 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
176 NBPtr->SwitchDCT (NBPtr, Dct);
177 DCTPtr = NBPtr->DCTPtr;
178 for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) {
179 NBPtr->SwitchChannel (NBPtr, Channel);
180 ChannelPtr = NBPtr->ChannelPtr;
181 ChannelPtr->DimmQrPresent = 0;
182 //
183 // Get the maximum number of DIMMs
184 //
185 MaxDimms = MAX_DIMMS_PER_CHANNEL;
186 NumDimmslots = GetMaxDimmsPerChannel (NBPtr->RefPtr->PlatformMemoryConfiguration,
187 MCTPtr->SocketId,
188 ChannelPtr->ChannelID);
189 DimmValidMask |= (NumDimmslots == 3) ? 0x7 : 0x3;
190
191 for (i = 0; i < MaxDimms; i++) {
192 // Bitmask representing dimm #i.
193 DimmMask = (UINT16)1 << i;
194 //
195 if (MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, i)) {
196 MCTPtr->DimmPresent |= DimmMask;
197 //
198 // Check for valid checksum value
199 //
200 AGESA_TESTPOINT (TpProcMemSPDChecking, &(NBPtr->MemPtr->StdHeader));
201 if (SpdBufferPtr[SPD_TYPE] == JED_DDR3SDRAM) {
202 ChannelPtr->ChDimmValid |= DimmMask;
203 MCTPtr->DimmValid |= DimmMask;
204 } else if (NBPtr->IsSupported[excel847_0 ]) {
205 return FALSE;
206 } else {
207 // Current socket is set up to only support DDR3 dimms.
208 IDS_ERROR_TRAP;
209 }
210 if (!MemTCRCCheck3 (SpdBufferPtr) && !SPDCtrl) {
211 //
212 // NV_SPDCHK_RESTRT is set to 0,
213 // cannot ignore faulty SPD checksum
214 //
215 // Indicate checksum error
216 ChannelPtr->DimmSpdCse |= DimmMask;
217 PutEventLog (AGESA_ERROR, MEM_ERROR_CHECKSUM_NV_SPDCHK_RESTRT_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
218 SetMemError (AGESA_ERROR, MCTPtr);
219 }
220 //
221 // Check module type information.
222 //
223 if (SpdBufferPtr[SPD_DIMM_TYPE] == JED_LRDIMM) {
224 //
225 // LRDIMMS
226 //
227 if (i < NumDimmslots) {
228 ChannelPtr->LrDimmPresent |= DimmMask;
229 MCTPtr->LrDimmPresent |= DimmMask;
230
231 if (!UserOptions.CfgMemoryLRDimmCapable) {
232 PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_LRDIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
233 IDS_ERROR_TRAP;
234 }
235 TechPtr->TechnologySpecificHook[LrdimmPresence] (TechPtr, &i);
236 }
237 }
238 if (SpdBufferPtr[SPD_DIMM_TYPE] == JED_RDIMM || SpdBufferPtr[SPD_DIMM_TYPE] == JED_MINIRDIMM) {
239 //
240 // RDIMMS
241 //
242 ChannelPtr->RegDimmPresent |= DimmMask;
243 MCTPtr->RegDimmPresent |= DimmMask;
244 if (!UserOptions.CfgMemoryRDimmCapable) {
245 PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_RDIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
246 IDS_ERROR_TRAP;
247 }
248 }
249 if ((SpdBufferPtr[SPD_DIMM_TYPE] == JED_UDIMM) && !UserOptions.CfgMemoryUDimmCapable) {
250 PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_UDIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
251 IDS_ERROR_TRAP;
252 }
253 if (SpdBufferPtr[SPD_DIMM_TYPE] == JED_SODIMM) {
254 ChannelPtr->SODimmPresent |= DimmMask;
255 if (!UserOptions.CfgMemorySODimmCapable) {
256 PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_SODIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
257 IDS_ERROR_TRAP;
258 }
259 }
260 //
261 // Check error correction type
262 //
263 if ((SpdBufferPtr[SPD_ECCBITS] & JED_ECC) != 0) {
264 MCTPtr->DimmEccPresent |= DimmMask; // Dimm has ECC
265 }
266 //
267 // Get the Dimm width data
268 //
269 Devwidth = SpdBufferPtr[SPD_DEV_WIDTH] & 0x7;
270 switch (Devwidth) {
271 case 0:
272 ChannelPtr->Dimmx4Present |= DimmMask;
273 if ((ChannelPtr->LrDimmPresent & DimmMask) == 0) {
274 //
275 // DimmNibbleAccess indicates that a DIMM will use nibble signaling and use nibble training.
276 // LRDIMMs will not use Nibble based signaling even if x4 parts are present.
277 //
278 if (i < NumDimmslots) {
279 ChannelPtr->DimmNibbleAccess |= DimmMask;
280 }
281 }
282 Devwidth = 4;
283 break;
284 case 1:
285 ChannelPtr->Dimmx8Present |= DimmMask;
286 Devwidth = 8;
287 break;
288 case 2:
289 ChannelPtr->Dimmx16Present |= DimmMask;
290 Devwidth = 16;
291 break;
292 default:
293 IDS_ERROR_TRAP;
294 }
295 //
296 // Check for 'analysis probe installed'
297 // if (SpdBufferPtr[SPD_ATTRIB] & JED_PROBE_MSK)
298 //
299 // Determine the geometry of the DIMM module
300 // if (SpdBufferPtr[SPD_DM_BANKS] & SP_DPL_BIT)
301 //
302 // specify the number of ranks
303 //
304 Value8 = ((SpdBufferPtr[SPD_RANKS] >> 3) & 0x07) + 1;
305 if (Value8 == 5) {
306 // Octal Rank
307 Value8 = 8;
308 }
309 //
310 // For LRDIMMS we will assume that if there are at least 4 Physical ranks, then it Could be used
311 // as a QR RDIMM with a rank Mux of x1 and therefore all four CS will be used. So an 8R LRDIMM will
312 // be marked as a QR even if Rank multiplication allows it to use only 2 logical ranks.
313 //
314 if ((ChannelPtr->LrDimmPresent & DimmMask) != 0) {
315 //
316 // LRDIMM Physical Ranks
317 //
318 ChannelPtr->LrdimmPhysicalRanks[i] = Value8;
319 }
320 if (Value8 > 2) {
321 if (!UserOptions.CfgMemoryQuadRankCapable) {
322 PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_QRDIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
323 }
324 //
325 // Mark this Dimm as Quad Rank
326 //
327 ChannelPtr->DimmQrPresent |= DimmMask;
328 Value8 = 2;
329 } else if (Value8 == 2) {
330 ChannelPtr->DimmDrPresent |= DimmMask; // Dual rank dimms
331 } else {
332 ChannelPtr->DimmSRPresent |= DimmMask; // Single rank dimms
333 }
334 //
335 // Calculate bus loading per Channel
336 if (Devwidth == 16) {
337 Devwidth = 4;
338 } else if (Devwidth == 4) {
339 Devwidth = 16;
340 }
341 //
342 // Double Addr bus load value for dual rank DIMMs (Unless LRDIMM)
343 //
344 if (((ChannelPtr->LrDimmPresent & DimmMask) == 0) && (Value8 == 2) ) {
345 Devwidth = Devwidth << 1;
346 }
347 //
348 ChannelPtr->Ranks = ChannelPtr->Ranks + Value8;
349 ChannelPtr->Loads = ChannelPtr->Loads + Devwidth;
350 if ((i < NumDimmslots) || ((ChannelPtr->DimmQrPresent & DimmMask) == 0)) {
351 ChannelPtr->Dimms++;
352 }
353 //
354 // Check address mirror support for Unbuffered Dimms or LRDimms
355 //
356 if ((ChannelPtr->RegDimmPresent & DimmMask) == 0) {
357 if ((SpdBufferPtr[SPD_ADDRMAP] & 1) != 0) {
358 ChannelPtr->DimmMirrorPresent |= DimmMask;
359 }
360 }
361 //
362 // Get byte62: Reference Raw Card information
363 //
364 ChannelPtr->RefRawCard[i] = SpdBufferPtr[SPD_RAWCARD] & 0x1F;
365 //
366 // Get control word values for RC3, RC4 and RC5
367 //
368 ChannelPtr->CtrlWrd03[i] = SpdBufferPtr[SPD_CTLWRD03] >> 4;
369 ChannelPtr->CtrlWrd04[i] = SpdBufferPtr[SPD_CTLWRD04] & 0x0F;
370 ChannelPtr->CtrlWrd05[i] = SpdBufferPtr[SPD_CTLWRD05] >> 4;
371 //
372 // Temporarily store info. of SPD byte 63 into CtrlWrd02(s),
373 // and they will be used late to calculate real RC2 and RC8 value
374 //
375 ChannelPtr->CtrlWrd02[i] = SpdBufferPtr[SPD_ADDRMAP] & 0x03;
376 //
377 // Copy the number of registers to the Ps Block to persist across frequency changes
378 //
379 NBPtr->PsPtr->NumOfReg[i] = SpdBufferPtr[SPD_ADDRMAP] & 0x03;
380 //
381 // Workaround for early revisions of DIMMs which SPD byte 63 is 0
382 //
383 if (NBPtr->PsPtr->NumOfReg[i] == JED_UNDEFINED) {
384 NBPtr->PsPtr->NumOfReg[i] = 1;
385 }
386 } // if DIMM present
387 } // Dimm loop
388
389 if (Channel == 0) {
390 DCTPtr->Timings.DctDimmValid = ChannelPtr->ChDimmValid;
391 DCTPtr->Timings.DimmMirrorPresent = ChannelPtr->DimmMirrorPresent;
392 DCTPtr->Timings.DimmSpdCse = ChannelPtr->DimmSpdCse;
393 DCTPtr->Timings.DimmQrPresent = ChannelPtr->DimmQrPresent;
394 DCTPtr->Timings.DimmDrPresent = ChannelPtr->DimmDrPresent;
395 DCTPtr->Timings.DimmSRPresent = ChannelPtr->DimmSRPresent;
396 DCTPtr->Timings.Dimmx4Present = ChannelPtr->Dimmx4Present;
397 DCTPtr->Timings.Dimmx8Present = ChannelPtr->Dimmx8Present;
398 DCTPtr->Timings.Dimmx16Present = ChannelPtr->Dimmx16Present;
399 }
400 if ((Channel != 1) || (Dct != 1)) {
401 MCTPtr->DimmPresent <<= 8;
402 MCTPtr->DimmValid <<= 8;
403 MCTPtr->RegDimmPresent <<= 8;
404 MCTPtr->LrDimmPresent <<= 8;
405 MCTPtr->DimmEccPresent <<= 8;
406 MCTPtr->DimmParPresent <<= 8;
407 DimmValidMask <<= 8;
408 }
409 } // Channel loop
410 } // DCT loop
411
412 // If we have DIMMs, some further general characteristics checking
413 if (MCTPtr->DimmValid != 0) {
414 // If there are registered dimms, all the dimms must be registered
415 if (MCTPtr->RegDimmPresent == MCTPtr->DimmValid) {
416 // All dimms registered
417 MCTPtr->Status[SbRegistered] = TRUE;
418 MCTPtr->Status[SbParDimms] = TRUE; // All DDR3 RDIMMs are parity capable
419 TechPtr->SetDqsEccTmgs = MemTSetDQSEccTmgsRDdr3; // Change the function pointer for DQS ECC timing
420 } else if (MCTPtr->RegDimmPresent != 0) {
421 // We have an illegal DIMM mismatch
422 PutEventLog (AGESA_FATAL, MEM_ERROR_MODULE_TYPE_MISMATCH_DIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
423 SetMemError (AGESA_FATAL, MCTPtr);
424 }
425 // If there are LrDimms, all the dimms must be LrDimms
426 if (MCTPtr->LrDimmPresent == (MCTPtr->DimmValid & DimmValidMask)) {
427 // All dimms LRDIMMs
428 MCTPtr->Status[SbLrdimms] = TRUE;
429 MCTPtr->Status[SbParDimms] = TRUE; // All DDR3 RDIMMs are parity capable
430 } else if (MCTPtr->LrDimmPresent != 0) {
431 // We have an illegal DIMM mismatch
432 PutEventLog (AGESA_FATAL, MEM_ERROR_MODULE_TYPE_MISMATCH_DIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
433 SetMemError (AGESA_FATAL, MCTPtr);
434 }
435
436 // check the ECC capability of the DIMMs
437 if (MCTPtr->DimmEccPresent == MCTPtr->DimmValid) {
438 MCTPtr->Status[SbEccDimms] = TRUE; // All dimms ECC capable
439 }
440 } else {
441 }
442
443 NBPtr->SwitchDCT (NBPtr, 0);
444 NBPtr->SwitchChannel (NBPtr, 0);
445 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
446}
447
448
449/* -----------------------------------------------------------------------------*/
450/**
451 *
452 * This function finds the maximum frequency that each channel is capable to run at.
453 *
454 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
455 *
456 * @return TRUE - indicates that a FATAL error has not occurred
457 * @return FALSE - indicates that a FATAL error has occurred
458 */
459
460BOOLEAN
461MemTSPDGetTargetSpeed3 (
462 IN OUT MEM_TECH_BLOCK *TechPtr
463 )
464{
Kyösti Mälkkiae296352016-04-19 07:17:35 +0300465 UINT8 *SpdBufferPtr = NULL;
zbao7d94cf92012-07-02 14:19:14 +0800466 UINT8 Dimm;
467 UINT8 Dct;
468 UINT8 Channel;
469 INT32 MTB_ps;
470 INT32 FTB_ps;
471 INT32 TCKmin_ps;
472 INT32 Value32;
473 MEM_NB_BLOCK *NBPtr;
474 DCT_STRUCT *DCTPtr;
475 CH_DEF_STRUCT *ChannelPtr;
476
477 NBPtr = TechPtr->NBPtr;
478 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
479 NBPtr->SwitchDCT (NBPtr, Dct);
480 DCTPtr = NBPtr->DCTPtr;
481 TCKmin_ps = 0;
482 for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) {
483 NBPtr->SwitchChannel (NBPtr, Channel);
484 ChannelPtr = NBPtr->ChannelPtr;
485 for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
486 if ((ChannelPtr->ChDimmValid & ((UINT8)1 << Dimm)) != 0) {
487 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, Dimm);
488
489 // Determine tCKmin(all) which is the largest tCKmin
490 // value for all modules on the memory Channel (SPD byte 12).
491 //
492 MTB_ps = ((INT32) SpdBufferPtr[SPD_DIVIDENT] * 1000) / SpdBufferPtr[SPD_DIVISOR];
493 FTB_ps = (SpdBufferPtr[SPD_FTB] >> 4) / (SpdBufferPtr[SPD_FTB] & 0xF);
494 Value32 = (MTB_ps * SpdBufferPtr[SPD_TCK]) + (FTB_ps * (INT8) SpdBufferPtr[SPD_TCK_FTB]) ;
495 if (TCKmin_ps < Value32) {
496 TCKmin_ps = Value32;
497 }
498 }
499 }
500 }
501 if (TCKmin_ps <= 938) {
502 DCTPtr->Timings.TargetSpeed = DDR2133_FREQUENCY;
503 } else if (TCKmin_ps <= 1071) {
504 DCTPtr->Timings.TargetSpeed = DDR1866_FREQUENCY;
505 } else if (TCKmin_ps <= 1250) {
506 DCTPtr->Timings.TargetSpeed = DDR1600_FREQUENCY;
507 } else if (TCKmin_ps <= 1500) {
508 DCTPtr->Timings.TargetSpeed = DDR1333_FREQUENCY;
509 } else if (TCKmin_ps <= 1875) {
510 DCTPtr->Timings.TargetSpeed = DDR1066_FREQUENCY;
511 } else if (TCKmin_ps <= 2500) {
512 DCTPtr->Timings.TargetSpeed = DDR800_FREQUENCY;
513 } else {
514 DCTPtr->Timings.TargetSpeed = DDR667_FREQUENCY;
515 }
516 }
517
518 // Ensure the target speed can be applied to all channels of the current node
519 NBPtr->SyncTargetSpeed (NBPtr);
520
521 // Set the start-up frequency
522 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
523 NBPtr->SwitchDCT (NBPtr, Dct);
524 NBPtr->DCTPtr->Timings.Speed = TechPtr->NBPtr->StartupSpeed;
525 }
526 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
527}
528
529/* -----------------------------------------------------------------------------*/
530/**
531 *
532 * This function check the symmetry of DIMM pairs (DIMM on Channel A matching with
533 * DIMM on Channel B), the overall DIMM population, and determine the width mode:
534 * 64-bit, 64-bit muxed, 128-bit.
535 *
536 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
537 *
538 * @return TRUE - indicates that a FATAL error has not occurred
539 * @return FALSE - indicates that a FATAL error has occurred
540 */
541
542BOOLEAN
543MemTSPDCalcWidth3 (
544 IN OUT MEM_TECH_BLOCK *TechPtr
545 )
546{
Kyösti Mälkkiae296352016-04-19 07:17:35 +0300547 UINT8 *SpdBufferAPtr = NULL;
548 UINT8 *SpdBufferBPtr = NULL;
zbao7d94cf92012-07-02 14:19:14 +0800549 MEM_NB_BLOCK *NBPtr;
550 DIE_STRUCT *MCTPtr;
551 DCT_STRUCT *DCTPtr;
552 UINT8 i;
553 UINT16 DimmMask;
554 UINT8 UngangMode;
555
556 NBPtr = TechPtr->NBPtr;
557 MCTPtr = NBPtr->MCTPtr;
558 DCTPtr = NBPtr->DCTPtr;
559 UngangMode = UserOptions.CfgMemoryModeUnganged;
560 // Does not support ganged mode for DDR3 dimms
561 ASSERT (UngangMode);
562 IDS_OPTION_HOOK (IDS_GANGING_MODE, &UngangMode, &(NBPtr->MemPtr->StdHeader));
563
564 // Check symmetry of channel A and channel B dimms for 128-bit mode
565 // capability.
566 //
567 AGESA_TESTPOINT (TpProcMemModeChecking, &(NBPtr->MemPtr->StdHeader));
568 i = 0;
569 if (!UngangMode) {
570 if (MCTPtr->DctData[0].Timings.DctDimmValid == MCTPtr->DctData[1].Timings.DctDimmValid) {
571 for (; i < MAX_DIMMS_PER_CHANNEL; i++) {
572 DimmMask = (UINT16)1 << i;
573 if ((DCTPtr->Timings.DctDimmValid & DimmMask) != 0) {
574 NBPtr->SwitchDCT (NBPtr, 0);
575 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferAPtr, i);
576 NBPtr->SwitchDCT (NBPtr, 1);
577 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferBPtr, i);
578 // compare rows and columns
579 if ((SpdBufferAPtr[SPD_ROW_SZ]&0x3F) != (SpdBufferBPtr[SPD_ROW_SZ]&0x3F)) {
580 break;
581 }
582 if ((SpdBufferAPtr[SPD_DENSITY]&0x0F) != (SpdBufferBPtr[SPD_DENSITY]&0x0F)) {
583 break;
584 }
585 // compare ranks and devwidth
586 if ((SpdBufferAPtr[SPD_DEV_WIDTH]&0x7F) != (SpdBufferBPtr[SPD_DEV_WIDTH]&0x7F)) {
587 break;
588 }
589 }
590 }
591 }
592 if (i < MAX_DIMMS_PER_CHANNEL) {
593 PutEventLog (AGESA_ALERT, MEM_ALERT_ORG_MISMATCH_DIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
594 SetMemError (AGESA_ALERT, MCTPtr);
595 } else {
596 NBPtr->Ganged = TRUE;
597 MCTPtr->GangedMode = TRUE;
598 MCTPtr->Status[Sb128bitmode] = TRUE;
599 NBPtr->SetBitField (NBPtr, BFDctGangEn, 1);
600 }
601 }
602
603 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
604}
605
606
607/* -----------------------------------------------------------------------------*/
608/**
609 *
610 * Initialize DCT Timing registers as per DIMM SPD.
611 * For primary timing (T, CL) use best case T value.
612 * For secondary timing params., use most aggressive settings
613 * of slowest DIMM.
614 *
615 * Note:
616 * There are three components to determining "maximum frequency": SPD component,
617 * Bus load component, and "Preset" max frequency component.
618 * The SPD component is a function of the min cycle time specified by each DIMM,
619 * and the interaction of cycle times from all DIMMs in conjunction with CAS
620 * latency. The SPD component only applies when user timing mode is 'Auto'.
621 *
622 * The Bus load component is a limiting factor determined by electrical
623 * characteristics on the bus as a result of varying number of device loads. The
624 * Bus load component is specific to each platform but may also be a function of
625 * other factors. The bus load component only applies when user timing mode is
626 * ' Auto'.
627 *
628 * The Preset component is subdivided into three items and is the minimum of
629 * the set: Silicon revision, user limit setting when user timing mode is 'Auto' and
630 * memclock mode is 'Limit', OEM build specification of the maximum frequency.
631 * The Preset component only applies when user timing mode is 'Auto'.
632 *
633 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
634 *
635 * @return TRUE - indicates that a FATAL error has not occurred
636 * @return FALSE - indicates that a FATAL error has occurred
637 */
638
639BOOLEAN
640MemTAutoCycTiming3 (
641 IN OUT MEM_TECH_BLOCK *TechPtr
642 )
643{
644 CONST UINT8 SpdIndexes[] = {
645 SPD_TRCD,
646 SPD_TRP,
647 SPD_TRTP,
648 SPD_TRAS,
649 SPD_TRC,
650 SPD_TWR,
651 SPD_TRRD,
652 SPD_TWTR,
653 SPD_TFAW
654 };
655
656 CONST UINT8 SpdFTBIndexes[] = {
657 SPD_TRCD_FTB,
658 SPD_TRP_FTB,
659 0,
660 0,
661 SPD_TRC_FTB,
662 0,
663 0,
664 0,
665 0
666 };
667
Kyösti Mälkkiae296352016-04-19 07:17:35 +0300668 UINT8 *SpdBufferPtr = NULL;
zbao7d94cf92012-07-02 14:19:14 +0800669 INT32 MiniMaxTmg[GET_SIZE_OF (SpdIndexes)];
670 UINT8 MiniMaxTrfc[4];
671
672 DIE_STRUCT *MCTPtr;
673 DCT_STRUCT *DCTPtr;
674 MEM_NB_BLOCK *NBPtr;
675 UINT16 DimmMask;
676 INT32 Value32;
677 INT32 MTB_ps;
678 INT32 FTB_ps;
679 INT32 TCK_ps;
680 UINT8 i;
681 UINT8 j;
682 UINT8 Value8;
683 UINT8 *StatTmgPtr;
684 UINT16 *StatDimmTmgPtr;
685
686 NBPtr = TechPtr->NBPtr;
687 MCTPtr = NBPtr->MCTPtr;
688 DCTPtr = NBPtr->DCTPtr;
689 // initialize mini-max arrays
690 for (j = 0; j < GET_SIZE_OF (MiniMaxTmg); j++) {
691 MiniMaxTmg[j] = 0;
692 }
693 for (j = 0; j < GET_SIZE_OF (MiniMaxTrfc); j++) {
694 MiniMaxTrfc[j] = 0;
695 }
696
697 // ======================================================================
698 // Get primary timing (CAS Latency and Cycle Time)
699 // ======================================================================
700 // Get OEM specific load variant max
701 //
702
703 //======================================================================
704 // Gather all DIMM mini-max values for cycle timing data
705 //======================================================================
706 //
707 DimmMask = 1;
708 for (i = 0; i < (MAX_CS_PER_CHANNEL / 2); i++) {
709 if ((DCTPtr->Timings.DctDimmValid & DimmMask) != 0) {
710 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, i);
711 MTB_ps = ((INT32) SpdBufferPtr[SPD_DIVIDENT] * 1000) / SpdBufferPtr[SPD_DIVISOR];
712 FTB_ps = (SpdBufferPtr[SPD_FTB] >> 4) / (SpdBufferPtr[SPD_FTB] & 0xF);
713
714 for (j = 0; j < GET_SIZE_OF (SpdIndexes); j++) {
715 Value32 = (UINT16)SpdBufferPtr[SpdIndexes[j]];
716 if (SpdIndexes[j] == SPD_TRC) {
717 Value32 |= ((UINT16)SpdBufferPtr[SPD_UPPER_TRC] & 0xF0) << 4;
718 } else if (SpdIndexes[j] == SPD_TRAS) {
719 Value32 |= ((UINT16)SpdBufferPtr[SPD_UPPER_TRAS] & 0x0F) << 8;
720 } else if (SpdIndexes[j] == SPD_TFAW) {
721 Value32 |= ((UINT16)SpdBufferPtr[SPD_UPPER_TFAW] & 0x0F) << 8;
722 }
723
724 Value32 *= MTB_ps;
725 if (SpdFTBIndexes[j] != 0) {
726 Value32 += (FTB_ps * (INT8) SpdBufferPtr[SpdFTBIndexes[j]]) ;
727 }
728 if (MiniMaxTmg[j] < Value32) {
729 MiniMaxTmg[j] = Value32;
730 }
731 }
732
733 // get Trfc0 - Trfc3 values
734 Value8 = SpdBufferPtr[SPD_DENSITY] & 0x0F;
735 if (MiniMaxTrfc[i] < Value8) {
736 MiniMaxTrfc[i] = Value8;
737 }
738 }
739 DimmMask <<= 1;
740 }
741
742 // ======================================================================
743 // Convert DRAM CycleTiming values and store into DCT structure
744 // ======================================================================
745 //
746 TCK_ps = 1000500 / DCTPtr->Timings.Speed;
747
748 StatDimmTmgPtr = &DCTPtr->Timings.DIMMTrcd;
749 StatTmgPtr = &DCTPtr->Timings.Trcd;
750 for (j = 0; j < GET_SIZE_OF (SpdIndexes); j++) {
751 Value32 = MiniMaxTmg[j];
752
753 MiniMaxTmg[j] = (MiniMaxTmg[j] + TCK_ps - 1) / TCK_ps;
754
755 StatDimmTmgPtr[j] = (UINT16) (Value32 / (1000 / 40));
756 StatTmgPtr[j] = (UINT8) MiniMaxTmg[j];
757 }
758 DCTPtr->Timings.Trfc0 = MiniMaxTrfc[0];
759 DCTPtr->Timings.Trfc1 = MiniMaxTrfc[1];
760 DCTPtr->Timings.Trfc2 = MiniMaxTrfc[2];
761 DCTPtr->Timings.Trfc3 = MiniMaxTrfc[3];
762
763 DCTPtr->Timings.CasL = MemTSPDGetTCL3 (TechPtr);
764
765 //======================================================================
766 // Program DRAM Timing values
767 //======================================================================
768 //
769 NBPtr->ProgramCycTimings (NBPtr);
770
771 MemFInitTableDrive (NBPtr, MTAfterAutoCycTiming);
772
773 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
774}
775
776/* -----------------------------------------------------------------------------*/
777/**
778 *
779 * This function sets the bank addressing, program Mask values and build a chip-select population map.
780 * This routine programs PCI 0:24N:2x80 config register.
781 * This routine programs PCI 0:24N:2x60,64,68,6C config registers (CS Mask 0-3)
782 *
783 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
784 *
785 * @return TRUE - indicates that a FATAL error has not occurred
786 * @return FALSE - indicates that a FATAL error has occurred
787 */
788
789BOOLEAN
790MemTSPDSetBanks3 (
791 IN OUT MEM_TECH_BLOCK *TechPtr
792 )
793{
Kyösti Mälkkiae296352016-04-19 07:17:35 +0300794 UINT8 *SpdBufferPtr = NULL;
zbao7d94cf92012-07-02 14:19:14 +0800795 UINT8 i;
796 UINT8 ChipSel;
797 UINT8 DimmID;
798 UINT8 Value8;
799 UINT8 Rows;
800 UINT8 Cols;
801 UINT8 Ranks;
802 UINT8 Banks;
803 UINT32 BankAddrReg;
804 UINT32 CsMask;
805 UINT16 CSSpdCSE;
806 UINT16 CSExclude;
807 UINT16 DimmQRDR;
808 DIE_STRUCT *MCTPtr;
809 DCT_STRUCT *DCTPtr;
810 MEM_NB_BLOCK *NBPtr;
811
812 NBPtr = TechPtr->NBPtr;
813 MCTPtr = NBPtr->MCTPtr;
814 DCTPtr = NBPtr->DCTPtr;
815 BankAddrReg = 0;
816 CSSpdCSE = 0;
817 CSExclude = 0;
818
819 for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel += 2) {
820 DimmID = ChipSel >> 1;
821
822 DimmQRDR = (DCTPtr->Timings.DimmQrPresent) | (DCTPtr->Timings.DimmDrPresent);
823 if ((DCTPtr->Timings.DimmSpdCse & ((UINT16) 1 << DimmID)) != 0) {
824 CSSpdCSE |= (UINT16) ((DimmQRDR & (UINT16) 1 << DimmID) ? 3 : 1) << ChipSel;
825 }
826 if ((DCTPtr->Timings.DimmExclude & ((UINT16) 1 << DimmID)) != 0) {
827 CSExclude |= (UINT16) ((DimmQRDR & (UINT16) 1 << DimmID) ? 3: 1) << ChipSel;
828 }
829
830 if ((DCTPtr->Timings.DctDimmValid & ((UINT16)1 << DimmID)) != 0) {
831 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, DimmID);
832
833 // Get the basic data
834 Rows = (SpdBufferPtr[SPD_ROW_SZ] >> 3) & 0x7;
835 Cols = SpdBufferPtr[SPD_COL_SZ] & 0x7;
836 Banks = (SpdBufferPtr[SPD_L_BANKS] >> 4) & 0x7;
837 Ranks = ((SpdBufferPtr[SPD_RANKS] >> 3) & 0x07) + 1;
838 if (Ranks == 5) {
839 Ranks = 8;
840 }
841 //
842 // Configure the bank encoding
843 // Use a 6-bit key into a lookup table.
844 // Key (index) = RRRBCC, where CC is the number of Columns minus 9,
845 // RRR is the number of Rows minus 12, and B is the number of banks
846 // minus 3.
847 //
848 Value8 = Cols;
849 Value8 |= (Banks == 1) ? 4 : 0;
850 Value8 |= Rows << 3;
851
852 if (MemTCheckBankAddr3 (Value8, &i)) {
853 //
854 // Mask value=(2pow(rows+cols+banks+3)-1)>>8,
855 // or 2pow(rows+cols+banks-5)-1
856 //
857 Value8 = (Rows + 12) + (Cols + 9) + (Banks + 3) + 3 - 8;
858 if (MCTPtr->Status[Sb128bitmode]) {
859 Value8++;
860 }
861
862 DCTPtr->Timings.CsPresent |= (UINT16)1 << ChipSel;
863
864 if (Ranks >= 2) {
865 DCTPtr->Timings.CsPresent |= (UINT16)1 << (ChipSel + 1);
866 }
867 //
868 // Determine LRDIMM Rank Multiplication
869 //
870 if (TechPtr->TechnologySpecificHook[LrdimmRankMultiplication] (TechPtr, &DimmID)) {
871 //
872 // Increase the CS Size by the rank multiplication factor
873 //
874 Value8 += ((NBPtr->ChannelPtr->LrDimmRankMult[DimmID]) >> 1);
875 CsMask = ((UINT32)1 << Value8) - 1;
876 CsMask &= NBPtr->CsRegMsk;
877 CsMask |= (NBPtr->GetBitField (NBPtr, BFRankDef0 + DimmID) & 0x03);
878 } else {
879 CsMask = ((UINT32)1 << Value8) - 1;
880 CsMask &= NBPtr->CsRegMsk;
881 }
882 //
883 // Update the DRAM CS Mask and BankAddrReg for this chipselect
884 //
885 if ((DCTPtr->Timings.CsPresent & (UINT16)3 << ChipSel) != 0) {
886 NBPtr->SetBitField (NBPtr, BFCSMask0Reg + (ChipSel >> 1), (CsMask));
887 BankAddrReg |= ((UINT32)i << (ChipSel << 1));
888 }
889 } else {
890 //
891 // Dimm is not supported, as no address mapping is found.
892 //
893 DCTPtr->Timings.CsPresent |= (UINT16)1 << ChipSel;
894 DCTPtr->Timings.CsTestFail |= (UINT16)1 << ChipSel;
895 if (Ranks >= 2) {
896 DCTPtr->Timings.CsPresent |= (UINT16)1 << (ChipSel + 1);
897 DCTPtr->Timings.CsTestFail |= (UINT16)1 << (ChipSel + 1);
898 }
899 PutEventLog (AGESA_ERROR, MEM_ERROR_NO_ADDRESS_MAPPING, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, DimmID, &NBPtr->MemPtr->StdHeader);
900 SetMemError (AGESA_ERROR, MCTPtr);
901 }
902 } //if (MemTCheckBankAddr3 (Value8, &i)
903 }
904 // For ranks that need to be excluded, the loading of this rank should be considered
905 // in timing, so need to set CsPresent before setting CsTestFail
906 if ((CSSpdCSE != 0) || (CSExclude != 0)) {
907 if (!NBPtr->MemPtr->ErrorHandling (MCTPtr, NBPtr->Dct, (CSSpdCSE | CSExclude), &NBPtr->MemPtr->StdHeader)) {
908 ASSERT (FALSE);
909 }
910 }
911
912 // If there are no chip selects, we have an error situation.
913 if (DCTPtr->Timings.CsPresent == 0) {
914 PutEventLog (AGESA_ERROR, MEM_ERROR_NO_CHIPSELECT, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
915 SetMemError (AGESA_ERROR, MCTPtr);
916 }
917
918 NBPtr->SetBitField (NBPtr, BFDramBankAddrReg, BankAddrReg);
919
920 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
921}
922
923/* -----------------------------------------------------------------------------*/
924/**
925 *
926 * This function returns the low bit that will be swapped to enable CS interleaving
927 *
928 * @param[in] BankEnc - AddrMap Bank encoding from F2x80
929 * @param[in] *LowBit - pointer to low bit
930 * @param[in] *HiBit - pointer hight bit
931 *
932 */
933
934VOID
935MemTGetCSIntLvAddr3 (
936 IN UINT8 BankEnc,
937 OUT UINT8 *LowBit,
938 OUT UINT8 *HiBit
939 )
940{
941 CONST UINT8 ArrCodesLo[] = {0, 8, 8, 0, 0, 8, 9, 8, 9, 9, 8, 9};
942 CONST UINT8 ArrCodesHi[] = {0, 20, 21, 0, 0, 22, 22, 23, 23, 24, 24, 25};
943 ASSERT (BankEnc < GET_SIZE_OF (ArrCodesLo));
944 ASSERT (BankEnc < GET_SIZE_OF (ArrCodesHi));
945 // return ArrCodes[BankEnc];
946 *LowBit = ArrCodesLo[BankEnc];
947 *HiBit = ArrCodesHi[BankEnc];
948}
949
950/*----------------------------------------------------------------------------
951 * LOCAL FUNCTIONS
952 *
953 *----------------------------------------------------------------------------
954 */
955
956/* -----------------------------------------------------------------------------*/
957/**
958 *
959 * This function determines if the checksum is correct
960 *
961 * @param[in] *SPDPtr - Pointer to SPD data
962 *
963 * @return TRUE - CRC check passes
964 * @return FALSE - CRC check fails
965 */
966
967BOOLEAN
968STATIC
969MemTCRCCheck3 (
970 IN OUT UINT8 *SPDPtr
971 )
972{
973 UINT16 Crc;
974 INT16 i;
975 INT16 j;
976 INT16 Count;
977
978 if (SPDPtr[SPD_TYPE] == JED_DDR3SDRAM) {
979 Count = (SPDPtr[SPD_BYTE_USED] & 0x80) ? 117 : 126;
980 Crc = 0;
981 for (j = 0; j < Count; j++) {
982 Crc = Crc ^ ((UINT16)SPDPtr[j] << 8);
983 for (i = 0; i < 8; i++) {
984 if (Crc & 0x8000) {
985 Crc = (Crc << 1) ^ 0x1021;
986 } else {
987 Crc = (Crc << 1);
988 }
989 }
990 }
991 if (*(UINT16 *) (SPDPtr + 126) == Crc) {
992 return TRUE;
993 }
994 }
995
996 return FALSE;
997}
998
999/* -----------------------------------------------------------------------------*/
1000/**
1001 *
1002 * This function returns the CAS latency of the current frequency (DCTPtr->Timings.Speed).
1003 *
1004 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
1005 *
1006 * @return CAS Latency
1007 */
1008
1009UINT8
1010STATIC
1011MemTSPDGetTCL3 (
1012 IN OUT MEM_TECH_BLOCK *TechPtr
1013 )
1014{
Kyösti Mälkkiae296352016-04-19 07:17:35 +03001015 UINT8 *SpdBufferPtr = NULL;
zbao7d94cf92012-07-02 14:19:14 +08001016 UINT8 CLdesired;
1017 UINT8 CLactual;
1018 UINT8 Dimm;
1019 UINT8 Channel;
1020 UINT16 CASLat;
1021 UINT16 Mask16;
1022 INT32 MTB_ps;
1023 INT32 FTB_ps;
1024 INT32 TAAmin_ps;
1025 INT32 TCKproposed_ps;
1026 INT32 Value32;
1027 BOOLEAN CltFail;
1028 MEM_NB_BLOCK *NBPtr;
1029 DCT_STRUCT *DCTPtr;
1030 CH_DEF_STRUCT *ChannelPtr;
1031
1032 NBPtr = TechPtr->NBPtr;
1033 DCTPtr = NBPtr->DCTPtr;
1034
1035 CASLat = 0xFFFF;
1036 TAAmin_ps = 0;
1037 CltFail = FALSE;
1038
1039 for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) {
1040 NBPtr->SwitchChannel (NBPtr, Channel);
1041 ChannelPtr = NBPtr->ChannelPtr;
1042 for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
1043 if ((ChannelPtr->ChDimmValid & ((UINT8)1 << Dimm)) != 0) {
1044 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, Dimm);
1045
1046 // Step 1: Determine the common set of supported CAS Latency
1047 // values for all modules on the memory Channel using the CAS
1048 // Latencies Supported in SPD bytes 14 and 15.
1049 //
1050 CASLat &= ((UINT16)SpdBufferPtr[SPD_CASHI] << 8) | SpdBufferPtr[SPD_CASLO];
1051
1052 // Step 2: Determine tAAmin(all) which is the largest tAAmin
1053 // value for all modules on the memory Channel (SPD byte 16).
1054 //
1055 MTB_ps = ((INT32) SpdBufferPtr[SPD_DIVIDENT] * 1000) / SpdBufferPtr[SPD_DIVISOR];
1056 FTB_ps = (SpdBufferPtr[SPD_FTB] >> 4) / (SpdBufferPtr[SPD_FTB] & 0xF);
1057 Value32 = (MTB_ps * SpdBufferPtr[SPD_TAA]) + (FTB_ps * (INT8) SpdBufferPtr[SPD_TAA_FTB]) ;
1058 if (TAAmin_ps < Value32) {
1059 TAAmin_ps = Value32;
1060 }
1061
1062 // Step 3: Determine tCKmin(all) which is the largest tCKmin
1063 // value for all modules on the memory Channel (SPD byte 12).
1064 // * This step has been done in SPDGetTargetSpeed
1065 }
1066 }
1067 }
1068
1069 TCKproposed_ps = 1000500 / DCTPtr->Timings.Speed;
1070
1071 // Step 4: For a proposed tCK value (tCKproposed) between tCKmin(all) and tCKmax,
1072 // determine the desired CAS Latency. If tCKproposed is not a standard JEDEC
1073 // value (2.5, 1.875, 1.5, or 1.25 ns) then tCKproposed must be adjusted to the
1074 // next lower standard tCK value for calculating CLdesired.
1075 // CLdesired = ceiling ( tAAmin(all) / tCKproposed )
1076 // where tAAmin is defined in Byte 16. The ceiling function requires that the
1077 // quotient be rounded up always.
1078 //
1079 CLdesired = (UINT8) ((TAAmin_ps + TCKproposed_ps - 1) / TCKproposed_ps);
1080
1081 // Step 5: Choose an actual CAS Latency (CLactual) that is greater than or equal
1082 // to CLdesired and is supported by all modules on the memory Channel as
1083 // determined in step 1. If no such value exists, choose a higher tCKproposed
1084 // value and repeat steps 4 and 5 until a solution is found.
1085 //
1086 CLactual = 4;
1087 for (Mask16 = 1; Mask16 < 0x8000; Mask16 <<= 1) {
1088 if (CASLat & Mask16) {
1089 if (CLdesired <= CLactual) {
1090 break;
1091 }
1092 }
1093 CLactual++;
1094 }
1095 if (Mask16 == 0x8000) {
1096 CltFail = TRUE;
1097 }
1098
1099 // Step 6: Once the calculation of CLactual is completed, the BIOS must also
1100 // verify that this CAS Latency value does not exceed tAAmax, which is 20 ns
1101 // for all DDR3 speed grades, by multiplying CLactual times tCKproposed. If
1102 // not, choose a lower CL value and repeat steps 5 and 6 until a solution is found.
1103 //
1104 if ((TCKproposed_ps * CLactual) > 20000) {
1105 CltFail = TRUE;
1106 }
1107
1108 if (!CltFail) {
1109 DCTPtr->Timings.CasL = CLactual;
1110 } else {
1111 // Fail to find supported Tcl, use 6 clocks since it is required for all DDR3 speed bin.
1112 DCTPtr->Timings.CasL = 6;
1113 }
1114
1115 return DCTPtr->Timings.CasL;
1116}
1117
1118/* -----------------------------------------------------------------------------*/
1119/**
1120 *
1121 * This function returns the encoded value of bank address.
1122 *
1123 * @param[in] Encode - RRRBCC, where CC is the number of Columns minus 9,
1124 * RRR is the number of Rows minus 12, and B is the number of banks
1125 * minus 3.
1126 * @param[out] *Index - index in bank address table
1127 * @return TRUE - encoded value is found.
1128 * FALSE - encoded value is not found.
1129 */
1130
1131BOOLEAN
1132STATIC
1133MemTCheckBankAddr3 (
1134 IN UINT8 Encode,
1135 OUT UINT8 *Index
1136 )
1137{
1138 UINT8 i;
1139 CONST UINT8 TabBankAddr[] = {
1140 0x3F, 0x01, 0x09, 0x3F, 0x3F, 0x11,
1141 0x0A, 0x19, 0x12, 0x1A, 0x21, 0x22
1142 };
1143
1144 for (i = 0; i < GET_SIZE_OF (TabBankAddr); i++) {
1145 if (Encode == TabBankAddr[i]) {
1146 *Index = i;
1147 return TRUE;
1148 }
1149 }
1150 return FALSE;
1151}
1152
1153/* -----------------------------------------------------------------------------*/
1154/**
1155 *
1156 * This function returns a pointer to the SPD Buffer of a specific dimm on
1157 * the current channel.
1158 *
1159 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
1160 * @param[in,out] **SpdBuffer - Pointer to a pointer to a UINT8 Buffer
1161 * @param[in] Dimm - Dimm number
1162 *
1163 *
1164 * @return BOOLEAN - Value of DimmPresent
1165 * TRUE = Dimm is present, pointer is valid
1166 * FALSE = Dimm is not present, pointer has not been modified.
1167 */
1168
1169BOOLEAN
1170MemTGetDimmSpdBuffer3 (
1171 IN OUT MEM_TECH_BLOCK *TechPtr,
1172 IN OUT UINT8 **SpdBuffer,
1173 IN UINT8 Dimm
1174 )
1175{
1176 CH_DEF_STRUCT *ChannelPtr;
1177 SPD_DEF_STRUCT *SPDPtr;
1178 BOOLEAN DimmPresent;
1179
1180 DimmPresent = FALSE;
1181 ChannelPtr = TechPtr->NBPtr->ChannelPtr;
1182 ASSERT (Dimm < (sizeof (ChannelPtr->DimmSpdPtr) / sizeof (ChannelPtr->DimmSpdPtr[0])))
1183 SPDPtr = ChannelPtr->DimmSpdPtr[Dimm];
1184
1185
1186 if (SPDPtr != NULL) {
1187 DimmPresent = SPDPtr->DimmPresent;
1188 if (DimmPresent) {
1189 *SpdBuffer = SPDPtr->Data;
1190 }
1191 }
1192 return DimmPresent;
1193}
1194