AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / Mem / NB / mnphy.c
1 /* $NoKeywords:$ */
2 /**
3  * @file
4  *
5  * mnphy.c
6  *
7  * Common Northbridge Phy support
8  *
9  * @xrefitem bom "File Content Label" "Release Content"
10  * @e project: AGESA
11  * @e sub-project: (Mem/NB)
12  * @e \$Revision: 60556 $ @e \$Date: 2011-10-17 20:19:58 -0600 (Mon, 17 Oct 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 "mm.h"
61 #include "mn.h"
62 #include "mt.h"
63 #include "mu.h"
64 #include "PlatformMemoryConfiguration.h"
65 #include "heapManager.h"
66 #include "merrhdl.h"
67 #include "Filecode.h"
68 CODE_GROUP (G1_PEICC)
69 RDATA_GROUP (G2_PEI)
70 #define FILECODE PROC_MEM_NB_MNPHY_FILECODE
71 /*----------------------------------------------------------------------------
72  *                          DEFINITIONS AND MACROS
73  *
74  *----------------------------------------------------------------------------
75  */
76 #define UNUSED_CLK 4
77
78 /*----------------------------------------------------------------------------
79  *                           TYPEDEFS AND STRUCTURES
80  *
81  *----------------------------------------------------------------------------
82  */
83 /// Type of an entry for processing phy init compensation for client NB
84 typedef struct {
85   BIT_FIELD_NAME IndexBitField;          ///< Bit field on which the value is decided
86   BIT_FIELD_NAME StartTargetBitField;    ///< First bit field to be modified
87   BIT_FIELD_NAME EndTargetBitField;      ///< Last bit field to be modified
88   UINT16 ExtraValue;                     ///< Extra value needed to be written to bit field
89   CONST UINT16 (*TxPrePN)[3][5];         ///< Pointer to slew rate table
90 } PHY_COMP_INIT_CLIENTNB;
91
92 /*----------------------------------------------------------------------------
93  *                        PROTOTYPES OF LOCAL FUNCTIONS
94  *
95  *----------------------------------------------------------------------------
96  */
97
98
99
100 /*----------------------------------------------------------------------------
101  *                            EXPORTED FUNCTIONS
102  *
103  *----------------------------------------------------------------------------
104  */
105 /* -----------------------------------------------------------------------------*/
106 /**
107  *
108  *
109  *   This function gets a delay value a PCI register during training
110  *
111  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
112  *     @param[in]   TrnDly - type of delay to be set
113  *     @param[in]   DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
114  *                  (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
115  *
116  *     @return      Value read
117  */
118
119 UINT32
120 MemNGetTrainDlyNb (
121   IN OUT   MEM_NB_BLOCK *NBPtr,
122   IN       TRN_DLY_TYPE TrnDly,
123   IN       DRBN DrbnVar
124   )
125 {
126   return NBPtr->MemNcmnGetSetTrainDly (NBPtr, 0, TrnDly, DrbnVar, 0);
127 }
128
129 /* -----------------------------------------------------------------------------*/
130 /**
131  *
132  *
133  *   This function sets a delay value a PCI register during training
134  *
135  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
136  *     @param[in]   TrnDly - type of delay to be set
137  *     @param[in]   DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
138  *                  (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
139  *     @param[in]   Field - Value to be programmed
140  *
141  */
142
143 VOID
144 MemNSetTrainDlyNb (
145   IN OUT   MEM_NB_BLOCK *NBPtr,
146   IN       TRN_DLY_TYPE TrnDly,
147   IN       DRBN DrbnVar,
148   IN       UINT16 Field
149   )
150 {
151   NBPtr->MemNcmnGetSetTrainDly (NBPtr, 1, TrnDly, DrbnVar, Field);
152 }
153
154 /* -----------------------------------------------------------------------------*/
155 /**
156  *
157  *
158  *   This function executes prototypical Phy fence training function.
159  *
160  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
161  *
162  */
163
164 VOID
165 MemNPhyFenceTrainingNb (
166   IN OUT   MEM_NB_BLOCK *NBPtr
167   )
168 {
169   NBPtr->MemPPhyFenceTrainingNb (NBPtr);
170 }
171
172 /* -----------------------------------------------------------------------------*/
173 /**
174  *
175  *
176  *   This function executes prototypical Phy fence training function.
177  *
178  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
179  *
180  */
181
182 VOID
183 MemNPhyFenceTrainingUnb (
184   IN OUT   MEM_NB_BLOCK *NBPtr
185   )
186 {
187   UINT8 FenceThresholdTxDll;
188   UINT8 FenceThresholdRxDll;
189   UINT8 FenceThresholdTxPad;
190   UINT16 Fence2Data;
191
192   // 1. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=10b.
193   // 2. Perform phy fence training.
194   // 3. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxDll].
195   MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 2);
196   MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 30, 26, BFPhyFence);
197   IDS_HDT_CONSOLE (MEM_FLOW, "\t\tFenceThresholdTxDll\n");
198   MemNTrainPhyFenceNb (NBPtr);
199   FenceThresholdTxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
200   NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxDll);
201
202   // 4. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=001b.
203   MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x1000);
204
205   // 5. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=01b.
206   // 6. Perform phy fence training.
207   // 7. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdRxDll].
208   MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 1);
209   MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 25, 21, BFPhyFence);
210   IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdRxDll\n");
211   MemNTrainPhyFenceNb (NBPtr);
212   FenceThresholdRxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
213   NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdRxDll);
214
215   // 8. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=000b.
216   MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x0000);
217
218   // 9. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=11b.
219   // 10. Perform phy fence training.
220   // 11. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxPad].
221   MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 3);
222   MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 20, 16, BFPhyFence);
223   IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdTxPad\n");
224   MemNTrainPhyFenceNb (NBPtr);
225   FenceThresholdTxPad = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
226   NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxPad);
227
228   // Program Fence2 threshold for Clk, Cmd, and Addr
229   if (FenceThresholdTxPad < 16) {
230     MemNSetBitFieldNb (NBPtr, BFClkFence2, FenceThresholdTxPad | 0x10);
231     MemNSetBitFieldNb (NBPtr, BFCmdFence2, FenceThresholdTxPad | 0x10);
232     MemNSetBitFieldNb (NBPtr, BFAddrFence2, FenceThresholdTxPad | 0x10);
233   } else {
234     MemNSetBitFieldNb (NBPtr, BFClkFence2, 0);
235     MemNSetBitFieldNb (NBPtr, BFCmdFence2, 0);
236     MemNSetBitFieldNb (NBPtr, BFAddrFence2, 0);
237   }
238
239   // Program Fence2 threshold for data
240   Fence2Data = 0;
241   if (FenceThresholdTxPad < 16) {
242     Fence2Data |= FenceThresholdTxPad | 0x10;
243   }
244   if (FenceThresholdRxDll < 16) {
245     Fence2Data |= (FenceThresholdRxDll | 0x10) << 10;
246   }
247   if (FenceThresholdTxDll < 16) {
248     Fence2Data |= (FenceThresholdTxDll | 0x10) << 5;
249   }
250   MemNSetBitFieldNb (NBPtr, BFDataFence2, Fence2Data);
251   NBPtr->FamilySpecificHook[ProgramFence2RxDll] (NBPtr, &Fence2Data);
252
253   if (NBPtr->MCTPtr->Status[SbLrdimms]) {
254     // 18. If motherboard routing requires CS[7:6] to adopt address timings, e.g. 3 LRDIMMs/ch with CS[7:6]
255     // routed across all DIMM sockets, BIOS performs the following:
256     if (GetMaxDimmsPerChannel (NBPtr->RefPtr->PlatformMemoryConfiguration,
257                                NBPtr->MCTPtr->SocketId,
258                                NBPtr->ChannelPtr->ChannelID) == 3) {
259       if (FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_NO_LRDIMM_CS67_ROUTING, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0, NULL, NULL) == NULL) {
260       //   A. Program D18F2xA8_dct[1:0][CSTimingMux67] = 1.
261       MemNSetBitFieldNb (NBPtr, BFCSTimingMux67, 1);
262       //   B. Program D18F2x9C_x0D0F_8021_dct[1:0]:
263       //       - DiffTimingEn = 1.
264       //       - IF (D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay] >=
265       //         D18F2x9C_x0D0F_E008_dct[1:0][FenceValue]) THEN Fence = 1 ELSE Fence = 0.
266       //       - Delay = D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay].
267       //
268       MemNSetBitFieldNb (NBPtr, BFDiffTimingEn, 1);
269       MemNSetBitFieldNb (NBPtr, BFFence, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay) >= MemNGetBitFieldNb (NBPtr, BFFenceValue)) ? 1 : 0);
270       MemNSetBitFieldNb (NBPtr, BFDelay, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay)));
271     }
272   }
273   }
274
275   // 19. Reprogram F2x9C_04.
276   MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl));
277
278 }
279
280 /* -----------------------------------------------------------------------------*/
281 /**
282  *
283  *
284  *   This function executes Phy fence training
285  *
286  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
287  *
288  */
289
290 VOID
291 MemNTrainPhyFenceNb (
292   IN OUT   MEM_NB_BLOCK *NBPtr
293   )
294 {
295   UINT8 Byte;
296   INT16 Avg;
297   UINT8 PREvalue;
298
299   if (MemNGetBitFieldNb (NBPtr, BFDisDramInterface)) {
300     return;
301   }
302
303   // 1. BIOS first programs a seed value to the phase recovery
304   //    engine registers.
305   //
306   IDS_HDT_CONSOLE (MEM_FLOW, "\t\tSeeds: ");
307   for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) {
308     // This includes ECC as byte 8
309     MemNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19);
310     IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", 19);
311   }
312
313   IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tPhyFenceTrEn = 1");
314   // 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1.
315   MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1);
316
317   if (!NBPtr->IsSupported[UnifiedNbFence]) {
318     // 3. Wait 200 MEMCLKs.
319     MemNWaitXMemClksNb (NBPtr, 200);
320   } else {
321     // 3. Wait 2000 MEMCLKs.
322     MemNWaitXMemClksNb (NBPtr, 2000);
323   }
324
325   // 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0.
326   MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 0);
327
328   // 5. BIOS reads the phase recovery engine registers
329   //    F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52.
330   // 6. Calculate the average value of the fine delay and subtract 8.
331   //
332   Avg = 0;
333   IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t  PRE: ");
334   for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) {
335     //
336     // This includes ECC as byte 8.   ECC Byte lane (8) is ignored by MemNGetTrainDlyNb function where
337     // ECC is not supported.
338     //
339     PREvalue = (UINT8) (0x1F & MemNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte)));
340     Avg = Avg + ((INT16) PREvalue);
341     IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", PREvalue);
342   }
343   Avg = ((Avg + 8) / 9);    // round up
344
345   Avg -= 8;
346   NBPtr->MemNPFenceAdjustNb (NBPtr, &Avg);
347
348   IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFence: %02x\n", Avg);
349
350   // 7. Write the value to F2x[1, 0]9C_x0C[PhyFence].
351   MemNSetBitFieldNb (NBPtr, BFPhyFence, Avg);
352
353   // 8. BIOS rewrites F2x[1, 0]9C_x04, DRAM Address/Command Timing Control
354   //    Register delays for both channels. This forces the phy to recompute
355   //    the fence.
356   //
357   MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl));
358 }
359
360 /* -----------------------------------------------------------------------------*/
361 /**
362  *
363  *
364  *   This function initializes the DDR phy compensation logic
365  *
366  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
367  *
368  */
369
370 VOID
371 MemNInitPhyCompNb (
372   IN OUT   MEM_NB_BLOCK *NBPtr
373   )
374 {
375   CONST UINT8 TableCompRiseSlew20x[] = {7, 3, 2, 2};
376   CONST UINT8 TableCompRiseSlew15x[] = {7, 7, 3, 2};
377   CONST UINT8 TableCompFallSlew20x[] = {7, 5, 3, 2};
378   CONST UINT8 TableCompFallSlew15x[] = {7, 7, 5, 3};
379   UINT8 i;
380   UINT8 j;
381   UINT8 CurrDct;
382   UINT8 CurrChannel;
383   BOOLEAN MarginImprv;
384   MarginImprv = FALSE;
385   CurrDct = NBPtr->Dct;
386   CurrChannel = NBPtr->Channel;
387   if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) {
388     if (NBPtr->MCTPtr->GangedMode == FALSE) {
389       for (i = 0; i < NBPtr->DctCount; i++) {
390         MemNSwitchDCTNb (NBPtr, i);
391         for (j = 0; j < NBPtr->ChannelCount; j++) {
392           NBPtr->SwitchChannel (NBPtr, j);
393           if ((NBPtr->ChannelPtr->Dimms == 4) && ((NBPtr->DCTPtr->Timings.Speed == DDR533_FREQUENCY) || (NBPtr->DCTPtr->Timings.Speed == DDR667_FREQUENCY))) {
394             MarginImprv = TRUE;
395           }
396         }
397       }
398       MemNSwitchDCTNb (NBPtr, CurrDct);
399       NBPtr->SwitchChannel (NBPtr, CurrChannel);
400     }
401   }
402
403   // 1. BIOS disables the phy compensation register by programming F2x9C_x08[DisAutoComp]=1
404   // 2. BIOS waits 5 us for the disabling of the compensation engine to complete.
405   // DisAutoComp will be cleared after Dram init has completed
406   //
407   MemNSwitchDCTNb (NBPtr, 0);
408   MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 1);
409   MemUWait10ns (500, NBPtr->MemPtr);
410   MemNSwitchDCTNb (NBPtr, CurrDct);
411
412   // 3. For each normalized driver strength code read from
413   // F2x[1, 0]9C_x00[AddrCmdDrvStren], program the
414   // corresponding 3 bit predriver code in F2x9C_x0A[D3Cmp1NCal, D3Cmp1PCal].
415   //
416   // 4. For each normalized driver strength code read from
417   // F2x[1, 0]9C_x00[DataDrvStren], program the corresponding
418   // 3 bit predriver code in F2x9C_x0A[D3Cmp0NCal, D3Cmp0PCal, D3Cmp2NCal,
419   // D3Cmp2PCal].
420   //
421   j = (UINT8) MemNGetBitFieldNb (NBPtr, BFAddrCmdDrvStren);
422   i = (UINT8) MemNGetBitFieldNb (NBPtr, BFDataDrvStren);
423
424   MemNSwitchDCTNb (NBPtr, 0);
425   MemNSetBitFieldNb (NBPtr, BFD3Cmp1NCal, TableCompRiseSlew20x[j]);
426   MemNSetBitFieldNb (NBPtr, BFD3Cmp1PCal, TableCompFallSlew20x[j]);
427
428   if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) {
429     MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]);
430     MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]);
431     MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]);
432     MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]);
433   }
434   if (NBPtr->IsSupported[CheckSlewWithoutMarginImprv]) {
435     ASSERT (i <= 3);
436     MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, TableCompRiseSlew15x[i]);
437     MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, TableCompFallSlew15x[i]);
438     MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, TableCompRiseSlew15x[i]);
439     MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, TableCompFallSlew15x[i]);
440   }
441   MemNSwitchDCTNb (NBPtr, CurrDct);
442 }
443
444 /* -----------------------------------------------------------------------------*/
445 /**
446  *
447  *
448  *   This is a general purpose function that executes before DRAM training
449  *
450  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
451  *
452  */
453
454 VOID
455 MemNBeforeDQSTrainingNb (
456   IN OUT   MEM_NB_BLOCK *NBPtr
457   )
458 {
459   UINT8 Dct;
460   UINT8 ChipSel;
461   UINT32 TestAddrRJ16;
462   UINT32 RealAddr;
463
464   MemTBeginTraining (NBPtr->TechPtr);
465
466   for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
467     MemNSwitchDCTNb (NBPtr, Dct);
468     if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
469       for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel += 2) {
470         if (MemNGetMCTSysAddrNb (NBPtr, ChipSel, &TestAddrRJ16)) {
471
472           RealAddr = MemUSetUpperFSbase (TestAddrRJ16, NBPtr->MemPtr);
473
474           MemUDummyCLRead (RealAddr);
475
476           MemNSetBitFieldNb (NBPtr, BFErr350, 0x8000);
477           MemUWait10ns (60, NBPtr->MemPtr);   // Wait 300ns
478           MemNSetBitFieldNb (NBPtr, BFErr350, 0x0000);
479           MemUWait10ns (400, NBPtr->MemPtr);  // Wait 2us
480           MemUProcIOClFlush (TestAddrRJ16, 1, NBPtr->MemPtr);
481           break;
482         }
483       }
484     }
485     if (NBPtr->IsSupported[CheckEccDLLPwrDnConfig]) {
486       if (!NBPtr->MCTPtr->Status[SbEccDimms]) {
487         MemNSetBitFieldNb (NBPtr, BFEccDLLPwrDnConf, 0x0010);
488       }
489       if (NBPtr->DCTPtr->Timings.Dimmx4Present == 0) {
490         MemNSetBitFieldNb (NBPtr, BFEccDLLConf, 0x0080);
491       }
492     }
493   }
494
495   MemTEndTraining (NBPtr->TechPtr);
496 }
497
498 /*-----------------------------------------------------------------------------*/
499 /**
500  *
501  *     Returns the parameters for a requested delay value to be used in training
502  *     The correct Min, Max and Mask are determined based on the type of Delay,
503  *     and the frequency
504  *
505  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
506  *     @param[in] TrnDly - Type of delay
507  *     @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct
508  *
509  */
510
511 VOID
512 MemNGetTrainDlyParmsNb (
513   IN OUT   MEM_NB_BLOCK *NBPtr,
514   IN       TRN_DLY_TYPE TrnDly,
515   IN OUT   TRN_DLY_PARMS *Parms
516   )
517 {
518   Parms->Min = 0;
519
520   if (TrnDly == AccessWrDatDly) {
521     Parms->Max = 0x1F;
522     Parms->Mask = 0x01F;
523   } else if (TrnDly == AccessRdDqsDly) {
524     if ( (NBPtr->IsSupported[CheckMaxRdDqsDlyPtr]) && (NBPtr->DCTPtr->Timings.Speed > DDR667_FREQUENCY) ) {
525       Parms->Max = 0x3E;
526       Parms->Mask = 0x03E;
527     } else {
528       Parms->Max = 0x1F;
529       Parms->Mask = 0x01F;
530     }
531   }
532 }
533
534 /*-----------------------------------------------------------------------------*/
535 /**
536  *
537  *     Returns the parameters for a requested delay value to be used in training
538  *     The correct Min, Max and Mask are determined based on the type of Delay,
539  *     and the frequency
540  *
541  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
542  *     @param[in] TrnDly - Type of delay
543  *     @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct
544  *
545  */
546
547 VOID
548 MemNGetTrainDlyParmsClientNb (
549   IN OUT   MEM_NB_BLOCK *NBPtr,
550   IN       TRN_DLY_TYPE TrnDly,
551   IN OUT   TRN_DLY_PARMS *Parms
552   )
553 {
554   Parms->Min = 0;
555
556   if (TrnDly == AccessWrDatDly) {
557     Parms->Max = 0x1F;
558     Parms->Mask = 0x01F;
559   } else if (TrnDly == AccessRdDqsDly) {
560     Parms->Max = 0x3E;
561     Parms->Mask = 0x03E;
562   }
563 }
564 /*-----------------------------------------------------------------------------*/
565 /**
566  *
567  *     Returns the parameters for a requested delay value to be used in training
568  *     The correct Min, Max and Mask are determined based on the type of Delay,
569  *     and the frequency
570  *
571  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
572  *     @param[in] TrnDly - Type of delay
573  *     @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct
574  *
575  */
576
577 VOID
578 MemNGetTrainDlyParmsUnb (
579   IN OUT   MEM_NB_BLOCK *NBPtr,
580   IN       TRN_DLY_TYPE TrnDly,
581   IN OUT   TRN_DLY_PARMS *Parms
582   )
583 {
584   Parms->Min = 0;
585
586   if ((TrnDly == AccessWrDatDly) || (TrnDly == AccessRdDqsDly)) {
587     Parms->Max = 0x1F;
588     Parms->Mask = 0x01F;
589   }
590 }
591 /*----------------------------------------------------------------------------
592  *                              LOCAL FUNCTIONS
593  *
594  *----------------------------------------------------------------------------
595  */
596 /* -----------------------------------------------------------------------------*/
597 /**
598  *
599  *
600  *   This function gets or set DQS timing during training.
601  *
602  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
603  *     @param[in]   TrnDly - type of delay to be set
604  *     @param[in]   DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
605  *                  (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
606  *     @param[in]   Field - Value to be programmed
607  *     @param[in]   IsSet - Indicates if the function will set or get
608  *
609  *     @return      value read, if the function is used as a "get"
610  */
611
612 UINT32
613 MemNcmnGetSetTrainDlyNb (
614   IN OUT   MEM_NB_BLOCK *NBPtr,
615   IN       UINT8 IsSet,
616   IN       TRN_DLY_TYPE TrnDly,
617   IN       DRBN DrbnVar,
618   IN       UINT16 Field
619   )
620 {
621   UINT16 Index;
622   UINT16 Offset;
623   UINT32 Value;
624   UINT32 Address;
625   UINT8 Dimm;
626   UINT8 Rank;
627   UINT8 Byte;
628   UINT8 Nibble;
629
630   Dimm = DRBN_DIMM (DrbnVar);
631   Rank = DRBN_RANK (DrbnVar);
632   Byte = DRBN_BYTE (DrbnVar);
633   Nibble = DRBN_NBBL (DrbnVar);
634
635   ASSERT (Dimm < 4);
636   ASSERT (Byte <= ECC_DLY);
637
638   switch (TrnDly) {
639   case AccessRcvEnDly:
640     Index = 0x10;
641     break;
642   case AccessWrDqsDly:
643     Index = 0x30;
644     break;
645   case AccessWrDatDly:
646     Index = 0x01;
647     break;
648   case AccessRdDqsDly:
649     Index = 0x05;
650     break;
651   case AccessPhRecDly:
652     Index = 0x50;
653     break;
654   default:
655     Index = 0;
656     IDS_ERROR_TRAP;
657   }
658
659   switch (TrnDly) {
660   case AccessRcvEnDly:
661   case AccessWrDqsDly:
662     Index += (Dimm * 3);
663     if (Byte & 0x04) {
664       // if byte 4,5,6,7
665       Index += 0x10;
666     }
667     if (Byte & 0x02) {
668       // if byte 2,3,6,7
669       Index++;
670     }
671     if (Byte > 7) {
672       Index += 2;
673     }
674     Offset = 16 * (Byte % 2);
675     Index |= (Rank << 8);
676     Index |= (Nibble << 9);
677     break;
678
679   case AccessRdDqsDly:
680   case AccessWrDatDly:
681
682     if (NBPtr->IsSupported[DimmBasedOnSpeed]) {
683       if (NBPtr->DCTPtr->Timings.Speed < DDR800_FREQUENCY) {
684         // if DDR speed is below 800, use DIMM 0 delays for all DIMMs.
685         Dimm = 0;
686       }
687     }
688
689     Index += (Dimm * 0x100);
690     if (Nibble) {
691       if (Rank) {
692         Index += 0xA0;
693       } else {
694         Index += 0x70;
695       }
696     } else if (Rank) {
697       Index += 0x60;
698     }
699     // break is not being used here because AccessRdDqsDly and AccessWrDatDly also need
700     // to run AccessPhRecDly sequence.
701   case AccessPhRecDly:
702     Index += (Byte / 4);
703     Offset = 8 * (Byte % 4);
704     break;
705   default:
706     Offset = 0;
707     IDS_ERROR_TRAP;
708   }
709
710   Address = Index;
711   MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
712   MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
713   Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
714
715   if (TrnDly == AccessRdDqsDly) {
716     NBPtr->FamilySpecificHook[AdjustRdDqsDlyOffset] (NBPtr, &Offset);
717   }
718
719   if (IsSet) {
720     if (TrnDly == AccessPhRecDly) {
721       Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
722     }
723
724     Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
725     MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
726     Address |= DCT_ACCESS_WRITE;
727     MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
728     MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
729
730     if (TrnDly == AccessPhRecDly) {
731       NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
732     }
733   } else {
734     Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF);
735   }
736
737   return Value;
738 }
739
740 /* -----------------------------------------------------------------------------*/
741 /**
742  *
743  *   This function gets or set DQS timing during training.
744  *
745  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
746  *     @param[in]   IsSet - Indicates if the function will set or get
747  *     @param[in]   TrnDly - type of delay to be set
748  *     @param[in]   DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
749  *                  (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
750  *     @param[in]   Field - Value to be programmed
751  *
752  *     @return      value read, if the function is used as a "get"
753  */
754 UINT32
755 MemNcmnGetSetTrainDlyClientNb (
756   IN OUT   MEM_NB_BLOCK *NBPtr,
757   IN       UINT8 IsSet,
758   IN       TRN_DLY_TYPE TrnDly,
759   IN       DRBN DrbnVar,
760   IN       UINT16 Field
761   )
762 {
763   UINT16 Index;
764   UINT16 Offset;
765   UINT32 Value;
766   UINT32 Address;
767   UINT8 Dimm;
768   UINT8 Byte;
769
770   Dimm = DRBN_DIMM (DrbnVar);
771   Byte = DRBN_BYTE (DrbnVar);
772
773   ASSERT (Dimm < 2);
774   ASSERT (Byte <= ECC_DLY);
775
776   if ((Byte > 7)) {
777     // Llano does not support ECC delay, so:
778     if (IsSet) {
779       // On write, ignore
780       return 0;
781     } else {
782       // On read, redirect to byte 0 to correct fence averaging
783       Byte = 0;
784     }
785   }
786
787   switch (TrnDly) {
788   case AccessRcvEnDly:
789     Index = 0x10;
790     break;
791   case AccessWrDqsDly:
792     Index = 0x30;
793     break;
794   case AccessWrDatDly:
795     Index = 0x01;
796     break;
797   case AccessRdDqsDly:
798     Index = 0x05;
799     break;
800   case AccessPhRecDly:
801     Index = 0x50;
802     break;
803   default:
804     Index = 0;
805     IDS_ERROR_TRAP;
806   }
807
808   switch (TrnDly) {
809   case AccessRcvEnDly:
810   case AccessWrDqsDly:
811     Index += (Dimm * 3);
812     if (Byte & 0x04) {
813       // if byte 4,5,6,7
814       Index += 0x10;
815     }
816     if (Byte & 0x02) {
817       // if byte 2,3,6,7
818       Index++;
819     }
820     Offset = 16 * (Byte % 2);
821     break;
822
823   case AccessRdDqsDly:
824   case AccessWrDatDly:
825     Index += (Dimm * 0x100);
826     // break is not being used here because AccessRdDqsDly and AccessWrDatDly also need
827     // to run AccessPhRecDly sequence.
828   case AccessPhRecDly:
829     Index += (Byte / 4);
830     Offset = 8 * (Byte % 4);
831     break;
832   default:
833     Offset = 0;
834     IDS_ERROR_TRAP;
835   }
836
837   Address = Index;
838   MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
839   Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
840
841   if (IsSet) {
842     if (TrnDly == AccessPhRecDly) {
843       Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
844     }
845
846     Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
847     MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
848     Address |= DCT_ACCESS_WRITE;
849     MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
850
851     if (TrnDly == AccessPhRecDly) {
852       NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
853     }
854     // Gross WrDatDly and WrDqsDly cannot be larger than 4
855     ASSERT (((TrnDly == AccessWrDatDly) || (TrnDly == AccessWrDqsDly)) ? (NBPtr->IsSupported[WLNegativeDelay] || (Field < 0xA0)) : TRUE);
856   } else {
857     Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF);
858   }
859
860   return Value;
861 }
862 /* -----------------------------------------------------------------------------*/
863 /**
864  *
865  *
866  *   This function gets or set DQS timing during training.
867  *
868  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
869  *     @param[in]   TrnDly - type of delay to be set
870  *     @param[in]   DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
871  *                  (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
872  *     @param[in]   Field - Value to be programmed
873  *     @param[in]   IsSet - Indicates if the function will set or get
874  *
875  *     @return      value read, if the function is used as a "get"
876  */
877
878 UINT32
879 MemNcmnGetSetTrainDlyUnb (
880   IN OUT   MEM_NB_BLOCK *NBPtr,
881   IN       UINT8 IsSet,
882   IN       TRN_DLY_TYPE TrnDly,
883   IN       DRBN DrbnVar,
884   IN       UINT16 Field
885   )
886 {
887   UINT16 Index;
888   UINT16 Offset;
889   UINT32 Value;
890   UINT32 Address;
891   UINT8 Dimm;
892   UINT8 Rank;
893   UINT8 Byte;
894   UINT8 Nibble;
895   UINT8 DimmNibble;
896
897   Dimm = DRBN_DIMM (DrbnVar);
898   Rank = DRBN_RANK (DrbnVar);
899   Byte = DRBN_BYTE (DrbnVar);
900   Nibble = DRBN_NBBL (DrbnVar);
901   DimmNibble = DRBN_DIMM_NBBL (DrbnVar);
902
903   ASSERT (Dimm < 4);
904   ASSERT (Byte <= ECC_DLY);
905   if ((Byte == ECC_DLY) && !NBPtr->MCTPtr->Status[SbEccDimms]) {
906     // When ECC is not enabled
907     if (IsSet) {
908       // On write, ignore
909       return 0;
910     } else {
911       // On read, redirect to byte 0 to correct fence averaging
912       Byte = 0;
913     }
914   }
915
916   switch (TrnDly) {
917   case AccessRcvEnDly:
918     Index = 0x10;
919     break;
920   case AccessWrDqsDly:
921     Index = 0x30;
922     break;
923   case AccessWrDatDly:
924     Index = 0x01;
925     break;
926   case AccessRdDqsDly:
927     Index = 0x05;
928     break;
929   case AccessRdDqs__Dly:
930     Index = 0x00;
931     break;
932   case AccessPhRecDly:
933     Index = 0x50;
934     break;
935   default:
936     Index = 0;
937     IDS_ERROR_TRAP;
938   }
939
940   switch (TrnDly) {
941   case AccessRcvEnDly:
942   case AccessWrDqsDly:
943     Index += (Dimm * 3);
944     if (Byte & 0x04) {
945       // if byte 4,5,6,7
946       Index += 0x10;
947     }
948     if (Byte & 0x02) {
949       // if byte 2,3,6,7
950       Index++;
951     }
952     if (Byte > 7) {
953       Index += 2;
954     }
955     Offset = 16 * (Byte % 2);
956     Index |= (Rank << 8);
957     Index |= (Nibble << 9);
958     Address = Index;
959     break;
960
961   case AccessRdDqsDly:
962   case AccessWrDatDly:
963
964     if (NBPtr->IsSupported[DimmBasedOnSpeed]) {
965       if (NBPtr->DCTPtr->Timings.Speed < DDR800_FREQUENCY) {
966         // if DDR speed is below 800, use DIMM 0 delays for all DIMMs.
967         Dimm = 0;
968       }
969     }
970
971     Index += (Dimm * 0x100);
972     if (Nibble) {
973       if (Rank) {
974         Index += 0xA0;
975       } else {
976         Index += 0x70;
977       }
978     } else if (Rank) {
979       Index += 0x60;
980     }
981     // break is not being used here because AccessRdDqsDly and AccessWrDatDly also need
982     // to run AccessPhRecDly sequence.
983   case AccessPhRecDly:
984     Index += (Byte / 4);
985     Offset = 8 * (Byte % 4);
986     Address = Index;
987     break;
988   case AccessRdDqs__Dly:
989     Address = 0x0D0F0000;
990     Index += (DimmNibble >> 1) * 0x100;
991     Index += 0x20;
992     Index = Index + Dimm;
993     Offset = 4 * ((DimmNibble & 0x01) * 2);
994     Address += Index;
995     break;
996   default:
997     Offset = 0;
998     IDS_ERROR_TRAP;
999     Address = Index;
1000   }
1001   MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
1002   MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
1003   Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
1004   if (TrnDly == AccessRdDqsDly) {
1005     NBPtr->FamilySpecificHook[AdjustRdDqsDlyOffset] (NBPtr, &Offset);
1006   }
1007
1008   if (IsSet) {
1009     if (TrnDly == AccessPhRecDly) {
1010       Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
1011     }
1012     if (TrnDly != AccessRdDqs__Dly) {
1013       Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF) << Offset)));
1014     } else {
1015       Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) 0x1F << Offset)));
1016     }
1017     MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
1018     Address |= DCT_ACCESS_WRITE;
1019     MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
1020     MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
1021     if (TrnDly == AccessPhRecDly) {
1022       NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
1023     }
1024   } else {
1025     if (TrnDly != AccessRdDqs__Dly) {
1026       Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF);
1027     } else {
1028       Value = (Value >> Offset) & (UINT32) (0x1F);
1029     }
1030   }
1031   return Value;
1032 }
1033 /* -----------------------------------------------------------------------------*/
1034 /**
1035  *
1036  *     This function initializes the training pattern.
1037  *
1038  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
1039  *
1040  *     @return      AGESA_STATUS - Result
1041  *                  AGESA_SUCCESS - Training pattern is ready to use
1042  *                  AGESA_ERROR   - Unable to initialize the pattern.
1043  */
1044
1045 AGESA_STATUS
1046 MemNTrainingPatternInitNb (
1047   IN OUT   MEM_NB_BLOCK *NBPtr
1048   )
1049 {
1050   MEM_TECH_BLOCK *TechPtr;
1051   ALLOCATE_HEAP_PARAMS AllocHeapParams;
1052   TRAIN_PATTERN TrainPattern;
1053   AGESA_STATUS Status;
1054
1055   TechPtr = NBPtr->TechPtr;
1056   TrainPattern = 0;
1057   //
1058   // Check the training type
1059   //
1060   if (TechPtr->TrainingType == TRN_DQS_POSITION) {
1061     //
1062     // DQS Position Training
1063     //
1064     if (NBPtr->PosTrnPattern == POS_PATTERN_256B) {
1065       //
1066       // 256 Bit pattern
1067       //
1068       if (NBPtr->MCTPtr->Status[Sb128bitmode]) {
1069         TrainPattern = TestPatternJD256B;
1070         TechPtr->PatternLength = 64;
1071       } else {
1072         TrainPattern = TestPatternJD256A;
1073         TechPtr->PatternLength = 32;
1074       }
1075     } else {
1076       //
1077       // 72 bit pattern will be used if PosTrnPattern is not specified
1078       //
1079       if (NBPtr->MCTPtr->Status[Sb128bitmode]) {
1080         TrainPattern = TestPatternJD1B;
1081         TechPtr->PatternLength = 18;
1082       } else {
1083         TrainPattern = TestPatternJD1A;
1084         TechPtr->PatternLength = 9;
1085       }
1086     }
1087   } else if (TechPtr->TrainingType == TRN_MAX_READ_LATENCY) {
1088     //
1089     // Max Read Latency Training
1090     //
1091     TrainPattern = TestPatternML;
1092     TechPtr->PatternLength = (NBPtr->MCTPtr->Status[Sb128bitmode]) ? 6 : 3;
1093   } else {
1094     //
1095     // Error - TechPtr->Training Type must be set to one of the types handled in this function
1096     //
1097     ASSERT (FALSE);
1098   }
1099   //
1100   // Allocate training buffer
1101   //
1102   AllocHeapParams.RequestedBufferSize = (TechPtr->PatternLength * 64 * 2) + 16;
1103   AllocHeapParams.BufferHandle = AMD_MEM_TRAIN_BUFFER_HANDLE;
1104   AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
1105   Status = HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader);
1106   ASSERT (Status == AGESA_SUCCESS);
1107   if (Status != AGESA_SUCCESS) {
1108     return Status;
1109   }
1110   TechPtr->PatternBufPtr = AllocHeapParams.BufferPtr;
1111   AlignPointerTo16Byte (&TechPtr->PatternBufPtr);
1112   TechPtr->TestBufPtr = TechPtr->PatternBufPtr + (TechPtr->PatternLength * 64);
1113
1114   // Prepare training pattern
1115   MemUFillTrainPattern (TrainPattern, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
1116
1117   return Status;
1118 }
1119
1120 /* -----------------------------------------------------------------------------*/
1121 /**
1122  *
1123  *     This function determined the settings for the Reliable Read/Write engine
1124  *       for each specific type of training
1125  *
1126  *     @param[in,out]   *NBPtr      - Pointer to the MEM_NB_BLOCK
1127  *     @param[in]       *OptParam   - Pointer to an Enum of TRAINING_TYPE
1128  *
1129  *     @return      TRUE
1130  */
1131
1132 BOOLEAN
1133 MemNSetupHwTrainingEngineUnb (
1134   IN OUT   MEM_NB_BLOCK *NBPtr,
1135   IN       VOID *OptParam
1136   )
1137 {
1138   TRAINING_TYPE TrnType;
1139   RRW_SETTINGS *Rrw;
1140
1141   TrnType = *(TRAINING_TYPE*) OptParam;
1142   Rrw = &NBPtr->RrwSettings;
1143   //
1144   // Common Settings
1145   //
1146   Rrw->TgtBankAddressA = CPG_BANK_ADDRESS_A;
1147   Rrw->TgtRowAddressA = CPG_ROW_ADDRESS_A;
1148   Rrw->TgtColAddressA = CPG_COL_ADDRESS_A;
1149   Rrw->TgtBankAddressB = CPG_BANK_ADDRESS_B;
1150   Rrw->TgtRowAddressB = CPG_ROW_ADDRESS_B;
1151   Rrw->TgtColAddressB = CPG_COL_ADDRESS_B;
1152   Rrw->CompareMaskHigh = CPG_COMPARE_MASK_HI;
1153   Rrw->CompareMaskLow = CPG_COMPARE_MASK_LOW;
1154   Rrw->CompareMaskEcc = CPG_COMPARE_MASK_ECC;
1155
1156   switch (TrnType) {
1157   case TRN_RCVR_ENABLE:
1158     //
1159     // Receiver Enable Training
1160     //
1161     NBPtr->TechPtr->PatternLength = 192;
1162     break;
1163   case TRN_MAX_READ_LATENCY:
1164     //
1165     // Max Read Latency Training
1166     //
1167     Rrw->CmdTgt = CMD_TGT_A;
1168     NBPtr->TechPtr->PatternLength = 32;
1169     Rrw->DataPrbsSeed = PRBS_SEED_32;
1170     break;
1171   case TRN_DQS_POSITION:
1172     //
1173     // Read/Write DQS Position training
1174     //
1175     Rrw->CmdTgt = CMD_TGT_AB;
1176     NBPtr->TechPtr->PatternLength = 256;
1177     Rrw->DataPrbsSeed = PRBS_SEED_256;
1178     break;
1179   default:
1180     ASSERT (FALSE);
1181   }
1182   return TRUE;
1183 }
1184
1185 /* -----------------------------------------------------------------------------*/
1186 /**
1187  *
1188  *     This function finalizes the training pattern.
1189  *
1190  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
1191  *     @param[in]       Index    - Index of Write Data Delay Value
1192  *     @param[in,out]   *Value   - Write Data Delay Value
1193  *     @return      BOOLEAN - TRUE - Use the value returned.
1194  *                            FALSE - No more values in table.
1195  */
1196
1197 BOOLEAN
1198 MemNGetApproximateWriteDatDelayNb (
1199   IN OUT   MEM_NB_BLOCK *NBPtr,
1200   IN       UINT8 Index,
1201   IN OUT   UINT8 *Value
1202   )
1203 {
1204   CONST UINT8 WriteDatDelayValue[] = {0x10, 0x4, 0x8, 0xC, 0x14, 0x18, 0x1C, 0x1F};
1205   if (Index < GET_SIZE_OF (WriteDatDelayValue)) {
1206     *Value = WriteDatDelayValue[Index];
1207     return TRUE;
1208   }
1209   return FALSE;
1210 }
1211
1212
1213 /* -----------------------------------------------------------------------------*/
1214 /**
1215  *
1216  *     This function finalizes the training pattern.
1217  *
1218  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
1219  *
1220  *     @return      AGESA_STATUS - Result
1221  *                  AGESA_SUCCESS - Training pattern has been finalized.
1222  *                  AGESA_ERROR   - Unable to initialize the pattern.
1223  */
1224
1225 AGESA_STATUS
1226 MemNTrainingPatternFinalizeNb (
1227   IN OUT   MEM_NB_BLOCK *NBPtr
1228   )
1229 {
1230   AGESA_STATUS Status;
1231   //
1232   // Deallocate training buffer
1233   //
1234   Status = HeapDeallocateBuffer (AMD_MEM_TRAIN_BUFFER_HANDLE, &NBPtr->MemPtr->StdHeader);
1235   ASSERT (Status == AGESA_SUCCESS);
1236   return Status;
1237 }
1238
1239 /* -----------------------------------------------------------------------------*/
1240 /**
1241  *
1242  *     This function returns the number of chipselects per channel.
1243  *
1244  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
1245  *
1246  *     @return
1247  */
1248
1249 UINT8
1250 MemNCSPerChannelNb (
1251   IN OUT   MEM_NB_BLOCK *NBPtr
1252   )
1253 {
1254   return MAX_CS_PER_CHANNEL;
1255 }
1256
1257 /* -----------------------------------------------------------------------------*/
1258 /**
1259  *
1260  *     This function returns the number of Chipselects controlled by each set
1261  *     of Delay registers under current conditions.
1262  *
1263  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
1264  *
1265  *     @return
1266  */
1267
1268 UINT8
1269 MemNCSPerDelayNb (
1270   IN OUT   MEM_NB_BLOCK *NBPtr
1271   )
1272 {
1273   return MAX_CS_PER_DELAY;
1274 }
1275
1276 /* -----------------------------------------------------------------------------*/
1277 /**
1278  *
1279  *     This function returns the minimum data eye width in 32nds of a UI for
1280  *     the type of data eye(Rd/Wr) that is being trained.  This value will
1281  *     be the minimum number of consecutive delays that yield valid data.
1282  *     Uses TechPtr->Direction to determine read or write.
1283  *
1284  *
1285  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
1286  *
1287  *     @return
1288  */
1289
1290 UINT8
1291 MemNMinDataEyeWidthNb (
1292   IN OUT   MEM_NB_BLOCK *NBPtr
1293   )
1294 {
1295   UINT8 MinRdDataeye;
1296   UINT8 MinWrDataeye;
1297   UINT8 *MinRdWrDataeyePtr;
1298
1299   MinRdWrDataeyePtr = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MIN_RD_WR_DATAEYE_WIDTH, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0,
1300                                            &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
1301
1302   if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
1303     if (MinRdWrDataeyePtr != NULL) {
1304       MinRdDataeye = MinRdWrDataeyePtr[0];
1305       return MinRdDataeye;
1306     } else {
1307       return MIN_RD_DATAEYE_WIDTH_NB;
1308     }
1309   } else {
1310     if (MinRdWrDataeyePtr != NULL) {
1311       MinWrDataeye = MinRdWrDataeyePtr[1];
1312       return MinWrDataeye;
1313     } else {
1314       return MIN_WR_DATAEYE_WIDTH_NB;
1315     }
1316   }
1317 }
1318
1319 /* -----------------------------------------------------------------------------*/
1320 /**
1321  *
1322  *     This function programs the phy registers according to the desired phy VDDIO voltage level
1323  *
1324  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
1325  *
1326  */
1327
1328 VOID
1329 MemNPhyVoltageLevelNb (
1330   IN OUT   MEM_NB_BLOCK *NBPtr
1331   )
1332 {
1333   BIT_FIELD_NAME BitField;
1334   BIT_FIELD_NAME BFEnd;
1335   UINT16 BFValue;
1336   UINT16 RegValue;
1337
1338   BFValue = (UINT16) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage) << 3;
1339   BFEnd = NBPtr->IsSupported[ProgramCsrComparator] ? BFCsrComparator : BFCmpVioLvl;
1340
1341   for (BitField = BFDataRxVioLvl; BitField <= BFEnd; BitField++) {
1342     RegValue = BFValue;
1343     if (BitField == BFCsrComparator) {
1344       RegValue >>= (3 - 2);
1345       // Setting this bit in DCT0 adjusts the comparator for DCT0 and DCT1. Setting this bit in DCT1 has no effect.
1346       NBPtr->SwitchDCT (NBPtr, 0);
1347       MemNSetBitFieldNb (NBPtr, BitField, RegValue);
1348       break;
1349     } else if (BitField == BFCmpVioLvl) {
1350       RegValue <<= (14 - 3);
1351       // Must set this bit on DCT0 even when DCT0 has no memory
1352       NBPtr->SwitchDCT (NBPtr, 0);
1353       MemNSetBitFieldNb (NBPtr, BitField, RegValue);
1354     }
1355     MemNBrdcstSetNb (NBPtr, BitField, RegValue);
1356   }
1357 }
1358
1359 /* -----------------------------------------------------------------------------*/
1360 /**
1361  *
1362  *
1363  *   This function adjusts Avg PRE value of Phy fence training according to specific CPU family.
1364  *
1365  *     @param[in,out]   *NBPtr  - Pointer to the MEM_NB_BLOCK
1366  *     @param[in,out]   *Value16 - Pointer to the value that we want to adjust
1367  *
1368  */
1369 VOID
1370 MemNPFenceAdjustUnb (
1371   IN OUT   MEM_NB_BLOCK *NBPtr,
1372   IN OUT   INT16 *Value16
1373   )
1374 {
1375   *Value16 += 2; //The Avg PRE value is subtracted by 6 only.
1376 }
1377
1378 /* -----------------------------------------------------------------------------*/
1379 /**
1380  *
1381  *
1382  *   This function initializes the DDR phy compensation logic
1383  *
1384  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
1385  *
1386  */
1387
1388 VOID
1389 MemNInitPhyCompClientNb (
1390   IN OUT   MEM_NB_BLOCK *NBPtr
1391   )
1392 {
1393   // Slew rate table array [x][y][z]
1394   // array[0]: slew rate for VDDIO 1.5V
1395   // array[1]: slew rate for VDDIO 1.35V
1396   // array[2]: slew rate for VDDIO 1.25V
1397   // array[x][y]: slew rate for a certain frequency
1398   // array[x][y][0]: frequency mask for current entry
1399   CONST STATIC UINT16 TxPrePNDataDqs[3][3][5] = {
1400     {{ (UINT16) DDR800, 0x924, 0x924, 0x924, 0x924},
1401      { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
1402      { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}},
1403     {{ (UINT16) DDR800, 0xFF6, 0xB6D, 0xB6D, 0x924},
1404      { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
1405      { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}},
1406     {{ (UINT16) DDR800, 0xFF6, 0xDAD, 0xDAD, 0x924},
1407      { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
1408      { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}
1409   };
1410   CONST STATIC UINT16 TxPrePNCmdAddr[3][3][5] = {
1411     {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492},
1412      { (UINT16) (DDR1066 + DDR1333), 0x6DB, 0x6DB, 0x6DB, 0x6DB},
1413      { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0xB6D, 0xB6D}},
1414     {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492},
1415      { (UINT16) (DDR1066 + DDR1333), 0x924, 0x6DB, 0x6DB, 0x6DB},
1416      { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0x924, 0x924}},
1417     {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492},
1418      { (UINT16) (DDR1066 + DDR1333), 0xDAD, 0x924, 0x6DB, 0x492},
1419      { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xDAD, 0xB64, 0xB64}}
1420   };
1421   CONST STATIC UINT16 TxPrePNClock[3][3][5] = {
1422     {{ (UINT16) DDR800, 0x924, 0x924, 0x924, 0x924},
1423      { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xB6D},
1424      { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}},
1425     {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924},
1426      { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xDAD},
1427      { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xDAD}},
1428     {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924},
1429      { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
1430      { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}
1431   };
1432
1433   CONST PHY_COMP_INIT_CLIENTNB PhyCompInitBitField[] = {
1434     // 3. Program TxPreP/TxPreN for Data and DQS according toTable 14 if VDDIO is 1.5V or Table 15 if 1.35V.
1435     //    A. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0[A,6]={0000b, TxPreP, TxPreN}.
1436     //    B. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]02={1000b, TxPreP, TxPreN}.
1437     {BFDqsDrvStren, BFDataByteTxPreDriverCal2Pad1, BFDataByteTxPreDriverCal2Pad1, 0, TxPrePNDataDqs},
1438     {BFDataDrvStren, BFDataByteTxPreDriverCal2Pad2, BFDataByteTxPreDriverCal2Pad2, 0, TxPrePNDataDqs},
1439     {BFDataDrvStren, BFDataByteTxPreDriverCal, BFDataByteTxPreDriverCal, 8, TxPrePNDataDqs},
1440     // 4. Program TxPreP/TxPreN for Cmd/Addr according toTable 16 if VDDIO is 1.5V or Table 17 if 1.35V.
1441     //    A. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0][12,0E,0A,06]={0000b, TxPreP, TxPreN}.
1442     //    B. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0]02={1000b, TxPreP, TxPreN}.
1443     {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCal2Pad1, BFCmdAddr0TxPreDriverCal2Pad2, 0, TxPrePNCmdAddr},
1444     {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCal2Pad1, BFAddrTxPreDriverCal2Pad4, 0, TxPrePNCmdAddr},
1445     {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCalPad0, BFCmdAddr0TxPreDriverCalPad0, 8, TxPrePNCmdAddr},
1446     {BFCkeDrvStren, BFAddrTxPreDriverCalPad0, BFAddrTxPreDriverCalPad0, 8, TxPrePNCmdAddr},
1447     {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCalPad0, BFCmdAddr1TxPreDriverCalPad0, 8, TxPrePNCmdAddr},
1448     // 5. Program TxPreP/TxPreN for Clock according toTable 18 if VDDIO is 1.5V or Table 19 if 1.35V.
1449     //    A. Program D18F2x[1,0]9C_x0D0F_2[1:0]02={1000b, TxPreP, TxPreN}.
1450     {BFClkDrvStren, BFClock0TxPreDriverCalPad0, BFClock1TxPreDriverCalPad0, 8, TxPrePNClock}
1451   };
1452
1453   BIT_FIELD_NAME CurrentBitField;
1454   UINT16 SpeedMask;
1455   CONST UINT16 (*TxPrePNArray)[5];
1456   UINT8 Voltage;
1457   UINT8 i;
1458   UINT8 j;
1459   UINT8 k;
1460   UINT8 Dct;
1461
1462   Dct = NBPtr->Dct;
1463   NBPtr->SwitchDCT (NBPtr, 0);
1464   // 1. Program D18F2x[1,0]9C_x0D0F_E003[DisAutoComp, DisablePreDriverCal] = {1b, 1b}.
1465   MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 0x6000);
1466   NBPtr->SwitchDCT (NBPtr, Dct);
1467
1468   SpeedMask = (UINT16) 1 << (NBPtr->DCTPtr->Timings.Speed / 66);
1469   Voltage = (UINT8) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage);
1470
1471   for (j = 0; j < GET_SIZE_OF (PhyCompInitBitField); j ++) {
1472     i = (UINT8) MemNGetBitFieldNb (NBPtr, PhyCompInitBitField[j].IndexBitField);
1473     TxPrePNArray = PhyCompInitBitField[j].TxPrePN[Voltage];
1474     for (k = 0; k < 3; k ++) {
1475       if ((TxPrePNArray[k][0] & SpeedMask) != 0) {
1476         for (CurrentBitField = PhyCompInitBitField[j].StartTargetBitField; CurrentBitField <= PhyCompInitBitField[j].EndTargetBitField; CurrentBitField ++) {
1477           MemNSetBitFieldNb (NBPtr, CurrentBitField, ((PhyCompInitBitField[j].ExtraValue << 12) | TxPrePNArray[k][i + 1]));
1478         }
1479         break;
1480       }
1481     }
1482     ASSERT (k < 3);
1483   }
1484
1485   NBPtr->FamilySpecificHook[ForceAutoComp] (NBPtr, NBPtr);
1486 }
1487
1488 /*-----------------------------------------------------------------------------
1489  *
1490  *
1491  *     This function re-enable phy compensation.
1492  *
1493  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1494  *     @param[in,out]  OptParam   - Optional parameter
1495  *
1496  *     @return    TRUE
1497  * ----------------------------------------------------------------------------
1498  */
1499 BOOLEAN
1500 MemNReEnablePhyCompNb (
1501   IN OUT   MEM_NB_BLOCK *NBPtr,
1502   IN OUT   VOID *OptParam
1503   )
1504 {
1505   UINT8 Dct;
1506
1507   Dct = NBPtr->Dct;
1508
1509   NBPtr->SwitchDCT (NBPtr, 0);
1510   // Clear DisableCal and set DisablePredriverCal
1511   MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 0x2000);
1512   NBPtr->SwitchDCT (NBPtr, Dct);
1513
1514   return TRUE;
1515 }
1516
1517 /*-----------------------------------------------------------------------------
1518  *
1519  *
1520  *     This function calculates the value of WrDqDqsEarly and programs it into
1521  *       the DCT and adds it to the WrDqsGrossDelay of each byte lane on each
1522  *       DIMM of the channel.
1523  *
1524  *
1525  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1526  *     @param[in,out]  OptParam   - Optional parameter
1527  *
1528  *     @return    TRUE
1529  * ----------------------------------------------------------------------------
1530  */
1531 BOOLEAN
1532 MemNCalcWrDqDqsEarlyUnb (
1533   IN OUT   MEM_NB_BLOCK *NBPtr,
1534   IN OUT   VOID *OptParam
1535   )
1536 {
1537   MEM_TECH_BLOCK *TechPtr;
1538   DCT_STRUCT *DCTPtr;
1539   CH_DEF_STRUCT *ChannelPtr;
1540   UINT8 Dimm;
1541   UINT8 ByteLane;
1542   UINT8 *WrDqsDlysPtr;
1543   UINT8 WrDqDqsEarly;
1544
1545   ASSERT ((NBPtr->IsSupported[WLSeedAdjust]) && (NBPtr->IsSupported[WLNegativeDelay]));
1546
1547   TechPtr = NBPtr->TechPtr;
1548   ChannelPtr = NBPtr->ChannelPtr;
1549   DCTPtr = NBPtr->DCTPtr;
1550
1551   ASSERT (NBPtr != NULL);
1552   ASSERT (ChannelPtr != NULL);
1553   ASSERT (DCTPtr != NULL);
1554   //
1555   // For each DIMM:
1556   // - The Critical Gross Delay (CGD) is the minimum GrossDly of all byte lanes and all DIMMs.
1557   // - If (CGD < 0) Then
1558   // -   D18F2xA8_dct[1:0][WrDqDqsEarly] = ABS(CGD)
1559   // -   WrDqsGrossDly = GrossDly + WrDqDqsEarly
1560   // - Else
1561   // -   D18F2xA8_dct[1:0][WrDqDqsEarly] = 0.
1562   // -   WrDqsGrossDly = GrossDly
1563   //
1564   WrDqDqsEarly = 0;
1565   if (TechPtr->WLCriticalDelay < 0) {
1566     IDS_HDT_CONSOLE (MEM_FLOW, "\t\tCalculating WrDqDqsEarly, adjusting WrDqs.\n");
1567     IDS_HDT_CONSOLE (MEM_FLOW, "\t\tMin. Critical Delay: %x\n", TechPtr->WLCriticalDelay);
1568     // We've saved the entire negative delay value, so take the ABS and convert to GrossDly.
1569     WrDqDqsEarly =  (UINT8) (0x00FF &((((ABS (TechPtr->WLCriticalDelay)) + 0x1F) / 0x20)));
1570     IDS_HDT_CONSOLE (MEM_FLOW, "\t\tWrDqDqsEarly : %02x\n\n", WrDqDqsEarly);
1571     //
1572     // Loop through All WrDqsDlys on all DIMMs
1573     //
1574     for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
1575       if ((NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << Dimm)) != 0) :
1576           ((DCTPtr->Timings.CsEnabled & ((UINT16) 3 << (Dimm << 1))) != 0)) {
1577         //
1578         // If LRDIMMs, only include the physical dimms, not logical Dimms
1579         //
1580         IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDimm %x:", Dimm);
1581         WrDqsDlysPtr = &(ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ())]);
1582         for (ByteLane = 0; ByteLane < TechPtr->DlyTableWidth (); ByteLane++) {
1583           WrDqsDlysPtr[ByteLane] += (WrDqDqsEarly << 5);
1584           NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDlysPtr[ByteLane]);
1585           IDS_HDT_CONSOLE (MEM_FLOW, " %02x", WrDqsDlysPtr[ByteLane]);
1586         }
1587         IDS_HDT_CONSOLE (MEM_FLOW, "\n");
1588       }
1589     }
1590   }
1591   MemNSetBitFieldNb (NBPtr, BFWrDqDqsEarly, WrDqDqsEarly);
1592   return TRUE;
1593 }
1594
1595 /*-----------------------------------------------------------------------------
1596  *
1597  *
1598  *     This function forces phy to M0 state
1599  *
1600  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1601  *     @param[in,out]  *OptParam   - Optional parameter
1602  *
1603  *     @return  FALSE - always
1604  * ----------------------------------------------------------------------------
1605  */
1606 BOOLEAN
1607 MemNForcePhyToM0Unb (
1608   IN OUT   MEM_NB_BLOCK *NBPtr,
1609   IN OUT   VOID *OptParam
1610   )
1611 {
1612   // 1. Program D18F2x9C_x0D0F_E013_dct[1:0] = 0118h.
1613   MemNBrdcstSetNb (NBPtr, BFPllRegWaitTime, 0x118);
1614   // 2. Force the phy to M0 with the following sequence:
1615   // A. Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 190h. Restore the default PLL lock time.
1616   MemNBrdcstSetNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
1617   // B. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80800000h.
1618   MemNBrdcstSetNb (NBPtr, BFDramPhyStatusReg, 0x80800000);
1619   NBPtr->SwitchDCT (NBPtr, 0);
1620   // C. Program D18F2x9C_x0D0F_E018_dct[0][PhyPSMasterChannel] = 0.
1621   MemNSetBitFieldNb (NBPtr, BFPhyPSMasterChannel, 0);
1622   // D. Program D18F2x9C_x0000_000B_dct[0] = 40000000h. CH0 only;
1623   MemNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x40000000);
1624   // E. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80000000h.
1625   MemNBrdcstSetNb (NBPtr, BFDramPhyStatusReg, 0x80000000);
1626
1627   return FALSE;
1628 }
1629
1630 /*-----------------------------------------------------------------------------
1631  *
1632  *
1633  *     This function sets SkewMemClk before enabling MemClk
1634  *
1635  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1636  *     @param[in,out]  *OptParam   - Optional parameter
1637  *
1638  *     @return  TRUE - always
1639  * ----------------------------------------------------------------------------
1640  */
1641 BOOLEAN
1642 MemNSetSkewMemClkUnb (
1643   IN OUT   MEM_NB_BLOCK *NBPtr,
1644   IN OUT   VOID *OptParam
1645   )
1646 {
1647   UINT8 Dct;
1648
1649   // SkewMemClk is set to 1 if all DCTs are enabled, else 0.
1650   for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
1651     MemNSwitchDCTNb (NBPtr, Dct);
1652     if (NBPtr->DCTPtr->Timings.DctMemSize == 0) {
1653     break;
1654     }
1655   }
1656   MemNSwitchDCTNb (NBPtr, 0);
1657   if (Dct == NBPtr->DctCount) {
1658     MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0x10);
1659   } else {
1660     MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0);
1661   }
1662
1663   return TRUE;
1664 }
1665
1666 /* -----------------------------------------------------------------------------*/
1667 /**
1668  *
1669  *     This function masks the RdDqsDly Bit 0 before writing to register for UNB.
1670  *
1671  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
1672  *     @param[in,out]   *Offset -  Bit offset of the field to be programmed
1673  *
1674  *     @return    TRUE
1675  */
1676 BOOLEAN
1677 MemNAdjustRdDqsDlyOffsetUnb (
1678   IN OUT   MEM_NB_BLOCK *NBPtr,
1679   IN OUT   VOID *Offset
1680   )
1681 {
1682   *(UINT16*) Offset = *(UINT16*) Offset + 1;
1683   return TRUE;
1684 }
1685
1686 /* -----------------------------------------------------------------------------*/
1687 /**
1688  *
1689  *
1690  *     This function delays MEMCLK to prevent WrDqs skew due to negative PRE result.
1691  *
1692  *
1693  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1694  *     @param[in,out]  OptParam   - Optional parameter
1695  *
1696  *     @return    TRUE
1697  * ----------------------------------------------------------------------------
1698  */
1699 BOOLEAN
1700 MemNCalcWrDqDqsEarlyClientNb (
1701   IN OUT   MEM_NB_BLOCK *NBPtr,
1702   IN OUT   VOID *OptParam
1703   )
1704 {
1705   MEM_TECH_BLOCK *TechPtr;
1706   DCT_STRUCT     *DCTPtr;
1707   CH_DEF_STRUCT  *ChannelPtr;
1708   UINT8  Dimm;
1709   UINT8  ByteLane;
1710   UINT8  *WrDqsDlysPtr;
1711   UINT8  NewClkDllDelay;
1712   UINT16 ClkDllFineDly;
1713   UINT32 AddrCmdTmg;
1714
1715   TechPtr = NBPtr->TechPtr;
1716   ChannelPtr = NBPtr->ChannelPtr;
1717   DCTPtr = NBPtr->DCTPtr;
1718
1719   ASSERT (NBPtr != NULL);
1720   ASSERT (ChannelPtr != NULL);
1721   ASSERT (DCTPtr != NULL);
1722
1723   if (NBPtr->IsSupported[WLNegativeDelay]) {
1724     if (TechPtr->WLCriticalDelay < 0) {
1725       NewClkDllDelay =  (UINT8) ABS (TechPtr->WLCriticalDelay);
1726
1727       // Prepare new delay for MEMCLK
1728       ClkDllFineDly = (UINT16) ((MemNGetBitFieldNb (NBPtr, BFPhyClkDllFine0) & 0xBF60) | NewClkDllDelay);
1729
1730       // Program bit 7(FenceBit) = 1 if NewClkDllDelay >= > F2x9C[FenceThresholdTxPad], else 0.
1731       ClkDllFineDly |= (NewClkDllDelay >= MemNGetBitFieldNb (NBPtr, BFPhyFence)) ? 0x80 : 0;
1732
1733       // Apply new delay to both chiplets
1734       MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine0, ClkDllFineDly | 0x4000);
1735       MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine0, ClkDllFineDly);
1736       MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine1, ClkDllFineDly | 0x4000);
1737       MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine1, ClkDllFineDly);
1738       IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tShift MemClk, AddrCmd, CsOdt, Cke by %d to eliminate negative WL\n", NewClkDllDelay);
1739
1740       //
1741       // Adjust AddrCmd/CsOdt/Cke timing by amount MemClk was delayed
1742       //
1743       AddrCmdTmg = MemNGetBitFieldNb (NBPtr, BFAddrTmgControl);
1744       AddrCmdTmg += (NewClkDllDelay << 16) | (NewClkDllDelay << 8) | NewClkDllDelay;
1745       AddrCmdTmg &= 0x003F3F3F;
1746       MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, AddrCmdTmg);
1747
1748       //
1749       // Adjust all WrDqsDlys on all DIMMs of the current channel
1750       //
1751       for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
1752         if ((DCTPtr->Timings.CsEnabled & ((UINT16)3 << (Dimm << 1))) != 0) {
1753           IDS_HDT_CONSOLE (MEM_FLOW, "\t\tCS%d\n\t\t\tWrDqs:", Dimm << 1);
1754           WrDqsDlysPtr = &(ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ())]);
1755           for (ByteLane = 0; ByteLane < 8; ByteLane++) {
1756             WrDqsDlysPtr[ByteLane] = (UINT8) (WrDqsDlysPtr[ByteLane] + NewClkDllDelay);
1757             NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDlysPtr[ByteLane]);
1758             IDS_HDT_CONSOLE (MEM_FLOW, " %02x", WrDqsDlysPtr[ByteLane]);
1759           }
1760           IDS_HDT_CONSOLE (MEM_FLOW, "\n");
1761         }
1762       }
1763     }
1764   }
1765
1766   return TRUE;
1767 }
1768 /* -----------------------------------------------------------------------------*/
1769 /**
1770  *
1771  *
1772  *     This function initializes RxEn Delays for RxEn seedless training
1773  *
1774  *
1775  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1776  *     @param[in,out]  OptParam   - Optional parameter
1777  *
1778  *     @return    TRUE
1779  * ----------------------------------------------------------------------------
1780  */
1781 BOOLEAN
1782 MemNInitializeRxEnSeedlessTrainingUnb (
1783   IN OUT   MEM_NB_BLOCK *NBPtr,
1784   IN OUT   VOID *OptParam
1785   )
1786 {
1787   UINT8 ByteLane;
1788   // Save original PRE based RxEnDly for RxEn Seedless training
1789   for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
1790     NBPtr->TechPtr->RxOrig[ByteLane] = NBPtr->ChannelPtr->RcvEnDlys[(NBPtr->TechPtr->ChipSel >> 1) * NBPtr->TechPtr->DlyTableWidth () + ByteLane];
1791   }
1792   return TRUE;
1793 }
1794 /*-----------------------------------------------------------------------------
1795  *
1796  *
1797  *     This function checks each bytelane for no window error.
1798  *
1799  *
1800  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1801  *     @param[in,out]   OptParam   - Optional parameter
1802  *
1803  *     @return    TRUE
1804  * ----------------------------------------------------------------------------
1805  */
1806 BOOLEAN
1807 MemNTrackRxEnSeedlessRdWrNoWindBLErrorUnb (
1808   IN OUT   MEM_NB_BLOCK *NBPtr,
1809   IN OUT   VOID *OptParam
1810   )
1811 {
1812   MemTTrackRxEnSeedlessRdWrNoWindBLError (NBPtr->TechPtr, OptParam);
1813   return TRUE;
1814 }
1815 /*-----------------------------------------------------------------------------
1816  *
1817  *
1818  *      This function checks each bytelane for small window error.
1819  *
1820  *
1821  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1822  *     @param[in,out]   OptParam   - Optional parameter
1823  *
1824  *     @return    TRUE
1825  * ----------------------------------------------------------------------------
1826  */
1827 BOOLEAN
1828 MemNTrackRxEnSeedlessRdWrSmallWindBLErrorUnb (
1829   IN OUT   MEM_NB_BLOCK *NBPtr,
1830   IN OUT   VOID *OptParam
1831   )
1832 {
1833   MemTTrackRxEnSeedlessRdWrSmallWindBLError (NBPtr->TechPtr, OptParam);
1834   return TRUE;
1835 }
1836 /*-----------------------------------------------------------------------------
1837  *
1838  *
1839  *      This function initializes a ByteLaneError error.
1840  *
1841  *
1842  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1843  *     @param[in,out]   OptParam   - Optional parameter
1844  *
1845  *     @return    TRUE
1846  * ----------------------------------------------------------------------------
1847  */
1848 BOOLEAN
1849 MemNInitialzeRxEnSeedlessByteLaneErrorUnb (
1850   IN OUT   MEM_NB_BLOCK *NBPtr,
1851   IN OUT   VOID *OptParam
1852   )
1853 {
1854   UINT8 ByteLane;
1855   for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
1856     NBPtr->TechPtr->ByteLaneError[ByteLane] = FALSE; // All Bytelanes have no errors
1857   }
1858   return TRUE;
1859 }
1860 /* -----------------------------------------------------------------------------*/
1861 /**
1862  *
1863  *
1864  *     This function sets phy power saving related settings in different MPstate context.
1865  *
1866  *
1867  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
1868  *
1869  *     @return    none
1870  * ----------------------------------------------------------------------------
1871  */
1872 VOID
1873 MemNPhyPowerSavingMPstateUnb (
1874   IN OUT   MEM_NB_BLOCK *NBPtr
1875   )
1876 {
1877   STATIC UINT8 Sequence[] = {8, 4, 3, 5, 2, 6, 1, 7, 0};
1878   UINT16 DllPower[9];
1879   UINT8 NumLanes;
1880   UINT8 DllWakeTime;
1881   UINT8 MaxRxStggrDly;
1882   UINT8 MinRcvEnGrossDly;
1883   UINT8 MinWrDatGrossDly;
1884   UINT8 dRxStggrDly;
1885   UINT8 dTxStggrDly;
1886   UINT8 TempStggrDly;
1887   UINT8 MaxTxStggrDly;
1888   UINT8 Tcwl;
1889   UINT8 i;
1890
1891   IDS_HDT_CONSOLE (MEM_FLOW, "Start Phy power saving setting for memory Pstate %d\n", NBPtr->MemPstate);
1892   // 4. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyU] = 1b.
1893   // 5. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyL] = 1b.
1894   // 6. D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0][RxDqsUDllPowerDown] = 1.
1895   MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13, MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) | 0x83);
1896   // 7. D18F2x9C_x0D0F_812F_dct[1:0][PARTri] = ~D18F2x90_dct[1:0][ParEn].
1897   // 8. D18F2x9C_x0D0F_812F_dct[1:0][Add17Tri, Add16Tri] = {1b, 1b}
1898   if (NBPtr->MemPstate == MEMORY_PSTATE0) {
1899     MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 0xA1);
1900   }
1901   // 9. IF (DimmsPopulated == 1)&& ((D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==010b) ||
1902   // (D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==011b)) THEN THEN
1903   // program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 1
1904   // ELSE program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 0 ENDIF.
1905   if ((NBPtr->ChannelPtr->Dimms == 1) && ((MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 2) || (MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 3))) {
1906     MemNSetBitFieldNb (NBPtr, BFReserved00C, 0x100);
1907   }
1908   // 10. Program D18F2x9C_x0D0F_0[F,7:0][50,10]_dct[1:0][EnRxPadStandby] = IF
1909   // (D18F2x94_dct[1:0][MemClkFreq] <= 800 MHz) THEN 1 ELSE 0 ENDIF.
1910   MemNSetBitFieldNb (NBPtr, BFEnRxPadStandby, (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) ? 0x1000 : 0);
1911   // 11. Program D18F2x9C_x0000_000D_dct[1:0]_mp[1:0] as follows:
1912   // If (DDR rate < = 1600) TxMaxDurDllNoLock = RxMaxDurDllNoLock = 8h
1913   // else TxMaxDurDllNoLock = RxMaxDurDllNoLock = 7h.
1914   if (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) {
1915     MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 8);
1916     MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 8);
1917   } else {
1918     MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 7);
1919     MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 7);
1920   }
1921   // TxCPUpdPeriod = RxCPUpdPeriod = 011b.
1922   MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 3);
1923   MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 3);
1924   // TxDLLWakeupTime = RxDLLWakeupTime = 11b.
1925   MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 3);
1926   MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 3);
1927
1928   if (NBPtr->IsSupported[DllStaggerEn]) {
1929     // 12. Program D18F2x9C_x0D0F_0[F,7:0][5C,1C]_dct[1:0] as follows.
1930     // Let Numlanes = 8. = 9 with ECC.
1931     NumLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8;
1932     // RxDllStggrEn = TxDllStggrEn = 1.
1933     for (i = 0; i < 9; i ++) {
1934       DllPower[i] = 0x8080;
1935     }
1936     // If (DDR rate > = 1866) DllWakeTime = 1, Else DllWakeTime = 0.
1937     DllWakeTime = (NBPtr->DCTPtr->Timings.Speed >= DDR1866_FREQUENCY) ? 1 : 0;
1938     // Let MaxRxStggrDly = (Tcl*2) + MIN(DqsRcvEnGrossDelay for all byte lanes (see D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4.
1939     MinRcvEnGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessRcvEnDly, FALSE);
1940     ASSERT ((NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly) >= 4);
1941     MaxRxStggrDly = NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly - 4;
1942     // Let (real) dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (Numlanes - 1).
1943     ASSERT (MaxRxStggrDly >= DllWakeTime);
1944     dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (NumLanes - 1);
1945     IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum RcvEnGrossDly: 0x%02x MaxRxStggrDly: 0x%02x dRxStggrDly: 0x%02x\n", MinRcvEnGrossDly, MaxRxStggrDly, dRxStggrDly);
1946     // For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program RxDllStggrDly[5:0] = an
1947     // increasing value, starting with 0 for the first byte lane in the sequence and increasing at a rate of dRxStggrDly
1948     // for each subsequent byte lane. Convert the real to integer by rounding down or using C (int) typecast after linearization.
1949     i = 9 - NumLanes;
1950     TempStggrDly = 0;
1951     for (; i < 9; i ++) {
1952       DllPower[Sequence[i]] |= ((TempStggrDly & 0x3F) << 8);
1953       TempStggrDly = TempStggrDly + dRxStggrDly;
1954     }
1955
1956     // Let MaxTxStggrDly = (Tcwl*2) + MIN(MIN (WrDatGrossDly for all byte lanes (see
1957     // D18F2x9C_x0000_0[3:0]0[2:1]_dct[1:0]_mp[1:0])), MIN(DqsRcvEnGrossDelay for all byte lanes (see
1958     // D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4.
1959     Tcwl = (UINT8) MemNGetBitFieldNb (NBPtr, BFTcwl);
1960     MinWrDatGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessWrDatDly, FALSE);
1961     MaxTxStggrDly = Tcwl * 2 + MIN (MinRcvEnGrossDly, MinWrDatGrossDly) - 4;
1962     // Let dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (Numlanes - 1).
1963     ASSERT (MaxTxStggrDly >= DllWakeTime);
1964     dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (NumLanes - 1);
1965     // For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program TxDllStggrDly[5:0] = an
1966     // increasing integer value, starting with 0 for the first byte lane in the sequence and increasing at a rate of
1967     // dTxStggrDly for each subsequent byte lane.
1968     IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum WrDatGrossDly: 0x%02x MaxTxStggrDly: 0x%02x dTxStggrDly: 0x%02x\n", MinWrDatGrossDly, MaxTxStggrDly, dTxStggrDly);
1969     i = 9 - NumLanes;
1970     TempStggrDly = 0;
1971     for (; i < 9; i ++) {
1972       DllPower[Sequence[i]] |= (TempStggrDly & 0x3F);
1973       TempStggrDly = TempStggrDly + dTxStggrDly;
1974     }
1975
1976     IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tByte Lane    :  ECC   07   06   05   04   03   02   01   00\n");
1977     IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tDll Power    : %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
1978                      DllPower[8], DllPower[7], DllPower[6], DllPower[5], DllPower[4], DllPower[3], DllPower[2], DllPower[1], DllPower[0]);
1979
1980     for (i = 0; i < NumLanes; i ++) {
1981       MemNSetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i, (MemNGetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i) & 0x4040) | DllPower[i]);
1982     }
1983   }
1984   // 13. Program D18F2x248_dct[1:0]_mp[1:0] and then D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0] as follows:
1985   // For M1 context program RxChMntClkEn=RxSsbMntClkEn=0.
1986   // For M0 context program RxChMntClkEn=RxSsbMntClkEn=1.
1987   if (NBPtr->MemPstate == MEMORY_PSTATE1) {
1988     MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 0);
1989     MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0);
1990   } else {
1991     MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 1);
1992     MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0x100);
1993   }
1994
1995   IDS_OPTION_HOOK (IDS_PHY_DLL_STANDBY_CTRL, NBPtr, &NBPtr->MemPtr->StdHeader);
1996 }
1997
1998 /* -----------------------------------------------------------------------------*/
1999 /**
2000  *
2001  *     This function resets RxFifo pointer during Read DQS training
2002  *
2003  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
2004  *     @param[in,out]   *OptParam - Optional parameter
2005  *
2006  *     @return    TRUE
2007  */
2008
2009 BOOLEAN
2010 MemNResetRxFifoPtrClientNb (
2011   IN OUT   MEM_NB_BLOCK *NBPtr,
2012   IN OUT   VOID *OptParam
2013   )
2014 {
2015   if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
2016     MemNSetBitFieldNb (NBPtr, BFRxPtrInitReq, 1);
2017     MemNPollBitFieldNb (NBPtr, BFRxPtrInitReq, 0, PCI_ACCESS_TIMEOUT, FALSE);
2018   }
2019   return TRUE;
2020 }
2021
2022 /* -----------------------------------------------------------------------------*/
2023 /**
2024  *      This function adjusts the Phase Mask based on ECC.
2025  *
2026  *
2027  *     @param[in,out]  *NBPtr     - Pointer to the MEM_NB_BLOCK
2028  *     @param[in,out]   OptParam   - Optional parameter
2029  *
2030  *     @return    TRUE
2031  * ----------------------------------------------------------------------------
2032  */
2033 BOOLEAN
2034 MemNAdjust2DPhaseMaskBasedOnEccUnb (
2035   IN OUT   MEM_NB_BLOCK *NBPtr,
2036   IN OUT   VOID *OptParam
2037   )
2038 {
2039   NBPtr->PhaseLaneMask = NBPtr->PhaseLaneMask & (UINT32) (NBPtr->MCTPtr->Status[SbEccDimms] ? 0x0FFFF : 0x3FFFF);
2040   return TRUE;
2041 }