aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/DDR3/mttwl3.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/DDR3/mttwl3.c')
-rw-r--r--src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/DDR3/mttwl3.c718
1 files changed, 718 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/DDR3/mttwl3.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/DDR3/mttwl3.c
new file mode 100644
index 0000000000..361b56a896
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/DDR3/mttwl3.c
@@ -0,0 +1,718 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mttwl3.c
+ *
+ * Technology Phy assisted write levelization for DDR3
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/Tech/DDR3)
+ * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $
+ *
+ **/
+/*****************************************************************************
+*
+ * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Advanced Micro Devices, Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+* ***************************************************************************
+*
+*/
+
+/*
+ *----------------------------------------------------------------------------
+ * MODULES USED
+ *
+ *----------------------------------------------------------------------------
+ */
+
+
+
+#include "AGESA.h"
+#include "Ids.h"
+#include "mm.h"
+#include "mn.h"
+#include "mu.h"
+#include "mt.h"
+#include "mp.h"
+#include "mtsdi3.h"
+#include "mtlrdimm3.h"
+#include "merrhdl.h"
+#include "OptionMemory.h"
+#include "PlatformMemoryConfiguration.h"
+#include "GeneralServices.h"
+#include "Filecode.h"
+CODE_GROUP (G1_PEICC)
+RDATA_GROUP (G1_PEICC)
+
+#define FILECODE PROC_MEM_TECH_DDR3_MTTWL3_FILECODE
+/*----------------------------------------------------------------------------
+ * DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+extern MEM_PSC_FLOW_BLOCK* memPlatSpecFlowArray[];
+
+/*----------------------------------------------------------------------------
+ * TYPEDEFS AND STRUCTURES
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+BOOLEAN
+STATIC
+MemTWriteLevelizationHw3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 Pass
+ );
+
+VOID
+STATIC
+MemTWLPerDimmHw3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 Dimm,
+ IN UINT8 Pass
+ );
+
+VOID
+STATIC
+MemTPrepareDIMMs3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 TargetDIMM,
+ IN BOOLEAN Wl
+ );
+
+VOID
+STATIC
+MemTProcConfig3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 Dimm,
+ IN UINT8 Pass
+ );
+
+VOID
+STATIC
+MemTBeginWLTrain3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 Dimm
+ );
+
+/*----------------------------------------------------------------------------
+ * EXPORTED FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function executes first pass of Phy assisted write levelization
+ * for a specific node (DDR800).
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - No fatal error occurs.
+ * @return FALSE - Fatal error occurs.
+ */
+
+BOOLEAN
+MemTWriteLevelizationHw3Pass1 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ return MemTWriteLevelizationHw3 (TechPtr, 1);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function executes second pass of Phy assisted write levelization
+ * for a specific node (DDR1066 and above).
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - No fatal error occurs.
+ * @return FALSE - Fatal error occurs.
+ */
+
+BOOLEAN
+MemTWriteLevelizationHw3Pass2 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ // If current speed is higher than start-up speed, do second pass of WL
+ if (TechPtr->NBPtr->DCTPtr->Timings.Speed > TechPtr->NBPtr->StartupSpeed) {
+ return MemTWriteLevelizationHw3 (TechPtr, 2);
+ }
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function prepares for Phy assisted training.
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - No fatal error occurs.
+ * @return FALSE - Fatal error occurs.
+ */
+
+BOOLEAN
+MemTPreparePhyAssistedTraining (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ // Disable auto refresh by configuring F2x[1, 0]8C[DisAutoRefresh] = 1.
+ TechPtr->NBPtr->BrdcstSet (TechPtr->NBPtr, BFDisAutoRefresh, 1);
+ // Disable ZQ calibration short command by configuring F2x[1, 0]94[ZqcsInterval] = 00b.
+ TechPtr->NBPtr->BrdcstSet (TechPtr->NBPtr, BFZqcsInterval, 0);
+ // Attempt to get the seeds value from PSC tables for WL and RxEn pass1 training if applicable.
+ if (!TechPtr->NBPtr->PsPtr->MemPGetPass1Seeds (TechPtr->NBPtr)) {
+ ASSERT (FALSE);
+ }
+
+ return (BOOLEAN) (TechPtr->NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function revert to normal settings when exiting from Phy assisted training.
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - No fatal error occurs.
+ * @return FALSE - Fatal error occurs.
+ */
+
+BOOLEAN
+MemTExitPhyAssistedTraining (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ MEM_NB_BLOCK *NBPtr;
+ NBPtr = TechPtr->NBPtr;
+
+ // 13.Program F2x[1, 0]8C[DisAutoRefresh] = 0.
+ NBPtr->BrdcstSet (NBPtr, BFDisAutoRefresh, 0);
+ // 14.Program F2x[1, 0]94[ZqcsInterval] to the proper interval for the current memory configuration.
+ NBPtr->BrdcstSet (NBPtr, BFZqcsInterval, 2);
+ NBPtr->FamilySpecificHook[ExitPhyAssistedTraining] (NBPtr, NBPtr);
+
+ return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
+}
+
+/*----------------------------------------------------------------------------
+ * LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function executed hardware based write levelization for a specific die
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ * @param[in] Pass - Pass number (1 (400Mhz) or 2 (>400Mhz))
+ *
+ * @pre Auto refresh and ZQCL must be disabled
+ *
+ * @return TRUE - No fatal error occurs.
+ * @return FALSE - Fatal error occurs.
+ */
+
+BOOLEAN
+STATIC
+MemTWriteLevelizationHw3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 Pass
+ )
+{
+ MEM_NB_BLOCK *NBPtr;
+ DCT_STRUCT *DCTPtr;
+ UINT8 Dct;
+ UINT8 ChipSel;
+
+ NBPtr = TechPtr->NBPtr;
+
+ IDS_HDT_CONSOLE (MEM_STATUS, "\nStart write leveling\n");
+ AGESA_TESTPOINT (TpProcMemWriteLevelizationTraining, &(NBPtr->MemPtr->StdHeader));
+ // Begin DQS Write timing training
+ for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
+ NBPtr->SwitchDCT (NBPtr, Dct);
+ IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
+ DCTPtr = NBPtr->DCTPtr;
+
+ TechPtr->WLCriticalDelay = 0x00;
+
+ //training for each Dimm/CS
+ for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay) {
+ if ((DCTPtr->Timings.CsEnabled & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSel)) != 0) {
+ if (!(NBPtr->MCTPtr->Status[SbLrdimms]) || ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0)) {
+ IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", ChipSel);
+ MemTWLPerDimmHw3 (TechPtr, (ChipSel / NBPtr->CsPerDelay), Pass);
+ }
+ }
+ }
+
+ NBPtr->FamilySpecificHook[CalcWrDqDqsEarly] (NBPtr, NULL);
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "End write leveling\n\n");
+ return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function initializes per DIMM write levelization
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ * @param[in] Dimm - DIMM to be trained
+ * @param[in] Pass - Pass number (1 (400Mhz) or 2 (>400Mhz))
+ *
+ */
+
+VOID
+STATIC
+MemTWLPerDimmHw3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 Dimm,
+ IN UINT8 Pass
+ )
+{
+ MEM_DATA_STRUCT *MemPtr;
+ MEM_NB_BLOCK *NBPtr;
+
+ NBPtr = TechPtr->NBPtr;
+ MemPtr = NBPtr->MemPtr;
+
+ ASSERT (Dimm < (NBPtr->CsPerChannel / NBPtr->CsPerDelay));
+
+ // 1. A. Specify the target Dimm that is to be trained by programming
+ // F2x[1, 0]9C_x08[TrDimmSel].
+ NBPtr->SetBitField (NBPtr, BFTrDimmSel, Dimm);
+
+ TechPtr->TargetDIMM = Dimm;
+ NBPtr->FamilySpecificHook[InitPerNibbleTrn] (NBPtr, NULL);
+ for (TechPtr->TrnNibble = NIBBLE_0; TechPtr->TrnNibble <= (NBPtr->FamilySpecificHook[TrainWlPerNibble] (NBPtr, &Dimm)? NIBBLE_0 : NIBBLE_1); TechPtr->TrnNibble++) {
+ // 2. Prepare the DIMMs for write levelization using DDR3-defined
+ // MR commands.
+ MemTPrepareDIMMs3 (TechPtr, Dimm, TRUE);
+
+ // 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
+ // satisfy DDR3-defined internal DRAM timing.
+ NBPtr->WaitXMemClks (NBPtr, 40);
+
+ // 4. Configure the processor's DDR phy for write levelization training:
+ MemTProcConfig3 (TechPtr, Dimm, Pass);
+
+ // 5. Begin write levelization training
+ MemTBeginWLTrain3 (TechPtr, Dimm);
+ }
+ // 7. Program the target Dimm back to normal operation
+ MemTPrepareDIMMs3 (TechPtr, Dimm, FALSE);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function prepares the DIMMS for Write Levelization
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ * @param[in] TargetDIMM - DIMM to be trained
+ * @param[in] Wl - Indicates if WL mode should be enabled
+ *
+ */
+
+VOID
+STATIC
+MemTPrepareDIMMs3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 TargetDIMM,
+ IN BOOLEAN Wl
+ )
+{
+ MEM_NB_BLOCK *NBPtr;
+ UINT8 ChipSel;
+
+ NBPtr = TechPtr->NBPtr;
+
+ AGESA_TESTPOINT (TpProcMemWlPrepDimms, &(NBPtr->MemPtr->StdHeader));
+ ASSERT (TargetDIMM < (NBPtr->CsPerChannel / NBPtr->CsPerDelay));
+ TechPtr->TargetDIMM = TargetDIMM;
+ if (!(TechPtr->TechnologySpecificHook[WlTrainingPrepareLrdimm] (TechPtr, &Wl))) {
+ for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
+ if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) {
+ NBPtr->SetBitField (NBPtr, BFMrsChipSel, ChipSel);
+ // Set MR1 to F2x7C[MrsAddress], F2x7C[MrsBank]=1
+ MemTEMRS13 (TechPtr, Wl, TargetDIMM);
+ NBPtr->SendMrsCmd (NBPtr);
+ // Set MR2 to F2x7C[MrsAddress], F2x7C[MrsBank]=1
+ MemTEMRS23 (TechPtr);
+ // Send command
+ NBPtr->SendMrsCmd (NBPtr);
+ }
+ }
+ if (Wl) {
+ // Program WrLvOdt for the Target DIMM (or CS)
+ NBPtr->SetBitField (NBPtr, BFWrLvOdt, NBPtr->ChannelPtr->PhyWLODT[TargetDIMM]);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs seed values for Write Levelization
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ * @param[in] Dimm - DIMM to be trained
+ * @param[in] Pass - Pass for WL training (1 - 400Mhz or 2 - >400Mhz)
+ *
+ */
+
+VOID
+STATIC
+MemTProcConfig3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 Dimm,
+ IN UINT8 Pass
+ )
+{
+ DIE_STRUCT *MCTPtr;
+ CH_DEF_STRUCT *ChannelPtr;
+ MEM_NB_BLOCK *NBPtr;
+ UINT16 WrDqsDly;
+ // Memclk Delay incurred by register.
+ UINT8 MemClkRegDly;
+ UINT8 ByteLane;
+ UINT8 DefaultSeed;
+ UINT8 CurrentSeed;
+ UINT8 *Seed;
+ UINT8 RCW2;
+ UINT16 Speed;
+ INT16 WrDqsBias;
+
+ NBPtr = TechPtr->NBPtr;
+ MCTPtr = NBPtr->MCTPtr;
+ ChannelPtr = TechPtr->NBPtr->ChannelPtr;
+
+ AGESA_TESTPOINT (TpProcMemWlConfigDimms, &(NBPtr->MemPtr->StdHeader));
+ RCW2 = ChannelPtr->CtrlWrd02[Dimm];
+ Speed = TechPtr->NBPtr->DCTPtr->Timings.Speed;
+
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Byte: 00 01 02 03 04 05 06 07 ECC\n");
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSeeds: ");
+ // Program an initialization Value to registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 to set
+ // the gross and fine delay for all the byte lane fields. If the target frequency is different than 400MHz,
+ // BIOS must execute two training passes for each Dimm. For pass 1 at a 400MHz MEMCLK frequency,
+ // use an initial total delay value.
+ if (Pass == 1) {
+ //
+ // Get the default value of seed
+ //
+ if (MCTPtr->Status[SbRegistered]) {
+ //
+ // RDIMM
+ //
+ if (Speed == DDR667_FREQUENCY) {
+ DefaultSeed = ((RCW2 & BIT0) == 0) ? 0x3B : 0x4B;
+ } else {
+ DefaultSeed = ((RCW2 & BIT0) == 0) ? 0x41 : 0x51;
+ }
+ } else if (ChannelPtr->SODimmPresent != 0) {
+ //
+ // SODIMMM
+ //
+ DefaultSeed = 0x12;
+ } else if (MCTPtr->Status[SbLrdimms]) {
+ //
+ // LRDIMM
+ //
+ DefaultSeed = 0xF7;
+ } else {
+ //
+ // UDIMMM
+ //
+ DefaultSeed = 0x1A;
+ }
+
+ NBPtr->FamilySpecificHook[OverrideWLSeed] (NBPtr, &DefaultSeed);
+ ASSERT (Speed >= DDR667_FREQUENCY);
+
+ // Get platform override seed
+ Seed = (UINT8 *) FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_WL_SEED, MCTPtr->SocketId, ChannelPtr->ChannelID, Dimm,
+ &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
+ for (ByteLane = 0; ByteLane < TechPtr->DlyTableWidth (); ByteLane++) {
+ // This includes ECC as byte 8
+ CurrentSeed = ((Seed != NULL) ? Seed[ByteLane] : DefaultSeed);
+ ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane] = CurrentSeed;
+
+ if (NBPtr->IsSupported[WLSeedAdjust]) {
+ if ((CurrentSeed & 0x20) != 0) {
+ // If (SeedGross is odd) then SeedPreGross = 1
+ CurrentSeed = (CurrentSeed & 0x1F) | 0x20;
+ } else {
+ // If (SeedGross is even) then SeedPreGross = 2
+ CurrentSeed = (CurrentSeed & 0x1F) | 0x40;
+ }
+ }
+
+ NBPtr->SetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), CurrentSeed);
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", CurrentSeed);
+ }
+ } else {
+ //10.Multiply the previously saved delay values in Pass 1, step #5 by (target frequency)/400 to find
+ //the gross and fine delay initialization values at the target frequency. Use these values as the initial
+ //seed values when executing Pass 2, step #4.
+ for (ByteLane = 0; ByteLane < TechPtr->DlyTableWidth (); ByteLane++) {
+ // This includes ECC as byte 8
+ WrDqsDly = ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane];
+ TechPtr->Bytelane = ByteLane;
+ NBPtr->FamilySpecificHook[TrainWlPerNibbleSeed] (NBPtr, &WrDqsDly);
+
+ if (MCTPtr->Status[SbRegistered]) {
+ //
+ // For Registered Dimms
+ //
+ MemClkRegDly = ((RCW2 & BIT0) == 0) ? 0x20 : 0x30;
+ } else {
+ //
+ // Unbuffered Dimms and LRDIMMs
+ //
+ MemClkRegDly = 0;
+ }
+ //
+ // Recover any adjustmen to delay for WrDqDqsEarly
+ //
+ WrDqsBias = 0;
+ NBPtr->FamilySpecificHook[AdjustWrDqsBeforeSeedScaling] (NBPtr, &WrDqsBias);
+
+ // Scale WrDqsDly to the next speed
+ WrDqsDly = (UINT16) (MemClkRegDly + ((((INT32) WrDqsDly - MemClkRegDly - WrDqsBias) * Speed) / TechPtr->PrevSpeed));
+
+ ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane] = (UINT8) WrDqsDly;
+
+ if (NBPtr->IsSupported[WLSeedAdjust]) {
+ if ((WrDqsDly & 0x20) != 0) {
+ // If (SeedGross is odd) then SeedPreGross = 1
+ WrDqsDly = (WrDqsDly & 0x1F) | 0x20;
+ } else {
+ // If (SeedGross is even) then SeedPreGross = 2
+ WrDqsDly = (WrDqsDly & 0x1F) | 0x40;
+ }
+ }
+ NBPtr->SetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDly);
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", WrDqsDly);
+ }
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function begins WL training for a specific DIMM
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ * @param[in] Dimm - DIMM to be trained
+ *
+ */
+
+VOID
+STATIC
+MemTBeginWLTrain3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr,
+ IN UINT8 Dimm
+ )
+{
+ MEM_DATA_STRUCT *MemPtr;
+ DIE_STRUCT *MCTPtr;
+ MEM_NB_BLOCK *NBPtr;
+ UINT8 ByteLane;
+ UINT8 Seed;
+ UINT8 Delay;
+ INT16 Delay16;
+
+ NBPtr = TechPtr->NBPtr;
+ MemPtr = NBPtr->MemPtr;
+ MCTPtr = NBPtr->MCTPtr;
+
+ AGESA_TESTPOINT (TpProcMemWlTrainTargetDimm, &(MemPtr->StdHeader));
+ // Assert ODT pins for write leveling
+ NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 1);
+
+ // Wait 10 MEMCLKs to allow for ODT signal settling.
+ NBPtr->WaitXMemClks (NBPtr, 10);
+
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tWrtLvTrEn = 1\n");
+ // Program F2x[1, 0]9C_x08[WrtLlTrEn]=1.
+ NBPtr->SetBitField (NBPtr, BFWrtLvTrEn, 1);
+
+ // Wait 200 MEMCLKs.
+ NBPtr->WaitXMemClks (NBPtr, 200);
+
+ // Program F2x[1, 0]9C_x08[WrtLlTrEn]=0.
+ NBPtr->SetBitField (NBPtr, BFWrtLvTrEn, 0);
+
+ // Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 to get the gross and fine Delay settings
+ // for the target Dimm and save these values.
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t PRE: ");
+ for (ByteLane = 0; ByteLane < (MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
+ // This includes ECC as byte 8
+ Seed = NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane];
+ Delay = (UINT8)NBPtr->GetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane));
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Delay);
+
+ TechPtr->Bytelane = ByteLane;
+ TechPtr->TargetDIMM = Dimm;
+ NBPtr->FamilySpecificHook[TrainWlPerNibbleAdjustWLDly] (NBPtr, &Delay);
+
+ if (NBPtr->IsSupported[WLSeedAdjust]) {
+ // Recover WrDqsGrossDly:
+ // WrDqsGrossDly = SeedGross + PhRecGrossDlyByte - SeedPreGross
+ if ((Seed & 0x20) != 0) {
+ // If (SeedGross is odd) then SeedPreGross = 1
+ if ((NBPtr->IsSupported[WLNegativeDelay]) && ((Seed & 0x80) != 0)) {
+ // If the seed was negative, save the most negative delay in WLCriticalDelay
+ TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, (INT16)Delay - 0x40);
+ Delay -= 0x40;
+ } else {
+ Delay += (Seed & 0xE0) - 0x20;
+ }
+ } else {
+ // If (SeedGross is even) then SeedPreGross = 2
+ if (((Seed & 0xE0) == 0) && (Delay < 0x40)) {
+ // If SeedGross is 0 and PhRecGrossDlyByte is less than SeedPreGross,
+ // we have a negative result and need to program the delay to 0
+ if (NBPtr->IsSupported[WLNegativeDelay]) {
+ //
+ // Save the lowest negative delay value across all Dimms and Bytelanes
+ //
+ TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, (INT16)Delay - 0x40);
+ Delay -= 0x40;
+ } else {
+ Delay = 0;
+ }
+ } else {
+ if (NBPtr->GetBitField (NBPtr, BFWrDqDqsEarly) != 0) {
+ Delay = Delay + (Seed & 0xE0);
+ Delay16 = Delay - 0x40;
+ Delay = (UINT8)Delay16;
+ TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, Delay16);
+ } else {
+ Delay += (Seed & 0xE0) - 0x40;
+ }
+ }
+ }
+ } else if (((Seed >> 5) == 0) && ((Delay >> 5) == 3)) {
+ IDS_OPTION_HOOK (IDS_CHECK_NEGATIVE_WL, &Delay, &(TechPtr->NBPtr->MemPtr->StdHeader));
+ // If seed has gross delay of 0 and PRE has gross delay of 3,
+ // then round the total delay of TxDqs to 0.
+ Delay = 0;
+ }
+
+ if ((!NBPtr->IsSupported[WLNegativeDelay]) && ((Delay > (Seed + 0x20)) || (Seed > (Delay + 0x20)))) {
+ //
+ // If PRE comes back with more than Seed +/- 0x20, then this is an
+ // unexpected condition. Log the condition.
+ //
+ PutEventLog (AGESA_ERROR, MEM_ERROR_WL_PRE_OUT_OF_RANGE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ((Seed << 8) + Delay), &NBPtr->MemPtr->StdHeader);
+ }
+ NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), Delay);
+ NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane] = Delay;
+ }
+
+ IDS_HDT_CONSOLE_DEBUG_CODE (
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWrDqs: ");
+ for (ByteLane = 0; ByteLane < (MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane]);
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\n");
+ );
+
+ // Disable write leveling ODT pins
+ NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 0);
+
+ // Wait 10 MEMCLKs to allow for ODT signal settling.
+ NBPtr->WaitXMemClks (NBPtr, 10);
+
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs register after Phy assisted training is finish.
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return TRUE - No fatal error occurs.
+ * @return FALSE - Fatal error occurs.
+ */
+
+BOOLEAN
+MemTExitPhyAssistedTrainingClient3 (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ MEM_NB_BLOCK *NBPtr;
+ UINT8 Dct;
+ UINT8 ChipSel;
+ NBPtr = TechPtr->NBPtr;
+
+ NBPtr->FamilySpecificHook[ReEnablePhyComp] (NBPtr, NBPtr);
+ NBPtr->BrdcstSet (NBPtr, BFRxPtrInitReq, 1);
+ NBPtr->PollBitField (NBPtr, BFRxPtrInitReq, 0, PCI_ACCESS_TIMEOUT, TRUE);
+ NBPtr->BrdcstSet (NBPtr, BFDisDllShutdownSR, 1);
+ NBPtr->BrdcstSet (NBPtr, BFEnterSelfRef, 1);
+ NBPtr->PollBitField (NBPtr, BFEnterSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
+ IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClkAlign = 2\n");
+ NBPtr->BrdcstSet (NBPtr, BFDbeGskMemClkAlignMode, 2);
+ NBPtr->BrdcstSet (NBPtr, BFExitSelfRef, 1);
+ NBPtr->PollBitField (NBPtr, BFExitSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
+ if (NBPtr->IsSupported[SetDllShutDown]) {
+ NBPtr->BrdcstSet (NBPtr, BFDisDllShutdownSR, 0);
+ }
+
+ // Calculate Max Latency for both channels to prepare for position training
+ for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
+ IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
+ NBPtr->SwitchDCT (NBPtr, Dct);
+ if (TechPtr->FindMaxDlyForMaxRdLat (TechPtr, &ChipSel)) {
+ NBPtr->SetMaxLatency (NBPtr, TechPtr->MaxDlyForMaxRdLat);
+ }
+ }
+ return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
+}