AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / Mem / NB / OR / mnphyor.c
1 /* $NoKeywords:$ */
2 /**
3  * @file
4  *
5  * mnphyor.c
6  *
7  * Northbridge Phy support for Orochi
8  *
9  * @xrefitem bom "File Content Label" "Release Content"
10  * @e project: AGESA
11  * @e sub-project: (Mem/NB/OR)
12  * @e \$Revision: 58126 $ @e \$Date: 2011-08-21 23:38:29 -0600 (Sun, 21 Aug 2011) $
13  *
14  **/
15 /*****************************************************************************
16 *
17 * Copyright (C) 2012 Advanced Micro Devices, Inc.
18 * All rights reserved.
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions are met:
22 *     * Redistributions of source code must retain the above copyright
23 *       notice, this list of conditions and the following disclaimer.
24 *     * Redistributions in binary form must reproduce the above copyright
25 *       notice, this list of conditions and the following disclaimer in the
26 *       documentation and/or other materials provided with the distribution.
27 *     * Neither the name of Advanced Micro Devices, Inc. nor the names of
28 *       its contributors may be used to endorse or promote products derived
29 *       from this software without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
35 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 *
42 * ***************************************************************************
43 *
44 */
45
46
47 /*
48  *----------------------------------------------------------------------------
49  *                                MODULES USED
50  *
51  *----------------------------------------------------------------------------
52  */
53
54
55
56 #include "AGESA.h"
57 #include "amdlib.h"
58 #include "Ids.h"
59 #include "mport.h"
60 #include "ma.h"
61 #include "mm.h"
62 #include "mn.h"
63 #include "mt.h"
64 #include "mu.h"
65 #include "OptionMemory.h"       // need def for MEM_FEAT_BLOCK_NB
66 #include "mnor.h"
67 #include "cpuRegisters.h"
68 #include "PlatformMemoryConfiguration.h"
69 #include "F15PackageType.h"
70 #include "Filecode.h"
71 CODE_GROUP (G3_DXE)
72 RDATA_GROUP (G3_DXE)
73
74
75 #define FILECODE PROC_MEM_NB_OR_MNPHYOR_FILECODE
76 /*----------------------------------------------------------------------------
77  *                          DEFINITIONS AND MACROS
78  *
79  *----------------------------------------------------------------------------
80  */
81 #define UNUSED_CLK 4
82
83
84 /// The structure of TxPrePN tables
85 typedef struct {
86   UINT32 Speed;                          ///< Applied memory speed
87   UINT16 TxPrePNVal[4];                  ///< Table values
88 } TXPREPN_STRUCT;
89
90 /// The entry of individual TxPrePN tables
91 typedef struct {
92   UINT8 TxPrePNTblSize;                  ///< Total Table size
93   CONST TXPREPN_STRUCT *TxPrePNTblPtr;   ///< Pointer to the table
94 } TXPREPN_ENTRY;
95
96 /// Type of an entry for processing phy init compensation for Orochi
97 typedef struct {
98   BIT_FIELD_NAME IndexBitField;          ///< Bit field on which the value is decided
99   BIT_FIELD_NAME StartTargetBitField;    ///< First bit field to be modified
100   BIT_FIELD_NAME EndTargetBitField;      ///< Last bit field to be modified
101   UINT16 ExtraValue;                     ///< Extra value needed to be written to bit field
102   CONST TXPREPN_ENTRY *TxPrePN;          ///< Pointer to slew rate table
103 } PHY_COMP_INIT_NB;
104 /*----------------------------------------------------------------------------
105  *                           TYPEDEFS AND STRUCTURES
106  *
107  *----------------------------------------------------------------------------
108  */
109
110 /*----------------------------------------------------------------------------
111  *                        PROTOTYPES OF LOCAL FUNCTIONS
112  *
113  *----------------------------------------------------------------------------
114  */
115
116
117
118 /*----------------------------------------------------------------------------
119  *                            EXPORTED FUNCTIONS
120  *
121  *----------------------------------------------------------------------------
122  */
123 /* -----------------------------------------------------------------------------*/
124
125
126 /* -----------------------------------------------------------------------------*/
127 /**
128  *
129  *
130  *   This function initializes the DDR phy compensation logic
131  *
132  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
133  *
134  */
135
136 VOID
137 MemNInitPhyCompOr (
138   IN OUT   MEM_NB_BLOCK *NBPtr
139   )
140 {
141   //
142   // Phy Predriver Calibration Codes for Data/DQS
143   //
144   CONST STATIC TXPREPN_STRUCT TxPrePNDataDqsV15Or[] = {
145     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.5V
146     {DDR667 + DDR800, {0x924, 0x924, 0x924, 0x924}},
147     {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xFF6}},
148     {DDR1600 + DDR1866, {0xFF6, 0xFF6, 0xFF6, 0xFF6}}
149   };
150   CONST STATIC TXPREPN_STRUCT TxPrePNDataDqsV135Or[] = {
151     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.35V
152     {DDR667 + DDR800, {0xFF6, 0xB6D, 0xB6D, 0x924}},
153     {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xFF6}},
154     {DDR1600 + DDR1866, {0xFF6, 0xFF6, 0xFF6, 0xFF6}}
155   };
156   CONST STATIC TXPREPN_STRUCT TxPrePNDataDqsV125Or[] = {
157     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.25V
158     {DDR667 + DDR800, {0xFF6, 0xDAD, 0xDAD, 0x924}},
159     {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xFF6}},
160     {DDR1600 + DDR1866, {0xFF6, 0xFF6, 0xFF6, 0xFF6}}
161   };
162   CONST STATIC TXPREPN_ENTRY TxPrePNDataDqsOr[] = {
163     {GET_SIZE_OF (TxPrePNDataDqsV15Or), (TXPREPN_STRUCT *)&TxPrePNDataDqsV15Or},
164     {GET_SIZE_OF (TxPrePNDataDqsV135Or), (TXPREPN_STRUCT *)&TxPrePNDataDqsV135Or},
165     {GET_SIZE_OF (TxPrePNDataDqsV125Or), (TXPREPN_STRUCT *)&TxPrePNDataDqsV125Or}
166   };
167
168   //
169   // Phy Predriver Calibration Codes for Data/DQS
170   //
171   CONST STATIC TXPREPN_STRUCT TxPrePNDataDqsV15OrB1[] = {
172     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.5V
173     {DDR667 + DDR800, {0xB6D, 0x6DB, 0x492, 0x492}},
174     {DDR1066 + DDR1333, {0xFFF, 0x924, 0x6DB, 0x6DB}},
175     {DDR1600 + DDR1866, {0xFFF, 0xFFF, 0xFFF, 0xB6D}}
176   };
177   CONST STATIC TXPREPN_STRUCT TxPrePNDataDqsV135OrB1[] = {
178     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.35V
179     {DDR667 + DDR800, {0xFFF, 0x924, 0x6DB, 0x492}},
180     {DDR1066 + DDR1333, {0xFFF, 0xDB6, 0xB6D, 0x6DB}},
181     {DDR1600 + DDR1866, {0xFFF, 0xFFF, 0xFFF, 0xDB6}}
182   };
183   CONST STATIC TXPREPN_STRUCT TxPrePNDataDqsV125OrB1[] = {
184     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.25V
185     {DDR667 + DDR800, {0xFFF, 0xB6D, 0x924, 0x6DB}},
186     {DDR1066 + DDR1333, {0xFFF, 0xFFF, 0xDB6, 0x924}},
187     {DDR1600 + DDR1866, {0xFFF, 0xFFF, 0xFFF, 0xFFF}}
188   };
189   CONST STATIC TXPREPN_ENTRY TxPrePNDataDqsOrB1[] = {
190     {GET_SIZE_OF (TxPrePNDataDqsV15OrB1), (TXPREPN_STRUCT *)&TxPrePNDataDqsV15OrB1},
191     {GET_SIZE_OF (TxPrePNDataDqsV135OrB1), (TXPREPN_STRUCT *)&TxPrePNDataDqsV135OrB1},
192     {GET_SIZE_OF (TxPrePNDataDqsV125OrB1), (TXPREPN_STRUCT *)&TxPrePNDataDqsV125OrB1}
193   };
194
195   //
196   // Phy Predriver Calibration Codes for Cmd/Addr
197   //
198   CONST STATIC TXPREPN_STRUCT TxPrePNCmdAddrV15Or[] = {
199     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.5V
200     {DDR667 + DDR800, {0x492, 0x492, 0x492, 0x492}},
201     {DDR1066 + DDR1333, {0x6DB, 0x6DB, 0x6DB, 0x6DB}},
202     {DDR1600 + DDR1866, {0xB6D, 0xB6D, 0xB6D, 0xB6D}}
203   };
204   CONST STATIC TXPREPN_STRUCT TxPrePNCmdAddrV135Or[] = {
205     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.35V
206     {DDR667 + DDR800, {0x492, 0x492, 0x492, 0x492}},
207     {DDR1066 + DDR1333, {0x924, 0x6DB, 0x6DB, 0x6DB}},
208     {DDR1600 + DDR1866, {0xB6D, 0xB6D, 0x924, 0x924}}
209   };
210   CONST STATIC TXPREPN_STRUCT TxPrePNCmdAddrV125Or[] = {
211     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.25V
212     {DDR667 + DDR800, {0x492, 0x492, 0x492, 0x492}},
213     {DDR1066 + DDR1333, {0xDAD, 0x924, 0x6DB, 0x492}},
214     {DDR1600 + DDR1866, {0xFF6, 0xDAD, 0xB64, 0xB64}}
215   };
216   CONST STATIC TXPREPN_ENTRY TxPrePNCmdAddrOr[] = {
217     {GET_SIZE_OF (TxPrePNCmdAddrV15Or), (TXPREPN_STRUCT *)&TxPrePNCmdAddrV15Or},
218     {GET_SIZE_OF (TxPrePNCmdAddrV135Or), (TXPREPN_STRUCT *)&TxPrePNCmdAddrV135Or},
219     {GET_SIZE_OF (TxPrePNCmdAddrV125Or), (TXPREPN_STRUCT *)&TxPrePNCmdAddrV125Or}
220   };
221
222   //
223   // Phy Predriver Calibration Codes for Clock
224   //
225   CONST STATIC TXPREPN_STRUCT TxPrePNClockV15Or[] = {
226     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.5V
227     {DDR667 + DDR800, {0x924, 0x924, 0x924, 0x924}},
228     {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xB6D}},
229     {DDR1600 + DDR1866, {0xFF6, 0xFF6, 0xFF6, 0xFF6}}
230   };
231   CONST STATIC TXPREPN_STRUCT TxPrePNClockV135Or[] = {
232     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.35V
233     {DDR667 + DDR800, {0xDAD, 0xDAD, 0x924, 0x924}},
234     {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xDAD}},
235     {DDR1600 + DDR1866, {0xFF6, 0xFF6, 0xFF6, 0xDAD}}
236   };
237   CONST STATIC TXPREPN_STRUCT TxPrePNClockV125Or[] = {
238     //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.25V
239     {DDR667 + DDR800, {0xDAD, 0xDAD, 0x924, 0x924}},
240     {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xFF6}},
241     {DDR1600 + DDR1866, {0xFF6, 0xFF6, 0xFF6, 0xFF6}}
242   };
243   CONST STATIC TXPREPN_ENTRY TxPrePNClockOr[] = {
244     {GET_SIZE_OF (TxPrePNClockV15Or), (TXPREPN_STRUCT *)&TxPrePNClockV15Or},
245     {GET_SIZE_OF (TxPrePNClockV135Or), (TXPREPN_STRUCT *)&TxPrePNClockV135Or},
246     {GET_SIZE_OF (TxPrePNClockV125Or), (TXPREPN_STRUCT *)&TxPrePNClockV125Or}
247   };
248
249   //
250   // Tables to describe the relationship between drive strength bit fields, PreDriver Calibration bit fields and also
251   // the extra value that needs to be written to specific PreDriver bit fields
252   //
253   CONST PHY_COMP_INIT_NB PhyCompInitBitFieldOr[] = {
254     // 3. Program TxPreP/TxPreN for Data and DQS according toTable 46 if VDDIO is 1.5V or Table 47 if 1.35V.
255     //    A. Program D18F2x9C_x0D0F_0[F,8:0]0[A,6]_dct[1:0]={0000b, TxPreP, TxPreN}.
256     //    B. Program D18F2x9C_x0D0F_0[F,8:0]0[A,6]_dct[1:0]={0000b, TxPreP, TxPreN}.
257     {BFDqsDrvStren, BFDataByteTxPreDriverCal2Pad1, BFDataByteTxPreDriverCal2Pad1, 0, TxPrePNDataDqsOr},
258     {BFDataDrvStren, BFDataByteTxPreDriverCal2Pad2, BFDataByteTxPreDriverCal2Pad2, 0, TxPrePNDataDqsOr},
259     {BFDataDrvStren, BFDataByteTxPreDriverCal, BFDataByteTxPreDriverCal, 8, TxPrePNDataDqsOr},
260     // 4. Program TxPreP/TxPreN for Cmd/Addr according to Table 49 if VDDIO is 1.5V or Table 50 if 1.35V.
261     //    A. Program D18F2x9C_x0D0F_[C,8][1:0][12,0E,0A,06]_dct[1:0]={0000b, TxPreP, TxPreN}.
262     //    B. Program D18F2x9C_x0D0F_[C,8][1:0]02_dct[1:0]={1000b, TxPreP, TxPreN}.
263     {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCal2Pad1, BFCmdAddr0TxPreDriverCal2Pad2, 0, TxPrePNCmdAddrOr},
264     {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCal2Pad1, BFAddrTxPreDriverCal2Pad4, 0, TxPrePNCmdAddrOr},
265     {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCalPad0, BFCmdAddr0TxPreDriverCalPad0, 8, TxPrePNCmdAddrOr},
266     {BFCkeDrvStren, BFAddrTxPreDriverCalPad0, BFAddrTxPreDriverCalPad0, 8, TxPrePNCmdAddrOr},
267     {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCalPad0, BFCmdAddr1TxPreDriverCalPad0, 8, TxPrePNCmdAddrOr},
268     // 5. Program TxPreP/TxPreN for Clock according to Table 52 if VDDIO is 1.5V or Table 53 if 1.35V.
269     //    A. Program D18F2x9C_x0D0F_2[2:0]02_dct[1:0]={1000b, TxPreP, TxPreN}.
270     {BFClkDrvStren, BFClock0TxPreDriverCalPad0, BFClock2TxPreDriverCalPad0, 8, TxPrePNClockOr}
271   };
272
273   BIT_FIELD_NAME CurrentBitField;
274   UINT16 SpeedMask;
275   UINT8 SizeOfTable;
276   UINT8 Voltage;
277   UINT8 i;
278   UINT8 j;
279   UINT8 k;
280   UINT8 Dct;
281   CONST TXPREPN_STRUCT *TblPtr;
282
283   Dct = NBPtr->Dct;
284   NBPtr->SwitchDCT (NBPtr, 0);
285   // 1. Program D18F2x[1,0]9C_x0D0F_E003[DisAutoComp, DisablePreDriverCal] = {1b, 1b}.
286   MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 0x6000);
287   NBPtr->SwitchDCT (NBPtr, Dct);
288
289   SpeedMask = (UINT16) 1 << (NBPtr->DCTPtr->Timings.Speed / 66);
290   Voltage = (UINT8) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage);
291
292   for (j = 0; j < GET_SIZE_OF (PhyCompInitBitFieldOr); j ++) {
293     i = (UINT8) MemNGetBitFieldNb (NBPtr, PhyCompInitBitFieldOr[j].IndexBitField);
294     TblPtr = (PhyCompInitBitFieldOr[j].TxPrePN[Voltage]).TxPrePNTblPtr;
295     SizeOfTable = (PhyCompInitBitFieldOr[j].TxPrePN[Voltage]).TxPrePNTblSize;
296
297     // Uses different TxPrePNDataDqsOr table for OR B1 and later
298     if (((NBPtr->MCTPtr->LogicalCpuid.Revision & AMD_F15_OR_LT_B1) == 0) &&
299         (PhyCompInitBitFieldOr[j].TxPrePN == TxPrePNDataDqsOr)) {
300       ASSERT (Voltage < sizeof (TxPrePNDataDqsOrB1) / sizeof (TxPrePNDataDqsOrB1[0]));
301       TblPtr = TxPrePNDataDqsOrB1[Voltage].TxPrePNTblPtr;
302       SizeOfTable = TxPrePNDataDqsOrB1[Voltage].TxPrePNTblSize;
303     }
304
305     for (k = 0; k < SizeOfTable; k++, TblPtr++) {
306       if ((TblPtr->Speed & SpeedMask) != 0) {
307         for (CurrentBitField = PhyCompInitBitFieldOr[j].StartTargetBitField; CurrentBitField <= PhyCompInitBitFieldOr[j].EndTargetBitField; CurrentBitField ++) {
308           MemNSetBitFieldNb (NBPtr, CurrentBitField, ((PhyCompInitBitFieldOr[j].ExtraValue << 12) | TblPtr->TxPrePNVal[i]));
309         }
310         break;
311       }
312     }
313     // Asserting if no table is found corresponding to current memory speed.
314     ASSERT (k < SizeOfTable);
315   }
316 }
317
318 /* -----------------------------------------------------------------------------*/
319 /**
320  *
321  *
322  *   This is a general purpose function that executes before DRAM training
323  *
324  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
325  *
326  */
327
328 VOID
329 MemNBeforeDQSTrainingOr (
330   IN OUT   MEM_NB_BLOCK *NBPtr
331   )
332 {
333   UINT8   Dct;
334   UINT32  PackageType;
335
336   for (Dct = 0; Dct < MAX_DCTS_PER_NODE_OR; Dct++) {
337     MemNSwitchDCTNb (NBPtr, Dct);
338     if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
339       //
340       // 2.10.6.8.2 - BIOS should program D18F2x210_dct[1:0]_nbp[3:0][MaxRdLatency] to 55h.
341       //
342       MemNSetBitFieldNb (NBPtr, BFMaxLatency, 0x55);
343     }
344     MemNSetBitFieldNb (NBPtr, BFTraceModeEn, 0);
345   }
346
347   // DisDatMsk: Reset: 0. BIOS: IF (G34r1 || C32r1) THEN 1 ELSE 0 ENDIF.
348   PackageType = LibAmdGetPackageType (&(NBPtr->MemPtr->StdHeader));
349   if (PackageType != PACKAGE_TYPE_AM3r2) {
350     MemNSetBitFieldNb (NBPtr, BFDisDatMsk, 1);
351   }
352 }
353
354 /* -----------------------------------------------------------------------------*/
355 /**
356  *
357  *
358  *   This is a function that executes after DRAM training for Orochi
359  *
360  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
361  *
362  */
363
364 VOID
365 MemNAfterDQSTrainingOr (
366   IN OUT   MEM_NB_BLOCK *NBPtr
367   )
368 {
369   UINT8 Dct;
370   UINT8 Dimm;
371   UINT8 Byte;
372   UINT16 Dly;
373   BOOLEAN DllShutDownEn;
374
375   DllShutDownEn = TRUE;
376   IDS_OPTION_HOOK (IDS_DLL_SHUT_DOWN, &DllShutDownEn, &(NBPtr->MemPtr->StdHeader));
377
378   for (Dct = 0; Dct < MAX_DCTS_PER_NODE_OR; Dct++) {
379     MemNSwitchDCTNb (NBPtr, Dct);
380     if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
381       //
382       // 2.10.6.6 DCT Training Specific Configuration
383       //
384       MemNSetBitFieldNb (NBPtr, BFAddrCmdTriEn, 1);
385       MemNSetBitFieldNb (NBPtr, BFDisAutoRefresh, 0);
386       if (DllShutDownEn && NBPtr->IsSupported[SetDllShutDown]) {
387         MemNSetBitFieldNb (NBPtr, BFDisDllShutdownSR, 0);
388       }
389       MemNSetBitFieldNb (NBPtr , BFForceAutoPchg, 0);
390       MemNSetBitFieldNb (NBPtr , BFDynPageCloseEn, 0);
391       if (NBPtr->RefPtr->EnableBankSwizzle) {
392         MemNSetBitFieldNb (NBPtr, BFBankSwizzleMode, 1);
393       }
394       MemNSetBitFieldNb (NBPtr, BFDcqBypassMax, 0x0F);
395       MemNPowerDownCtlOr (NBPtr);
396       MemNSetBitFieldNb (NBPtr, BFDisSimulRdWr, 0);
397       MemNSetBitFieldNb (NBPtr, BFZqcsInterval, 2);
398       //
399       // Post Training values for BFRxMaxDurDllNoLock, BFTxMaxDurDllNoLock,
400       //  and BFEnRxPadStandby are handled by Power savings code
401       //
402       // BFBwCapEn and BFODTSEn are handled by OnDimmThermal Code
403       //
404       // BFDctSelIntLvEn is programmed by Interleaving feature
405       //
406       // BFL3Scrub, BFDramScrub, and DramScrubReDirEn are programmed by ECC Feature code
407       //
408       //
409       MemNSetBitFieldNb (NBPtr, BFL3ScrbRedirDis, 0);
410       // Doing DataTxFifoWrDly override for NB PState 0
411       MemNDataTxFifoWrDlyOverrideOr (NBPtr, NBPtr);
412     }
413   }
414
415   //
416   // Synch RdDqs__Dly to RdDqsDly for S3 Save/Restore
417   //
418   for (Dct = 0; Dct < MAX_DCTS_PER_NODE_OR; Dct++) {
419     MemNSwitchDCTNb (NBPtr, Dct);
420     if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
421       if (!(NBPtr->DctCachePtr->Is__x4)) {
422         // Only synch when 1D training has been performed or training with x8 DIMMs
423         for (Dimm = 0; Dimm < 4; Dimm++) {
424           for (Byte = 0; Byte < 9; Byte++) {
425             Dly = (UINT16) MemNGetTrainDlyNb (NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS (Dimm, Byte));
426             MemNSetTrainDlyNb (NBPtr, AccessRdDqs__Dly, DIMM_NBBL_ACCESS (Dimm, Byte * 2), Dly);
427             MemNSetTrainDlyNb (NBPtr, AccessRdDqs__Dly, DIMM_NBBL_ACCESS (Dimm, (Byte * 2) + 1), Dly);
428             NBPtr->ChannelPtr->RdDqs__Dlys[(Dimm * MAX_NUMBER_LANES) + (Byte * 2)] = (UINT8) Dly;
429             NBPtr->ChannelPtr->RdDqs__Dlys[(Dimm * MAX_NUMBER_LANES) + (Byte * 2) + 1] = (UINT8) Dly;
430           }
431         }
432       }
433     }
434   }
435 }
436 /* -----------------------------------------------------------------------------*/
437 /**
438  *
439  *     This function overrides the seed for hardware based RcvEn training of Orochi.
440  *
441  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
442  *     @param[in,out]   *SeedPtr - Pointer to the seed value.
443  *
444  *     @return    TRUE
445  */
446
447 BOOLEAN
448 MemNOverrideRcvEnSeedOr (
449   IN OUT   MEM_NB_BLOCK *NBPtr,
450   IN OUT   VOID *SeedPtr
451   )
452 {
453   UINT16 *SeedPointer;
454   SeedPointer = (UINT16*) SeedPtr;
455
456   //
457   // Get seed value saved in PS block
458   //
459   *SeedPointer = NBPtr->PsPtr->HWRxENSeedVal;
460   *SeedPointer -= (0x20 * (UINT16) MemNGetBitFieldNb (NBPtr, BFWrDqDqsEarly));
461
462   return TRUE;
463 }
464 /* -----------------------------------------------------------------------------*/
465 /**
466  *
467  *     This function overrides the seed for Pass N hardware based RcvEn training of Orochi.
468  *
469  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
470  *     @param[in,out]   *SeedTotal - Pointer to the SeedTotal
471  *
472  *     @return    TRUE
473  */
474
475 BOOLEAN
476 MemNOverrideRcvEnSeedPassNOr (
477   IN OUT   MEM_NB_BLOCK *NBPtr,
478   IN OUT   VOID *SeedTotal
479   )
480 {
481   UINT16 RegisterDelay;
482   UINT16 SeedTotalPreScaling;
483   UINT8 *SpdBufferPtr;
484   if (NBPtr->MCTPtr->Status[SbLrdimms]) {
485     // LRDIMMs
486     NBPtr->TechPtr->GetDimmSpdBuffer (NBPtr->TechPtr, &SpdBufferPtr, (NBPtr->TechPtr->ChipSel >> 1));
487     RegisterDelay = 0x10 + (((SpdBufferPtr[67] & 1) == 0) ? (0x30 - (SpdBufferPtr[70] & 7)): 0x30);
488   } else if (NBPtr->MCTPtr->Status[SbRegistered]) {
489     // Registered
490     RegisterDelay = ((NBPtr->ChannelPtr->CtrlWrd02[(NBPtr->TechPtr->ChipSel >> 1)] & BIT0) == 0) ? 0x20: 0x30;
491   } else {
492     // UDIMMs
493     RegisterDelay = 0;
494   }
495   if (NBPtr->TechPtr->PrevPassRcvEnDly[NBPtr->TechPtr->Bytelane] < (0x20 + RegisterDelay)) {
496     SeedTotalPreScaling = 0x20 + RegisterDelay;
497   } else {
498     SeedTotalPreScaling = NBPtr->TechPtr->PrevPassRcvEnDly[NBPtr->TechPtr->Bytelane] - 0x20 - RegisterDelay;
499   }
500   *(UINT16*) SeedTotal = ((UINT16) (((UINT32) SeedTotalPreScaling * NBPtr->DCTPtr->Timings.Speed) / NBPtr->TechPtr->PrevSpeed)) + RegisterDelay;
501   return TRUE;
502 }
503 /* -----------------------------------------------------------------------------*/
504 /**
505  *
506  *     This function overrides the seed for write leveing training of Orochi.
507  *
508  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
509  *     @param[in,out]   *SeedPtr - Pointer to the seed value.
510  *
511  *     @return    TRUE
512  */
513
514 BOOLEAN
515 MemNOverrideWLSeedOr (
516   IN OUT   MEM_NB_BLOCK *NBPtr,
517   IN OUT   VOID *SeedPtr
518   )
519 {
520   DIE_STRUCT *MCTPtr;
521   CH_DEF_STRUCT *ChannelPtr;
522   UINT8 RCW2;
523
524   MCTPtr = NBPtr->MCTPtr;
525   ChannelPtr = NBPtr->ChannelPtr;
526   RCW2 = ChannelPtr->CtrlWrd02[NBPtr->TechPtr->TargetDIMM];
527
528   //
529   // Get the default value of seed
530   //
531   if (ChannelPtr->SODimmPresent != 0) {
532     //
533     // SODIMMM
534     //
535     *(UINT8*) SeedPtr = 0x12;
536   } else {
537     //
538     // Get seed value saved in PS block
539     //
540     *(UINT8*) SeedPtr = NBPtr->PsPtr->WLSeedVal;
541
542     if (MCTPtr->Status[SbRegistered]) {
543       *(UINT8*) SeedPtr += ((RCW2 & BIT0) == 0) ? 0 : 0x10;
544     }
545   }
546
547   return TRUE;
548 }
549 /* -----------------------------------------------------------------------------*/
550 /**
551  *
552  *     This function enables nibble based training for Write Levelization for Orochi.
553  *
554  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
555  *     @param[in,out]   *Dimm - Pointer to DIMM to be trained
556  *
557  *     @return    TRUE
558  */
559
560 BOOLEAN
561 MemNTrainWlPerNibbleOr (
562   IN OUT   MEM_NB_BLOCK *NBPtr,
563   IN OUT   VOID *Dimm
564   )
565 {
566   UINT8 ByteLane;
567   if ((NBPtr->ChannelPtr->Dimmx4Present & (1 << *(UINT16*) Dimm)) != 0) {
568     if (NBPtr->TechPtr->TrnNibble <= NIBBLE_1) {
569       //For x4 DIMMs, BIOS trains both nibbles of a byte lane by programming
570       //D18F2x9C_x0000_0008_dct[1:0][TrNibbleSel] to specify the nibble. BIOS repeats steps 3 through
571       //5 and uses the average of the trained values for the delay setting.
572       if (NBPtr->TechPtr->TrnNibble == NIBBLE_1) {
573         NBPtr->SetBitField (NBPtr, BFTrNibbleSel, NBPtr->TechPtr->TrnNibble);
574       }
575       return FALSE;
576     } else {
577       IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tWrDqs: ");
578       for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8) ; ByteLane++) {
579         IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", NBPtr->TechPtr->WlNibbleDly[ByteLane]);
580       }
581       IDS_HDT_CONSOLE (MEM_FLOW, "   <<< Nibble AVG\n\n");
582       return FALSE;
583     }
584   } else  {
585     return TRUE;
586   }
587 }
588 /* -----------------------------------------------------------------------------*/
589 /**
590  *
591  *     This function adjusts the WL DQS Delay based on nibble traning results for Orochi.
592  *
593  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
594  *     @param[in,out]   *Delay - Pointer to Wr Dqs Delay
595  *
596  *     @return    FALSE - Supported
597  *     @return    TRUE - Not supported
598  */
599 BOOLEAN
600 MemNTrainWlPerNibbleAdjustWLDlyOr (
601   IN OUT   MEM_NB_BLOCK *NBPtr,
602   IN OUT   VOID *Delay
603   )
604 {
605   MEM_TECH_BLOCK *TechPtr;
606   UINT8 Bytelane;
607   TechPtr = NBPtr->TechPtr;
608   Bytelane = TechPtr->Bytelane;
609   if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << TechPtr->TargetDIMM)) != 0) {
610     if (TechPtr->TrnNibble == NIBBLE_1) {
611       *(UINT8*) Delay = (TechPtr->WlNibbleDly[Bytelane] + *(UINT8*) Delay + 1) / 2;
612       if (Bytelane == (NBPtr->MCTPtr->Status[SbEccDimms] ? 8 : 7)) {
613         IDS_HDT_CONSOLE (MEM_FLOW, "   <<< Nibble 1");
614       }
615     } else {
616       if (Bytelane == (NBPtr->MCTPtr->Status[SbEccDimms] ? 8 : 7)) {
617         IDS_HDT_CONSOLE (MEM_FLOW, "   <<< Nibble 0");
618       }
619     }
620     TechPtr->WlNibbleDly[Bytelane] = *(UINT8*) Delay;
621     return FALSE;
622   } else {
623     return TRUE;
624   }
625 }
626 /* -----------------------------------------------------------------------------*/
627 /**
628  *
629  *     This function sets the correct seed for Nibble based Write Levelization.
630  *
631  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
632  *     @param[in,out]   *WrDqsDly   - Pointer to WrDqs value
633  *
634  *     @return    FALSE - Supported
635  *     @return    TRUE - Not supported
636  */
637
638 BOOLEAN
639 MemNTrainWlPerNibbleSeedOr (
640   IN OUT   MEM_NB_BLOCK *NBPtr,
641   IN OUT   VOID *WrDqsDly
642   )
643 {
644   if (NBPtr->TechPtr->TrnNibble == NIBBLE_0) {
645     NBPtr->TechPtr->WlNibble0Seed[NBPtr->TechPtr->Bytelane] = *(UINT16*) WrDqsDly;
646   } else {
647     *(UINT16*) WrDqsDly = NBPtr->TechPtr->WlNibble0Seed[NBPtr->TechPtr->Bytelane];
648   }
649   return TRUE;
650 }
651 /* -----------------------------------------------------------------------------*/
652 /**
653  *
654  *     This function initializes nibble based Receiver Enable Training for Orochi.
655  *
656  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
657  *     @param[in,out]   *OptParam - Optional paramater
658  *
659  *     @return    FALSE - Supported
660  *     @return    TRUE - Not supported
661  */
662 BOOLEAN
663 MemNInitPerNibbleTrnOr (
664   IN OUT   MEM_NB_BLOCK *NBPtr,
665   IN OUT   VOID *OptParam
666   )
667 {
668   // Program D18F2x9C_x0000_0008_dct[1:0][TrNibbleSel]=0
669   NBPtr->TechPtr->TrnNibble = NIBBLE_0;
670   NBPtr->SetBitField (NBPtr, BFTrNibbleSel, NIBBLE_0);
671   return TRUE;
672 }
673 /* -----------------------------------------------------------------------------*/
674 /**
675  *
676  *     This function enables nibble based Receiver Enable Training for Orochi.
677  *
678  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
679  *     @param[in,out]   *ChipSel - Pointer to ChipSel to be trained
680  *
681  *     @return    FALSE - Supported
682  *     @return    TRUE - Not supported
683  */
684
685 BOOLEAN
686 MemNTrainRxEnPerNibbleOr (
687   IN OUT   MEM_NB_BLOCK *NBPtr,
688   IN OUT   VOID *ChipSel
689   )
690 {
691   if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (*(UINT16*) ChipSel >> 1))) != 0) {
692     if (NBPtr->TechPtr->TrnNibble == NIBBLE_1) {
693       // For x4 DIMMs, BIOS trains both nibbles of a byte lane by programming
694       // D18F2x9C_x0000_0008_dct[1:0][TrNibbleSel] to specify the nibble. BIOS repeats steps 2 through
695       // 7 and uses the average of the trained values for the delay setting.
696       NBPtr->SetBitField (NBPtr, BFTrNibbleSel, NBPtr->TechPtr->TrnNibble);
697     }
698     return FALSE;
699   } else {
700     return TRUE;
701   }
702 }
703 /* -----------------------------------------------------------------------------*/
704 /**
705  *
706  *     This function adjusts the RxEn Delay based on nibble traning results for Orochi.
707  *
708  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
709  *     @param[in,out]   *RcvEnDly - Pointer to RcvEn Dqs Delay
710  *
711  *     @return    FALSE - Supported
712  *     @return    TRUE - Not supported
713  */
714 BOOLEAN
715 MemNTrainRxEnAdjustDlyPerNibbleOr (
716   IN OUT   MEM_NB_BLOCK *NBPtr,
717   IN OUT   VOID *RcvEnDly
718   )
719 {
720   MEM_TECH_BLOCK *TechPtr;
721   UINT8 Bytelane;
722   TechPtr = NBPtr->TechPtr;
723   Bytelane = TechPtr->Bytelane;
724   if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) != 0) {
725     if (TechPtr->TrnNibble == NIBBLE_1) {
726       *(UINT16*) RcvEnDly = (TechPtr->RxEnNibbleDly[Bytelane] + *(UINT16*) RcvEnDly + 1) / 2;
727       if (Bytelane == (NBPtr->MCTPtr->Status[SbEccDimms] ? 8 : 7)) {
728         IDS_HDT_CONSOLE (MEM_FLOW, "   <<< Nibble 1");
729       }
730       TechPtr->RxEnNibbleDly[Bytelane] = *(UINT16*) RcvEnDly;
731       return TRUE;
732     } else {
733       if (Bytelane == (NBPtr->MCTPtr->Status[SbEccDimms] ? 8 : 7)) {
734         IDS_HDT_CONSOLE (MEM_FLOW, "   <<< Nibble 0");
735       }
736       TechPtr->RxEnNibbleDly[Bytelane] = *(UINT16*) RcvEnDly;
737       return FALSE;
738     }
739   } else {
740     return TRUE;
741   }
742 }
743 /* -----------------------------------------------------------------------------*/
744 /**
745  *
746  *     This function calculates the average nibble based Receiver Enable Training for Orochi.
747  *
748  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
749  *     @param[in,out]   *OptParam   - Optional parameter
750  *
751  *     @return    FALSE - Supported
752  *     @return    TRUE - Not supported
753  */
754
755 BOOLEAN
756 MemNTrainRxEnGetAvgDlyPerNibbleOr (
757   IN OUT   MEM_NB_BLOCK *NBPtr,
758   IN OUT   VOID *OptParam
759   )
760 {
761   UINT8 ByteLane;
762   if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (NBPtr->TechPtr->ChipSel >> 1))) != 0) {
763     if (NBPtr->TechPtr->TrnNibble == NIBBLE_1) {
764       IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t     RxEn: ");
765       for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8) ; ByteLane++) {
766         IDS_HDT_CONSOLE (MEM_FLOW, "%03x ", NBPtr->TechPtr->RxEnNibbleDly[ByteLane]);
767       }
768       IDS_HDT_CONSOLE (MEM_FLOW, "   <<< Nibble AVG\n\n");
769       return TRUE;
770     } else {
771       return FALSE;
772     }
773   } else {
774     return TRUE;
775   }
776 }
777
778 /* -----------------------------------------------------------------------------*/
779 /**
780  *
781  *     This function returns false if nibble training is being used and nibble 1
782  *     is being trained.
783  *
784  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
785  *     @param[in,out]   *ChipSel - Pointer to ChipSel to be trained
786  *
787  *     @return    FALSE - Supported
788  *     @return    TRUE - Not supported
789  */
790
791 BOOLEAN
792 MemNTrainingNibbleZeroOr (
793   IN OUT   MEM_NB_BLOCK *NBPtr,
794   IN OUT   VOID *ChipSel
795   )
796 {
797   if (((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (NBPtr->TechPtr->ChipSel >> 1))) != 0) &&
798     (NBPtr->TechPtr->TrnNibble == NIBBLE_1)) {
799     return FALSE;
800   } else {
801     return TRUE;
802   }
803 }
804
805 /* -----------------------------------------------------------------------------*/
806 /**
807  *
808  *
809  *   This function adjusts Avg PRE value of Phy fence training for OR.
810  *
811  *     @param[in,out]   *NBPtr  - Pointer to the MEM_NB_BLOCK
812  *     @param[in,out]   *Value16 - Pointer to the value that we want to adjust
813  *
814  */
815 VOID
816 MemNPFenceAdjustOr (
817   IN OUT   MEM_NB_BLOCK *NBPtr,
818   IN OUT   INT16 *Value16
819   )
820 {
821   *Value16 += 2; //The Avg PRE value is subtracted by 6 only.
822   if (*Value16 < 0) {
823     *Value16 = 0;
824   }
825 }
826
827 /* -----------------------------------------------------------------------------*/
828 /**
829  *
830  *     This function adjusts WrDqsBias before seed scaling
831  *
832  *     @param[in,out]   *NBPtr     - Pointer to the MEM_NB_BLOCK
833  *     @param[in,out]   *WrDqsBias - Pointer to WrDqsBias
834  *
835  *     @return    FALSE - Supported
836  *     @return    TRUE - Not supported
837  */
838
839 BOOLEAN
840 MemNAdjustWrDqsBeforeSeedScalingOr (
841   IN OUT   MEM_NB_BLOCK *NBPtr,
842   IN OUT   VOID *WrDqsBias
843   )
844 {
845   // Subtract (0x20 * WrDqDqsEarly) since it is a non-scalable component
846   * (INT16 *) WrDqsBias = (INT16) (0x20 * MemNGetBitFieldNb (NBPtr, BFWrDqDqsEarly));
847   return TRUE;
848 }
849