blob: a868c4b7d308ef66ae46de64f86817541a1b98a0 [file] [log] [blame]
zbao7d94cf92012-07-02 14:19:14 +08001/**
2 * @file
3 *
4 * mtlrdimm3.c
5 *
6 * Technology initialization and control workd support for DDR3 LRDIMMS
7 *
8 * @xrefitem bom "File Content Label" "Release Content"
9 * @e project: AGESA
10 * @e sub-project: (Mem/Tech/DDR3)
11 * @e \$Revision: 63425 $ @e \$Date: 2011-12-22 11:24:10 -0600 (Thu, 22 Dec 2011) $
12 *
13 **/
14/*****************************************************************************
15*
Siyuan Wang641f00c2013-06-08 11:50:55 +080016 * Copyright (c) 2008 - 2012, Advanced Micro Devices, Inc.
17 * All rights reserved.
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions are met:
21 * * Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * * Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * * Neither the name of Advanced Micro Devices, Inc. nor the names of
27 * its contributors may be used to endorse or promote products derived
28 * from this software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
34 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
37 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
zbao7d94cf92012-07-02 14:19:14 +080040* ***************************************************************************
41*
42*/
43
44/*
45 *----------------------------------------------------------------------------
46 * MODULES USED
47 *
48 *----------------------------------------------------------------------------
49 */
50
51
52
53#include "AGESA.h"
54#include "amdlib.h"
55#include "Ids.h"
56#include "mm.h"
57#include "mn.h"
58#include "mu.h"
59#include "mt.h"
60#include "mt3.h"
61#include "mtspd3.h"
62#include "mtrci3.h"
63#include "mtsdi3.h"
64#include "mtlrdimm3.h"
65#include "merrhdl.h"
66#include "GeneralServices.h"
67#include "Filecode.h"
68CODE_GROUP (G1_PEICC)
69RDATA_GROUP (G1_PEICC)
70#define FILECODE PROC_MEM_TECH_DDR3_MTLRDIMM3_FILECODE
71/*----------------------------------------------------------------------------
72 * DEFINITIONS AND MACROS
73 *
74 *----------------------------------------------------------------------------
75 */
76
77/*----------------------------------------------------------------------------
78 * TYPEDEFS AND STRUCTURES
79 *
80 *----------------------------------------------------------------------------
81 */
82
83/*----------------------------------------------------------------------------
84 * PROTOTYPES OF LOCAL FUNCTIONS
85 *
86 *----------------------------------------------------------------------------
87 */
88
89VOID
90STATIC
91MemTSendMBCtlWord3 (
92 IN OUT MEM_TECH_BLOCK *TechPtr,
93 IN UINT8 Fn,
94 IN UINT8 Rcw,
95 IN UINT8 Value
96 );
97
98VOID
99STATIC
100MemTSendExtMBCtlWord3 (
101 IN OUT MEM_TECH_BLOCK *TechPtr,
102 IN UINT16 Addr,
103 IN UINT16 Data,
104 IN UINT8 Len
105 );
106
107UINT8
108STATIC
109MemTGetSpecialMBCtlWord3 (
110 IN OUT MEM_TECH_BLOCK *TechPtr,
111 IN UINT8 Dimm,
112 IN UINT8 Fn,
113 IN UINT8 Rc
114 );
115
116BOOLEAN
117STATIC
118MemTLrDimmControlRegInit3 (
119 IN OUT MEM_TECH_BLOCK *TechPtr,
120 IN OUT VOID *OptParam
121 );
122
123BOOLEAN
124STATIC
125MemTLrDimmFreqChgCtrlWrd3 (
126 IN OUT MEM_TECH_BLOCK *TechPtr,
127 IN OUT VOID *OptParam
128 );
129
130BOOLEAN
131STATIC
132MemTWLPrepareLrdimm3 (
133 IN OUT MEM_TECH_BLOCK *TechPtr,
134 IN OUT VOID *Wl
135 );
136
137BOOLEAN
138STATIC
139MemTSendAllMRCmdsLR3 (
140 IN OUT MEM_TECH_BLOCK *TechPtr,
141 IN OUT VOID *CsPtr
142 );
143
144VOID
145STATIC
146MemTEMRS1Lr3 (
147 IN OUT MEM_TECH_BLOCK *TechPtr,
148 IN UINT8 ChipSel,
149 IN UINT8 PhyRank
150 );
151
152VOID
153STATIC
154MemTEMRS2Lr3 (
155 IN OUT MEM_TECH_BLOCK *TechPtr,
156 IN UINT8 ChipSel
157 );
158
159
160BOOLEAN
161STATIC
162MemTLrdimmRankMultiplication (
163 IN OUT MEM_TECH_BLOCK *TechPtr,
164 IN OUT VOID *DimmID
165 );
166
167BOOLEAN
168STATIC
169MemTLrdimmBuf2DramTrain3 (
170 IN OUT MEM_TECH_BLOCK *TechPtr,
171 IN OUT VOID *OptParam
172 );
173
174BOOLEAN
175STATIC
176MemTLrdimmSyncTrainedDlys (
177 IN OUT MEM_TECH_BLOCK *TechPtr,
178 IN OUT VOID *OptParam
179 );
180
181BOOLEAN
182STATIC
183MemTLrdimmPresence (
184 IN OUT MEM_TECH_BLOCK *TechPtr,
185 IN OUT VOID *DimmID
186 );
187
188UINT32
189STATIC
190MemTLrDimmGetBufferID (
191 IN OUT MEM_TECH_BLOCK *TechPtr,
192 IN UINT8 Dimm
193 );
194
195VOID
196STATIC
197MemTLrdimmInitHook (
198 IN OUT MEM_TECH_BLOCK *TechPtr,
199 IN LRDIMM_HOOK_ENTRYPOINT Entrypoint,
200 IN UINT8 Dimm,
201 IN OUT VOID *OptParam
202 );
203
204/*----------------------------------------------------------------------------
205 * EXPORTED FUNCTIONS
206 *
207 *----------------------------------------------------------------------------
208 */
209
210/* -----------------------------------------------------------------------------*/
211/**
212 *
213 * This function initializes LRDIMM functions.
214 *
215 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
216 *
217 */
218
219BOOLEAN
220MemTLrdimmConstructor3 (
221 IN OUT MEM_TECH_BLOCK *TechPtr
222 )
223{
224 TechPtr->TechnologySpecificHook[LrdimmSendAllMRCmds] = MemTSendAllMRCmdsLR3;
225 TechPtr->TechnologySpecificHook[LrdimmControlRegInit] = MemTLrDimmControlRegInit3;
226 TechPtr->TechnologySpecificHook[LrdimmFreqChgCtrlWrd] = MemTLrDimmFreqChgCtrlWrd3;
227 TechPtr->TechnologySpecificHook[WlTrainingPrepareLrdimm] = MemTWLPrepareLrdimm3;
228 TechPtr->TechnologySpecificHook[LrdimmRankMultiplication] = MemTLrdimmRankMultiplication;
229 TechPtr->TechnologySpecificHook[LrdimmBuf2DramTrain] = MemTLrdimmBuf2DramTrain3;
230 TechPtr->TechnologySpecificHook[LrdimmSyncTrainedDlys] = MemTLrdimmSyncTrainedDlys;
231 TechPtr->TechnologySpecificHook[LrdimmPresence] = MemTLrdimmPresence;
232 return TRUE;
233}
234
235/*----------------------------------------------------------------------------
236 * LOCAL FUNCTIONS
237 *
238 *----------------------------------------------------------------------------
239 */
240
241/* -----------------------------------------------------------------------------*/
242/**
243 *
244 * This function sends a Control word command to an LRDIMM Memory Buffer
245 *
246 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
247 * @param[in] Fn - control word function
248 * @param[in] Rcw - control word number
249 * @param[in] Value - value to send
250 *
251 */
252
253VOID
254STATIC
255MemTSendMBCtlWord3 (
256 IN OUT MEM_TECH_BLOCK *TechPtr,
257 IN UINT8 Fn,
258 IN UINT8 Rcw,
259 IN UINT8 Value
260 )
261{
262 MEM_NB_BLOCK *NBPtr;
263
264 NBPtr = TechPtr->NBPtr;
265
266 ASSERT (Rcw != RCW_FN_SELECT); // RC7 can only be used for function select
267 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tF%dRC%d = %x\n", Fn, Rcw, Value);
268 //
269 // Select the MB Function by sending the Fn number
270 // to the Function Select Control Word
271 //
272 MemUWait10ns (800, NBPtr->MemPtr);
273 MemTSendCtlWord3 (TechPtr, RCW_FN_SELECT, Fn);
274 //
275 // Send the value to the control word
276 //
277 MemUWait10ns (800, NBPtr->MemPtr);
278 MemTSendCtlWord3 (TechPtr, Rcw, Value);
279}
280
281/* -----------------------------------------------------------------------------*/
282/**
283 *
284 * This function sends a an Extended Control word command to an LRDIMM Memory Buffer
285 *
286 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
287 * @param[in] Addr - Extended Control Word Address
288 * Addr[15:12] Extended Control Workd Function Select
289 * Addr[11:0] Extended Control Word CSR Address
290 * @param[in] Data - value to send
291 * @param[in] Len - Length of data. 1 or 2 bytes
292 *
293 */
294
295VOID
296STATIC
297MemTSendExtMBCtlWord3 (
298 IN OUT MEM_TECH_BLOCK *TechPtr,
299 IN UINT16 Addr,
300 IN UINT16 Data,
301 IN UINT8 Len
302 )
303{
304 MEM_NB_BLOCK *NBPtr;
305
306 NBPtr = TechPtr->NBPtr;
307
308 ASSERT ((Len == 1) || (Len == 2));
309 if (Len == 2 ) {
310 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tExtRC_x%04x = %04x\n", Addr, Data);
311 } else {
312 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tExtRC_x%04x = %02x\n", Addr, (UINT8) (Data & 0xFF) );
313 }
314 //
315 // Select the MB Function by sending the Fn number
316 // to the Function Select Control Word
317 //
318 MemUWait10ns (800, NBPtr->MemPtr);
319 MemTSendCtlWord3 (TechPtr, RCW_FN_SELECT, 13);
320 //
321 // Send address via control words
322 //
323 MemUWait10ns (800, NBPtr->MemPtr);
324 MemTSendCtlWord3 (TechPtr, 9, (UINT8) (Addr >> 12));
325 MemUWait10ns (800, NBPtr->MemPtr);
326 MemTSendCtlWord3 (TechPtr, 10, (UINT8) (Addr & 0xF));
327 MemUWait10ns (800, NBPtr->MemPtr);
328 MemTSendCtlWord3 (TechPtr, 11, (UINT8) ((Addr >> 4) & 0x0F));
329 MemUWait10ns (800, NBPtr->MemPtr);
330 MemTSendCtlWord3 (TechPtr, 12, (UINT8) ((Addr >> 8) & 0x0F));
331 //
332 // Send the Lower Byte of Data
333 //
334 MemUWait10ns (800, NBPtr->MemPtr);
335 MemTSendCtlWord3 (TechPtr, 14, (UINT8) (Data & 0xF));
336 MemUWait10ns (800, NBPtr->MemPtr);
337 MemTSendCtlWord3 (TechPtr, 15, (UINT8) ((Data >> 4) & 0x0F));
338 //
339 // Send the Upper Byte of Data
340 //
341 if (Len == 2) {
342 MemUWait10ns (800, NBPtr->MemPtr);
343 MemTSendCtlWord3 (TechPtr, 10, (UINT8) ((Addr & 0xF) + 1));
344 MemUWait10ns (800, NBPtr->MemPtr);
345 MemTSendCtlWord3 (TechPtr, 14, (UINT8) ((Data >> 8) & 0xF));
346 MemUWait10ns (800, NBPtr->MemPtr);
347 MemTSendCtlWord3 (TechPtr, 15, (UINT8) ((Data >> 12) & 0xF));
348 }
349}
350
351/* -----------------------------------------------------------------------------*/
352/**
353 *
354 * This function gets the value of special RCW
355 *
356 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
357 * @param[in] Dimm - Physical LR DIMM number
358 * @param[in] Fn - control word function
359 * @param[in] Rc - control word number
360 *
361 * @return Special RCW value
362 *
363 */
364
365UINT8
366STATIC
367MemTGetSpecialMBCtlWord3 (
368 IN OUT MEM_TECH_BLOCK *TechPtr,
369 IN UINT8 Dimm,
370 IN UINT8 Fn,
371 IN UINT8 Rc
372 )
373{
374 CONST UINT8 F0RC13PhyRankTab[] = {3, 2, 0, 1, 0};
375 UINT8 PhyRanks;
376 UINT8 LogRanks;
377 UINT8 DramCap;
378 UINT8 Value8;
379 UINT8 *SpdBufferPtr;
380 MEM_NB_BLOCK *NBPtr;
381
382 NBPtr = TechPtr->NBPtr;
383
384 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, Dimm);
385
386 Value8 = 0;
387 switch (Fn) {
388 case 0:
389 switch (Rc) {
390 case 8:
391 // F0RC8
392 Value8 = NBPtr->PsPtr->F0RC8;
393 break;
394 case 10:
395 // F0RC10
396 // 2:0 OperatingSpeed: operating speed. BIOS: Table 88.
397 if (NBPtr->DCTPtr->Timings.Speed == DDR667_FREQUENCY) {
398 Value8 = 0;
399 } else {
400 Value8 = (UINT8) (NBPtr->DCTPtr->Timings.Speed / 133) - 3;
401 }
402 break;
403 case 11:
404 // F0RC11
405 // 3:2 ParityCalculation: partiy calculation. BIOS: Table.
406 // 1:0 OperatingVoltage: operating voltage. BIOS: IF(VDDIO == 1.5) THEN 00b ELSEIF (VDDIO ==
407 // 1.35) THEN 01b ELSE 10b ENDIF.
408 DramCap = SpdBufferPtr[SPD_DENSITY] & 0xF;
409 if (NBPtr->PsPtr->LrdimmRowAddrBits[Dimm] > 16) {
410 Value8 = 8;
411 } else {
412 Value8 = 4;
413 }
414 Value8 |= CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage);
415 break;
416 case 13:
417 // F0RC13
418 // 3:2 NumLogicalRanks: partiy calculation. BIOS: Table 90.
419 // 1:0 NumPhysicalRanks: operating voltage. BIOS: Table 89.
420 LogRanks = NBPtr->ChannelPtr->LrDimmLogicalRanks[Dimm] >> 1;
421 PhyRanks = F0RC13PhyRankTab[(SpdBufferPtr[SPD_RANKS] >> 3) & 7];
422 Value8 = (LogRanks << 2) | PhyRanks;
423 break;
424 case 14:
425 // F0RC14
426 // 3 DramBusWidth: DRAM bus width. BIOS: IF (DeviceWidth==0) THEN 0 ELSE 1 ENDIF.
427 // 2 MRSCommandControl: MRS command control. BIOS: IF (F0RC15[RankMultiplicationControl]
428 // > 0) THEN 1 ELSE 0 ENDIF.
429 // 1 RefreshPrechargeCommandControl: refresh and precharge command control. BIOS: IF
430 // (F0RC15[RankMultiplicationControl] > 0) THEN D18F2xA8_dct[1:0][LrDimmEnhRefEn] ELSE 0 ENDIF.
431 // 0 AddressMirror: address mirror. BIOS: RankMap. See D18F2x[5C:40]_dct[1:0][OnDimmMirror].
432 if ((SpdBufferPtr[SPD_DEV_WIDTH] & 7) != 0) {
433 Value8 |= 8;
434 }
435 if (NBPtr->ChannelPtr->LrDimmRankMult[Dimm] > 1) {
436 Value8 |= 4;
437 if (NBPtr->GetBitField (NBPtr, BFLrDimmEnhRefEn) == 1) {
438 Value8 |= 2;
439 }
440 }
441 if ((SpdBufferPtr[SPD_ADDRMAP] & 1) != 0) {
442 Value8 |= 1;
443 }
444 break;
445 case 15:
446 // F0RC15
447 // 3:0 RankMultiplicationControl: rank multiplication control. BIOS: Table 91.
448 DramCap = SpdBufferPtr[SPD_DENSITY] & 0xF;
449 ASSERT ((DramCap >= 2) && (DramCap <= 4)); // BKDG only lists 1Gb, 2Gb, and 4Gb
450 switch (NBPtr->ChannelPtr->LrDimmRankMult[Dimm]) {
451 case 1:
452 Value8 = 0;
453 break;
454 case 2:
455 Value8 = DramCap - 1;
456 break;
457 case 4:
458 Value8 = DramCap + 3;
459 break;
460 default:
461 ASSERT (FALSE);
462 }
463 break;
464 default:
465 ASSERT (FALSE);
466 }
467 break;
468 case 1:
469 switch (Rc) {
470 case 0:
471 // F1RC0
472 Value8 = NBPtr->PsPtr->F1RC0;
473 Value8 |= (UINT8) NBPtr->GetBitField (NBPtr, BFCSTimingMux67) << 3;
474 break;
475 case 1:
476 // F1RC1
477 Value8 = NBPtr->PsPtr->F1RC1;
478 break;
479 case 2:
480 // F1RC2
481 Value8 = NBPtr->PsPtr->F1RC2;
482 break;
483 case 9:
484 // F1RC9
485 if (NBPtr->GetBitField (NBPtr, BFLrDimmEnhRefEn) == 0) {
486 Value8 = 1;
487 }
488 break;
489 default:
490 ASSERT (FALSE);
491 }
492 break;
493 case 3:
494 switch (Rc) {
495 case 0:
496 // F3RC0
497 // 3 TDQSControl: TDQS control. BIOS: 0.
498 // 2:0 RttNom: RttNom. BIOS: Table 57, Table 60
499 Value8 = NBPtr->PsPtr->RttNom[Dimm << 1];
500 break;
501 case 1:
502 // F3RC1
503 // 3 Vref: Vref. BIOS: 0.
504 // 2:0 RttWr: RttWr. BIOS: Table 57, Table 60.
505 Value8 = NBPtr->PsPtr->RttWr[Dimm << 1];
506 break;
507 case 6:
508 // F3RC6
509 // IF (D18F2x90_dct[1:0][X4Dimm] == 0) THEN 1 ELSE 0
510 if (NBPtr->GetBitField (NBPtr, BFX4Dimm) == 0) {
511 Value8 = 8;
512 }
513 break;
514 default:
515 ASSERT (FALSE);
516 }
517 break;
518 default:
519 ASSERT (FALSE);
520 }
521
522 return Value8;
523}
524
525/* -----------------------------------------------------------------------------*/
526/**
527 *
528 * This function sends LRDIMM Control Words to all LRDIMMS
529 *
530 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
531 * @param[in,out] OptParam - Optional parameter
532 *
533 * @return TRUE
534 */
535
536BOOLEAN
537STATIC
538MemTLrDimmControlRegInit3 (
539 IN OUT MEM_TECH_BLOCK *TechPtr,
540 IN OUT VOID *OptParam
541 )
542{
543 CONST UINT8 RCWInitTable[] = {
544 // RCW, Mask, SPD
545 F0, RC0, 0x00, SPD_NONE,
546 F0, RC1, 0x00, SPD_NONE,
547 F0, RC2, 0x03, SPD_67,
548 F0, RC10, 0x00, SPECIAL_CASE,
549 F0, RC11, 0x00, SPECIAL_CASE,
550
551 F1, RC8, 0x0F, SPD_69,
552 F1, RC11, 0xF0, SPD_69,
553 F1, RC12, 0x0F, SPD_70,
554 F1, RC13, 0xF0, SPD_70,
555 F1, RC14, 0x0F, SPD_71,
556 F1, RC15, 0xF0, SPD_71,
557
558 WAIT_6US, 0, 0, 0,
559
560 F0, RC3, 0xF0, SPD_67,
561 F0, RC4, 0x0F, SPD_68,
562 F0, RC5, 0xF0, SPD_68,
563
564 F0, RC6, 0x00, SPD_NONE,
565 F0, RC8, 0x00, SPECIAL_CASE,
566 F0, RC9, 0x0C, SPD_NONE,
567 F0, RC13, 0x00, SPECIAL_CASE,
568 F0, RC14, 0x00, SPECIAL_CASE,
569 F0, RC15, 0x00, SPECIAL_CASE,
570
571 F1, RC0, 0x00, SPECIAL_CASE,
572 F1, RC1, 0x00, SPECIAL_CASE,
573 F1, RC2, 0x00, SPECIAL_CASE,
574 F1, RC3, 0x00, SPD_NONE,
575 F1, RC9, 0x00, SPECIAL_CASE,
576 F1, RC10, 0x00, SPD_NONE,
577
578 F2, RC0, 0x00, SPD_NONE,
579 F2, RC1, 0x00, SPD_NONE,
580 F2, RC2, 0x0F, SPD_NONE,
581 F2, RC3, 0x00, SPD_NONE,
582
583 F3, RC0, 0x00, SPECIAL_CASE,
584 F3, RC1, 0x00, SPECIAL_CASE,
585 F3, RC2, 0x01, SPD_NONE,
586 F3, RC6, 0x00, SPECIAL_CASE
587 //
588 // F3 RC[8,9] are programmed in MDQ RC loop
589 //
590 // F[10:3] RC[11,10] are programmed in QxODT RC loop
591 //
592 // F[15,14] RC[15:0] are programmed in personality RC loop
593 };
594
595 UINT8 Dimm;
596 UINT16 i;
597 UINT16 DimmMask;
598 UINT8 Fn;
599 UINT8 Rc;
600 UINT8 Mask;
601 UINT8 Spd;
602 UINT8 *SpdBufferPtr;
603 UINT8 FreqDiffOffset;
604 UINT8 Value8;
605 UINT32 Temp32;
606 MEM_DATA_STRUCT *MemPtr;
607 MEM_NB_BLOCK *NBPtr;
608
609 NBPtr = TechPtr->NBPtr;
610 MemPtr = NBPtr->MemPtr;
611
612 if (NBPtr->MCTPtr->Status[SbLrdimms]) {
613 for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
614 DimmMask = (UINT16)1 << Dimm;
615 if ((NBPtr->ChannelPtr->LrDimmPresent & DimmMask) != 0) {
616 //
617 // Select the Target Chipselects
618 //
619 NBPtr->SetBitField (NBPtr, BFMrsChipSel, (Dimm << 1));
620 NBPtr->SetBitField (NBPtr, BFCtrlWordCS, 3 << (Dimm << 1));
621
622 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, Dimm);
623
624 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tSending LRDIMM Control Words: Dimm %02x\n", Dimm);
625
626 for (i = 0; i < sizeof (RCWInitTable) ; i += 4) {
627 Fn = RCWInitTable[i];
628 Rc = RCWInitTable[i + 1];
629 Mask = RCWInitTable[i + 2];
630 Spd = RCWInitTable[i + 3];
631
632 if (Fn == WAIT_6US) {
633 MemUWait10ns (600, MemPtr); // wait 6us for TSTAB
634 } else {
635 if (Spd == SPD_NONE) {
636 Value8 = Mask;
637 } else if (Spd == SPECIAL_CASE) {
638 Value8 = MemTGetSpecialMBCtlWord3 (TechPtr, Dimm, Fn, Rc);
639 } else {
640 Value8 = (Mask > 0x0F) ? ((SpdBufferPtr[Spd] & Mask) >> 4) : (SpdBufferPtr[Spd] & Mask);
641 }
642 MemTSendMBCtlWord3 (TechPtr, Fn, Rc, Value8);
643 }
644 }
645
646 FreqDiffOffset = (UINT8) (SPD_FREQ_DIFF_OFFSET * (((NBPtr->DCTPtr->Timings.Speed / 133) - 3) / 2));
647 //
648 // Send RCW to program MDQ termination and drive strength
649 //
650 for (Rc = 8; Rc <= 9; Rc++) {
651 Value8 = SpdBufferPtr[SPD_MDQ_800_1066 + FreqDiffOffset];
652 Value8 = (Rc == 9) ? (Value8 >> 4) : Value8;
653 MemTSendMBCtlWord3 (TechPtr, 3, Rc, Value8 & 0x07);
654 }
655 //
656 // Send RCW to program QxODT
657 //
658 for (Fn = 3; Fn <= 10; Fn ++) {
659 for (Rc = 10; Rc <= 11; Rc++) {
660 Value8 = SpdBufferPtr[SPD_QXODT_800_1066 + FreqDiffOffset + ((Fn - 3) >> 1)];
661 Value8 = (Rc == 11) ? (Value8 >> 4) : (Value8 & 0x0F);
662 Value8 = ((Fn & 1) == 0) ? (Value8 >> 2) : (Value8 & 0x03);
663 MemTSendMBCtlWord3 (TechPtr, Fn, Rc, Value8);
664 }
665 }
666
667 MemTLrdimmInitHook (TechPtr, AFTER_TSTAB, Dimm, 0);
668 //
669 // Send Personality bytes from SPD
670 //
671 for (i = 0; i < 15; i ++) {
672 Value8 = SpdBufferPtr[SPD_PERSONALITY_BYTE + i];
673 Fn = (UINT8) (14 + (i >> 3));
674 Rc = (UINT8) ((i << 1) & 0x0F);
675 if ( i == 0) {
676 Value8 |= 0x01;
677 } else if ( i > 10) {
678 Rc += 2;
679 }
680 MemTSendMBCtlWord3 (TechPtr, Fn, Rc, (Value8 & 0x0F));
681 if (i == 3) {
682 Fn++;
683 } else {
684 Rc++;
685 }
686 MemTSendMBCtlWord3 (TechPtr, Fn, Rc, (Value8 >> 4));
687 }
688 //
689 // Send Extended Control Words to Buffer
690 //
691 // ExtRC_xAC
692 //
693 MemTSendExtMBCtlWord3 (TechPtr, 0x00AC, 0, 1);
694 //
695 // ExtRC_xB8-BF
696 //
697 Value8 = SpdBufferPtr[SPD_MR1_MR2_800_1066 + FreqDiffOffset];
698 for (i = 0x00B8; i < 0x00C0; i++) {
699 MemTSendExtMBCtlWord3 (TechPtr, i, Value8, 1);
700 if (i == 0xB9) {
701 //
702 // Phys ranks > 1, Rtt_nom = 0
703 //
704 Value8 &= 0xE3;
705 }
706 }
707 // ExtRC_xC8
708 Value8 = (UINT8) (NBPtr->MemNGetMR0CL (NBPtr) & 0x000000FF);
709 Value8 = ((Value8 & 0xE0) | ((Value8 & 0x04) << 1));
710 Value8 |= 1 << 2; // RBT
711 Value8 |= NBPtr->GetBitField (NBPtr, BFBurstCtrl); // BL
712 MemTSendExtMBCtlWord3 (TechPtr, 0x00C8 , Value8, 1);
713 // ExtRC_xC9
714 // PPD
715 Value8 = (UINT8) (NBPtr->GetBitField (NBPtr, BFPchgPDModeSel) & 0x000000FF);
716 NBPtr->FamilySpecificHook[MR0_PPD] (NBPtr, &Value8);
717 IDS_OPTION_HOOK (IDS_MEM_MR0, &Value8, &TechPtr->NBPtr->MemPtr->StdHeader);
718 Value8 <<= 4;
719 // WR
720 Temp32 = NBPtr->MemNGetMR0WR (NBPtr);
721 Value8 |= (UINT8) ((Temp32 >> 8) & 0x000000FF);
722 MemTSendExtMBCtlWord3 (TechPtr, 0x00C9 , Value8, 1);
723 // ExtRC_xCA
724 MemTSendExtMBCtlWord3 (TechPtr, 0x00CA , 0, 1);
725 // ExtRC_xCB
726 MemTSendExtMBCtlWord3 (TechPtr, 0x00CB , 0, 1);
727 // ExtRC_xCC
728 // CWL
729 Value8 = (UINT8) (NBPtr->MemNGetMR2CWL (NBPtr) & 0x000000FF);
730 // SRT|ASR
731 Value8 |= 0x40;
732 MemTSendExtMBCtlWord3 (TechPtr, 0x00CC , Value8, 1);
733 // ExtRC_xCD
734 MemTSendExtMBCtlWord3 (TechPtr, 0x00CD , 0, 1);
735 // ExtRC_xCE
736 MemTSendExtMBCtlWord3 (TechPtr, 0x00CE , 0, 1);
737 // ExtRC_xCF
738 MemTSendExtMBCtlWord3 (TechPtr, 0x00CF , 0, 1);
739 }
740 }
741 }
742 return TRUE;
743}
744
745/* -----------------------------------------------------------------------------*/
746/**
747 *
748 * This function sends LRDIMM Control Words to all LRDIMMS
749 *
750 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
751 * @param[in,out] OptParam - Optional parameter
752 *
753 * @return FALSE - The current channel does not have LRDIMM populated
754 * TRUE - The current channel has LRDIMM populated
755 */
756BOOLEAN
757STATIC
758MemTLrDimmFreqChgCtrlWrd3 (
759 IN OUT MEM_TECH_BLOCK *TechPtr,
760 IN OUT VOID *OptParam
761 )
762{
763 UINT8 Dct;
764 MEM_NB_BLOCK *NBPtr;
765
766 NBPtr = TechPtr->NBPtr;
767
768 if (NBPtr->MCTPtr->Status[SbLrdimms]) {
769 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
770 MemNSwitchDCTNb (NBPtr, Dct);
771 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
772 MemTLrDimmControlRegInit3 (TechPtr, NULL);
773 }
774 }
775 return TRUE;
776 }
777 return FALSE;
778}
779
780/*-----------------------------------------------------------------------------
781 *
782 *
783 * This function prepares LRDIMMs for WL training.
784 *
785 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
786 * @param[in,out] *Wl - Indicates if WL mode should be enabled
787 *
788 * @return TRUE - LRDIMMs present
789 * ----------------------------------------------------------------------------
790 */
791BOOLEAN
792STATIC
793MemTWLPrepareLrdimm3 (
794 IN OUT MEM_TECH_BLOCK *TechPtr,
795 IN OUT VOID *Wl
796 )
797{
798 UINT8 Dimm;
799 UINT8 Value8;
800 UINT16 MrsAddress;
801 MEM_NB_BLOCK *NBPtr;
802 NBPtr = TechPtr->NBPtr;
803 MrsAddress = 0;
804 if (NBPtr->MCTPtr->Status[SbLrdimms]) {
805 for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
806 if (*(BOOLEAN *) Wl == TRUE) {
807 // Program WrLvOdt
808 NBPtr->SetBitField (NBPtr, BFWrLvOdt, NBPtr->ChannelPtr->PhyWLODT[Dimm]);
809 }
810 if ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << Dimm)) != 0) {
811 if (Dimm == TechPtr->TargetDIMM) {
812 if (*(BOOLEAN *) Wl == TRUE) {
813 //
814 // Select the Target Chipselects
815 //
816 NBPtr->SetBitField (NBPtr, BFMrsChipSel, (Dimm << 1));
817 NBPtr->SetBitField (NBPtr, BFCtrlWordCS, 3 << (Dimm << 1));
818 // Program F0RC12 to 1h
819 MemTSendMBCtlWord3 (TechPtr, F0, RC12, 0x01);
820 if (NBPtr->ChannelPtr->Dimms >= 2) {
821 // For two or more LRDIMMs per channel program the buffer RttNom to the
822 // corresponding specifed RttWr termination
823 Value8 = NBPtr->MemNGetDynDramTerm (NBPtr, Dimm << 2);
824 } else {
825 // Program RttNom as normal
826 Value8 = NBPtr->MemNGetDramTerm (NBPtr, Dimm << 2);
827 }
828 if ((Value8 & ((UINT8) 1 << 2)) != 0) {
829 MrsAddress |= ((UINT16) 1 << 9);
830 }
831 if ((Value8 & ((UINT8) 1 << 1)) != 0) {
832 MrsAddress |= ((UINT16) 1 << 6);
833 }
834 if ((Value8 & ((UINT8) 1 << 0)) != 0) {
835 MrsAddress |= ((UINT16) 1 << 2);
836 }
837 NBPtr->SetBitField (NBPtr, BFMrsAddress, MrsAddress);
838 } else {
839 // Program F0RC12 to 0h
840 MemTSendMBCtlWord3 (TechPtr, F0, RC12, 0x00);
841 }
842 }
843 }
844 }
845 return TRUE;
846 } else {
847 return FALSE;
848 }
849}
850
851/* -----------------------------------------------------------------------------*/
852/**
853 *
854 * This send all MR commands to all physical ranks of an LRDIMM
855 *
856 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
857 * @param[in] *CsPtr - Target Chip Select
858 *
859 * @return FALSE - The current channel does not have LRDIMM populated
860 * TRUE - The current channel has LRDIMM populated
861 */
862BOOLEAN
863STATIC
864MemTSendAllMRCmdsLR3 (
865 IN OUT MEM_TECH_BLOCK *TechPtr,
866 IN OUT VOID *CsPtr
867 )
868{
869 UINT8 *SpdBufferPtr;
870 UINT8 Rank;
871 UINT8 PhyRank;
872 UINT8 ChipSel;
873 MEM_NB_BLOCK *NBPtr;
874
875 NBPtr = TechPtr->NBPtr;
876 ChipSel = *((UINT8 *) CsPtr);
877
878 if (NBPtr->MCTPtr->Status[SbLrdimms]) {
879 //
880 // For LRDIMMs, MR0, MR2, and MR3 can be broadcast to any physicall ranks behind
881 // each logical rank(CS) by setting MRSAddress[13]. MR1[Rtt_Nom] needs to be programmed
882 // differently per physical rank, so it must target a physical rank using MrsAddress[17:14].
883 // The actual bits used to index the physical rank are determined by the DRAM Capacity.
884 //
885 // This function will be called once for each CS where CSPresent is set, so each time
886 // it only needs to handle the Physical ranks behind each CS. If a Dimm is not using some
887 // CS due to Rank Mux, those CSPresent bits will have been already cleared.
888 //
889
890 //
891 // Select target chip select
892 //
893 NBPtr->SetBitField (NBPtr, BFMrsChipSel, ChipSel);
894 //
895 // 13.Send EMRS(2)
896 //
897 MemTEMRS2Lr3 (TechPtr, ChipSel);
898 NBPtr->SetBitField (NBPtr, BFMrsAddressHi, 1); // Set Address bit 13 to broadcast
899 NBPtr->SendMrsCmd (NBPtr);
900 //
901 // 14.Send EMRS(3). Ordinarily at this time, MrsAddress[2:0]=000b
902 //
903 MemTEMRS33 (TechPtr);
904 NBPtr->SetBitField (NBPtr, BFMrsAddressHi, 1); // Set Address bit 13 to broadcast
905 NBPtr->SendMrsCmd (NBPtr);
906 //
907 // 15.Send EMRS(1). Send to each physical rank.
908 //
909 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, ChipSel >> 1);
910 //
911 // Determine first physical rank relative to the LRDIMM for this CS
912 //
913 PhyRank = ((((ChipSel & NBPtr->ChannelPtr->LrDimmLogicalRanks[ChipSel >> 1]) >> 1) & 2) | (ChipSel & 1));
914 for (Rank = 0; Rank < NBPtr->ChannelPtr->LrDimmRankMult[ChipSel >> 1]; Rank++) {
915 MemTEMRS1Lr3 (TechPtr, ChipSel, PhyRank);
916 //
917 // Set Address bit 14, 15, 16, or 17 to select physical rank, relative to the CS, according to the device size
918 //
919 NBPtr->SetBitField (NBPtr, BFMrsAddressHi, Rank << ((SpdBufferPtr[SPD_DENSITY] & 0xF) - 1 ) );
920 NBPtr->SendMrsCmd (NBPtr);
921 //
922 // Index to the next physical rank
923 //
924 PhyRank = PhyRank + NBPtr->ChannelPtr->LrDimmLogicalRanks[ChipSel >> 1];
925 }
926 //
927 // 16.Send MRS with MrsAddress[8]=1(reset the DLL)
928 //
929 MemTMRS3 (TechPtr);
930 NBPtr->SetBitField (NBPtr, BFMrsAddressHi, 1); // Set Address bit 13 to broadcast
931 NBPtr->SendMrsCmd (NBPtr);
932 //
933 // If LRDIMM, return TRUE to skip sending regular MR commands.
934 //
935 return TRUE;
936 }
937 //
938 // If not LRDIMM, send regular MR commands.
939 //
940 return FALSE;
941}
942
943/* -----------------------------------------------------------------------------*/
944/**
945 *
946 * This function calculates the EMRS1 value for an LRDIMM
947 *
948 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
949 * @param[in] ChipSel - Chip select number
950 * @param[in] PhyRank - Physical rank number
951 */
952
953VOID
954STATIC
955MemTEMRS1Lr3 (
956 IN OUT MEM_TECH_BLOCK *TechPtr,
957 IN UINT8 ChipSel,
958 IN UINT8 PhyRank
959 )
960{
961 UINT16 MrsAddress;
962 UINT8 Value8;
963 UINT8 *SpdBufferPtr;
964 UINT8 FreqDiffOffset;
965 MEM_NB_BLOCK *NBPtr;
966
967 NBPtr = TechPtr->NBPtr;
968
969 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, ChipSel >> 1);
970 FreqDiffOffset = (UINT8) (SPD_FREQ_DIFF_OFFSET * (((NBPtr->DCTPtr->Timings.Speed / 133) - 3) / 2));
971
972 // BA2=0,BA1=0,BA0=1
973 NBPtr->SetBitField (NBPtr, BFMrsBank, 1);
974
975 MrsAddress = 0;
976
977 // program MrsAddress[5,1]=output driver impedance control (DIC): 01b
978 MrsAddress |= ((UINT16) 1 << 1);
979
980 // program MrsAddress[5,1]=output driver impedance control (DIC):
981 // DIC is read from SPD byte 77, 83, or 89 depending on DDR speed
982 Value8 = SpdBufferPtr[SPD_MR1_MR2_800_1066 + FreqDiffOffset] & 3;
983 if ((Value8 & ((UINT8) 1 << 1)) != 0) {
984 MrsAddress |= ((UINT16) 1 << 5);
985 }
986 if ((Value8 & ((UINT8) 1 << 0)) != 0) {
987 MrsAddress |= ((UINT16) 1 << 1);
988 }
989
990 // program MrsAddress[9,6,2]=nominal termination resistance of ODT (RTT):
991 // RttNom is read from SPD byte 77, 83, or 89 depending on DDR speed
992 if (PhyRank <= 1) {
993 Value8 = (SpdBufferPtr[SPD_MR1_MR2_800_1066 + FreqDiffOffset] >> 2) & 7;
994 if ((Value8 & ((UINT8) 1 << 2)) != 0) {
995 MrsAddress |= ((UINT16) 1 << 9);
996 }
997 if ((Value8 & ((UINT8) 1 << 1)) != 0) {
998 MrsAddress |= ((UINT16) 1 << 6);
999 }
1000 if ((Value8 & ((UINT8) 1 << 0)) != 0) {
1001 MrsAddress |= ((UINT16) 1 << 2);
1002 }
1003 }
1004
1005 NBPtr->SetBitField (NBPtr, BFMrsAddress, MrsAddress);
1006}
1007
1008/* -----------------------------------------------------------------------------*/
1009/**
1010 *
1011 * This function calculates the EMRS2 value for an LRDIMM
1012 *
1013 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
1014 * @param[in] ChipSel - Chip select number
1015 */
1016
1017VOID
1018STATIC
1019MemTEMRS2Lr3 (
1020 IN OUT MEM_TECH_BLOCK *TechPtr,
1021 IN UINT8 ChipSel
1022 )
1023{
1024 UINT8 RttWr;
1025 UINT8 *SpdBufferPtr;
1026 UINT8 FreqDiffOffset;
1027 MEM_NB_BLOCK *NBPtr;
1028
1029 NBPtr = TechPtr->NBPtr;
1030
1031 // Save default RttWr
1032 RttWr = NBPtr->PsPtr->RttWr[ChipSel];
1033
1034 // Override RttWr with the value read from SPD byte 77, 83, or 89 depending on DDR speed
1035 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, ChipSel >> 1);
1036 FreqDiffOffset = (UINT8) (SPD_FREQ_DIFF_OFFSET * (((NBPtr->DCTPtr->Timings.Speed / 133) - 3) / 2));
1037 NBPtr->PsPtr->RttWr[ChipSel] = SpdBufferPtr[SPD_MR1_MR2_800_1066 + FreqDiffOffset] >> 6;
1038
1039 // Call EMRS2 calculation
1040 MemTEMRS23 (TechPtr);
1041
1042 // Restore RttWr
1043 NBPtr->PsPtr->RttWr[ChipSel] = RttWr;
1044}
1045
1046/*-----------------------------------------------------------------------------
1047 *
1048 *
1049 * This function to determine the Rank Multiplication to use for an LRDIMM
1050 *
1051 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
1052 * @param[in,out] *DimmID - Dimm ID
1053 *
1054 * @return TRUE - LRDIMM Support is installed and LRDIMMs are present
1055 * ----------------------------------------------------------------------------
1056 */
1057BOOLEAN
1058STATIC
1059MemTLrdimmRankMultiplication (
1060 IN OUT MEM_TECH_BLOCK *TechPtr,
1061 IN OUT VOID *DimmID
1062 )
1063{
1064 BOOLEAN RetVal;
1065 UINT8 *SpdBufferPtr;
1066 UINT8 Dimm;
1067 UINT8 NumDimmslots;
1068 UINT8 DramCapacity;
1069 UINT8 Ranks;
1070 UINT8 Rows;
1071 UINT8 RankMult;
1072 MEM_NB_BLOCK *NBPtr;
1073 CH_DEF_STRUCT *ChannelPtr;
1074
1075 ASSERT (TechPtr != NULL);
1076 ASSERT (DimmID != NULL);
1077
1078 Dimm = *(UINT8*)DimmID;
1079 ASSERT (Dimm < MAX_DIMMS_PER_CHANNEL);
1080
1081 NBPtr = TechPtr->NBPtr;
1082 ChannelPtr = NBPtr->ChannelPtr;
1083 RetVal = FALSE;
1084 RankMult = 0;
1085
1086 if (!MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, Dimm)) {
1087 ASSERT (FALSE);
1088 }
1089
1090 NumDimmslots = GetMaxDimmsPerChannel (NBPtr->RefPtr->PlatformMemoryConfiguration,
1091 NBPtr->MCTPtr->SocketId,
1092 ChannelPtr->ChannelID);
1093
1094 if (NBPtr->MCTPtr->Status[SbLrdimms]) {
1095 RetVal = TRUE;
1096 //
1097 // Determine LRDIMM Rank Multiplication
1098 //
1099 Ranks = ((SpdBufferPtr[SPD_RANKS] >> 3) & 0x07) + 1;
1100 if (Ranks == 5) {
1101 Ranks = 8;
1102 }
1103 DramCapacity = (SpdBufferPtr[SPD_DENSITY] & 0x0F);
1104 Rows = 12 + ((SpdBufferPtr[SPD_ROW_SZ] >> 3) & 0x7);
1105
1106 if (Ranks < 4) {
1107 RankMult = 1;
1108 } else if (Ranks == 4) {
1109 RankMult = (NumDimmslots < 3) ? 1 : 2;
1110 } else if (Ranks == 8) {
1111 RankMult = ((NumDimmslots < 3) && (DramCapacity < 4)) ? 2 : 4;
1112 }
1113 //
1114 // Save Rank Information
1115 //
1116 ChannelPtr->LrDimmRankMult[Dimm] = RankMult;
1117 ChannelPtr->LrDimmLogicalRanks[Dimm] = Ranks / RankMult;
1118 NBPtr->PsPtr->LrdimmRowAddrBits[Dimm] = Rows + (RankMult >> 1);
1119 //
1120 // Program RankDef
1121 //
1122 NBPtr->SetBitField (NBPtr, BFRankDef0 + Dimm, (RankMult == 4) ? 3 : RankMult);
1123 //
1124 // If LrdimmRowAddressBits > 16, then we must be using some CS signals for rank
1125 // multiplication. If this is the case, then we want to clear the CSPresent bits
1126 // that correspond to those chipselects.
1127 // If there are 3 DIMMs per channel, then it will always be CS67, if there are
1128 // 2DPCH, then DIMM0 will use CS45, and DIMM1 will use CS67.
1129 //
1130 if ((ChannelPtr->LrDimmLogicalRanks[Dimm] < 4) && (Dimm >= NumDimmslots)) {
1131 NBPtr->DCTPtr->Timings.CsPresent &= ~(0x3 << (Dimm << 1));
1132 ChannelPtr->LrDimmRankMult[Dimm] = 0;
1133 ChannelPtr->LrDimmLogicalRanks[Dimm] = 0;
1134 NBPtr->PsPtr->LrdimmRowAddrBits[Dimm] = 0;
1135 } else {
1136 IDS_HDT_CONSOLE_DEBUG_CODE (
1137 if (Dimm < NumDimmslots) {
1138 IDS_HDT_CONSOLE (MEM_FLOW,"\tDimm %d: Log. Ranks:%d Phys. Ranks:%d RowAddrBits:%d RankMult:%d\n",
1139 Dimm,
1140 ChannelPtr->LrDimmLogicalRanks[Dimm],
1141 ChannelPtr->LrdimmPhysicalRanks[Dimm],
1142 NBPtr->PsPtr->LrdimmRowAddrBits[Dimm],
1143 RankMult
1144 );
1145 }
1146 );
1147 }
1148 }
1149 return RetVal;
1150}
1151
1152/* -----------------------------------------------------------------------------
1153 *
1154 * This function performs buffer to DRAM training for LRDIMMs
1155 *
1156 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
1157 * @param[in,out] OptParam - Optional parameter
1158 *
1159 * @return TRUE
1160 */
1161
1162BOOLEAN
1163STATIC
1164MemTLrdimmBuf2DramTrain3 (
1165 IN OUT MEM_TECH_BLOCK *TechPtr,
1166 IN OUT VOID *OptParam
1167 )
1168{
1169 MEM_DATA_STRUCT *MemPtr;
1170 MEM_NB_BLOCK *NBPtr;
1171 UINT8 Dct;
1172 UINT8 Dimm;
1173 UINT8 ChipSel;
1174 UINT16 DimmMask;
1175 UINT8 i;
1176
1177 NBPtr = TechPtr->NBPtr;
1178 MemPtr = NBPtr->MemPtr;
1179
1180 if (NBPtr->MCTPtr->Status[SbLrdimms]) {
1181 IDS_HDT_CONSOLE (MEM_FLOW, "\nStart Buffer to DRAM training\n");
1182 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
1183 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
1184 NBPtr->SwitchDCT (NBPtr, Dct);
1185 //
1186 // ODM needs to be set after Dram Init
1187 //
1188 if (NBPtr->StartupSpeed == NBPtr->DCTPtr->Timings.Speed) {
1189 for (ChipSel = 1; ChipSel < MAX_CS_PER_CHANNEL; ChipSel += 2) {
1190 if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) {
1191 if ((NBPtr->DCTPtr->Timings.DimmMirrorPresent & (1 << (ChipSel >> 1))) != 0) {
1192 NBPtr->SetBitField (NBPtr, BFCSBaseAddr0Reg + ChipSel, ((NBPtr->GetBitField (NBPtr, BFCSBaseAddr0Reg + ChipSel)) | ((UINT32)1 << BFOnDimmMirror )));
1193 }
1194 }
1195 }
1196 }
1197 //
1198 // Buffer to DRAM training
1199 //
1200 for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
1201 DimmMask = (UINT16)1 << Dimm;
1202 if ((NBPtr->ChannelPtr->LrDimmPresent & DimmMask) != 0) {
1203 IDS_HDT_CONSOLE (MEM_STATUS, "\t\nDimm %d\n", Dimm);
1204 //
1205 // Select the Target Chipselects
1206 //
1207 NBPtr->SetBitField (NBPtr, BFMrsChipSel, (Dimm << 1));
1208 NBPtr->SetBitField (NBPtr, BFCtrlWordCS, 3 << (Dimm << 1));
1209
1210 NBPtr->SetBitField (NBPtr, BFLrDimmErrOutMonEn, 1);
1211 MemTSendMBCtlWord3 (TechPtr, F2, RC3, 8);
1212 // Send F0RC12 with data = 0010b.
1213 MemTSendMBCtlWord3 (TechPtr, F0, RC12, 2);
1214 //
1215 // Wait until D18F2xA0_dct[1:0][RcvParErr]=0 or tCAL * the number of physical ranks expires.
1216 //
1217 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tWaiting %d ms...\n", 10 * NBPtr->ChannelPtr->LrdimmPhysicalRanks[Dimm]);
1218 for (i = 0; i < (NBPtr->ChannelPtr->LrdimmPhysicalRanks[Dimm] * 10); i++) {
1219 MemUWait10ns (1000000, MemPtr);
1220 //
1221 // @todo: Provide option for polling RcvParErr to optimize DRAM bus timing.
1222 //
1223 }
1224 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tRcvParErr = %02x\n", NBPtr->GetBitField (NBPtr, BFRcvParErr));
1225 NBPtr->SetBitField (NBPtr, BFLrDimmErrOutMonEn, 0);
1226 MemTSendMBCtlWord3 (TechPtr, F2, RC3, 0);
1227 // Configure for normal operation: Send F0RC12 with data = 0000b.
1228 MemTSendMBCtlWord3 (TechPtr, F0, RC12, 0);
1229 }
1230 }
1231 }
1232 IDS_HDT_CONSOLE (MEM_FLOW, "\nEnd Buffer to DRAM training\n");
1233 }
1234 return TRUE;
1235}
1236
1237/* -----------------------------------------------------------------------------*/
1238/**
1239 *
1240 * This function copies trained delays of the first rank of a QR LRDIMM to the third rank
1241 *
1242 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
1243 * @param[in,out] OptParam - Optional parameter
1244 *
1245 * @return TRUE
1246 */
1247
1248BOOLEAN
1249STATIC
1250MemTLrdimmSyncTrainedDlys (
1251 IN OUT MEM_TECH_BLOCK *TechPtr,
1252 IN OUT VOID *OptParam
1253 )
1254{
1255 UINT8 i;
1256 UINT8 Dimm;
1257 UINT8 Dct;
1258 MEM_NB_BLOCK *NBPtr;
1259 CH_DEF_STRUCT *ChannelPtr;
1260 UINT16 WrDqsDly;
1261 UINT16 RcvEnDly;
1262 UINT16 RdDqsDly;
1263 UINT16 WrDatDly;
1264 UINT8 RdDqs2dDly;
1265 NBPtr = TechPtr->NBPtr;
1266
1267 if (NBPtr->MCTPtr->Status[SbLrdimms]) {
1268 IDS_HDT_CONSOLE (MEM_STATUS, "\tSync LRDIMM Delays to remaining ranks.\n");
1269 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
1270 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
1271 NBPtr->SwitchDCT (NBPtr, Dct);
1272 ChannelPtr = NBPtr->ChannelPtr;
1273 for (Dimm = 0; Dimm < 2; Dimm++) {
1274 if (ChannelPtr->LrDimmLogicalRanks[Dimm] > 2) {
1275 // If logical QR LRDIMM, copy trained delays from first rank to third rank
1276 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDimm %d -> Dimm %d\n",Dimm, Dimm + 2);
1277 for (i = 0; i < TechPtr->DlyTableWidth (); i++) {
1278 WrDqsDly = ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + i];
1279 NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm + 2, i), WrDqsDly);
1280 ChannelPtr->WrDqsDlys[(Dimm + 2) * TechPtr->DlyTableWidth () + i] = (UINT8)WrDqsDly;
1281
1282 RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * TechPtr->DlyTableWidth () + i];
1283 NBPtr->SetTrainDly (NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Dimm + 2, i), RcvEnDly);
1284 ChannelPtr->RcvEnDlys[(Dimm + 2) * TechPtr->DlyTableWidth () + i] = RcvEnDly;
1285
1286 RdDqsDly = ChannelPtr->RdDqsDlys[Dimm * TechPtr->DlyTableWidth () + i];
1287 NBPtr->SetTrainDly (NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS (Dimm + 2, i), RdDqsDly);
1288 ChannelPtr->RdDqsDlys[(Dimm + 2) * TechPtr->DlyTableWidth () + i] = (UINT8)RdDqsDly;
1289
1290 WrDatDly = ChannelPtr->WrDatDlys[Dimm * TechPtr->DlyTableWidth () + i];
1291 NBPtr->SetTrainDly (NBPtr, AccessWrDatDly, DIMM_BYTE_ACCESS (Dimm + 2, i), WrDatDly);
1292 ChannelPtr->WrDatDlys[(Dimm + 2) * TechPtr->DlyTableWidth () + i] = (UINT8)WrDatDly;
1293 }
1294 if ((ChannelPtr->DimmNibbleAccess & (1 << Dimm)) != 0) {
1295 //
1296 // If 2D x4 (Not Currently POR for LRDIMMs)
1297 //
1298 for (i = 0; i < MAX_NUMBER_LANES; i++) {
1299 if (ChannelPtr->LrDimmLogicalRanks[Dimm] > 2) {
1300 // If logical QR LRDIMM, copy trained delays from first rank to third rank
1301 RdDqs2dDly = ChannelPtr->RdDqs2dDlys[Dimm * MAX_NUMBER_LANES + i];
1302 NBPtr->SetTrainDly (NBPtr, excel845 , DIMM_NBBL_ACCESS (Dimm + 2, i),
1303 ChannelPtr->RdDqs2dDlys[Dimm * MAX_NUMBER_LANES + i]);
1304 ChannelPtr->RdDqs2dDlys[(Dimm + 2) * MAX_NUMBER_LANES + i] = (UINT8)RdDqs2dDly;
1305 }
1306 }
1307 }
1308 }
1309 }
1310 }
1311 return TRUE;
1312 } else {
1313 return FALSE;
1314 }
1315}
1316
1317/* -----------------------------------------------------------------------------*/
1318/**
1319 *
1320 * This function performs LRDIMM specific tasks during Dimm Presence detection
1321 *
1322 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
1323 * @param[in,out] *DimmID - Dimm ID
1324 *
1325 * @return TRUE
1326 *
1327 */
1328
1329BOOLEAN
1330STATIC
1331MemTLrdimmPresence (
1332 IN OUT MEM_TECH_BLOCK *TechPtr,
1333 IN OUT VOID *DimmID
1334 )
1335{
1336 MEM_NB_BLOCK *NBPtr;
1337 UINT32 BufferID;
1338 UINT8 Dimm;
1339 NBPtr = TechPtr->NBPtr;
1340 Dimm = *(UINT8*) DimmID;
1341
1342 BufferID = MemTLrDimmGetBufferID (TechPtr, Dimm);
1343 if ((BufferID == 0x0020B304) || (BufferID == 0x0020B380)) {
1344 IDS_HDT_CONSOLE (MEM_FLOW, "\tDimm %d: Unsupported LRDIMM Buffer Revision\n", Dimm);
1345 PutEventLog (AGESA_WARNING, MEM_WARNING_UNSUPPORTED_LRDIMM, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, Dimm, &NBPtr->MemPtr->StdHeader);
1346 NBPtr->DCTPtr->Timings.CsTestFail |= (UINT16)0x3 << (Dimm << 1);
1347 }
1348 return TRUE;
1349}
1350
1351/* -----------------------------------------------------------------------------*/
1352/**
1353 *
1354 * This function returns LRDIMM Buffer ID Info from the SPD
1355 *
1356 *
1357 * @param[in,out] *TechPtr - Pointer to the Technology Block
1358 * @param[in] Dimm - Dimm number
1359 *
1360 * @return Buffer ID Information
1361 *
1362 */
1363
1364UINT32
1365STATIC
1366MemTLrDimmGetBufferID (
1367 IN OUT MEM_TECH_BLOCK *TechPtr,
1368 IN UINT8 Dimm
1369 )
1370{
1371 UINT8 *SpdBufferPtr;
1372 UINT32 BufferID;
1373
1374 BufferID = 0;
1375 MemTGetDimmSpdBuffer3 (TechPtr, &SpdBufferPtr, Dimm);
1376 BufferID = (SpdBufferPtr[64] << 16) | (SpdBufferPtr[66] << 8) | (SpdBufferPtr[65]);
1377 return BufferID;
1378}
1379
1380/* -----------------------------------------------------------------------------*/
1381/**
1382 *
1383 * This function implements special case Initialization hooks for LRDIMMs
1384 *
1385 * @param[in] TechPtr - Tech Block Pointer
1386 * @param[in] Entrypoint - Entrypoint to indicate when this hook is called
1387 * @param[in] Dimm - Dimm being configured when hook is called
1388 * @param[in] OptParam - Not Used
1389 */
1390
1391VOID
1392STATIC
1393MemTLrdimmInitHook (
1394 IN OUT MEM_TECH_BLOCK *TechPtr,
1395 IN LRDIMM_HOOK_ENTRYPOINT Entrypoint,
1396 IN UINT8 Dimm,
1397 IN OUT VOID *OptParam
1398 )
1399{
1400 MEM_NB_BLOCK *NBPtr;
1401 UINT8 i;
1402 CONST UINT16 AfterTstabRcwTable[] = {
1403 0x0270, 0x0000,
1404 0x0122, 0x0074,
1405 0x0124, 0x009B,
1406 0x0126, 0x00C2,
1407 0x0128, 0x00E8,
1408 0x01D2, 0x5942,
1409 0x01D4, 0x836D,
1410 0x01CE, 0x5942,
1411 0x01D0, 0x836D,
1412 0x01D6, 0x017F,
1413 0x01D8, 0x0000,
1414 0x01F0, 0x008E,
1415 0x01F2, 0x00BA,
1416 0x01F4, 0x00E8,
1417 0x01F6, 0x0114,
1418 0x0B40, 0x7054,
1419 0x0B42, 0xA48A,
1420 0x0B3C, 0x7054,
1421 0x0B3E, 0xA48A,
1422 0x0B38, 0x0100,
1423 0x0B3A, 0x0000,
1424
1425 0x0274, 0x55AA,
1426 0x3012, 0x0080,
1427 0x3018, 0x6B80
1428 };
1429 if (MemTLrDimmGetBufferID (TechPtr, Dimm) != 0x0021B304) {
1430 return;
1431 }
1432 NBPtr = TechPtr->NBPtr;
1433 switch (Entrypoint) {
1434 case AFTER_TSTAB:
1435 MemTSendMBCtlWord3 (TechPtr, F14, RC0, 0xB);
1436 for ( i = 0 ; i < (sizeof (AfterTstabRcwTable) / sizeof (UINT16)); i += 2 ) {
1437 MemTSendExtMBCtlWord3 (TechPtr, AfterTstabRcwTable[i], AfterTstabRcwTable[i + 1], 2);
1438 }
1439 break;
1440 default:
1441 //
1442 // If a hook entrypoint is called, it should have a case for it.
1443 //
1444 ASSERT (FALSE);
1445 break;
1446 }
1447}