7 * Technology Phy assisted write levelization for DDR3
9 * @xrefitem bom "File Content Label" "Release Content"
11 * @e sub-project: (Mem/Tech/DDR3)
12 * @e \$Revision: 57883 $ @e \$Date: 2011-08-15 10:41:06 -0600 (Mon, 15 Aug 2011) $
15 /*****************************************************************************
17 * Copyright (C) 2012 Advanced Micro Devices, Inc.
18 * All rights reserved.
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.
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.
42 * ***************************************************************************
47 *----------------------------------------------------------------------------
50 *----------------------------------------------------------------------------
63 #include "mtlrdimm3.h"
65 #include "OptionMemory.h"
66 #include "PlatformMemoryConfiguration.h"
67 #include "GeneralServices.h"
72 #define FILECODE PROC_MEM_TECH_DDR3_MTTWL3_FILECODE
73 /*----------------------------------------------------------------------------
74 * DEFINITIONS AND MACROS
76 *----------------------------------------------------------------------------
78 extern MEM_PSC_FLOW_BLOCK* memPlatSpecFlowArray[];
80 /*----------------------------------------------------------------------------
81 * TYPEDEFS AND STRUCTURES
83 *----------------------------------------------------------------------------
86 /*----------------------------------------------------------------------------
87 * PROTOTYPES OF LOCAL FUNCTIONS
89 *----------------------------------------------------------------------------
94 MemTWriteLevelizationHw3 (
95 IN OUT MEM_TECH_BLOCK *TechPtr,
102 IN OUT MEM_TECH_BLOCK *TechPtr,
110 IN OUT MEM_TECH_BLOCK *TechPtr,
118 IN OUT MEM_TECH_BLOCK *TechPtr,
126 IN OUT MEM_TECH_BLOCK *TechPtr,
130 /*----------------------------------------------------------------------------
133 *----------------------------------------------------------------------------
137 /* -----------------------------------------------------------------------------*/
140 * This function executes first pass of Phy assisted write levelization
141 * for a specific node (DDR800).
143 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
145 * @return TRUE - No fatal error occurs.
146 * @return FALSE - Fatal error occurs.
150 MemTWriteLevelizationHw3Pass1 (
151 IN OUT MEM_TECH_BLOCK *TechPtr
154 return MemTWriteLevelizationHw3 (TechPtr, 1);
157 /* -----------------------------------------------------------------------------*/
160 * This function executes second pass of Phy assisted write levelization
161 * for a specific node (DDR1066 and above).
163 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
165 * @return TRUE - No fatal error occurs.
166 * @return FALSE - Fatal error occurs.
170 MemTWriteLevelizationHw3Pass2 (
171 IN OUT MEM_TECH_BLOCK *TechPtr
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);
181 /* -----------------------------------------------------------------------------*/
184 * This function prepares for Phy assisted training.
186 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
188 * @return TRUE - No fatal error occurs.
189 * @return FALSE - Fatal error occurs.
193 MemTPreparePhyAssistedTraining (
194 IN OUT MEM_TECH_BLOCK *TechPtr
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)) {
206 return (BOOLEAN) (TechPtr->NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
209 /* -----------------------------------------------------------------------------*/
212 * This function revert to normal settings when exiting from Phy assisted training.
214 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
216 * @return TRUE - No fatal error occurs.
217 * @return FALSE - Fatal error occurs.
221 MemTExitPhyAssistedTraining (
222 IN OUT MEM_TECH_BLOCK *TechPtr
226 NBPtr = TechPtr->NBPtr;
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);
234 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
237 /*----------------------------------------------------------------------------
240 *----------------------------------------------------------------------------
243 /* -----------------------------------------------------------------------------*/
246 * This function executed hardware based write levelization for a specific die
248 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
249 * @param[in] Pass - Pass number (1 (400Mhz) or 2 (>400Mhz))
251 * @pre Auto refresh and ZQCL must be disabled
253 * @return TRUE - No fatal error occurs.
254 * @return FALSE - Fatal error occurs.
259 MemTWriteLevelizationHw3 (
260 IN OUT MEM_TECH_BLOCK *TechPtr,
269 NBPtr = TechPtr->NBPtr;
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;
279 TechPtr->WLCriticalDelay = 0x00;
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);
291 NBPtr->FamilySpecificHook[CalcWrDqDqsEarly] (NBPtr, NULL);
293 IDS_HDT_CONSOLE (MEM_FLOW, "End write leveling\n\n");
294 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
297 /* -----------------------------------------------------------------------------*/
300 * This function initializes per DIMM write levelization
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))
311 IN OUT MEM_TECH_BLOCK *TechPtr,
316 MEM_DATA_STRUCT *MemPtr;
319 NBPtr = TechPtr->NBPtr;
320 MemPtr = NBPtr->MemPtr;
322 ASSERT (Dimm < MAX_DIMMS_PER_CHANNEL);
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);
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
333 MemTPrepareDIMMs3 (TechPtr, Dimm, TRUE);
335 // 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
336 // satisfy DDR3-defined internal DRAM timing.
337 NBPtr->WaitXMemClks (NBPtr, 40);
339 // 4. Configure the processor's DDR phy for write levelization training:
340 MemTProcConfig3 (TechPtr, Dimm, Pass);
342 // 5. Begin write levelization training
343 MemTBeginWLTrain3 (TechPtr, Dimm);
345 // 7. Program the target Dimm back to normal operation
346 MemTPrepareDIMMs3 (TechPtr, Dimm, FALSE);
349 /* -----------------------------------------------------------------------------*/
352 * This function prepares the DIMMS for Write Levelization
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
363 IN OUT MEM_TECH_BLOCK *TechPtr,
371 NBPtr = TechPtr->NBPtr;
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);
386 NBPtr->SendMrsCmd (NBPtr);
390 // Program WrLvOdt for the Target DIMM (or CS)
391 NBPtr->SetBitField (NBPtr, BFWrLvOdt, NBPtr->ChannelPtr->PhyWLODT[TargetDIMM]);
396 /* -----------------------------------------------------------------------------*/
399 * This function programs seed values for Write Levelization
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)
410 IN OUT MEM_TECH_BLOCK *TechPtr,
416 CH_DEF_STRUCT *ChannelPtr;
419 // Memclk Delay incurred by register.
429 NBPtr = TechPtr->NBPtr;
430 MCTPtr = NBPtr->MCTPtr;
431 ChannelPtr = TechPtr->NBPtr->ChannelPtr;
433 AGESA_TESTPOINT (TpProcMemWlConfigDimms, &(NBPtr->MemPtr->StdHeader));
434 RCW2 = ChannelPtr->CtrlWrd02[Dimm];
435 Speed = TechPtr->NBPtr->DCTPtr->Timings.Speed;
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.
445 // Get the default value of seed
447 if (MCTPtr->Status[SbRegistered]) {
451 if (Speed == DDR667_FREQUENCY) {
452 DefaultSeed = ((RCW2 & BIT0) == 0) ? 0x3B : 0x4B;
454 DefaultSeed = ((RCW2 & BIT0) == 0) ? 0x41 : 0x51;
456 } else if (ChannelPtr->SODimmPresent != 0) {
461 } else if (MCTPtr->Status[SbLrdimms]) {
473 NBPtr->FamilySpecificHook[OverrideWLSeed] (NBPtr, &DefaultSeed);
474 ASSERT (Speed >= DDR667_FREQUENCY);
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;
484 if (NBPtr->IsSupported[WLSeedAdjust]) {
485 if ((CurrentSeed & 0x20) != 0) {
486 // If (SeedGross is odd) then SeedPreGross = 1
487 CurrentSeed = (CurrentSeed & 0x1F) | 0x20;
489 // If (SeedGross is even) then SeedPreGross = 2
490 CurrentSeed = (CurrentSeed & 0x1F) | 0x40;
494 NBPtr->SetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), CurrentSeed);
495 IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", CurrentSeed);
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);
507 if (MCTPtr->Status[SbRegistered]) {
509 // For Registered Dimms
511 MemClkRegDly = ((RCW2 & BIT0) == 0) ? 0x20 : 0x30;
514 // Unbuffered Dimms and LRDIMMs
519 // Recover any adjustmen to delay for WrDqDqsEarly
522 NBPtr->FamilySpecificHook[AdjustWrDqsBeforeSeedScaling] (NBPtr, &WrDqsBias);
524 // Scale WrDqsDly to the next speed
525 WrDqsDly = (UINT16) (MemClkRegDly + ((((INT32) WrDqsDly - MemClkRegDly - WrDqsBias) * Speed) / TechPtr->PrevSpeed));
527 ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane] = (UINT8) WrDqsDly;
529 if (NBPtr->IsSupported[WLSeedAdjust]) {
530 if ((WrDqsDly & 0x20) != 0) {
531 // If (SeedGross is odd) then SeedPreGross = 1
532 WrDqsDly = (WrDqsDly & 0x1F) | 0x20;
534 // If (SeedGross is even) then SeedPreGross = 2
535 WrDqsDly = (WrDqsDly & 0x1F) | 0x40;
538 NBPtr->SetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDly);
539 IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", WrDqsDly);
542 IDS_HDT_CONSOLE (MEM_FLOW, "\n");
545 /* -----------------------------------------------------------------------------*/
548 * This function begins WL training for a specific DIMM
550 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
551 * @param[in] Dimm - DIMM to be trained
558 IN OUT MEM_TECH_BLOCK *TechPtr,
562 MEM_DATA_STRUCT *MemPtr;
570 NBPtr = TechPtr->NBPtr;
571 MemPtr = NBPtr->MemPtr;
572 MCTPtr = NBPtr->MCTPtr;
574 AGESA_TESTPOINT (TpProcMemWlTrainTargetDimm, &(MemPtr->StdHeader));
575 // Assert ODT pins for write leveling
576 NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 1);
578 // Wait 10 MEMCLKs to allow for ODT signal settling.
579 NBPtr->WaitXMemClks (NBPtr, 10);
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);
586 NBPtr->WaitXMemClks (NBPtr, 200);
588 // Program F2x[1, 0]9C_x08[WrtLlTrEn]=0.
589 NBPtr->SetBitField (NBPtr, BFWrtLvTrEn, 0);
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);
600 TechPtr->Bytelane = ByteLane;
601 TechPtr->TargetDIMM = Dimm;
602 NBPtr->FamilySpecificHook[TrainWlPerNibbleAdjustWLDly] (NBPtr, &Delay);
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);
614 Delay += (Seed & 0xE0) - 0x20;
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]) {
623 // Save the lowest negative delay value across all Dimms and Bytelanes
625 TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, (INT16)Delay - 0x40);
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);
637 Delay += (Seed & 0xE0) - 0x40;
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.
648 if ((!NBPtr->IsSupported[WLNegativeDelay]) && ((Delay > (Seed + 0x20)) || (Seed > (Delay + 0x20)))) {
650 // If PRE comes back with more than Seed +/- 0x20, then this is an
651 // unexpected condition. Log the condition.
653 PutEventLog (AGESA_ERROR, MEM_ERROR_WL_PRE_OUT_OF_RANGE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ((Seed << 8) + Delay), &NBPtr->MemPtr->StdHeader);
655 NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), Delay);
656 NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane] = Delay;
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]);
664 IDS_HDT_CONSOLE (MEM_FLOW, "\n\n");
667 // Disable write leveling ODT pins
668 NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 0);
670 // Wait 10 MEMCLKs to allow for ODT signal settling.
671 NBPtr->WaitXMemClks (NBPtr, 10);
675 /* -----------------------------------------------------------------------------*/
678 * This function programs register after Phy assisted training is finish.
680 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
682 * @return TRUE - No fatal error occurs.
683 * @return FALSE - Fatal error occurs.
687 MemTExitPhyAssistedTrainingClient3 (
688 IN OUT MEM_TECH_BLOCK *TechPtr
694 NBPtr = TechPtr->NBPtr;
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);
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);
718 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);