AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / Recovery / Mem / NB / mrndct.c
1 /* $NoKeywords:$ */
2 /**
3  * @file
4  *
5  * mrndct.c
6  *
7  * Northbridge common DCT support for Recovery
8  *
9  * @xrefitem bom "File Content Label" "Release Content"
10  * @e project: AGESA
11  * @e sub-project: (Proc/Recovery/Mem/NB)
12  * @e \$Revision: 50454 $ @e \$Date: 2011-04-10 21:20:37 -0600 (Sun, 10 Apr 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 "OptionMemory.h"
58 #include "PlatformMemoryConfiguration.h"
59 #include "Ids.h"
60 #include "mrport.h"
61 #include "cpuFamRegisters.h"
62 #include "mm.h"
63 #include "mn.h"
64 #include "mt.h"
65 #include "mru.h"
66 #include "ma.h"
67 #include "Filecode.h"
68 #define FILECODE PROC_RECOVERY_MEM_NB_MRNDCT_FILECODE
69 /*----------------------------------------------------------------------------
70  *                          DEFINITIONS AND MACROS
71  *
72  *----------------------------------------------------------------------------
73  */
74 #define RECDEF_CSMASK_REG         0x00083FE0
75 #define RECDEF_DRAM_BASE_REG      0x00000003
76
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)[4];         ///< Pointer to slew rate table
90 } REC_PHY_COMP_INIT_CLIENTNB;
91
92 /*----------------------------------------------------------------------------
93  *                        PROTOTYPES OF LOCAL FUNCTIONS
94  *
95  *----------------------------------------------------------------------------
96  */
97 VOID
98 STATIC
99 MemRecTCtlOnDimmMirrorNb (
100   IN OUT   MEM_NB_BLOCK *NBPtr,
101   IN       BOOLEAN SetFlag
102   );
103
104 VOID
105 STATIC
106 MemRecNSwapBitsNb (
107   IN OUT   MEM_NB_BLOCK *NBPtr
108   );
109
110 VOID
111 STATIC
112 MemRecNProgNbPstateDependentRegClientNb (
113   IN OUT   MEM_NB_BLOCK *NBPtr
114   );
115
116 VOID
117 STATIC
118 MemRecNTrainPhyFenceNb (
119   IN OUT   MEM_NB_BLOCK *NBPtr
120   );
121
122 VOID
123 STATIC
124 MemRecNCommonReadWritePatternUnb (
125   IN OUT   MEM_NB_BLOCK *NBPtr,
126   IN       UINT8  CmdType,
127   IN       UINT16 ClCount
128   );
129
130 /*----------------------------------------------------------------------------
131  *                            EXPORTED FUNCTIONS
132  *
133  *----------------------------------------------------------------------------
134  */
135
136 /* -----------------------------------------------------------------------------*/
137 /**
138  *
139  *      This function programs the memory controller with configuration parameters
140  *
141  *
142  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
143  *
144  *     @return          TRUE - An Error value lower than AGESA_ERROR may have occurred
145  *     @return          FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred
146  */
147
148 BOOLEAN
149 MemRecNAutoConfigNb (
150   IN OUT   MEM_NB_BLOCK *NBPtr
151   )
152 {
153   UINT8 Dimm;
154   UINT8 Dct;
155   UINT8 ChipSel;
156   UINT32 CSBase;
157   DCT_STRUCT *DCTPtr;
158   CH_DEF_STRUCT *ChannelPtr;
159   UINT16 i;
160
161   Dct = NBPtr->Dct;
162   DCTPtr = NBPtr->DCTPtr;
163   ChannelPtr = NBPtr->ChannelPtr;
164
165   //Prepare variables for future usage.
166   for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
167     if ((ChannelPtr->ChDimmValid & (UINT8) 1 << Dimm) != 0) {
168       DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2);
169       if (((ChannelPtr->DimmDrPresent & (UINT8) 1 << Dimm) == 0) && ((ChannelPtr->DimmQrPresent & (UINT8) 1 << Dimm) == 0)) {
170         continue;
171       } else {
172         DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2 + 1);
173       }
174     }
175   }
176
177   Dimm = NBPtr->DimmToBeUsed;
178
179   //Temporarily set all CS Base/Limit registers (corresponding to Dimms exist on a channel) with 256MB size for WL training.
180   CSBase = 0;
181   for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
182     if (DCTPtr->Timings.CsPresent & (UINT8) 1 << ChipSel) {
183
184       CSBase &= (UINT32) ~0x08; //Clear OnDimmMirror bit.
185       if (((ChipSel & 1) != 0) && ((ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0)) {
186         CSBase |= (UINT32) 0x08; //Set OnDimmMirror bit.
187       }
188       MemRecNSetBitFieldNb (NBPtr, (BFCSBaseAddr0Reg + ChipSel), (CSBase | 0x01));
189       CSBase += 0x100000;
190       if ((ChipSel & 1) == 0) {
191         MemRecNSetBitFieldNb (NBPtr, (BFCSMask0Reg + (ChipSel >> 1)), RECDEF_CSMASK_REG);
192       }
193     }
194   }
195   MemRecNSetBitFieldNb (NBPtr, BFDramBaseReg0, RECDEF_DRAM_BASE_REG);
196   MemRecNSetBitFieldNb (NBPtr, BFDramLimitReg0, 0x70000);
197
198   // Disable the other DCT
199   NBPtr->MemRecNSwitchDctNb (NBPtr, Dct ^ 0x01);
200   MemRecNSetBitFieldNb (NBPtr, BFDisDramInterface, 1);
201   NBPtr->MemRecNSwitchDctNb (NBPtr, Dct);
202   if (Dct != 0) {
203     // If DCT 1, set DctSelBase registers
204     MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseAddrReg, 0x00000003);
205     MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseOffsetReg, 0x00000000);
206   }
207
208   // Use default values for common registers
209   i = 0;
210   while (NBPtr->RecModeDefRegArray[i] != NULL) {
211     MemRecNSetBitFieldNb (NBPtr, NBPtr->RecModeDefRegArray[i], NBPtr->RecModeDefRegArray[i + 1]);
212     i += 2;
213   }
214
215   // Other specific settings
216   MemRecNSetBitFieldNb (NBPtr, BFX4Dimm, ChannelPtr->Dimmx4Present );
217
218   if ((ChannelPtr->RegDimmPresent  == 0) && (ChannelPtr->SODimmPresent == 0)) {
219     MemRecNSetBitFieldNb (NBPtr, BFUnBuffDimm, 1);
220   }
221
222   if ((NBPtr->ChannelPtr->RegDimmPresent != 0) && (NBPtr->ChannelPtr->TechType == DDR3_TECHNOLOGY)) {
223     MemRecNSetBitFieldNb (NBPtr, BFSubMemclkRegDly, 1);
224   }
225   MemRecNSetBitFieldNb (NBPtr, BFOdtSwizzle, 1);
226
227   return TRUE;
228 }
229
230
231 /* -----------------------------------------------------------------------------*/
232 /**
233  *
234  *   This function gets platform specific config/timing values from the interface layer and
235  *   programs them into DCT.
236  *
237  *
238  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
239  *
240  *     @return          TRUE - An Error value lower than AGESA_ERROR may have occurred
241  *     @return          FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred
242  */
243
244 BOOLEAN
245 MemRecNPlatformSpecNb (
246   IN OUT   MEM_NB_BLOCK *NBPtr
247   )
248 {
249   UINT8 p;
250
251   p = 0;
252   for (p = 0; p < MAX_PLATFORM_TYPES; p++) {
253     if (NBPtr->MemPtr->GetPlatformCfg[p] (NBPtr->MemPtr, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr) == AGESA_SUCCESS) {
254       MemRecNSetBitFieldNb (NBPtr, BFODCControl, NBPtr->ChannelPtr->DctOdcCtl);
255       MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, NBPtr->ChannelPtr->DctAddrTmg);
256       return TRUE;
257     }
258   }
259   return FALSE;
260 }
261
262
263 /* -----------------------------------------------------------------------------*/
264 /**
265  *
266  *   This function  reads MemClkFreqVal bit to see if the DIMMs are present in this node.
267  *  If the DIMMs are present then set the DRAM Enable bit for this node.
268  *
269  *  Setting dram init starts up the DCT state machine, initializes the
270  *  dram devices with MRS commands, and kicks off any
271  *  HW memory clear process that the chip is capable of.  The sooner
272  *  that dram init is set for all nodes, the faster the memory system
273  *  initialization can complete.  Thus, the init loop is unrolled into
274  *  two loops so as to start the processes for non BSP nodes sooner.
275  *  This procedure will not wait for the process to finish.  Synchronization is
276  *  handled elsewhere.
277  *
278  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
279  *
280  */
281
282 VOID
283 MemRecNStartupDCTNb (
284   IN OUT   MEM_NB_BLOCK *NBPtr
285   )
286 {
287   // 1. Ensure F2x[1, 0]9C_x08[DisAutoComp] = 1.
288   // 2. BIOS waits 5 us for the disabling of the compensation engine to complete.
289   // ------- Done in InitPhyComp_Nb -------
290   //
291   MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 1);
292   MemRecUWait10ns (500, NBPtr->MemPtr);
293
294   //MemRecNSetBitFieldNb (NBPtr, BFInitDram, 1);    // HW Dram init
295   AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
296   NBPtr->TechPtr->DramInit (NBPtr->TechPtr);
297
298   // 7. Program F2x[1, 0]9C_x08[DisAutoComp] = 0.
299   // 8. BIOS must wait 750 us for the phy compensation engine
300   //    to reinitialize.
301   //
302   MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 0);
303   MemRecUWait10ns (75000, NBPtr->MemPtr);
304
305   while (MemRecNGetBitFieldNb (NBPtr, BFDramEnabled) == 0);
306 }
307
308 /* -----------------------------------------------------------------------------*/
309 /**
310  *
311  *  This function initializes the DRAM devices on all DCTs at the same time
312  *
313  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
314  *
315  */
316
317 VOID
318 MemRecNStartupDCTClientNb (
319   IN OUT   MEM_NB_BLOCK *NBPtr
320   )
321 {
322   IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", NBPtr->Dct);
323
324   // Program D18F2x[1,0]9C_x0000_000B = 80000000h. #109999.
325   MemRecNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x80000000);
326
327   // Program D18F2x[1,0]9C_x0D0F_E013[PllRegWaitTime] = 0118h. #193770.
328   MemRecNSetBitFieldNb (NBPtr, BFPllRegWaitTime, 0x118);
329
330   // Phy Voltage Level Programming
331   MemRecNPhyVoltageLevelNb (NBPtr);
332
333   // Run frequency change sequence
334   MemRecNSetBitFieldNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
335   MemRecNSetBitFieldNb (NBPtr, BFMemClkFreq, 6);
336   MemRecNProgNbPstateDependentRegClientNb (NBPtr);
337   MemRecNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 1);
338   MemRecNSetBitFieldNb (NBPtr, BFPllLockTime, 0x000F);
339
340   IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClkAlign=0\n");
341   IDS_HDT_CONSOLE (MEM_FLOW, "\tEnDramInit = 1 for DCT%d\n", NBPtr->Dct);
342   MemRecNSetBitFieldNb (NBPtr, BFDbeGskMemClkAlignMode, 0);
343   MemRecNSetBitFieldNb (NBPtr, BFEnDramInit, 1);
344
345   // Run DramInit sequence
346   AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
347   NBPtr->TechPtr->DramInit (NBPtr->TechPtr);
348   IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq: %d MHz\n", DDR800_FREQUENCY);
349 }
350
351 /* -----------------------------------------------------------------------------*/
352 /**
353  *
354  *   This function  sets the maximum round-trip latency in the system from the processor to the DRAM
355  *   devices and back.
356
357  *
358  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
359  *     @param[in]     MaxRcvEnDly - Maximum receiver enable delay value
360  *
361  */
362
363 VOID
364 MemRecNSetMaxLatencyNb (
365   IN OUT   MEM_NB_BLOCK *NBPtr,
366   IN       UINT16 MaxRcvEnDly
367   )
368 {
369   UINT16 SubTotal;
370
371   AGESA_TESTPOINT (TpProcMemRcvrCalcLatency, &(NBPtr->MemPtr->StdHeader));
372
373   // Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs UINTs.
374   SubTotal = 6 * 2;
375
376   // If registered DIMMs are being used then add 1 MEMCLK to the sub-total.
377   if (MemRecNGetBitFieldNb (NBPtr, BFUnBuffDimm) == 0) {
378     SubTotal += 2;
379   }
380
381   // if (AddrCmdSetup || CsOdtSetup || CkeSetup) then K := K + 2;
382   SubTotal += 2;
383
384   // If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs,
385   // then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total.
386   //
387   SubTotal += 8 - 5;
388
389   // Add the maximum (worst case) delay value of DqsRcvEnGrossDelay
390   // that exists across all DIMMs and byte lanes.
391   //
392   SubTotal += MaxRcvEnDly >> 5;
393
394   // Add 5.5 to the sub-total. 5.5 represents part of the processor
395   // specific constant delay value in the DRAM clock domain.
396   //
397   SubTotal += 5;             // add 5.5 1/2MemClk
398
399   // Convert the sub-total (in 1/2 MEMCLKs) to northbridge clocks (NCLKs)
400   // as follows (assuming DDR400 and assuming that no P-state or link speed
401   // changes have occurred).
402   //
403   //     Simplified formula:
404   //     SubTotal *= (Fn2xD4[NBFid]+4)/4
405   //
406   SubTotal = SubTotal * ((UINT16) MemRecNGetBitFieldNb (NBPtr, BFNbFid) + 4);
407   SubTotal /= 4;
408
409   // Add 5 NCLKs to the sub-total. 5 represents part of the processor
410   // specific constant value in the northbridge clock domain.
411   //
412   SubTotal += 5;
413
414   // Program the F2x[1, 0]78[MaxRdLatency] register with the total delay value
415   MemRecNSetBitFieldNb (NBPtr, BFMaxLatency, SubTotal);
416 }
417
418
419 /* -----------------------------------------------------------------------------*/
420 /**
421  *
422  *   Set Dram ODT for mission mode and write leveling mode.
423  *
424  *     @param[in,out]   *NBPtr     - Pointer to the MEM_NB_BLOCK
425  *     @param[in]       OdtMode    - Mission mode or write leveling mode
426  *     @param[in]       ChipSelect - Chip select number
427  *     @param[in]       TargetCS   - Chip select number that is being trained
428  *
429  */
430
431 VOID
432 MemRecNSetDramOdtNb (
433   IN OUT   MEM_NB_BLOCK *NBPtr,
434   IN       ODT_MODE OdtMode,
435   IN       UINT8 ChipSelect,
436   IN       UINT8 TargetCS
437   )
438 {
439   UINT8 DramTerm;
440   UINT8 DramTermDyn;
441
442   DramTerm = NBPtr->ChannelPtr->Reserved[0];
443   DramTermDyn = NBPtr->ChannelPtr->Reserved[1];
444
445   if (OdtMode == WRITE_LEVELING_MODE) {
446     if (ChipSelect == TargetCS) {
447       DramTerm = DramTermDyn;
448       MemRecNSetBitFieldNb (NBPtr, BFWrLvOdt, NBPtr->ChannelPtr->PhyWLODT[TargetCS >> 1]);
449     }
450   }
451   MemRecNSetBitFieldNb (NBPtr, BFDramTerm, DramTerm);
452   MemRecNSetBitFieldNb (NBPtr, BFDramTermDyn, DramTermDyn);
453 }
454
455 /*----------------------------------------------------------------------------
456  *                              LOCAL FUNCTIONS
457  *
458  *----------------------------------------------------------------------------
459  */
460
461 /* -----------------------------------------------------------------------------*/
462 /**
463  *
464  *   This function sends an MRS command
465  *
466  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
467  *
468  */
469
470 VOID
471 MemRecNSendMrsCmdNb (
472   IN OUT   MEM_NB_BLOCK *NBPtr
473   )
474 {
475   BOOLEAN ClearODM;
476   ClearODM = FALSE;
477   if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
478     ClearODM = FALSE;
479     if ((NBPtr->MCTPtr->LogicalCpuid.Revision & AMD_F10_C0) != 0) {
480       if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
481         if (MemRecNGetBitFieldNb (NBPtr, BFEnDramInit) == 0) {
482           // For C0, if EnDramInit bit is cleared, ODM needs to be cleared before sending MRS
483           MemRecTCtlOnDimmMirrorNb (NBPtr, FALSE);
484           ClearODM = TRUE;
485         }
486       }
487     }
488   }
489
490   MemRecNSwapBitsNb (NBPtr);
491
492   IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS%d MR%d %04x\n",
493               MemRecNGetBitFieldNb (NBPtr, BFMrsChipSel),
494               MemRecNGetBitFieldNb (NBPtr, BFMrsBank),
495               MemRecNGetBitFieldNb (NBPtr, BFMrsAddress));
496
497   // 1.Set SendMrsCmd=1
498   MemRecNSetBitFieldNb (NBPtr, BFSendMrsCmd, 1);
499
500   // 2.Wait for SendMrsCmd=0
501   while (MemRecNGetBitFieldNb (NBPtr, BFSendMrsCmd)) {}
502
503   if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
504     if (ClearODM) {
505       // Restore ODM if necessary
506       MemRecTCtlOnDimmMirrorNb (NBPtr, TRUE);
507     }
508   }
509 }
510
511 /* -----------------------------------------------------------------------------*/
512 /**
513  *
514  *   This function sends the ZQCL command
515  *
516  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
517  *
518  */
519
520 VOID
521 MemRecNSendZQCmdNb (
522   IN OUT   MEM_NB_BLOCK *NBPtr
523   )
524 {
525   // 1.Program MrsAddress[10]=1
526   MemRecNSetBitFieldNb (NBPtr, BFMrsAddress, (UINT32) 1 << 10);
527
528   // 2.Set SendZQCmd=1
529   MemRecNSetBitFieldNb (NBPtr, BFSendZQCmd, 1);
530
531   // 3.Wait for SendZQCmd=0
532   while (MemRecNGetBitFieldNb (NBPtr, BFSendZQCmd)) {}
533
534   // 4.Wait 512 MEMCLKs
535   MemRecUWait10ns (128, NBPtr->MemPtr);   // 512*2.5ns=1280, wait 1280ns
536 }
537
538
539 /* -----------------------------------------------------------------------------*/
540 /**
541  *
542  *      This function disables/enables F2x[1, 0][5C:40][OnDimmMirror]
543  *
544  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
545  *     @param[in]   SetFlag   - Enable or disable flag - TRUE - Enable, FALSE - DISABLE
546  *
547  */
548
549 VOID
550 STATIC
551 MemRecTCtlOnDimmMirrorNb (
552   IN OUT   MEM_NB_BLOCK *NBPtr,
553   IN       BOOLEAN SetFlag
554   )
555 {
556   UINT8 Chipsel;
557   UINT32 CSBaseAddrReg;
558
559   for (Chipsel = 0; Chipsel < MAX_CS_PER_CHANNEL; Chipsel += 2) {
560     CSBaseAddrReg = MemRecNGetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel);
561     if ((CSBaseAddrReg & 1) == 1) {
562       if (SetFlag && ((NBPtr->ChannelPtr->DimmMirrorPresent & ((UINT8) 1 << (Chipsel >> 1))) != 0)) {
563         CSBaseAddrReg |= ((UINT32) 1 << BFOnDimmMirror);
564       } else {
565         CSBaseAddrReg &= ~((UINT32) 1 << BFOnDimmMirror);
566       }
567       MemRecNSetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel, CSBaseAddrReg);
568     }
569   }
570 }
571 /* -----------------------------------------------------------------------------*/
572 /**
573  *
574  *
575  *   This function swaps bits for OnDimmMirror support
576  *
577  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
578  *
579  */
580
581 VOID
582 STATIC
583 MemRecNSwapBitsNb (
584   IN OUT   MEM_NB_BLOCK *NBPtr
585   )
586 {
587   UINT8 ChipSel;
588   UINT32 MRSReg;
589
590   ChipSel = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFMrsChipSel);
591   if ((ChipSel & 1) != 0) {
592     MRSReg = MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg);
593     if ((NBPtr->ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0) {
594       MRSReg = (MRSReg & 0xFFFCFE07) | ((MRSReg&0x100A8) << 1) | ((MRSReg&0x20150) >> 1);
595       MemRecNSetBitFieldNb (NBPtr, BFDramInitRegReg, MRSReg);
596     }
597   }
598 }
599
600 /* -----------------------------------------------------------------------------*/
601 /**
602  *
603  *
604  *   This function gets the total of sync components for Max Read Latency calculation
605  *
606  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
607  *
608  *     @return      Total in 1/2 MEMCLKs
609  */
610
611 UINT32
612 MemRecNTotalSyncComponentsClientNb (
613   IN OUT   MEM_NB_BLOCK *NBPtr
614   )
615 {
616   UINT32 T;
617   UINT32 P;
618   UINT32 AddrTmgCtl;
619   UINT32 MemClkPeriod;
620
621   AGESA_TESTPOINT (TpProcMemRcvrCalcLatency , &(NBPtr->MemPtr->StdHeader));
622
623   // P = P + ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16) where RdPtrInitMin = RdPtrInit
624   P = 0;
625
626   AddrTmgCtl = MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl);
627   if (((AddrTmgCtl >> 16) & 0x20) != (AddrTmgCtl & 0x20)) {
628     P += 1;
629   }
630
631   // IF (DbeGskMemClkAlignMode==01b || (DbeGskMemClkAlignMode==00b && !(AddrCmdSetup==CsOdtSetup==CkeSetup)))
632   // THEN P = P + 1
633
634   // IF (SlowAccessMode==1) THEN P = P + 2
635
636   // T = T + (0.5 * MemClkPeriod) - 786 ps
637   MemClkPeriod = 1000000 / DDR800_FREQUENCY;
638   T = MemClkPeriod / 2 - 768;
639
640   // If (AddrCmdSetup==0 && CsOdtSetup==0 && CkeSetup==0)
641   // then P = P + 1
642   // else P = P + 2
643   if ((AddrTmgCtl & 0x0202020) == 0) {
644     P += 1;
645   } else {
646     P += 2;
647   }
648
649   // P = P + (2 * (D18F2x[1,0]88[Tcl] clocks - 1))
650   P += 2 * 5;  // Tcl = 6 clocks
651
652   // (DisCutThroughMode = 0), so P = P + 3
653   P += 3;
654
655   return ((P * MemClkPeriod + 1) / 2) + T;
656 }
657
658 /* -----------------------------------------------------------------------------*/
659 /**
660  *
661  *     This function programs the phy registers according to the desired phy VDDIO voltage level
662  *
663  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
664  *
665  */
666
667 VOID
668 MemRecNPhyVoltageLevelNb (
669   IN OUT   MEM_NB_BLOCK *NBPtr
670   )
671 {
672   BIT_FIELD_NAME BitField;
673   UINT16 Value;
674   UINT16 Mask;
675
676   Mask = 0xFFE7;
677   Value = (UINT16) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage) << 3;
678
679   for (BitField = BFDataRxVioLvl; BitField <= BFCmpVioLvl; BitField++) {
680     if (BitField == BFCmpVioLvl) {
681       Mask = 0x3FFF;
682       Value <<= (14 - 3);
683     }
684     MemRecNSetBitFieldNb (NBPtr, BitField, ((MemRecNGetBitFieldNb (NBPtr, BitField) & Mask)) | Value);
685   }
686 }
687
688 /* -----------------------------------------------------------------------------*/
689 /**
690  *
691  *
692  *   This function executes Phy fence training
693  *
694  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
695  *
696  */
697
698 VOID
699 STATIC
700 MemRecNTrainPhyFenceNb (
701   IN OUT   MEM_NB_BLOCK *NBPtr
702   )
703 {
704   UINT8 Byte;
705   UINT16 Avg;
706   UINT8 PREvalue;
707
708   if (MemRecNGetBitFieldNb (NBPtr, BFDisDramInterface)) {
709     return;
710   }
711
712   // 1. BIOS first programs a seed value to the phase recovery
713   //    engine registers.
714   //
715   IDS_HDT_CONSOLE (MEM_FLOW, "\t\tSeeds: ");
716   for (Byte = 0; Byte < 9; Byte++) {
717     // This includes ECC as byte 8
718     MemRecNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19);
719     IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", 19);
720   }
721
722   IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tPhyFenceTrEn = 1");
723   // 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1.
724   MemRecNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1);
725
726   MemRecUWait10ns (5000, NBPtr->MemPtr);
727
728   // 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0.
729   MemRecNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 0);
730
731   // 5. BIOS reads the phase recovery engine registers
732   //    F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52.
733   // 6. Calculate the average value of the fine delay and subtract 8.
734   //
735   Avg = 0;
736   for (Byte = 0; Byte < 9; Byte++) {
737     // This includes ECC as byte 8
738     PREvalue = (UINT8) (0x1F & MemRecNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte)));
739     Avg = Avg + ((UINT16) PREvalue);
740   }
741   Avg = ((Avg + 8) / 9);    // round up
742   Avg -= 6;
743
744   // 7. Write the value to F2x[1, 0]9C_x0C[PhyFence].
745   MemRecNSetBitFieldNb (NBPtr, BFPhyFence, Avg);
746
747   // 8. BIOS rewrites F2x[1, 0]9C_x04, DRAM Address/Command Timing Control
748   //    Register delays for both channels. This forces the phy to recompute
749   //    the fence.
750   //
751   MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl));
752 }
753
754 /* -----------------------------------------------------------------------------*/
755 /**
756  *
757  *  This function calculates and programs NB P-state dependent registers
758  *
759  *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
760  *
761  */
762
763 VOID
764 STATIC
765 MemRecNProgNbPstateDependentRegClientNb (
766   IN OUT   MEM_NB_BLOCK *NBPtr
767   )
768 {
769   UINT8 i;
770   UINT8 NclkFid;
771   UINT16 MemClkDid;
772   UINT8 PllMult;
773   UINT8 NclkDiv;
774   UINT8 RdPtrInit;
775   UINT32 NclkPeriod;
776   UINT32 MemClkPeriod;
777   INT32 PartialSum2x;
778   INT32 PartialSumSlotI2x;
779   INT32 RdPtrInitRmdr2x;
780
781   NclkFid = (UINT8) (MemRecNGetBitFieldNb (NBPtr, BFMainPllOpFreqId) + 0x10);
782   MemClkDid = 2; //BKDG recommended value for DDR800
783   PllMult = 16;  //BKDG recommended value for DDR800
784   NclkDiv = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFNbPs0NclkDiv);
785
786   NclkPeriod = (2500 * NclkDiv) / NclkFid;
787   MemClkPeriod = 1000000 / DDR800_FREQUENCY;
788   NBPtr->NBClkFreq = ((UINT32) NclkFid * 400) / NclkDiv;
789
790   IDS_HDT_CONSOLE (MEM_FLOW, "\n\tNB P%d  Freq: %dMHz\n", 0, NBPtr->NBClkFreq);
791   IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClk Freq: %dMHz\n", DDR800_FREQUENCY);
792
793   // D18F2x[1,0]78[RdPtrInit] = IF (D18F2x[1,0]94[MemClkFreq] >= 667 MHz) THEN 7 ELSE 8 ENDIF (Llano)
794   //                                                                      THEN 2 ELSE 3 ENDIF (Ontario)
795   RdPtrInit = NBPtr->FreqChangeParam->RdPtrInitLower667;
796   MemRecNSetBitFieldNb (NBPtr, BFRdPtrInit, RdPtrInit);
797   IDS_HDT_CONSOLE (MEM_FLOW, "\t\tRdPtr: %d\n", RdPtrInit);
798
799   // Program D18F2x[1,0]F4_x30[DbeGskFifoNumerator] and D18F2x[1,0]F4_x31[DbeGskFifoDenominator].
800   MemRecNSetBitFieldNb (NBPtr, BFDbeGskFifoNumerator, NclkFid * MemClkDid * 16);
801   MemRecNSetBitFieldNb (NBPtr, BFDbeGskFifoDenominator, PllMult * NclkDiv);
802
803   IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoNumerator: %d\n", NclkFid * MemClkDid * 16);
804   IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoDenominator: %d\n", PllMult * NclkDiv);
805
806   // Program D18F2x[1,0]F4_x32[DataTxFifoSchedDlyNegSlot1, DataTxFifoSchedDlySlot1,
807   // DataTxFifoSchedDlyNegSlot0, DataTxFifoSchedDlySlot0].
808   //   PartialSum = ((7 * NclkPeriod) + (1.5 * MemClkPeriod) + 520ps)*MemClkFrequency - tCWL -
809   //   CmdSetup - PtrSeparation - 1. (Llano)
810   //   PartialSum = ((5 * NclkPeriod) + MemClkPeriod) + 520ps)*MemClkFrequency - tCWL -
811   //   CmdSetup - PtrSeparation - 1. (Ontario)
812   PartialSum2x = NBPtr->FreqChangeParam->NclkPeriodMul2x * NclkPeriod;
813   PartialSum2x += NBPtr->FreqChangeParam->MemClkPeriodMul2x * MemClkPeriod;
814   PartialSum2x += 520 * 2;
815
816   // PtrSeparation = ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16)/2 + RdPtrInitRmdr
817   // RdPtrInitRmdr = (((2.25 * MemClkPeriod) - 1520ps) MOD MemClkPeriod)/MemClkPeriod
818   RdPtrInitRmdr2x = ((NBPtr->FreqChangeParam->SyncTimeMul4x * MemClkPeriod) / 2) - 2 * (NBPtr->FreqChangeParam->TDataPropLower800 + 520);
819   RdPtrInitRmdr2x %= MemClkPeriod;
820   PartialSum2x -= RdPtrInitRmdr2x;
821   PartialSum2x = (PartialSum2x + MemClkPeriod - 1) / MemClkPeriod;  // round-up here
822   PartialSum2x -= 2 * 5;  //Tcwl + 5
823
824   if ((MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl) & 0x0202020) == 0) {
825     PartialSum2x -= 1;
826   } else {
827     PartialSum2x -= 2;
828   }
829   PartialSum2x -= 2;
830
831   // If PartialSumSlotN is positive:
832   //   DataTxFifoSchedDlySlotN=CEIL(PartialSumSlotN).
833   //   DataTxFifoSchedDlyNegSlotN=0.
834   // Else if PartialSumSlotN is negative:
835   //   DataTxFifoSchedDlySlotN=ABS(CEIL(PartialSumSlotN*MemClkPeriod/NclkPeriod)).
836   //   DataTxFifoSchedDlyNegSlotN=1.
837   for (i = 0; i < 2; i++) {
838     PartialSumSlotI2x = PartialSum2x;
839     if (i == 0) {
840     PartialSumSlotI2x += 2;
841     }
842     if (PartialSumSlotI2x > 0) {
843       MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 0);
844       MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, (PartialSumSlotI2x + 1) / 2);
845       IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: %d\n", i, (PartialSumSlotI2x + 1) / 2);
846     } else {
847       MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 1);
848       PartialSumSlotI2x = ((-PartialSumSlotI2x) * MemClkPeriod) / (2 * NclkPeriod);
849       MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, PartialSumSlotI2x);
850       IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: -%d\n", i, PartialSumSlotI2x);
851     }
852   }
853   // Program ProcOdtAdv
854   MemRecNSetBitFieldNb (NBPtr, BFProcOdtAdv, 0);
855 }
856
857 /* -----------------------------------------------------------------------------*/
858 /**
859  *
860  *
861  *      This function reads cache lines continuously using TCB CPG engine
862  *
863  *     @param[in,out] NBPtr  - Pointer to the MEM_NB_BLOCK
864  *     @param[in,out] Buffer - Array of bytes to be filled with data read from DRAM
865  *     @param[in]     Address - System Address [47:16]
866  *     @param[in] ClCount - Number of cache lines
867  *
868  */
869
870 VOID
871 MemRecNContReadPatternClientNb (
872   IN OUT   MEM_NB_BLOCK *NBPtr,
873   IN       UINT8 Buffer[],
874   IN       UINT32 Address,
875   IN       UINT16 ClCount
876   )
877 {
878   // 1. Program D18F2x1C0[RdDramTrainMode]=1.
879   MemRecNSetBitFieldNb (NBPtr, BFRdDramTrainMode, 1);
880
881   // 2. Program D18F2x1C0[TrainLength] to the appropriate number of cache lines.
882   MemRecNSetBitFieldNb (NBPtr, BFTrainLength, ClCount);
883
884   // 3. Program the DRAM training address as follows:
885   MemRecNSetBitFieldNb (NBPtr, BFWrTrainAdrPtrLo, (Address >> 6));
886
887   // 4. Program D18F2x1D0[WrTrainBufAddr]=000h
888   MemRecNSetBitFieldNb (NBPtr, BFWrTrainBufAddr, 0);
889
890   // 5. Program D18F2x1C0[RdTrainGo]=1.
891   MemRecNSetBitFieldNb (NBPtr, BFRdTrainGo, 1);
892
893   // 6. Wait for D18F2x1C0[RdTrainGo]=0.
894   while (MemRecNGetBitFieldNb (NBPtr, BFRdTrainGo) != 0) {}
895
896   // 7. Read D18F2x1E8[TrainCmpSts] and D18F2x1E8[TrainCmpSts2].
897
898   // 8. Program D18F2x1C0[RdDramTrainMode]=0.
899   MemRecNSetBitFieldNb (NBPtr, BFRdDramTrainMode, 0);
900 }
901
902 /* -----------------------------------------------------------------------------*/
903 /**
904  *
905  *     This is function sets the platform specific settings for the systems with UDIMMs configuration
906  *
907  *     @param[in,out]   *MemData           Pointer to MEM_DATA_STRUCTURE
908  *     @param[in]       SocketID        Socket number
909  *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
910  *
911  *     @return          AGESA_SUCCESS
912  *     @return          CurrentChannel->DctAddrTmg        Address Command Timing Settings for specified channel
913  *     @return          CurrentChannel->DctOdcCtl         Drive Strength settings for specified channel
914  *     @return          CurrentChannel->Reserved[0]       Dram Term for specified channel
915  *     @return          CurrentChannel->Reserved[1]       Dynamic Dram Term for specified channel
916  *     @return          CurrentChannel->PhyWLODT[0]       WL ODT for DIMM0
917  *     @return          CurrentChannel->PhyWLODT[1]       WL ODT for DIMM1
918  *     @return          CurrentChannel->PhyWLODT[2]       WL ODT for DIMM2
919  *     @return          CurrentChannel->PhyWLODT[3]       WL ODT for DIMM3
920  *
921  */
922 AGESA_STATUS
923 MemRecNGetPsCfgUDIMM3Nb (
924   IN OUT   MEM_DATA_STRUCT *MemData,
925   IN       UINT8 SocketID,
926   IN OUT   CH_DEF_STRUCT *CurrentChannel
927   )
928 {
929   UINT32 AddrTmgCTL;
930   UINT32 DctOdcCtl;
931   UINT8 Dimms;
932   UINT8  MaxDimmPerCH;
933   UINT8 DramTerm;
934   UINT8 DramTermDyn;
935
936   if ((CurrentChannel->RegDimmPresent != 0) || (CurrentChannel->SODimmPresent != 0)) {
937     return AGESA_UNSUPPORTED;
938   }
939
940   Dimms = CurrentChannel->Dimms;
941   MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
942
943   if (MaxDimmPerCH == 1) {
944     return AGESA_UNSUPPORTED;
945   } else {
946     DctOdcCtl = 0x20223323;
947     AddrTmgCTL = 0x00390039;
948     if (Dimms == 1) {
949       DctOdcCtl = 0x20113222;
950       AddrTmgCTL = 0x00390039;
951       if (CurrentChannel->Loads == 16) {
952         AddrTmgCTL = 0x003B0000;
953       }
954     }
955   }
956   CurrentChannel->DctAddrTmg = AddrTmgCTL;
957   CurrentChannel->DctOdcCtl = DctOdcCtl;
958
959   // ODT
960   if (Dimms == 1) {
961     DramTerm = 1; // 60 ohms
962     DramTermDyn = 0; // Disable
963     if ((MaxDimmPerCH == 3) && (CurrentChannel->DimmDrPresent != 0)) {
964       DramTermDyn = 1; // 60 ohms
965     }
966   } else {
967     DramTerm = 3; // 40 ohms
968     DramTermDyn = 2; // 120 ohms
969   }
970   CurrentChannel->Reserved[0] = DramTerm;
971   CurrentChannel->Reserved[1] = DramTermDyn;
972
973   // WL ODT
974   if (Dimms == 1) {
975     CurrentChannel->PhyWLODT[0] = 0;
976     CurrentChannel->PhyWLODT[1] = (CurrentChannel->DimmDrPresent != 0) ? 8 : 2;
977   } else {
978     CurrentChannel->PhyWLODT[0] = 3;
979     CurrentChannel->PhyWLODT[1] = 3;
980   }
981   CurrentChannel->PhyWLODT[2] = 0;
982   CurrentChannel->PhyWLODT[3] = 0;
983
984   return AGESA_SUCCESS;
985 }
986
987 /* -----------------------------------------------------------------------------*/
988 /**
989  *
990  *     This is function sets the platform specific settings for the systems with SODIMMs configuration
991  *
992  *     @param[in,out]   *MemData           Pointer to MEM_DATA_STRUCTURE
993  *     @param[in]       SocketID        Socket number
994  *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
995  *
996  *     @return          AGESA_SUCCESS
997  *     @return          CurrentChannel->DctAddrTmg        Address Command Timing Settings for specified channel
998  *     @return          CurrentChannel->DctOdcCtl         Drive Strength settings for specified channel
999  *     @return          CurrentChannel->Reserved[0]       Dram Term for specified channel
1000  *     @return          CurrentChannel->Reserved[1]       Dynamic Dram Term for specified channel
1001  *     @return          CurrentChannel->PhyWLODT[0]       WL ODT for DIMM0
1002  *     @return          CurrentChannel->PhyWLODT[1]       WL ODT for DIMM1
1003  *     @return          CurrentChannel->PhyWLODT[2]       WL ODT for DIMM2
1004  *     @return          CurrentChannel->PhyWLODT[3]       WL ODT for DIMM3
1005  *
1006  */
1007 AGESA_STATUS
1008 MemRecNGetPsCfgSODIMM3Nb (
1009   IN OUT   MEM_DATA_STRUCT *MemData,
1010   IN       UINT8 SocketID,
1011   IN OUT   CH_DEF_STRUCT *CurrentChannel
1012   )
1013 {
1014   UINT32 AddrTmgCTL;
1015   UINT32 DctOdcCtl;
1016   UINT8  MaxDimmPerCH;
1017   UINT8 Dimms;
1018   UINT8 DramTerm;
1019   UINT8 DramTermDyn;
1020
1021   if (CurrentChannel->SODimmPresent != CurrentChannel->ChDimmValid) {
1022     return AGESA_UNSUPPORTED;
1023   }
1024
1025   Dimms = CurrentChannel->Dimms;
1026   MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
1027
1028   if (MaxDimmPerCH == 1) {
1029     DctOdcCtl = 0x00113222;
1030     AddrTmgCTL = 0;
1031   } else {
1032     DctOdcCtl = 0x00223323;
1033     AddrTmgCTL = 0x00000039;
1034     if (Dimms == 1) {
1035       DctOdcCtl = 0x00113222;
1036       AddrTmgCTL = 0;
1037     }
1038   }
1039   CurrentChannel->DctAddrTmg = AddrTmgCTL;
1040   CurrentChannel->DctOdcCtl = DctOdcCtl;
1041
1042   // ODT
1043   if (Dimms == 1) {
1044     DramTerm = 2; // 120 ohms
1045     DramTermDyn = 0; // Disable
1046     if (MaxDimmPerCH == 2) {
1047       DramTerm = 1; // 60 ohms
1048     }
1049   } else {
1050     DramTerm = 3; // 40 ohms
1051     DramTermDyn = 2; // 120 ohms
1052   }
1053   CurrentChannel->Reserved[0] = DramTerm;
1054   CurrentChannel->Reserved[1] = DramTermDyn;
1055
1056   // WL ODT
1057   if (Dimms == 1) {
1058     if (MaxDimmPerCH == 1) {
1059       CurrentChannel->PhyWLODT[0] = (CurrentChannel->DimmDrPresent != 0) ? 4 : 1;
1060       CurrentChannel->PhyWLODT[1] = 0;
1061     } else {
1062       CurrentChannel->PhyWLODT[0] = 0;
1063       CurrentChannel->PhyWLODT[1] = (CurrentChannel->DimmDrPresent != 0) ? 8 : 2;
1064     }
1065   } else {
1066     CurrentChannel->PhyWLODT[0] = 3;
1067     CurrentChannel->PhyWLODT[1] = 3;
1068   }
1069   CurrentChannel->PhyWLODT[2] = 0;
1070   CurrentChannel->PhyWLODT[3] = 0;
1071
1072   return AGESA_SUCCESS;
1073 }
1074
1075 /* -----------------------------------------------------------------------------*/
1076 /**
1077  *
1078  *     This is function sets the platform specific settings for the systems with RDIMMs configuration
1079  *
1080  *     @param[in,out]   *MemData           Pointer to MEM_DATA_STRUCTURE
1081  *     @param[in]       SocketID        Socket number
1082  *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
1083  *
1084  *     @return          AGESA_SUCCESS
1085  *     @return          CurrentChannel->DctAddrTmg        Address Command Timing Settings for specified channel
1086  *     @return          CurrentChannel->DctOdcCtl         Drive Strength settings for specified channel
1087  *     @return          CurrentChannel->Reserved[0]       Dram Term for specified channel
1088  *     @return          CurrentChannel->Reserved[1]       Dynamic Dram Term for specified channel
1089  *     @return          CurrentChannel->PhyWLODT[0]       WL ODT for DIMM0
1090  *     @return          CurrentChannel->PhyWLODT[1]       WL ODT for DIMM1
1091  *     @return          CurrentChannel->PhyWLODT[2]       WL ODT for DIMM2
1092  *     @return          CurrentChannel->PhyWLODT[3]       WL ODT for DIMM3
1093  *
1094  */
1095
1096 AGESA_STATUS
1097 MemRecNGetPsCfgRDIMM3Nb (
1098   IN OUT   MEM_DATA_STRUCT *MemData,
1099   IN       UINT8 SocketID,
1100   IN OUT   CH_DEF_STRUCT *CurrentChannel
1101   )
1102 {
1103   STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg2DIMMsWlODT[] = {
1104     {SR_DIMM0,            {0x01, 0x00, 0x00, 0x00}, 1},
1105     {DR_DIMM0,            {0x04, 0x00, 0x00, 0x00}, 1},
1106     {QR_DIMM0,            {0x05, 0x00, 0x00, 0x00}, 1},
1107     {SR_DIMM1,            {0x00, 0x02, 0x00, 0x00}, 1},
1108     {DR_DIMM1,            {0x00, 0x08, 0x00, 0x00}, 1},
1109     {QR_DIMM1,            {0x00, 0x0A, 0x00, 0x00}, 1},
1110     {SR_DIMM0 + DR_DIMM0 + SR_DIMM1 + DR_DIMM1, {0x03, 0x03, 0x00, 0x00}, 2},
1111     {SR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
1112     {DR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
1113     {QR_DIMM0 + SR_DIMM1, {0x03, 0x07, 0x06, 0x00}, 2},
1114     {QR_DIMM0 + DR_DIMM1, {0x03, 0x07, 0x06, 0x00}, 2},
1115     {QR_DIMM0 + QR_DIMM1, {0x0B, 0x07, 0x0E, 0x0D}, 2}
1116   };
1117   STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg3DIMMsWlODT[] = {
1118     {SR_DIMM2 + DR_DIMM2, {0x00, 0x00, 0x04, 0x00}, 1},
1119     {SR_DIMM0 + DR_DIMM0, {0x01, 0x02, 0x00, 0x00}, 1},
1120     {SR_DIMM0 + DR_DIMM0 + SR_DIMM2 + DR_DIMM2, {0x05, 0x00, 0x05, 0x00}, 2},
1121     {SR_DIMM0 + DR_DIMM0 + SR_DIMM1 + DR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x07, 0x07, 0x07, 0x00}, 3},
1122     {QR_DIMM1, {0x00, 0x0A, 0x00, 0x0A}, 1},
1123     {QR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x00, 0x06, 0x0E, 0x0C}, 2},
1124     {SR_DIMM0 + DR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
1125     {SR_DIMM0 + DR_DIMM0 + QR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x0F, 0x07, 0x0F, 0x0D}, 3}
1126   };
1127   STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg4DIMMsWlODT[] = {
1128     {ANY_DIMM3, {0x00, 0x00, 0x00, 0x08}, 1},
1129     {ANY_DIMM2 + ANY_DIMM3, {0x00, 0x00, 0x0C, 0x0C}, 2},
1130     {ANY_DIMM1 + ANY_DIMM2 + ANY_DIMM3, {0x00, 0x0E, 0x0E, 0x0E}, 3},
1131     {ANY_DIMM0 + ANY_DIMM1 + ANY_DIMM2 + ANY_DIMM3, {0x0F, 0x0F, 0x0F, 0x0F}, 4}
1132   };
1133
1134   UINT8 i;
1135   UINT8 j;
1136   UINT8 Dimms;
1137   UINT8 DimmQrPresent;
1138   UINT32 AddrTmgCTL;
1139   UINT32 DctOdcCtl;
1140   UINT8 PhyWLODT[4];
1141   UINT8 DramTerm;
1142   UINT8 DramTermDyn;
1143   UINT16 DIMMRankType;
1144   UINT16 _DIMMRankType_;
1145   UINT8 DimmTpMatch;
1146   UINT8  MaxDimmPerCH;
1147   UINT8 PSCfgWlODTSize;
1148   CONST ADV_R_PSCFG_WL_ODT_ENTRY *PSCfgWlODTPtr;
1149
1150   if (CurrentChannel->RegDimmPresent != CurrentChannel->ChDimmValid) {
1151     return AGESA_UNSUPPORTED;
1152   }
1153
1154   DIMMRankType = MemRecNGetPsRankType (CurrentChannel);
1155   MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
1156   Dimms = CurrentChannel->Dimms;
1157   PSCfgWlODTPtr = RecPSCfg2DIMMsWlODT;
1158   PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg2DIMMsWlODT);
1159   PhyWLODT[0] = PhyWLODT[1] = PhyWLODT[2] = PhyWLODT[3] = 0xFF;
1160   DimmQrPresent = CurrentChannel->DimmQrPresent;
1161
1162   if (MaxDimmPerCH == 4) {
1163     AddrTmgCTL = (Dimms > 2) ? 0x002F0000 : 0;
1164     DctOdcCtl = (Dimms == 1) ? 0x20113222 : 0x20223222;
1165     PSCfgWlODTPtr = RecPSCfg4DIMMsWlODT;
1166     PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg4DIMMsWlODT);
1167   } else  if (MaxDimmPerCH == 3) {
1168     AddrTmgCTL = 0;
1169     DctOdcCtl = 0x20223222;
1170     if (Dimms == 3) {
1171       AddrTmgCTL = 0x00380038;
1172       DctOdcCtl = 0x20113222;
1173     }
1174     if (Dimms == 1) {
1175       DctOdcCtl = 0x20113222;
1176     }
1177     PSCfgWlODTPtr = RecPSCfg3DIMMsWlODT;
1178     PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg3DIMMsWlODT);
1179   } else if (MaxDimmPerCH == 2) {
1180     AddrTmgCTL = 0;
1181     DctOdcCtl = 0x20223222;
1182     if ((Dimms == 1) && (DimmQrPresent == 0)) {
1183       DctOdcCtl = 0x20113222;
1184     }
1185   } else {
1186     AddrTmgCTL = 0;
1187     DctOdcCtl = (DimmQrPresent == 0) ? 0x20113222 : 0x20223222;
1188   }
1189   CurrentChannel->DctAddrTmg = AddrTmgCTL;
1190   CurrentChannel->DctOdcCtl = DctOdcCtl;
1191
1192   // ODT
1193   if (Dimms == 1) {
1194     DramTerm = 1; // 60 ohms
1195     DramTermDyn = 0; // Disable
1196     if (DimmQrPresent != 0) {
1197       DramTermDyn = 2; // 120 ohms
1198     }
1199   } else {
1200     DramTerm = 3; // 40 ohms
1201     DramTermDyn = 2; // 120 ohms
1202     if (DimmQrPresent != 0) {
1203       DramTerm = 1; // 60 ohms
1204     }
1205   }
1206   CurrentChannel->Reserved[0] = DramTerm;
1207   CurrentChannel->Reserved[1] = DramTermDyn;
1208
1209   // WL ODT
1210   for (i = 0; i  < PSCfgWlODTSize; i++, PSCfgWlODTPtr++) {
1211     if (Dimms != PSCfgWlODTPtr->Dimms) {
1212       continue;
1213     }
1214     DimmTpMatch = 0;
1215     _DIMMRankType_ = DIMMRankType & PSCfgWlODTPtr->DIMMRankType;
1216     for (j = 0; j < MAX_DIMMS_PER_CHANNEL; j++) {
1217       if ((_DIMMRankType_ & (UINT16) 0x0F << (j << 2)) != 0) {
1218         DimmTpMatch++;
1219       }
1220     }
1221     if (DimmTpMatch == PSCfgWlODTPtr->Dimms) {
1222       PhyWLODT[0] = PSCfgWlODTPtr->PhyWrLvOdt[0];
1223       PhyWLODT[1] = PSCfgWlODTPtr->PhyWrLvOdt[1];
1224       PhyWLODT[2] = PSCfgWlODTPtr->PhyWrLvOdt[2];
1225       PhyWLODT[3] = PSCfgWlODTPtr->PhyWrLvOdt[3];
1226       break;
1227     }
1228   }
1229   CurrentChannel->PhyWLODT[0] = PhyWLODT[0];
1230   CurrentChannel->PhyWLODT[1] = PhyWLODT[1];
1231   CurrentChannel->PhyWLODT[2] = PhyWLODT[2];
1232   CurrentChannel->PhyWLODT[3] = PhyWLODT[3];
1233
1234   return AGESA_SUCCESS;
1235 }
1236
1237
1238 /* -----------------------------------------------------------------------------*/
1239 /**
1240  *
1241  *
1242  *      This function returns the max dimms for a given memory channel on a given
1243  *  processor.  It first searches the platform override table for the max dimms
1244  *  value.  If it is not provided, the AGESA default value is returned. The target
1245  *  socket must be a valid present socket.
1246  *
1247  *     @param[in] PlatformMemoryConfiguration - Platform config table
1248  *     @param[in] SocketID  - ID of the processor that owns the channel
1249  *     @param[in] ChannelID - Channel to get max dimms for
1250  *
1251  *
1252  *     @return UINT8 - Max Number of Dimms for that channel
1253  */
1254 UINT8
1255 RecGetMaxDimmsPerChannel (
1256   IN       PSO_TABLE *PlatformMemoryConfiguration,
1257   IN       UINT8 SocketID,
1258   IN       UINT8 ChannelID
1259   )
1260 {
1261   UINT8  *DimmsPerChPtr;
1262   UINT8  MaxDimmPerCH;
1263
1264   DimmsPerChPtr = MemRecFindPSOverrideEntry (PlatformMemoryConfiguration, PSO_MAX_DIMMS, SocketID, ChannelID, 0);
1265   if (DimmsPerChPtr != NULL) {
1266     MaxDimmPerCH = *DimmsPerChPtr;
1267   } else {
1268     MaxDimmPerCH = 2;
1269   }
1270
1271   return MaxDimmPerCH;
1272 }
1273
1274 /* -----------------------------------------------------------------------------*/
1275 /**
1276  *
1277  *    This is the default return function of the ARDK block. The function always
1278  *    returns   AGESA_UNSUPPORTED
1279  *
1280  *     @param[in,out]   *MemData           Pointer to MEM_DATA_STRUCTURE
1281  *     @param[in]       SocketID        Socket number
1282  *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
1283  *
1284  *     @return          AGESA_UNSUPPORTED  AGESA status indicating that default is unsupported
1285  *
1286  */
1287
1288 AGESA_STATUS
1289 MemRecNGetPsCfgDef (
1290   IN OUT   MEM_DATA_STRUCT *MemData,
1291   IN       UINT8 SocketID,
1292   IN OUT   CH_DEF_STRUCT *CurrentChannel
1293   )
1294 {
1295   return AGESA_UNSUPPORTED;
1296 }
1297
1298 /* -----------------------------------------------------------------------------*/
1299 /**
1300  *
1301  *    This function returns the rank type map of a channel.
1302  *
1303  *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
1304  *
1305  *     @return          UINT16 - The map of rank type.
1306  *
1307  */
1308 UINT16
1309 MemRecNGetPsRankType (
1310   IN       CH_DEF_STRUCT *CurrentChannel
1311   )
1312 {
1313   UINT8 i;
1314   UINT16 DIMMRankType;
1315
1316   DIMMRankType = 0;
1317   for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i++) {
1318     if ((CurrentChannel->DimmQrPresent & (UINT8) 1 << i) != 0) {
1319       if (i < 2) {
1320         DIMMRankType |= (UINT16) 4 << (i << 2);
1321       }
1322     } else if ((CurrentChannel->DimmDrPresent & (UINT8) 1 << i) != 0) {
1323       DIMMRankType |= (UINT16) 2 << (i << 2);
1324     } else if ((CurrentChannel->DimmSRPresent & (UINT8) 1 << i) != 0) {
1325       DIMMRankType |= (UINT16) 1 << (i << 2);
1326     }
1327   }
1328   return DIMMRankType;
1329 }
1330
1331 UINT32
1332 MemRecNcmnGetSetTrainDlyClientNb (
1333   IN OUT   MEM_NB_BLOCK *NBPtr,
1334   IN       UINT8 IsSet,
1335   IN       TRN_DLY_TYPE TrnDly,
1336   IN       DRBN DrbnVar,
1337   IN       UINT16 Field
1338   )
1339 {
1340   UINT16 Index;
1341   UINT16 Offset;
1342   UINT32 Value;
1343   UINT32 Address;
1344   UINT8 Dimm;
1345   UINT8 Byte;
1346
1347   Dimm = DRBN_DIMM (DrbnVar);
1348   Byte = DRBN_BYTE (DrbnVar);
1349
1350   ASSERT (Dimm < 2);
1351   ASSERT (Byte <= ECC_DLY);
1352
1353   if ((Byte > 7)) {
1354     // LN and ON do not support ECC delay, so:
1355     if (IsSet) {
1356       // On write, ignore
1357       return 0;
1358     } else {
1359       // On read, redirect to byte 0 to correct fence averaging
1360       Byte = 0;
1361     }
1362   }
1363
1364   switch (TrnDly) {
1365   case AccessRcvEnDly:
1366     Index = 0x10;
1367     break;
1368   case AccessWrDqsDly:
1369     Index = 0x30;
1370     break;
1371   case AccessWrDatDly:
1372     Index = 0x01;
1373     break;
1374   case AccessRdDqsDly:
1375     Index = 0x05;
1376     break;
1377   case AccessPhRecDly:
1378     Index = 0x50;
1379     break;
1380   default:
1381     Index = 0;
1382     IDS_ERROR_TRAP;
1383   }
1384
1385   switch (TrnDly) {
1386   case AccessRcvEnDly:
1387   case AccessWrDqsDly:
1388     Index += (Dimm * 3);
1389     if (Byte & 0x04) {
1390       // if byte 4,5,6,7
1391       Index += 0x10;
1392     }
1393     if (Byte & 0x02) {
1394       // if byte 2,3,6,7
1395       Index++;
1396     }
1397     Offset = 16 * (Byte % 2);
1398     break;
1399
1400   case AccessRdDqsDly:
1401   case AccessWrDatDly:
1402     Index += (Dimm * 0x100);
1403     // break is not being used here because AccessRdDqsDly and AccessWrDatDly also need
1404     // to run AccessPhRecDly sequence.
1405   case AccessPhRecDly:
1406     Index += (Byte / 4);
1407     Offset = 8 * (Byte % 4);
1408     break;
1409   default:
1410     Offset = 0;
1411     IDS_ERROR_TRAP;
1412   }
1413
1414   Address = Index;
1415   MemRecNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
1416   Value = MemRecNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
1417
1418   if (IsSet) {
1419     if (TrnDly == AccessPhRecDly) {
1420       Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
1421     }
1422
1423     Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
1424     MemRecNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
1425     Address |= DCT_ACCESS_WRITE;
1426     MemRecNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
1427
1428     if (TrnDly == AccessPhRecDly) {
1429       NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
1430     }
1431     // Gross WrDatDly and WrDqsDly cannot be larger than 4
1432     ASSERT (((TrnDly == AccessWrDatDly) || (TrnDly == AccessWrDqsDly)) ? (NBPtr->IsSupported[WLNegativeDelay] || (Field < 0xA0)) : TRUE);
1433   } else {
1434     Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF);
1435   }
1436
1437   return Value;
1438 }
1439
1440
1441 /* -----------------------------------------------------------------------------*/
1442 /**
1443  *
1444  *
1445  *      This function reads cache lines continuously using PRBS engine
1446  *
1447  *     @param[in,out] NBPtr  - Pointer to the MEM_NB_BLOCK
1448  *     @param[in,out] Buffer - Array of bytes to be filled with data read from DRAM
1449  *     @param[in]     Address - System Address [47:16]
1450  *     @param[in] ClCount - Number of cache lines
1451  *
1452  */
1453
1454 VOID
1455 MemRecNContReadPatternUnb (
1456   IN OUT   MEM_NB_BLOCK *NBPtr,
1457   IN       UINT8 Buffer[],
1458   IN       UINT32 Address,
1459   IN       UINT16 ClCount
1460   )
1461 {
1462   MemRecNCommonReadWritePatternUnb (NBPtr, CMD_TYPE_READ, ClCount);
1463 }
1464
1465 /* -----------------------------------------------------------------------------*/
1466 /**
1467  *
1468  *     This function generates a continuous stream of writes to DRAM using the
1469  *       Unified Northbridge Reliable Read/Write Engine.
1470  *
1471  *     @param[in,out] NBPtr   - Pointer to the MEM_NB_BLOCK
1472  *     @param[in,out] Address - Unused by this function
1473  *     @param[in]     Pattern - Unused by this function
1474  *     @param[in]     ClCount - Number of cache lines to write
1475  *
1476  */
1477
1478 VOID
1479 MemRecNContWritePatternUnb (
1480   IN OUT   MEM_NB_BLOCK *NBPtr,
1481   IN       UINT32 Address,
1482   IN       UINT8 Pattern[],
1483   IN       UINT16 ClCount
1484   )
1485 {
1486   MemRecNCommonReadWritePatternUnb (NBPtr, CMD_TYPE_WRITE, ClCount);
1487 }
1488
1489 /* -----------------------------------------------------------------------------*/
1490 /**
1491  *
1492  *     This function generates either read or write DRAM cycles for training
1493  *     using PRBS engine
1494  *
1495  *     @param[in,out] NBPtr   - Pointer to the MEM_NB_BLOCK
1496  *     @param[in]     CmdType - Read/Write
1497  *     @param[in]     ClCount - Number of cache lines to write
1498  *
1499  */
1500
1501 VOID
1502 STATIC
1503 MemRecNCommonReadWritePatternUnb (
1504   IN OUT   MEM_NB_BLOCK *NBPtr,
1505   IN       UINT8  CmdType,
1506   IN       UINT16 ClCount
1507   )
1508 {
1509   MEM_TECH_BLOCK *TechPtr;
1510
1511   TechPtr = NBPtr->TechPtr;
1512
1513   // Enable PRBS, also set ResetAllErr
1514   MemRecNSetBitFieldNb (NBPtr, BFCmdTestEnable, 3);
1515
1516   // Send activate command
1517   MemRecNSetBitFieldNb (NBPtr, BFDramCmd2Reg, (UINT32) 1 << (TechPtr->ChipSel + 22) | ((UINT32) 1 << 31));
1518   MemRecUWait10ns (750, NBPtr->MemPtr);
1519
1520   // Setup test address
1521   MemRecNSetBitFieldNb (NBPtr, BFTgtChipSelectA, TechPtr->ChipSel);
1522   MemRecNSetBitFieldNb (NBPtr, BFDataPrbsSeed, PRBS_SEED_256);
1523   MemRecNSetBitFieldNb (NBPtr, BFCmdCount, ClCount);
1524
1525   // Select read or write command
1526   MemRecNSetBitFieldNb (NBPtr, BFCmdType, CmdType);
1527
1528   // Send command and wait for completion
1529   MemRecNSetBitFieldNb (NBPtr, BFSendCmd, 1);
1530   while (MemRecNGetBitFieldNb (NBPtr, BFTestStatus) == 0) {}
1531   while (MemRecNGetBitFieldNb (NBPtr, BFCmdSendInProg) != 0) {}
1532   MemRecNSetBitFieldNb (NBPtr, BFSendCmd, 0);
1533
1534   // Send precharge all command
1535   MemRecNSetBitFieldNb (NBPtr, BFDramCmd2Reg, (UINT32) 1 << (TechPtr->ChipSel + 22) | ((UINT32) 1 << 30));
1536   MemRecUWait10ns (750, NBPtr->MemPtr);
1537
1538   // Disable PRBS
1539   MemRecNSetBitFieldNb (NBPtr, BFCmdTestEnable, 0);
1540 }
1541
1542 /* -----------------------------------------------------------------------------*/
1543 /**
1544  *
1545  *     This function checks the Error status bits for comparison results using PRBS
1546  *
1547  *     @param[in,out]   *NBPtr    - Pointer to the MEM_NB_BLOCK
1548  *     @param[in]       Buffer[]  -  Not used in this implementation
1549  *     @param[in]       Pattern[] - Not used in this implementation
1550  *     @param[in]       ByteCount - Not used in this implementation
1551  *
1552  *     @return  PASS - Bitmap of results of comparison
1553  */
1554
1555 UINT16
1556 MemRecNCompareTestPatternUnb (
1557   IN OUT   MEM_NB_BLOCK *NBPtr,
1558   IN       UINT8 Buffer[],
1559   IN       UINT8 Pattern[],
1560   IN       UINT16 ByteCount
1561   )
1562 {
1563   UINT16 i;
1564   UINT16 Pass;
1565   UINT32 NibbleErrSts;
1566
1567   NibbleErrSts = MemRecNGetBitFieldNb (NBPtr, BFNibbleErrSts);
1568
1569   Pass = 0;
1570   for (i = 0; i < 8; i++) {
1571     Pass |= ((NibbleErrSts & 0x03) > 0 ) ? (1 << i) : 0;
1572     NibbleErrSts >>= 2;
1573   }
1574   Pass = ~Pass;
1575   return Pass;
1576 }
1577