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