AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / Mem / Tech / DDR3 / mttwl3.c
1 /* $NoKeywords:$ */
2 /**
3  * @file
4  *
5  * mttwl3.c
6  *
7  * Technology Phy assisted write levelization for DDR3
8  *
9  * @xrefitem bom "File Content Label" "Release Content"
10  * @e project: AGESA
11  * @e sub-project: (Mem/Tech/DDR3)
12  * @e \$Revision: 57883 $ @e \$Date: 2011-08-15 10:41:06 -0600 (Mon, 15 Aug 2011) $
13  *
14  **/
15 /*****************************************************************************
16 *
17 * Copyright (C) 2012 Advanced Micro Devices, Inc.
18 * All rights reserved.
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions are met:
22 *     * Redistributions of source code must retain the above copyright
23 *       notice, this list of conditions and the following disclaimer.
24 *     * Redistributions in binary form must reproduce the above copyright
25 *       notice, this list of conditions and the following disclaimer in the
26 *       documentation and/or other materials provided with the distribution.
27 *     * Neither the name of Advanced Micro Devices, Inc. nor the names of
28 *       its contributors may be used to endorse or promote products derived
29 *       from this software without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
35 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 *
42 * ***************************************************************************
43 *
44 */
45
46 /*
47  *----------------------------------------------------------------------------
48  *                                MODULES USED
49  *
50  *----------------------------------------------------------------------------
51  */
52
53
54
55 #include "AGESA.h"
56 #include "Ids.h"
57 #include "mm.h"
58 #include "mn.h"
59 #include "mu.h"
60 #include "mt.h"
61 #include "mp.h"
62 #include "mtsdi3.h"
63 #include "mtlrdimm3.h"
64 #include "merrhdl.h"
65 #include "OptionMemory.h"
66 #include "PlatformMemoryConfiguration.h"
67 #include "GeneralServices.h"
68 #include "Filecode.h"
69 CODE_GROUP (G1_PEICC)
70 RDATA_GROUP (G2_PEI)
71
72 #define FILECODE PROC_MEM_TECH_DDR3_MTTWL3_FILECODE
73 /*----------------------------------------------------------------------------
74  *                          DEFINITIONS AND MACROS
75  *
76  *----------------------------------------------------------------------------
77  */
78 extern MEM_PSC_FLOW_BLOCK* memPlatSpecFlowArray[];
79
80 /*----------------------------------------------------------------------------
81  *                           TYPEDEFS AND STRUCTURES
82  *
83  *----------------------------------------------------------------------------
84  */
85
86 /*----------------------------------------------------------------------------
87  *                        PROTOTYPES OF LOCAL FUNCTIONS
88  *
89  *----------------------------------------------------------------------------
90  */
91
92 BOOLEAN
93 STATIC
94 MemTWriteLevelizationHw3 (
95   IN OUT   MEM_TECH_BLOCK *TechPtr,
96   IN       UINT8 Pass
97   );
98
99 VOID
100 STATIC
101 MemTWLPerDimmHw3 (
102   IN OUT   MEM_TECH_BLOCK *TechPtr,
103   IN       UINT8 Dimm,
104   IN       UINT8 Pass
105   );
106
107 VOID
108 STATIC
109 MemTPrepareDIMMs3 (
110   IN OUT   MEM_TECH_BLOCK *TechPtr,
111   IN       UINT8 TargetDIMM,
112   IN       BOOLEAN Wl
113   );
114
115 VOID
116 STATIC
117 MemTProcConfig3 (
118   IN OUT   MEM_TECH_BLOCK *TechPtr,
119   IN       UINT8 Dimm,
120   IN       UINT8 Pass
121   );
122
123 VOID
124 STATIC
125 MemTBeginWLTrain3 (
126   IN OUT   MEM_TECH_BLOCK *TechPtr,
127   IN       UINT8 Dimm
128   );
129
130 /*----------------------------------------------------------------------------
131  *                            EXPORTED FUNCTIONS
132  *
133  *----------------------------------------------------------------------------
134  */
135
136
137 /* -----------------------------------------------------------------------------*/
138 /**
139  *
140  *      This function executes first pass of Phy assisted write levelization
141  *      for a specific node (DDR800).
142  *
143  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
144  *
145  *     @return          TRUE -  No fatal error occurs.
146  *     @return          FALSE - Fatal error occurs.
147  */
148
149 BOOLEAN
150 MemTWriteLevelizationHw3Pass1 (
151   IN OUT   MEM_TECH_BLOCK *TechPtr
152   )
153 {
154   return MemTWriteLevelizationHw3 (TechPtr, 1);
155 }
156
157 /* -----------------------------------------------------------------------------*/
158 /**
159  *
160  *      This function executes second pass of Phy assisted write levelization
161  *      for a specific node (DDR1066 and above).
162  *
163  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
164  *
165  *     @return          TRUE -  No fatal error occurs.
166  *     @return          FALSE - Fatal error occurs.
167  */
168
169 BOOLEAN
170 MemTWriteLevelizationHw3Pass2 (
171   IN OUT   MEM_TECH_BLOCK *TechPtr
172   )
173 {
174   // If current speed is higher than start-up speed, do second pass of WL
175   if (TechPtr->NBPtr->DCTPtr->Timings.Speed > TechPtr->NBPtr->StartupSpeed) {
176     return MemTWriteLevelizationHw3 (TechPtr, 2);
177   }
178   return TRUE;
179 }
180
181 /* -----------------------------------------------------------------------------*/
182 /**
183  *
184  *      This function prepares for Phy assisted training.
185  *
186  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
187  *
188  *     @return          TRUE -  No fatal error occurs.
189  *     @return          FALSE - Fatal error occurs.
190  */
191
192 BOOLEAN
193 MemTPreparePhyAssistedTraining (
194   IN OUT   MEM_TECH_BLOCK *TechPtr
195   )
196 {
197   //  Disable auto refresh by configuring F2x[1, 0]8C[DisAutoRefresh] = 1.
198   TechPtr->NBPtr->BrdcstSet (TechPtr->NBPtr, BFDisAutoRefresh, 1);
199   //  Disable ZQ calibration short command by configuring F2x[1, 0]94[ZqcsInterval] = 00b.
200   TechPtr->NBPtr->BrdcstSet (TechPtr->NBPtr, BFZqcsInterval, 0);
201   //  Attempt to get the seeds value from PSC tables for WL and RxEn pass1 training if applicable.
202   if (!TechPtr->NBPtr->PsPtr->MemPGetPass1Seeds (TechPtr->NBPtr)) {
203     ASSERT (FALSE);
204   }
205
206   return (BOOLEAN) (TechPtr->NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
207 }
208
209 /* -----------------------------------------------------------------------------*/
210 /**
211  *
212  *      This function revert to normal settings when exiting from Phy assisted training.
213  *
214  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
215  *
216  *     @return          TRUE -  No fatal error occurs.
217  *     @return          FALSE - Fatal error occurs.
218  */
219
220 BOOLEAN
221 MemTExitPhyAssistedTraining (
222   IN OUT   MEM_TECH_BLOCK *TechPtr
223   )
224 {
225   MEM_NB_BLOCK *NBPtr;
226   NBPtr = TechPtr->NBPtr;
227
228   //  13.Program F2x[1, 0]8C[DisAutoRefresh] = 0.
229   NBPtr->BrdcstSet (NBPtr, BFDisAutoRefresh, 0);
230   //  14.Program F2x[1, 0]94[ZqcsInterval] to the proper interval for the current memory configuration.
231   NBPtr->BrdcstSet (NBPtr, BFZqcsInterval, 2);
232   NBPtr->FamilySpecificHook[ExitPhyAssistedTraining] (NBPtr, NBPtr);
233
234   return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
235 }
236
237 /*----------------------------------------------------------------------------
238  *                              LOCAL FUNCTIONS
239  *
240  *----------------------------------------------------------------------------
241  */
242
243 /* -----------------------------------------------------------------------------*/
244 /**
245  *
246  *      This function executed hardware based write levelization for a specific die
247  *
248  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
249  *     @param[in] Pass - Pass number (1 (400Mhz) or 2 (>400Mhz))
250  *
251  *     @pre   Auto refresh and ZQCL must be disabled
252  *
253  *     @return          TRUE -  No fatal error occurs.
254  *     @return          FALSE - Fatal error occurs.
255  */
256
257 BOOLEAN
258 STATIC
259 MemTWriteLevelizationHw3 (
260   IN OUT   MEM_TECH_BLOCK *TechPtr,
261   IN       UINT8 Pass
262   )
263 {
264   MEM_NB_BLOCK  *NBPtr;
265   DCT_STRUCT *DCTPtr;
266   UINT8 Dct;
267   UINT8 Dimm;
268
269   NBPtr = TechPtr->NBPtr;
270
271   IDS_HDT_CONSOLE (MEM_STATUS, "\nStart write leveling\n");
272   AGESA_TESTPOINT (TpProcMemWriteLevelizationTraining, &(NBPtr->MemPtr->StdHeader));
273   // Begin DQS Write timing training
274   for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
275     NBPtr->SwitchDCT (NBPtr, Dct);
276     IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
277     DCTPtr = NBPtr->DCTPtr;
278
279     TechPtr->WLCriticalDelay = 0x00;
280
281     //training for each Dimm
282     for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
283       if ((DCTPtr->Timings.CsEnabled & ((UINT16)3 << (Dimm << 1))) != 0) {
284         if (!(NBPtr->MCTPtr->Status[SbLrdimms]) || ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << Dimm)) != 0)) {
285           IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", Dimm << 1);
286           MemTWLPerDimmHw3 (TechPtr, Dimm, Pass);
287         }
288       }
289     }
290
291     NBPtr->FamilySpecificHook[CalcWrDqDqsEarly] (NBPtr, NULL);
292   }
293   IDS_HDT_CONSOLE (MEM_FLOW, "End write leveling\n\n");
294   return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
295 }
296
297 /* -----------------------------------------------------------------------------*/
298 /**
299  *
300  *      This function initializes per DIMM write levelization
301  *
302  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
303  *     @param[in] Dimm - DIMM to be trained
304  *     @param[in] Pass - Pass number (1 (400Mhz) or 2 (>400Mhz))
305  *
306  */
307
308 VOID
309 STATIC
310 MemTWLPerDimmHw3 (
311   IN OUT   MEM_TECH_BLOCK *TechPtr,
312   IN       UINT8 Dimm,
313   IN       UINT8 Pass
314   )
315 {
316   MEM_DATA_STRUCT *MemPtr;
317   MEM_NB_BLOCK  *NBPtr;
318
319   NBPtr = TechPtr->NBPtr;
320   MemPtr = NBPtr->MemPtr;
321
322   ASSERT (Dimm < MAX_DIMMS_PER_CHANNEL);
323
324   // 1. A. Specify the target Dimm that is to be trained by programming
325   //     F2x[1, 0]9C_x08[TrDimmSel].
326   NBPtr->SetBitField (NBPtr, BFTrDimmSel, Dimm);
327
328   TechPtr->TargetDIMM = Dimm;
329   NBPtr->FamilySpecificHook[InitPerNibbleTrn] (NBPtr, NULL);
330   for (TechPtr->TrnNibble = NIBBLE_0; TechPtr->TrnNibble <= (NBPtr->FamilySpecificHook[TrainWlPerNibble] (NBPtr, &Dimm)? NIBBLE_0 : NIBBLE_1); TechPtr->TrnNibble++) {
331     // 2. Prepare the DIMMs for write levelization using DDR3-defined
332     // MR commands.
333     MemTPrepareDIMMs3 (TechPtr, Dimm, TRUE);
334
335     // 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
336     //     satisfy DDR3-defined internal DRAM timing.
337     NBPtr->WaitXMemClks (NBPtr, 40);
338
339     // 4. Configure the processor's DDR phy for write levelization training:
340     MemTProcConfig3 (TechPtr, Dimm, Pass);
341
342     // 5. Begin write levelization training
343     MemTBeginWLTrain3 (TechPtr, Dimm);
344   }
345   // 7. Program the target Dimm back to normal operation
346   MemTPrepareDIMMs3 (TechPtr, Dimm, FALSE);
347 }
348
349 /* -----------------------------------------------------------------------------*/
350 /**
351  *
352  *      This function prepares the DIMMS for Write Levelization
353  *
354  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
355  *     @param[in] TargetDIMM - DIMM to be trained
356  *     @param[in] Wl - Indicates if WL mode should be enabled
357  *
358  */
359
360 VOID
361 STATIC
362 MemTPrepareDIMMs3 (
363   IN OUT   MEM_TECH_BLOCK *TechPtr,
364   IN       UINT8 TargetDIMM,
365   IN       BOOLEAN Wl
366   )
367 {
368   MEM_NB_BLOCK  *NBPtr;
369   UINT8 ChipSel;
370
371   NBPtr = TechPtr->NBPtr;
372
373   AGESA_TESTPOINT (TpProcMemWlPrepDimms, &(NBPtr->MemPtr->StdHeader));
374   ASSERT (TargetDIMM < MAX_DIMMS_PER_CHANNEL);
375   TechPtr->TargetDIMM = TargetDIMM;
376   if (!(TechPtr->TechnologySpecificHook[WlTrainingPrepareLrdimm] (TechPtr, &Wl))) {
377     for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
378       if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) {
379         NBPtr->SetBitField (NBPtr, BFMrsChipSel, ChipSel);
380         // Set MR1 to F2x7C[MrsAddress], F2x7C[MrsBank]=1
381         MemTEMRS13 (TechPtr, Wl, TargetDIMM);
382         NBPtr->SendMrsCmd (NBPtr);
383         // Set MR2 to F2x7C[MrsAddress], F2x7C[MrsBank]=1
384         MemTEMRS23 (TechPtr);
385         // Send command
386         NBPtr->SendMrsCmd (NBPtr);
387       }
388     }
389     if (Wl) {
390       // Program WrLvOdt for the Target DIMM (or CS)
391       NBPtr->SetBitField (NBPtr, BFWrLvOdt, NBPtr->ChannelPtr->PhyWLODT[TargetDIMM]);
392     }
393   }
394 }
395
396 /* -----------------------------------------------------------------------------*/
397 /**
398  *
399  *      This function programs seed values for Write Levelization
400  *
401  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
402  *     @param[in] Dimm - DIMM to be trained
403  *     @param[in] Pass - Pass for WL training (1 - 400Mhz or 2 - >400Mhz)
404  *
405  */
406
407 VOID
408 STATIC
409 MemTProcConfig3 (
410   IN OUT   MEM_TECH_BLOCK *TechPtr,
411   IN       UINT8 Dimm,
412   IN       UINT8 Pass
413   )
414 {
415   DIE_STRUCT *MCTPtr;
416   CH_DEF_STRUCT *ChannelPtr;
417   MEM_NB_BLOCK *NBPtr;
418   UINT16 WrDqsDly;
419   // Memclk Delay incurred by register.
420   UINT8 MemClkRegDly;
421   UINT8 ByteLane;
422   UINT8 DefaultSeed;
423   UINT8 CurrentSeed;
424   UINT8 *Seed;
425   UINT8 RCW2;
426   UINT16 Speed;
427   INT16 WrDqsBias;
428
429   NBPtr = TechPtr->NBPtr;
430   MCTPtr = NBPtr->MCTPtr;
431   ChannelPtr = TechPtr->NBPtr->ChannelPtr;
432
433   AGESA_TESTPOINT (TpProcMemWlConfigDimms, &(NBPtr->MemPtr->StdHeader));
434   RCW2 = ChannelPtr->CtrlWrd02[Dimm];
435   Speed = TechPtr->NBPtr->DCTPtr->Timings.Speed;
436
437   IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Byte: 00 01 02 03 04 05 06 07 ECC\n");
438   IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSeeds: ");
439   //  Program an initialization Value to registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 to set
440   //  the gross and fine delay for all the byte lane fields. If the target frequency is different than 400MHz,
441   //  BIOS must execute two training passes for each Dimm. For pass 1 at a 400MHz MEMCLK frequency,
442   //  use an initial total delay value.
443   if (Pass == 1) {
444     //
445     // Get the default value of seed
446     //
447     if (MCTPtr->Status[SbRegistered]) {
448       //
449       // RDIMM
450       //
451       if (Speed == DDR667_FREQUENCY) {
452         DefaultSeed = ((RCW2 & BIT0) == 0) ? 0x3B : 0x4B;
453       } else {
454         DefaultSeed = ((RCW2 & BIT0) == 0) ? 0x41 : 0x51;
455       }
456     } else if (ChannelPtr->SODimmPresent != 0) {
457       //
458       // SODIMMM
459       //
460       DefaultSeed = 0x12;
461     } else if (MCTPtr->Status[SbLrdimms]) {
462       //
463       // LRDIMM
464       //
465       DefaultSeed = 0xF7;
466     } else {
467       //
468       // UDIMMM
469       //
470       DefaultSeed = 0x1A;
471     }
472
473     NBPtr->FamilySpecificHook[OverrideWLSeed] (NBPtr, &DefaultSeed);
474     ASSERT (Speed >= DDR667_FREQUENCY);
475
476     // Get platform override seed
477     Seed = (UINT8 *) FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_WL_SEED, MCTPtr->SocketId, ChannelPtr->ChannelID, Dimm,
478                                           &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
479     for (ByteLane = 0; ByteLane < TechPtr->DlyTableWidth (); ByteLane++) {
480       // This includes ECC as byte 8
481       CurrentSeed = ((Seed != NULL) ? Seed[ByteLane] : DefaultSeed);
482       ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane] = CurrentSeed;
483
484       if (NBPtr->IsSupported[WLSeedAdjust]) {
485         if ((CurrentSeed & 0x20) != 0) {
486           // If (SeedGross is odd) then SeedPreGross = 1
487           CurrentSeed = (CurrentSeed & 0x1F) | 0x20;
488         } else {
489           // If (SeedGross is even) then SeedPreGross = 2
490           CurrentSeed = (CurrentSeed & 0x1F) | 0x40;
491         }
492       }
493
494       NBPtr->SetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), CurrentSeed);
495       IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", CurrentSeed);
496     }
497   } else {
498     //10.Multiply the previously saved delay values in Pass 1, step #5 by (target frequency)/400 to find
499     //the gross and fine delay initialization values at the target frequency. Use these values as the initial
500     //seed values when executing Pass 2, step #4.
501     for (ByteLane = 0; ByteLane < TechPtr->DlyTableWidth (); ByteLane++) {
502       // This includes ECC as byte 8
503       WrDqsDly = ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane];
504       TechPtr->Bytelane = ByteLane;
505       NBPtr->FamilySpecificHook[TrainWlPerNibbleSeed] (NBPtr, &WrDqsDly);
506
507       if (MCTPtr->Status[SbRegistered]) {
508         //
509         // For Registered Dimms
510         //
511         MemClkRegDly = ((RCW2 & BIT0) == 0) ? 0x20 : 0x30;
512       } else {
513         //
514         // Unbuffered Dimms and LRDIMMs
515         //
516         MemClkRegDly = 0;
517       }
518       //
519       // Recover any adjustmen to delay for WrDqDqsEarly
520       //
521       WrDqsBias = 0;
522       NBPtr->FamilySpecificHook[AdjustWrDqsBeforeSeedScaling] (NBPtr, &WrDqsBias);
523
524       // Scale WrDqsDly to the next speed
525       WrDqsDly = (UINT16) (MemClkRegDly + ((((INT32) WrDqsDly - MemClkRegDly - WrDqsBias) * Speed) / TechPtr->PrevSpeed));
526
527       ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane] = (UINT8) WrDqsDly;
528
529       if (NBPtr->IsSupported[WLSeedAdjust]) {
530         if ((WrDqsDly & 0x20) != 0) {
531           // If (SeedGross is odd) then SeedPreGross = 1
532           WrDqsDly = (WrDqsDly & 0x1F) | 0x20;
533         } else {
534           // If (SeedGross is even) then SeedPreGross = 2
535           WrDqsDly = (WrDqsDly & 0x1F) | 0x40;
536         }
537       }
538       NBPtr->SetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDly);
539       IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", WrDqsDly);
540     }
541   }
542   IDS_HDT_CONSOLE (MEM_FLOW, "\n");
543 }
544
545 /* -----------------------------------------------------------------------------*/
546 /**
547  *
548  *      This function begins WL training for a specific DIMM
549  *
550  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
551  *     @param[in] Dimm - DIMM to be trained
552  *
553  */
554
555 VOID
556 STATIC
557 MemTBeginWLTrain3 (
558   IN OUT   MEM_TECH_BLOCK *TechPtr,
559   IN       UINT8 Dimm
560   )
561 {
562   MEM_DATA_STRUCT *MemPtr;
563   DIE_STRUCT *MCTPtr;
564   MEM_NB_BLOCK *NBPtr;
565   UINT8 ByteLane;
566   UINT8 Seed;
567   UINT8 Delay;
568   INT16 Delay16;
569
570   NBPtr = TechPtr->NBPtr;
571   MemPtr = NBPtr->MemPtr;
572   MCTPtr = NBPtr->MCTPtr;
573
574   AGESA_TESTPOINT (TpProcMemWlTrainTargetDimm, &(MemPtr->StdHeader));
575   // Assert ODT pins for write leveling
576   NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 1);
577
578   // Wait 10 MEMCLKs to allow for ODT signal settling.
579   NBPtr->WaitXMemClks (NBPtr, 10);
580
581   IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tWrtLvTrEn = 1\n");
582   // Program F2x[1, 0]9C_x08[WrtLlTrEn]=1.
583   NBPtr->SetBitField (NBPtr, BFWrtLvTrEn, 1);
584
585   //  Wait 200 MEMCLKs.
586   NBPtr->WaitXMemClks (NBPtr, 200);
587
588   //  Program F2x[1, 0]9C_x08[WrtLlTrEn]=0.
589   NBPtr->SetBitField (NBPtr, BFWrtLvTrEn, 0);
590
591   //  Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 to get the gross and fine Delay settings
592   //  for the target Dimm and save these values.
593   IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t  PRE: ");
594   for (ByteLane = 0; ByteLane < (MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
595     // This includes ECC as byte 8
596     Seed = NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane];
597     Delay = (UINT8)NBPtr->GetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane));
598     IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Delay);
599
600     TechPtr->Bytelane = ByteLane;
601     TechPtr->TargetDIMM = Dimm;
602     NBPtr->FamilySpecificHook[TrainWlPerNibbleAdjustWLDly] (NBPtr, &Delay);
603
604     if (NBPtr->IsSupported[WLSeedAdjust]) {
605       // Recover WrDqsGrossDly:
606       // WrDqsGrossDly = SeedGross + PhRecGrossDlyByte - SeedPreGross
607       if ((Seed & 0x20) != 0) {
608         // If (SeedGross is odd) then SeedPreGross = 1
609         if ((NBPtr->IsSupported[WLNegativeDelay]) && ((Seed & 0x80) != 0)) {
610           // If the seed was negative, save the most negative delay in WLCriticalDelay
611           TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, (INT16)Delay - 0x40);
612           Delay -= 0x40;
613         } else {
614           Delay += (Seed & 0xE0) - 0x20;
615         }
616       } else {
617         // If (SeedGross is even) then SeedPreGross = 2
618         if (((Seed & 0xE0) == 0) && (Delay < 0x40)) {
619           // If SeedGross is 0 and PhRecGrossDlyByte is less than SeedPreGross,
620           // we have a negative result and need to program the delay to 0
621           if (NBPtr->IsSupported[WLNegativeDelay]) {
622             //
623             // Save the lowest negative delay value across all Dimms and Bytelanes
624             //
625             TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, (INT16)Delay - 0x40);
626             Delay -= 0x40;
627           } else {
628             Delay = 0;
629           }
630         } else {
631           if (NBPtr->GetBitField (NBPtr, BFWrDqDqsEarly) != 0) {
632             Delay = Delay + (Seed & 0xE0);
633             Delay16 = Delay - 0x40;
634             Delay = (UINT8)Delay16;
635             TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, Delay16);
636           } else {
637             Delay += (Seed & 0xE0) - 0x40;
638           }
639         }
640       }
641     } else if (((Seed >> 5) == 0) && ((Delay >> 5) == 3)) {
642       IDS_OPTION_HOOK (IDS_CHECK_NEGATIVE_WL, &Delay, &(TechPtr->NBPtr->MemPtr->StdHeader));
643       // If seed has gross delay of 0 and PRE has gross delay of 3,
644       // then round the total delay of TxDqs to 0.
645       Delay = 0;
646     }
647
648     if ((!NBPtr->IsSupported[WLNegativeDelay]) && ((Delay > (Seed + 0x20)) || (Seed > (Delay + 0x20)))) {
649       //
650       // If PRE comes back with more than Seed +/- 0x20, then this is an
651       //   unexpected condition.  Log the condition.
652       //
653       PutEventLog (AGESA_ERROR, MEM_ERROR_WL_PRE_OUT_OF_RANGE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ((Seed << 8) + Delay), &NBPtr->MemPtr->StdHeader);
654     }
655     NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), Delay);
656     NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane] = Delay;
657   }
658
659   IDS_HDT_CONSOLE_DEBUG_CODE (
660     IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWrDqs: ");
661     for (ByteLane = 0; ByteLane < (MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
662       IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane]);
663     }
664     IDS_HDT_CONSOLE (MEM_FLOW, "\n\n");
665   );
666
667   // Disable write leveling ODT pins
668   NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 0);
669
670   // Wait 10 MEMCLKs to allow for ODT signal settling.
671   NBPtr->WaitXMemClks (NBPtr, 10);
672
673 }
674
675 /* -----------------------------------------------------------------------------*/
676 /**
677  *
678  *      This function programs register after Phy assisted training is finish.
679  *
680  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
681  *
682  *     @return          TRUE -  No fatal error occurs.
683  *     @return          FALSE - Fatal error occurs.
684  */
685
686 BOOLEAN
687 MemTExitPhyAssistedTrainingClient3 (
688   IN OUT   MEM_TECH_BLOCK *TechPtr
689   )
690 {
691   MEM_NB_BLOCK  *NBPtr;
692   UINT8  Dct;
693   UINT8  ChipSel;
694   NBPtr = TechPtr->NBPtr;
695
696   NBPtr->FamilySpecificHook[ReEnablePhyComp] (NBPtr, NBPtr);
697   NBPtr->BrdcstSet (NBPtr, BFRxPtrInitReq, 1);
698   NBPtr->PollBitField (NBPtr, BFRxPtrInitReq, 0, PCI_ACCESS_TIMEOUT, TRUE);
699   NBPtr->BrdcstSet (NBPtr, BFDisDllShutdownSR, 1);
700   NBPtr->BrdcstSet (NBPtr, BFEnterSelfRef, 1);
701   NBPtr->PollBitField (NBPtr, BFEnterSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
702   IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClkAlign = 2\n");
703   NBPtr->BrdcstSet (NBPtr, BFDbeGskMemClkAlignMode, 2);
704   NBPtr->BrdcstSet (NBPtr, BFExitSelfRef, 1);
705   NBPtr->PollBitField (NBPtr, BFExitSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
706   if (NBPtr->IsSupported[SetDllShutDown]) {
707     NBPtr->BrdcstSet (NBPtr, BFDisDllShutdownSR, 0);
708   }
709
710   // Calculate Max Latency for both channels to prepare for position training
711   for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
712     IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
713     NBPtr->SwitchDCT (NBPtr, Dct);
714     if (TechPtr->FindMaxDlyForMaxRdLat (TechPtr, &ChipSel)) {
715       NBPtr->SetMaxLatency (NBPtr, TechPtr->MaxDlyForMaxRdLat);
716     }
717   }
718   return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
719 }