aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING
diff options
context:
space:
mode:
authorSiyuan Wang <wangsiyuanbuaa@gmail.com>2013-07-25 15:14:15 +0800
committerBruce Griffith <Bruce.Griffith@se-eng.com>2013-08-04 05:40:37 +0200
commitaffe85fbc8a13d35960aa92ae87cbb6330ad253f (patch)
tree9c1ace69f12b06b6544faf041994aa4288fb2e45 /src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING
parentae8d06969bdde9b1250bc3c4ad93f5db408dae98 (diff)
AMD Kabini: Add AGESA/PI code for new processor family
Change-Id: Icb6f64e2e3cfd678fb4fb4f13f0e4b678d5acc4a Signed-off-by: Siyuan Wang <SiYuan.Wang@amd.com> Signed-off-by: Siyuan Wang <wangsiyuanbuaa@gmail.com> Reviewed-by: Nick Dill <nick.dill@se-eng.com> Tested-by: Bruce Griffith <bruce.griffith@se-eng.com> Reviewed-on: http://review.coreboot.org/3836 Tested-by: build bot (Jenkins) Reviewed-by: Martin Roth <martin.roth@se-eng.com>
Diffstat (limited to 'src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING')
-rw-r--r--src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c348
-rw-r--r--src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c115
-rw-r--r--src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c1112
-rw-r--r--src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c424
-rw-r--r--src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c1368
-rw-r--r--src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h266
6 files changed, 3633 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c
new file mode 100644
index 0000000000..b355d6b093
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c
@@ -0,0 +1,348 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mfRdWr2DKb.c
+ *
+ * KB - Specific funtion for 2D Read and write training feature
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/Feat/RdWr2DTraining/Kb)
+ * @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 "amdlib.h"
+#include "AdvancedApi.h"
+#include "GeneralServices.h"
+#include "Ids.h"
+#include "heapManager.h"
+#include "mm.h"
+#include "mn.h"
+#include "mu.h"
+#include "mt.h"
+#include "mport.h"
+#include "merrhdl.h"
+#include "OptionMemory.h"
+#include "mfRdWr2DTraining.h"
+#include "Filecode.h"
+CODE_GROUP (G1_PEICC)
+RDATA_GROUP (G1_PEICC)
+
+#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_KB_MFRDWR2DKB_FILECODE
+/*----------------------------------------------------------------------------
+ * DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * TYPEDEFS AND STRUCTURES
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+BOOLEAN
+MemFRdWr2DTrainingInitKB (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ );
+
+BOOLEAN
+STATIC
+MemFRdWr2DProgramVrefKB (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *VrefPtr
+ );
+
+BOOLEAN
+STATIC
+MemFRdWr2DScaleVrefKB (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *Vref
+ );
+
+BOOLEAN
+STATIC
+MemFRdWr2DProgramIntExtVrefSelectKB (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ );
+
+BOOLEAN
+STATIC
+MemFRdWr2DProgramDataPatternKB (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID* PatternIndexPtr
+ );
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function initializes the 2D Read/Write Training Feature Hooks for KB
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return BOOLEAN
+ * TRUE - Function was implemented
+ *
+ */
+
+BOOLEAN
+MemFRdWr2DTrainingInitKB (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ ASSERT (NBPtr != NULL);
+ NBPtr->FamilySpecificHook[RdWr2DTraining] = MemFAmdRdWr2DTraining;
+ NBPtr->FamilySpecificHook[CheckRdWr2DTrainingPerConfig] = MemFCheckRdWr2DTrainingPerConfig;
+ NBPtr->FamilySpecificHook[RdWr2DSelectIntExtVref] = MemFRdWr2DProgramIntExtVrefSelectKB;
+ NBPtr->FamilySpecificHook[RdWr2DProgramVref] = MemFRdWr2DProgramVrefKB;
+ NBPtr->FamilySpecificHook[RdWr2DScaleVref] = MemFRdWr2DScaleVrefKB;
+ NBPtr->FamilySpecificHook[RdWr2DProgramDelays] = MemFRdWr2DProgramDelays;
+ NBPtr->FamilySpecificHook[RdWr2DDataCollection] = MemFRdWr2DEyeRimSearch;
+ NBPtr->FamilySpecificHook[RdWr2DInitVictim] = MemFRdWr2DInitVictim;
+ NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] = MemFRdWr2DInitVictimChipSel;
+ NBPtr->FamilySpecificHook[RdWr2DStartVictim] = MemFRdWr2DStartVictim;
+ NBPtr->FamilySpecificHook[RdWr2DFinalizeVictim] = MemFRdWr2DFinalizeVictim;
+ NBPtr->FamilySpecificHook[RdWr2DCompareInPhase] = MemFRdWr2DCompareInPhase;
+ NBPtr->FamilySpecificHook[RdWr2DCompare180Phase] = MemFRdWr2DCompare180Phase;
+ NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] = MemFRdWr2DProgramDataPatternKB;
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs Vref for 2D Read/Write Training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *VrefPtr - Pointer to Vref value
+ *
+ * @return BOOLEAN
+ * TRUE - Success
+ * FAIL (External Callout only)
+ *
+ */
+BOOLEAN
+STATIC
+MemFRdWr2DProgramVrefKB (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *VrefPtr
+ )
+{
+ AGESA_STATUS Status;
+ MEM_DATA_STRUCT *MemPtr;
+ ID_INFO CallOutIdInfo;
+ VOLTAGE_ADJUST Va;
+ UINT8 Vref;
+
+ ASSERT (NBPtr != NULL);
+ ASSERT (VrefPtr != NULL);
+ MemPtr = NBPtr->MemPtr;
+ Vref = *(UINT8*)VrefPtr;
+ CallOutIdInfo.IdField.SocketId = NBPtr->MCTPtr->SocketId;
+ CallOutIdInfo.IdField.ModuleId = NBPtr->MCTPtr->DieId;
+ LibAmdMemCopy ((VOID *)&Va, (VOID *)MemPtr, (UINTN)sizeof (Va.StdHeader), &MemPtr->StdHeader);
+ Va.MemData = MemPtr;
+ Status = AGESA_SUCCESS;
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ if (NBPtr->RefPtr->ExternalVrefCtl == FALSE) {
+ //
+ // Internal vref control
+ //
+ ASSERT (Vref < 61);
+ if (Vref < 30) {
+ Vref = (62 - Vref);
+ } else {
+ Vref = (Vref - 30);
+ }
+ NBPtr->SetBitField (NBPtr, BFVrefDAC, Vref << 3);
+ } else {
+ //
+ // External vref control
+ //
+ AGESA_TESTPOINT (TpProcMemBefore2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader));
+ NBPtr->MemPtr->ParameterListPtr->ExternalVrefValue = Vref;
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n2D Read Training External CPU Vref Callout \n");
+ Va.VoltageType = VTYPE_CPU_VREF;
+ Va.AdjustValue = Vref = (Vref - 15) << 1;
+ Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va);
+ AGESA_TESTPOINT (TpProcMemAfter2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader));
+ }
+ } else {
+ //
+ // DIMM Vref Control
+ //
+ Va.VoltageType = VTYPE_DIMM_VREF;
+ //
+ // Offset by 15 and multiply by 2.
+ //
+ Va.AdjustValue = Vref = (Vref - 15) << 1;
+ Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va);
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDimm Vref = %c%d% ", (Va.AdjustValue < 0) ? '-':'+', (Va.AdjustValue < 0) ? (0 - Va.AdjustValue) : Va.AdjustValue );
+ if (Status != AGESA_SUCCESS) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "* Dimm Vref Callout Failed *");
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ }
+ return (Status == AGESA_SUCCESS) ? TRUE : FALSE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function scales Vref from the range used in Data Collection to
+ * the range that is programmed into the register.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *Vref - Pointer to UINT8 Vref Value to scale.
+ *
+ * @return BOOLEAN
+ * TRUE Function was implemented
+ *
+ */
+BOOLEAN
+STATIC
+MemFRdWr2DScaleVrefKB (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *Vref
+ )
+{
+ *(UINT8*)Vref = *(UINT8*)Vref * 2;
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs Vref to internal or external control for 2D Read
+ * or Write Training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *OptParam - Unused
+ *
+ * @return BOOLEAN
+ * TRUE - External Vref was selected
+ * FALSE - Internal Vref was selected
+ *
+ */
+BOOLEAN
+STATIC
+MemFRdWr2DProgramIntExtVrefSelectKB (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ )
+{
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ NBPtr->SetBitField (NBPtr, BFVrefSel, (NBPtr->RefPtr->ExternalVrefCtl ? 0x0002 : 0x0001));
+ }
+ return NBPtr->RefPtr->ExternalVrefCtl;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs the Data Pattern that will be sent and compared
+ * against.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *PatternIndexPtr - Pointer to a generic index used to
+ * determine which pattern to program.
+ *
+ * @return BOOLEAN
+ * TRUE
+ *
+ */
+BOOLEAN
+STATIC
+MemFRdWr2DProgramDataPatternKB (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID* PatternIndexPtr
+ )
+{
+ UINT8 SeedCount;
+ UINT32 PrbsSeed;
+ CONST STATIC UINT32 CmdStreamLenTbl[4] = {13, 61, 127, 251};
+
+ ASSERT (NBPtr != 0);
+ ASSERT (PatternIndexPtr != NULL);
+ SeedCount = *(UINT8*)PatternIndexPtr;
+ ASSERT (SeedCount <= (NBPtr->MaxSeedCount - 1));
+ MemNSetBitFieldNb (NBPtr, BFCmdStreamLen, CmdStreamLenTbl[SeedCount]);
+ PrbsSeed = 0x7EA05;
+ switch (SeedCount) {
+ case 0:
+ MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x01);
+ PrbsSeed = 0x7FFFF;
+ break;
+ case 1:
+ MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x04);
+ MemNSetBitFieldNb (NBPtr, BFXorPatOvr, 0xFF);
+ PrbsSeed = 0x7EA05;
+ break;
+ case 2:
+ MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x03);
+ MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern0, 0x55555549);
+ MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern1, 0x55555555);
+ MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern2, 0x55555555);
+ MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern3, 0x55555555);
+ break;
+ case 3:
+ MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x03);
+ MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern0, 0xA5A5A55A);
+ MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern1, 0xA5A5A5A5);
+ MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern2, 0xA5A5A5A5);
+ MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern3, 0xA5A5A5A5);
+ break;
+ default:
+ ASSERT (FALSE);
+ }
+ ASSERT (PrbsSeed != 0);
+ //
+ // Program the PRBS Seed
+ //
+ NBPtr->SetBitField (NBPtr, BFDataPrbsSeed, PrbsSeed);
+ return TRUE;
+}
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c
new file mode 100644
index 0000000000..62bfb82366
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c
@@ -0,0 +1,115 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mfRdDqs2DTraining.c
+ *
+ * RD DQS 2 dimensional training
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/Feat/RdWr2DTraining)
+ * @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 "mport.h"
+#include "mfRdWr2DTraining.h"
+#include "Filecode.h"
+CODE_GROUP (G1_PEICC)
+RDATA_GROUP (G1_PEICC)
+
+#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDDQS2DTRAINING_FILECODE
+/*----------------------------------------------------------------------------
+ * DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+ /*----------------------------------------------------------------------------
+ * TYPEDEFS AND STRUCTURES
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * EXPORTED FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function executes 2D training for Read DQS
+ *
+ * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ * FALSE - Errors occurred
+ *
+ */
+
+BOOLEAN
+MemFAmdRdDqs2DTraining (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ );
+
+BOOLEAN
+MemFAmdRdDqs2DTraining (
+ IN OUT MEM_TECH_BLOCK *TechPtr
+ )
+{
+ IDS_HDT_CONSOLE (MEM_STATUS, "\n\nStart Read DQS 2D training.\n\n");
+ TechPtr->Direction = DQS_READ_DIR;
+ return TechPtr->NBPtr->FamilySpecificHook[RdWr2DTraining] (TechPtr->NBPtr, NULL);
+}
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c
new file mode 100644
index 0000000000..090a9379a9
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c
@@ -0,0 +1,1112 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mfRdWr2DEyeRimSearch.c
+ *
+ * Eye Rim Sampling Algorithm for use in 2D Read DQS or 2D Write Data Training
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/Feat/RdWr2DTraining)
+ * @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 "amdlib.h"
+#include "AdvancedApi.h"
+#include "GeneralServices.h"
+#include "Ids.h"
+#include "heapManager.h"
+#include "mm.h"
+#include "mn.h"
+#include "mu.h"
+#include "mt.h"
+#include "mport.h"
+#include "merrhdl.h"
+#include "OptionMemory.h"
+#include "mfRdWr2DTraining.h"
+#include "Filecode.h"
+CODE_GROUP (G1_PEICC)
+RDATA_GROUP (G1_PEICC)
+
+#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDWR2DEYERIMSEARCH_FILECODE
+/*----------------------------------------------------------------------------
+ * DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+
+/*----------------------------------------------------------------------------
+ * TYPEDEFS AND STRUCTURES
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+BOOLEAN
+STATIC
+MemFInitializeEyeRimSearch (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ );
+
+VOID
+STATIC
+MemTEyeFill (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ );
+
+UINT8
+STATIC
+DetermineSavedState (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x
+ );
+
+UINT8
+STATIC
+GetPassFailValue (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x
+ );
+
+VOID
+STATIC
+SetPassFailValue (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x,
+ IN UINT8 result
+ );
+
+VOID
+STATIC
+SetSavedState (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x,
+ IN UINT8 result
+ );
+
+INT8
+STATIC
+Get1DTrainedEyeCenter (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane
+ );
+
+BOOLEAN
+STATIC
+CheckForFail (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x
+ );
+
+
+INT8
+STATIC
+xlateY (
+ IN INT8 y
+ );
+
+BOOLEAN
+STATIC
+ClearSampledPassResults (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ );
+
+UINT32
+STATIC
+CompareInPhase (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ );
+
+UINT32
+STATIC
+Compare180Phase (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ );
+
+VOID
+STATIC
+ProgramVref (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 Vref
+ );
+
+VOID
+STATIC
+StartAggressors (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN BOOLEAN TurnOn
+ );
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Initialize Eye Rim Search
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return BOOLEAN
+ * TRUE
+ */
+BOOLEAN
+STATIC
+MemFInitializeEyeRimSearch (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ UINT8 lane;
+ UINT8 vref;
+ MEM_RD_WR_2D_ENTRY *Data;
+ MEM_RD_WR_2D_RIM_ENTRY *RimData;
+
+ ASSERT (NBPtr != NULL);
+ TechPtr = NBPtr->TechPtr;
+ Data = TechPtr->RdWr2DData;
+ RimData = Data->SavedData;
+ //
+ // Set Boundaries
+ //
+ RimData->xMax = Data->MaxRdWrSweep - 1;
+ RimData->yMax = (NBPtr->TotalMaxVrefRange / 2) - 1;
+ RimData->xMin = RimData->xMax * -1;
+ RimData->yMin = RimData->yMax * -1;
+
+ RimData->ParallelSampling = EYERIM_PARALLEL_SAMPLING;
+ RimData->BroadcastDelays = EYERIM_BROADCAST_DELAYS;
+ RimData->Dirs[0] = 1;
+ RimData->Dirs[1] = -1;
+
+ RimData->SampleCount = 0;
+ RimData->VrefUpdates = 0;
+ RimData->RdWrDlyUpdates = 0;
+ for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane ++ ) {
+ for ( vref = 0; vref < NBPtr->TotalMaxVrefRange; vref ++ ) {
+ // 00b = state not saved
+ // 01b = state saved
+ RimData->LaneSaved[lane].Vref[vref].PosRdWrDly = 0;
+ RimData->LaneSaved[lane].Vref[vref].NegRdWrDly = 0;
+ // 00b = Fail
+ // 01b = Pass
+ Data->Lane[lane].Vref[vref].PosRdWrDly = 0;
+ Data->Lane[lane].Vref[vref].NegRdWrDly = 0;
+ }
+ }
+ if (TechPtr->Direction == DQS_READ_DIR) {
+ NBPtr->MaxSeedCount = MAX_2D_RD_SEED_COUNT;
+ } else {
+ //
+ // Set the seed count in terms of the Total desired bit times
+ //
+ NBPtr->MaxSeedCount = (UINT8) (NBPtr->TotalBitTimes2DWrTraining / (256 * 8 * MIN ( 1, NBPtr->MaxAggressorDimms[NBPtr->Dct])));
+ };
+ TechPtr->DqsRdWrPosSaved = 0;
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\tTotal Bit Times: %d Max Seed Count: %d\n",(TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalBitTimes2DRdTraining:NBPtr->TotalBitTimes2DWrTraining,NBPtr->MaxSeedCount);
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ * This function collects data for Eye Rim Search
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *OptParam - Not Used
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ * FALSE - Errors occurred
+ *
+ */
+BOOLEAN
+MemFRdWr2DEyeRimSearch (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID * OptParam
+ )
+{
+ UINT8 lane;
+ UINT8 j;
+ UINT8 k;
+ INT8 ydir;
+ INT8 xdir;
+ INT8 xi;
+ INT8 yi;
+ INT8 x;
+ INT8 y;
+ INT8 xmax;
+ INT8 ymax;
+ INT8 xmin;
+ INT8 ymin;
+ INT8 xo;
+ INT8 xt;
+ INT8 yo;
+ INT8 yt;
+ UINT8 slane;
+ UINT8 result;
+ INT8 states[2];
+ UINT8 InitialCS;
+ UINT8 ChipSel;
+ UINT8 Aggr;
+ UINT8 SeedCount;
+ UINT32 InPhaseResult;
+ UINT32 PhaseResult180;
+ UINT32 Result;
+ MEM_RD_WR_2D_ENTRY *Data;
+ MEM_RD_WR_2D_RIM_ENTRY RimData;
+ MEM_TECH_BLOCK *TechPtr;
+ RD_WR_2D *VrefPtr;
+ ALLOCATE_HEAP_PARAMS AllocHeapParams;
+
+ ASSERT (NBPtr != NULL);
+ TechPtr = NBPtr->TechPtr;
+ Data = TechPtr->RdWr2DData;
+ //
+ // Allocate Storage for Rim Search
+ //
+ AllocHeapParams.RequestedBufferSize = MemFRdWr2DGetMaxLanes (NBPtr) * NBPtr->TotalMaxVrefRange * sizeof (RD_WR_2D);
+ AllocHeapParams.BufferHandle = AMD_MEM_2D_RD_WR_RIM_HANDLE;
+ AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
+ if (HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader) == AGESA_SUCCESS) {
+ VrefPtr = (RD_WR_2D *) AllocHeapParams.BufferPtr;
+ } else {
+ SetMemError (AGESA_FATAL, NBPtr->MCTPtr);
+ PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_2D, 0, 0, 0, 0, &NBPtr->MemPtr->StdHeader);
+ return FALSE;
+ }
+ for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane++) {
+ RimData.LaneSaved[lane].Vref = &VrefPtr[lane * NBPtr->TotalMaxVrefRange];
+ }
+ Data->SavedData = &RimData;
+
+ MemFInitializeEyeRimSearch (NBPtr);
+ NBPtr->FamilySpecificHook[RdWr2DSelectIntExtVref] (NBPtr, NULL);
+ InitialCS = TechPtr->ChipSel;
+ NBPtr->FamilySpecificHook[Adjust2DPhaseMaskBasedOnEcc] (NBPtr, &NBPtr);
+ NBPtr->FamilySpecificHook[RdWr2DInitVictim] (NBPtr, NULL);
+ //
+ // EnableDisable continuous writes on the agressor channels
+ //
+
+ for (Aggr = 0; Aggr < MAX (1 , NBPtr->MaxAggressorDimms[NBPtr->Dct]) ; Aggr += (NBPtr->IsSupported[PerDimmAggressors2D] ? 2 : NBPtr->CsPerDelay) ) {
+ ClearSampledPassResults (NBPtr);
+ //
+ // Enable continuous writes on the aggressors
+ //
+ StartAggressors (NBPtr, TRUE);
+ for (ChipSel = InitialCS; ChipSel < (InitialCS + NBPtr->CsPerDelay); ChipSel++) {
+ xmax = RimData.xMax;
+ xmin = RimData.xMin;
+ ymax = RimData.yMax;
+ ymin = RimData.yMin;
+ if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) {
+ TechPtr->ChipSel = ChipSel;
+ Data->RdWrDly = Data->MaxRdWrSweep;
+ for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane ++ ) {
+ //
+ // Two loops to handle each quadrant from the trained point
+ //
+ for ( j = 0; j < 2; j++) {
+ ydir = RimData.Dirs[j];
+ for ( k = 0; k < 2; k++) {
+ xdir = RimData.Dirs[k];
+ //
+ // Sample Loops - stay w/n the defined quadrant
+ // assume xmax = -1 * xmin, ymax = -1 * ymin
+ // initial point must always pass
+ // end point - boundary or two consecutive fails along the y-axis
+ //
+ // Initial dx, dy step state
+ // Starting dy at 8 to reduce samples necessary searching for the eye height
+ states[0] = 0;
+ states[1] = 8;
+ // xi and yi are inital trained points, assumed to be inside the eye
+ // These set the coordinate syst em for the quadrants
+ xi = Get1DTrainedEyeCenter (NBPtr, lane);
+ yi = 0; // Initial center is always at Vref nominal
+ x = xi;
+ y = yi;
+ while (
+ ((xdir > 0 && x >= xi && x <= xmax) ||
+ (xdir < 0 && x <= xi && x >= xmin)) &&
+ ((ydir > 0 && y >= yi && y <= ymax) ||
+ (ydir < 0 && y <= yi && y >= ymin)) &&
+ !(y == yi && (xdir * (xi - x) >= 2))
+ ) {
+ //
+ // Decide if result is already sampled, or need to take the sample
+ //
+ if (0 == DetermineSavedState (NBPtr, lane, y, x)) {
+ RimData.SampleCount++;
+ //
+ // Result for this point is not in the cache, sample it
+ //
+ for (slane = 0; slane < MemFRdWr2DGetMaxLanes (NBPtr); slane ++ ) {
+ if (!RimData.ParallelSampling && ( slane != lane)) {
+ continue;
+ }
+ //
+ // Calculate the relative offset from the initial trained position for this lane
+ //
+ xo = Get1DTrainedEyeCenter (NBPtr, slane);
+ yo = 0;
+ xt = xo + (x - xi);
+ yt = yo + (y - yi);
+ if (xt > xmax || xt < xmin || yt > ymax || yt < ymin) {
+ continue;
+ }
+ //
+ // Update the vref and lane delays (yt, xt)
+ //
+ if (slane == lane) {
+ //
+ // For processors w/ a single Vref per channel, only set Vref once
+ //
+ if ( NBPtr->Vref != xlateY (yt)) {
+ //
+ // If not already set
+ //
+ RimData.VrefUpdates++;
+ NBPtr->Vref = xlateY (yt);
+ ProgramVref (NBPtr, NBPtr->Vref);
+ }
+ }
+ if (RimData.BroadcastDelays) {
+ //
+ // When BroadcastDelays, only set RdDqs once
+ //
+ if (slane == lane) {
+ //
+ // If current lane
+ //
+ if ( Data->RdWrDly != (UINT8) (xt & RimData.xMax)) {
+ //
+ // If the not already set
+ // Account for rollover.
+ //
+ RimData.RdWrDlyUpdates++;
+ Data->RdWrDly = (UINT8) (xt & RimData.xMax);
+ NBPtr->FamilySpecificHook[RdWr2DProgramDelays] (NBPtr, &Data->RdWrDly);
+ }
+ }
+ } else {
+ //
+ // For Reads, Program all RdDqs Delay Values the same
+ // For Writes, Program all WrDat Values with same value offset by WrDqs for that lane.
+ //
+ if ( Data->RdWrDly != (UINT8) (xt & RimData.xMax)) {
+ //
+ // If the not already set
+ // Account for rollover.
+ //
+ RimData.RdWrDlyUpdates++;
+ Data->RdWrDly = (UINT8) (xt & RimData.xMax);
+ MemTSetDQSDelayAllCSR (TechPtr, (UINT8) (xt & RimData.xMax));
+ }
+ }
+ }
+ //
+ // Perform Memory RW test
+ //
+ InPhaseResult = 0;
+ PhaseResult180 = 0;
+ NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] (NBPtr, NULL);
+ for (SeedCount = 0; SeedCount < NBPtr->MaxSeedCount; SeedCount++) {
+ //
+ // Begin continuous reads and writes on the victim channels
+ //
+ NBPtr->FamilySpecificHook[RdWr2DStartVictim] (NBPtr, &SeedCount);
+ //
+ // Occasionally check if all trained lanes have already failed
+ //
+ if ((NBPtr->MaxSeedCount < 4) || ((SeedCount % (NBPtr->MaxSeedCount / 4)) == 0)) {
+ InPhaseResult |= CompareInPhase (NBPtr);
+ PhaseResult180 |= Compare180Phase (NBPtr);
+ if (((InPhaseResult & NBPtr->PhaseLaneMask) == NBPtr->PhaseLaneMask) &&
+ ((PhaseResult180& NBPtr->PhaseLaneMask) == NBPtr->PhaseLaneMask)) {
+ break;
+ }
+ }
+ }
+ //
+ // Obtain the results
+ //
+ for (slane = 0; slane < MemFRdWr2DGetMaxLanes (NBPtr); slane ++ ) {
+ if (!RimData.ParallelSampling && (slane != lane)) {
+ continue;
+ }
+ //
+ // Calculate the relative offset from legacy trained
+ //
+ xo = Get1DTrainedEyeCenter (NBPtr, RimData.BroadcastDelays?lane:slane);
+ yo = 0;
+ xt = xo + (x - xi);
+ yt = yo + (y - yi);
+ if (xt > xmax || xt < xmin || yt > ymax || yt < ymin) {
+ continue;
+ }
+ //
+ // In this example, data{}{}{} = 1 is a Fail, 0 is a Pass
+ // In-Phase Results
+ //
+ if (CheckForFail (NBPtr, slane, yt, xt)) {
+ //
+ // Don't overwrite a fail
+ //
+ if (xt >= 0) {
+ if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) {
+ // x8, so combine "Nibble X" and "Nibble X+1" results
+ Result = (InPhaseResult >> (slane * 2)) & 0x03;
+ } else {
+ // x4, so use "Nibble" results
+ Result = (InPhaseResult >> slane) & 0x01;
+ }
+ SetPassFailValue (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F));
+ SetSavedState (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F));
+ } else {
+ if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) {
+ // x8, so combine "Nibble X" and "Nibble X+1" results
+ Result = (PhaseResult180 >> (slane * 2)) & 0x03;
+ } else {
+ // x4, so use "Nibble" results
+ Result = (PhaseResult180 >> slane) & 0x01;
+ }
+ SetPassFailValue (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F));
+ SetSavedState (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F));
+ }
+ }
+ }
+ }
+ // Decide the next sample point based on the result of the current lane
+ result = GetPassFailValue (NBPtr, lane, y, x);
+ InPhaseResult = 0;
+ PhaseResult180 = 0;
+ // States && comments are relative to the ++ Quadrant
+ if (result == 3) {
+ // Current Pass
+ if (states[0] > 0 || states[1] > 0) {
+ if (states[1] > 1 && y * ydir <= ymax - states[1]) {
+ // Current Pass, Continue searching up by leaps
+ states[0] = 0;
+ } else if (states[1] > 1 && y * ydir <= ymax - 4) {
+ // Current Pass, Continue searching up by leaps
+ states[0] = 0;
+ states[1] = 4;
+ } else if (states[1] > 1 && y * ydir <= ymax - 2) {
+ // Current Pass, Continue searching up by leaps
+ states[0] = 0;
+ states[1] = 2;
+ } else if (y * ydir <= ymax - 1) {
+ // Current Pass, Continue searching up by one
+ states[0] = 0;
+ states[1] = 1;
+ } else if (y * ydir == ymax) {
+ // Current Pass and at the top edge, Move right
+ states[0] = 1;
+ states[1] = 0;
+ } else {
+ ASSERT (FALSE);
+ }
+ } else {
+ // Move right one
+ states[0] = 1;
+ states[1] = 0;
+ }
+ } else if (result == 2) {
+ // Current Fail
+ if (states[0] > 0) {
+ // Search down and left
+ states[0] = -1;
+ states[1] = -1;
+ } else if (states[1] > 1 || states[1] < 0) {
+ // Search down
+ states[0] = 0;
+ states[1] = -1;
+ } else if (states[1] == 1) {
+ // Move down and right
+ states[0] = 1;
+ states[1] = -1;
+ } else {
+ ASSERT (FALSE);
+ }
+ } else {
+ ASSERT (FALSE);
+ }
+ // Update the coordinates based on the new state and quadrant direction
+ x += xdir * states[0];
+ y += ydir * states[1];
+ }
+ }
+ }
+ }
+ }
+ }
+ //
+ // Disable continuous writes on the agressor channels
+ //
+ StartAggressors (NBPtr, FALSE);
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW,"\n\tSampleCount:%d",RimData.SampleCount);
+ IDS_HDT_CONSOLE (MEM_FLOW,"\t\tVref Updates:%d",RimData.VrefUpdates);
+ IDS_HDT_CONSOLE (MEM_FLOW,"\t\tRdDqs Dly Updates:%d\n",RimData.RdWrDlyUpdates);
+ //
+ // Finilazing Victim Continuous writes
+ //
+ NBPtr->FamilySpecificHook[RdWr2DFinalizeVictim] (NBPtr, NULL);
+ TechPtr->ChipSel = InitialCS;
+ //
+ // Fill eye based on Rim Search results
+ //
+ MemTEyeFill (NBPtr);
+ //
+ // Display the results the completed eye
+ //
+ MemFRdWr2DDisplaySearch (NBPtr, Data);
+ //
+ // Restore environment settings after training
+ //
+ if (HeapDeallocateBuffer (AMD_MEM_2D_RD_WR_RIM_HANDLE, &NBPtr->MemPtr->StdHeader) != AGESA_SUCCESS) {
+ SetMemError (AGESA_FATAL, NBPtr->MCTPtr);
+ PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_DEALLOCATE_FOR_2D, 0, 0, 0, 0, &NBPtr->MemPtr->StdHeader);
+ }
+ return TRUE;
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ * Fill the data eye
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ */
+VOID
+STATIC
+MemTEyeFill (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ INT8 x;
+ INT8 y;
+ UINT8 lane;
+ UINT8 result;
+ INT8 yLastPass;
+ UINT8 xMax;
+ UINT8 yMax;
+ UINT8 xMin;
+ UINT8 yMin;
+ BOOLEAN FirstPassFound;
+ MEM_RD_WR_2D_RIM_ENTRY *SavedData;
+
+ ASSERT (NBPtr != NULL);
+ SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData;
+
+ xMax = SavedData->xMax;
+ yMax = SavedData->yMax;
+ xMin = SavedData->xMin;
+ yMin = SavedData->yMin;
+ for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane++) {
+ for (x = xMin ; x <= xMax ; x++) {
+ FirstPassFound = FALSE;
+ yLastPass = yMin;
+ //
+ // Scan for the last passing value
+ //
+ for (y = yMax ; y >= yLastPass ; y--) {
+ result = GetPassFailValue (NBPtr, lane, y, x);
+ if (result == 0) {
+ //
+ // Not Saved, Mark it as FAIL. (Should already be cleared)
+ //
+ } else if (result == 2) {
+ //
+ // FAIL, so Mark as FAIL (Do nothing)
+ //
+ } else if (result == 3) {
+ //
+ // PASS, Mark it and save y value (This will end the loop)
+ //
+ SetPassFailValue (NBPtr, lane, y, x, result);
+ yLastPass = y;
+ } else {
+ ASSERT (FALSE);
+ }
+ }
+ //
+ // Scan for the first pass, the fill until the last pass
+ //
+ for (y = yMin ; y < yLastPass ; y++) {
+ result = GetPassFailValue (NBPtr, lane, y, x);
+ if (result == 0) {
+ //
+ // Not Saved, if we've already found a first Passing value, mark it as a PASS
+ // otherwise, mark it as FAIL. (Should already be cleared)
+ //
+ if (FirstPassFound == TRUE) {
+ SetPassFailValue (NBPtr, lane, y, x, result);
+ }
+ } else if (result == 2) {
+ //
+ // FAIL, so Mark as FAIL (Do nothing)
+ //
+ } else if (result == 3) {
+ //
+ // PASS, Mark it and set FirstPassFound
+ //
+ SetPassFailValue (NBPtr, lane, y, x, result);
+ FirstPassFound = TRUE;
+ } else {
+ ASSERT (FALSE);
+ }
+ } // y Loop
+ } //x Loop
+ } // Lane Loop
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Get the 1D trained center
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] lane - UINT8 of current lane
+ *
+ * @return INT8 - Eye Center
+ */
+INT8
+STATIC
+Get1DTrainedEyeCenter (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ ASSERT (NBPtr != NULL);
+ TechPtr = NBPtr->TechPtr;
+ if (TechPtr->Direction == DQS_READ_DIR) {
+ if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) {
+ // Program Byte based for x8 and x16
+ return (INT8)NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + lane];
+ } else {
+ return (INT8)NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + (lane >> 1)];
+ }
+ } else {
+ return (INT8) (NBPtr->ChannelPtr->WrDatDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + lane] -
+ NBPtr->ChannelPtr->WrDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + lane]);
+ }
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Determine if a Byte Lane result has been saved
+ *
+ * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK
+ * @param[in] lane - Current lane
+ * @param[in] y - Vref value
+ * @param[in] x - Delay
+ *
+ * @return UINT8
+ * 1 - value saved
+ * 0 - value not saved
+ */
+UINT8
+STATIC
+DetermineSavedState (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x
+ )
+{
+ MEM_RD_WR_2D_RIM_ENTRY *SavedData;
+
+ ASSERT (NBPtr != NULL);
+ SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData;
+
+ if (x >= 0) {
+ return (UINT8) (SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1);
+ } else {
+ return (UINT8) (SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1);
+ }
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Determine if a failure has occured
+ *
+ * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK
+ * @param[in] lane - Current lane
+ * @param[in] y - Vref value
+ * @param[in] x - Delay Value
+ *
+ * @return BOOLEAN
+ * FALSE - Fail
+ * TRUE - Pass
+ */
+BOOLEAN
+STATIC
+CheckForFail (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x
+ )
+{
+ MEM_RD_WR_2D_RIM_ENTRY *SavedData;
+
+ ASSERT (NBPtr != NULL);
+ SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData;
+
+ if (x >= 0) {
+ if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) {
+ // value not saved, so it is not fail
+ return TRUE;
+ } else {
+ // value saved, so examine result
+ if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) {
+ // result = fail
+ return FALSE;
+ } else {
+ // result = pass
+ return TRUE;
+ }
+ }
+ } else {
+ if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) {
+ // value not saved, so it is not fail
+ return TRUE;
+ } else {
+ // value saved, so examine result
+ if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) {
+ // result = fail
+ return FALSE;
+ } else {
+ // result = pass
+ return TRUE;
+ }
+ }
+ }
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Get pass fail state of lane
+ *
+ * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK
+ * @param[in] lane - Current lane
+ * @param[in] y - Vref value
+ * @param[in] x - Delay Value
+ *
+ * @return UINT8
+ * 0 - Value not saved,
+ * 2 - Fail,
+ * 3 - Pass
+ */
+UINT8
+STATIC
+GetPassFailValue (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ MEM_RD_WR_2D_ENTRY* RdWr2DData;
+ MEM_RD_WR_2D_RIM_ENTRY *SavedData;
+
+ ASSERT (NBPtr != NULL);
+ TechPtr = NBPtr->TechPtr;
+ RdWr2DData = TechPtr->RdWr2DData;
+ SavedData = RdWr2DData->SavedData;
+
+ if (x >= 0) {
+ if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) {
+ // value not saved
+ return 0;
+ } else {
+ // value saved, so return pass/fail
+ return ((RdWr2DData->Lane[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) ? 2 : 3;
+ }
+ } else {
+ if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) {
+ // value not saved
+ return 0;
+ } else {
+ // value saved, so return pass/fail
+ return ((RdWr2DData->Lane[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) ? 2 : 3;
+ }
+ }
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Set the Pass/Fail state of lane
+ *
+ * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK
+ * @param[in] lane - Current lane
+ * @param[in] y - Vref value
+ * @param[in] x - Delay Value
+ * @param[in] result - result value
+ *
+ */
+VOID
+STATIC
+SetPassFailValue (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x,
+ IN UINT8 result
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ MEM_RD_WR_2D_ENTRY* RdWr2DData;
+ MEM_RD_WR_2D_RIM_ENTRY *SavedData;
+
+ ASSERT (NBPtr != NULL);
+ TechPtr = NBPtr->TechPtr;
+ RdWr2DData = TechPtr->RdWr2DData;
+ SavedData = RdWr2DData->SavedData;
+
+ if (x >= 0) {
+ RdWr2DData->Lane[lane].Vref[xlateY (y)].PosRdWrDly |= (result == 0) ? (1 << (SavedData->xMax - (x & SavedData->xMax))) : 0;
+ } else {
+ RdWr2DData->Lane[lane].Vref[xlateY (y)].NegRdWrDly |= (result == 0) ? (1 << (SavedData->xMax - (x & SavedData->xMax))) : 0;
+ }
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Set the save state of lane
+ *
+ * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK
+ * @param[in] lane - Current lane
+ * @param[in] y - Vref value
+ * @param[in] x - Delay Value
+ * @param[in] result - result value
+ *
+ */
+VOID
+STATIC
+SetSavedState (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 lane,
+ IN INT8 y,
+ IN INT8 x,
+ IN UINT8 result
+ )
+{
+ MEM_RD_WR_2D_RIM_ENTRY *SavedData;
+
+ ASSERT (NBPtr != NULL);
+ SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData;
+
+ if (x >= 0) {
+ SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly |= (1 << (SavedData->xMax - (x & SavedData->xMax)));
+ } else {
+ SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly |= (1 << (SavedData->xMax - (x & SavedData->xMax)));
+ }
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Translate Vref into a positive, linear value that can be used as an
+ * array index.
+ *
+ * @param[in] y - INT8 of the (signed) Vref value
+ *
+ * @return UINT8 - Translated Value
+ */
+INT8
+STATIC
+xlateY (
+ IN INT8 y
+ )
+{
+ ASSERT ( y > -0x10);
+ ASSERT ( y < 0x10);
+ return (y + 0xF) & 0x1F;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Re-walk the eye rim for each aggressor combination, which invalidates
+ * previous Passes in the sample array. Previous Fails in the sample array
+ * remain valid. Knowledge of previous fails and speeds sampling for the
+ * subsequent walks, esp. when used in conjunction w/ ParallelSampling.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return BOOLEAN
+ * TRUE
+ */
+BOOLEAN
+STATIC
+ClearSampledPassResults (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ UINT8 lane;
+ UINT8 vref;
+ MEM_RD_WR_2D_ENTRY *Data;
+ MEM_RD_WR_2D_RIM_ENTRY *RimData;
+
+ ASSERT (NBPtr != NULL);
+
+ Data = (MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData;
+ RimData = Data->SavedData;
+
+ for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane ++ ) {
+ for ( vref = 0; vref < NBPtr->TotalMaxVrefRange; vref ++ ) {
+ RimData->LaneSaved[lane].Vref[vref].PosRdWrDly &= ~(Data->Lane[lane].Vref[vref].PosRdWrDly);
+ Data->Lane[lane].Vref[vref].PosRdWrDly = 0;
+ RimData->LaneSaved[lane].Vref[vref].NegRdWrDly &= ~(Data->Lane[lane].Vref[vref].NegRdWrDly);
+ Data->Lane[lane].Vref[vref].NegRdWrDly = 0;
+ }
+ }
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Perform in-phase comparison
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return UINT32 - Bitmap of results of comparison
+ * 1 = FAIL
+ * 0 = PASS
+ */
+
+UINT32
+STATIC
+CompareInPhase (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ UINT32 Result;
+ Result = 0;
+ NBPtr->FamilySpecificHook[RdWr2DCompareInPhase] (NBPtr, &Result);
+ return Result;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Perform 180 Degree out-of-phase comparison
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return UINT32 - Bitmap of results of comparison
+ * 1 = FAIL
+ * 0 = PASS
+ */
+
+UINT32
+STATIC
+Compare180Phase (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ UINT32 Result;
+ Result = 0;
+ NBPtr->FamilySpecificHook[RdWr2DCompare180Phase] (NBPtr, &Result);
+ return Result;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Program Vref after scaling to accomodate the register definition
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] Vref - UINT8 value of Vref relative to the sample point
+ *
+ */
+
+VOID
+STATIC
+ProgramVref (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 Vref
+ )
+{
+ UINT8 ScaledVref;
+ ScaledVref = Vref;
+ NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &ScaledVref);
+ NBPtr->FamilySpecificHook[RdWr2DProgramVref] (NBPtr, &ScaledVref);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * Turn Aggressor Channels On or Off
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] TurnOn - BOOLEAN
+ * TRUE - Turn On, False = Turn Off.
+ */
+
+VOID
+STATIC
+StartAggressors (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN BOOLEAN TurnOn
+ )
+{
+ NBPtr->FamilySpecificHook[TurnOnAggressorChannels] (NBPtr, &TurnOn);
+}
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c
new file mode 100644
index 0000000000..b8d8429822
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c
@@ -0,0 +1,424 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mfRdWr2DPatternGeneration.c
+ *
+ * Common Northbridge features
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/Feat/RdWr2DTraining)
+ * @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 "AdvancedApi.h"
+#include "amdlib.h"
+#include "Ids.h"
+#include "mm.h"
+#include "mn.h"
+#include "mu.h"
+#include "mport.h"
+#include "PlatformMemoryConfiguration.h"
+#include "merrhdl.h"
+#include "OptionMemory.h"
+#include "mfRdWr2DTraining.h"
+#include "Filecode.h"
+CODE_GROUP (G1_PEICC)
+RDATA_GROUP (G1_PEICC)
+
+#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDWR2DPATTERNGENERATION_FILECODE
+/*----------------------------------------------------------------------------
+ * DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+
+/*----------------------------------------------------------------------------
+ * TYPEDEFS AND STRUCTURES
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+ /* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function Initializes the Victim for 2D RdDqs Training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *OptParam - Unused
+ *
+ * @return BOOLEAN
+ * TRUE
+ */
+BOOLEAN
+MemFRdWr2DInitVictim (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ )
+{
+ RRW_SETTINGS *Rrw;
+ UINT8 InitialCS;
+ UINT8 ChipSel;
+ UINT8 SeedCount;
+ BOOLEAN OptPatWr2D;
+
+ OptPatWr2D = (NBPtr->IsSupported[OptimizedPatternWrite2D]) && (NBPtr->TechPtr->Direction == DQS_READ_DIR);
+ InitialCS = NBPtr->TechPtr->ChipSel;
+ Rrw = &NBPtr->RrwSettings;
+ // Program the Bubble Count and CmdStreamLen
+ NBPtr->SetBitField (NBPtr, BFBubbleCnt, 32);
+ NBPtr->SetBitField (NBPtr, BFBubbleCnt2, 0);
+ NBPtr->SetBitField (NBPtr, BFCmdStreamLen, 63);
+ // Set Comparison Masks
+ NBPtr->SetBitField (NBPtr, BFDramDqMaskLow, Rrw->CompareMaskLow);
+ NBPtr->SetBitField (NBPtr, BFDramDqMaskHigh, Rrw->CompareMaskHigh);
+ // If All Dimms are ECC Capable Test ECC. Otherwise, mask it off
+ NBPtr->SetBitField (NBPtr, BFDramEccMask, (NBPtr->MCTPtr->Status[SbEccDimms] == TRUE) ? Rrw->CompareMaskEcc : 0xFF);
+ // Program the Starting Address
+ NBPtr->SetBitField (NBPtr, BFTgtBankA, Rrw->TgtBankAddressA);
+ NBPtr->SetBitField (NBPtr, BFTgtAddressA, Rrw->TgtColAddressA);
+ NBPtr->SetBitField (NBPtr, BFTgtBankB, Rrw->TgtBankAddressB);
+ NBPtr->SetBitField (NBPtr, BFTgtAddressB, Rrw->TgtColAddressB);
+ NBPtr->SetBitField (NBPtr, BFCmdTgt, CMD_TGT_AB);
+ // Wait for RRW Engine to be ready and turn it on
+ NBPtr->PollBitField (NBPtr, BFCmdSendInProg, 0, PCI_ACCESS_TIMEOUT, FALSE);
+ NBPtr->SetBitField (NBPtr, BFCmdTestEnable, 1);
+ for (ChipSel = InitialCS; ChipSel < (InitialCS + NBPtr->CsPerDelay); ChipSel++) {
+ // Ensure that Odd and Even CS are trained
+ if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) == 0) {
+ continue;
+ }
+ NBPtr->TechPtr->ChipSel = ChipSel;
+ for (SeedCount = 0; SeedCount < (OptPatWr2D ? NBPtr->MaxSeedCount : 1 ); SeedCount++) {
+ if (OptPatWr2D) {
+ //
+ // Set BankAddress according to seed
+ //
+ ASSERT (NBPtr->MaxSeedCount <= 4);
+ Rrw->TgtBankAddressA = (SeedCount * 2) + CPG_BANK_ADDRESS_A;
+ Rrw->TgtBankAddressB = (SeedCount * 2) + CPG_BANK_ADDRESS_B;
+ }
+ //
+ // Send ACTIVATE to all Banks
+ //
+ // Set Chip select
+ MemNSetBitFieldNb (NBPtr, BFCmdChipSelect, (1 << NBPtr->TechPtr->ChipSel));
+ // Set Bank Address
+ MemNSetBitFieldNb (NBPtr, BFCmdBank, Rrw->TgtBankAddressA);
+ // Set Row Address
+ MemNSetBitFieldNb (NBPtr, BFCmdAddress, Rrw->TgtRowAddressA);
+ // Send the command
+ MemNSetBitFieldNb (NBPtr, BFSendActCmd, 1);
+ // Wait for command complete
+ MemNPollBitFieldNb (NBPtr, BFSendActCmd, 0, PCI_ACCESS_TIMEOUT, FALSE);
+ // Set Chip select
+ MemNSetBitFieldNb (NBPtr, BFCmdChipSelect, (1 << NBPtr->TechPtr->ChipSel));
+ // Set Bank Address
+ MemNSetBitFieldNb (NBPtr, BFCmdBank, Rrw->TgtBankAddressB);
+ // Set Row Address
+ MemNSetBitFieldNb (NBPtr, BFCmdAddress, Rrw->TgtRowAddressB);
+ // Send the command
+ MemNSetBitFieldNb (NBPtr, BFSendActCmd, 1);
+ // Wait for command complete
+ MemNPollBitFieldNb (NBPtr, BFSendActCmd, 0, PCI_ACCESS_TIMEOUT, FALSE);
+
+ if (OptPatWr2D) {
+ //
+ // Write the Pattern to the bank pairs for each seed.
+ //
+ NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] (NBPtr, NULL);
+ NBPtr->SetBitField (NBPtr, BFTgtBankA, Rrw->TgtBankAddressA);
+ NBPtr->SetBitField (NBPtr, BFTgtBankB, Rrw->TgtBankAddressB);
+ // Program the PRBS Seed
+ NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] (NBPtr, &SeedCount);
+ //
+ // Enable continuous writes on the victim channels
+ //
+ // Set the Command Count
+ NBPtr->SetBitField (NBPtr, BFCmdCount, 256);
+ NBPtr->SetBitField (NBPtr, BFCmdType, CMD_TYPE_WRITE);
+ NBPtr->SetBitField (NBPtr, BFSendCmd, 1);
+ // Wait for TestStatus = 1 and CmdSendInProg = 0.
+ NBPtr->PollBitField (NBPtr, BFTestStatus, 1, PCI_ACCESS_TIMEOUT, FALSE);
+ NBPtr->SetBitField (NBPtr, BFSendCmd, 0);
+ }
+ }
+ }
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ //
+ // Do not use LFSR Rollover during Wr Training
+ //
+ NBPtr->SetBitField (NBPtr, BFLfsrRollOver, 1);
+ }
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function Initializes the Victim chipSelects for 2D Read or Write
+ * Training Continuous Writes
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *OptParam - Unused
+ *
+ * @return BOOLEAN
+ * TRUE
+ */
+BOOLEAN
+MemFRdWr2DInitVictimChipSel (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ )
+{
+ NBPtr->SetBitField (NBPtr, BFTgtChipSelectA, NBPtr->TechPtr->ChipSel);
+ NBPtr->SetBitField (NBPtr, BFTgtChipSelectB, NBPtr->TechPtr->ChipSel);
+ NBPtr->SetBitField (NBPtr, BFResetAllErr, 1);
+ return TRUE;
+}
+
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function checks the In Phase Error status bits for comparison
+ * results for RD/WR 2D training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[out] *Result - Pointer to UINT32 for storing results
+ *
+ * @return BOOLEAN
+ * TRUE
+ *
+ */
+BOOLEAN
+MemFRdWr2DCompareInPhase (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ OUT VOID *Result
+ )
+{
+ *(UINT32*)Result = NBPtr->GetBitField (NBPtr, BFNibbleErrSts);
+ return TRUE;
+}
+
+ /*-----------------------------------------------------------------------------*/
+/**
+ *
+ * This function checks the 180 Error status bits for RD/WR 2D training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[out] *Result - Pointer to UINT32 for storing results
+ *
+ * @return BOOLEAN
+ * TRUE
+ *
+ */
+BOOLEAN
+MemFRdWr2DCompare180Phase (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ OUT VOID *Result
+ )
+{
+ *(UINT32*)Result = NBPtr->GetBitField (NBPtr, BFNibbleErr180Sts);
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function starts the Victim for 2D RdDqs Training Continuous Writes
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] SeedCountPtr - UINT8 Pointer to Seed count
+ *
+ * @return BOOLEAN
+ * TRUE
+ *
+ */
+BOOLEAN
+MemFRdWr2DStartVictim (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *SeedCountPtr
+ )
+{
+ BOOLEAN OptPatWr2D;
+ UINT32 CommandCount;
+ UINT8 SeedCount;
+
+ ASSERT (NBPtr != NULL);
+ ASSERT (SeedCountPtr != NULL);
+
+ SeedCount = *(UINT8*)SeedCountPtr;
+
+ OptPatWr2D = FALSE;
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ OptPatWr2D = (NBPtr->IsSupported[OptimizedPatternWrite2D]);
+ CommandCount = NBPtr->TotalBitTimes2DRdTraining / (8 * NBPtr->MaxSeedCount * NBPtr->MaxAggressorDimms[NBPtr->Dct]);
+ } else {
+ CommandCount = 256;
+ }
+
+ // Program the PRBS Seed
+ NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] (NBPtr, &SeedCount);
+ if (OptPatWr2D) {
+ //
+ // Set BankAddress according to seed
+ //
+ NBPtr->SetBitField (NBPtr, BFTgtBankA, (SeedCount * 2) + CPG_BANK_ADDRESS_A);
+ NBPtr->SetBitField (NBPtr, BFTgtBankB, (SeedCount * 2) + CPG_BANK_ADDRESS_B);
+ } else {
+ //
+ // Enable continuous writes on the victim channels
+ //
+ // Set the Command Count
+ NBPtr->SetBitField (NBPtr, BFCmdCount, 256);
+ NBPtr->SetBitField (NBPtr, BFCmdType, CMD_TYPE_WRITE);
+ NBPtr->SetBitField (NBPtr, BFSendCmd, 1);
+ // Wait for TestStatus = 1 and CmdSendInProg = 0.
+ NBPtr->PollBitField (NBPtr, BFTestStatus, 1, PCI_ACCESS_TIMEOUT, FALSE);
+ NBPtr->SetBitField (NBPtr, BFSendCmd, 0);
+ }
+ //
+ // Enable continuous reads on the victim channels
+ //
+ // Set the Command Count
+ ASSERT (NBPtr->MaxAggressorDimms[NBPtr->Dct] != 0);
+ //
+ NBPtr->SetBitField (NBPtr, BFCmdCount, CommandCount );
+ // Reset All Errors and Disable StopOnErr
+ NBPtr->SetBitField (NBPtr, BFCmdType, CMD_TYPE_READ);
+ NBPtr->SetBitField (NBPtr, BFSendCmd, 1);
+ // Wait for TestStatus = 1 and CmdSendInProg = 0
+ NBPtr->PollBitField (NBPtr, BFTestStatus, 1, PCI_ACCESS_TIMEOUT, FALSE);
+ //}
+ NBPtr->SetBitField (NBPtr, BFSendCmd, 0);
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function finalizes the Victim for 2D RdDqs Training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *OptParam - Unused
+ *
+ * @return BOOLEAN
+ * TRUE - Success
+ */
+BOOLEAN
+MemFRdWr2DFinalizeVictim (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ )
+{
+ UINT8 InitialCS;
+ UINT8 ChipSel;
+ InitialCS = NBPtr->TechPtr->ChipSel;
+ NBPtr->SetBitField (NBPtr, BFLfsrRollOver, 0);
+ for (ChipSel = InitialCS; ChipSel < (InitialCS + NBPtr->CsPerDelay); ChipSel++) {
+ // Ensure that Odd and Even CS are precharged
+ if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) == 0) {
+ continue;
+ }
+ NBPtr->TechPtr->ChipSel = ChipSel;
+ // Send the Precharge All Command
+ MemNRrwPrechargeCmd (NBPtr, ChipSel, PRECHARGE_ALL_BANKS);
+ }
+ // Turn Off the RRW Engine
+ NBPtr->SetBitField (NBPtr, BFCmdTestEnable, 0);
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs the Data Pattern that will be sent and compared
+ * against.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *PatternIndexPtr - Pointer to a generic index used to
+ * determine which pattern to program.
+ *
+ * @return BOOLEAN
+ * TRUE
+ *
+ */
+BOOLEAN
+MemFRdWr2DProgramDataPattern (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID* PatternIndexPtr
+ )
+{
+ UINT8 SeedCount;
+ UINT32 PrbsSeed;
+ CONST STATIC UINT32 PrbsSeedTbl[4] = {0x7ea05, 0x44443, 0x22b97, 0x3f167};
+ CONST STATIC UINT32 CmdStreamLenTbl[4] = {13, 61, 127, 251};
+
+ ASSERT (NBPtr != 0);
+ ASSERT (PatternIndexPtr != NULL);
+ SeedCount = *(UINT8*)PatternIndexPtr;
+ ASSERT (SeedCount <= (NBPtr->MaxSeedCount - 1));
+ //
+ // Program the Command Stream Length
+ //
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ NBPtr->SetBitField (NBPtr, BFCmdStreamLen, CmdStreamLenTbl[SeedCount]);
+ } else {
+ NBPtr->SetBitField (NBPtr, BFCmdStreamLen, 63);
+ }
+ PrbsSeed = PrbsSeedTbl[SeedCount % GET_SIZE_OF (PrbsSeedTbl)];
+ ASSERT (PrbsSeed != 0);
+ //
+ // Program the PRBS Seed
+ //
+ NBPtr->SetBitField (NBPtr, BFDataPrbsSeed, PrbsSeed);
+ return TRUE;
+}
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c
new file mode 100644
index 0000000000..6a9e873e94
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c
@@ -0,0 +1,1368 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mfRdWr2DTraining.c
+ *
+ * Common Read/Write 2D Training Feature Function
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/Feat/RdWr2DTraining)
+ * @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 "amdlib.h"
+#include "Ids.h"
+#include "AdvancedApi.h"
+#include "GeneralServices.h"
+#include "heapManager.h"
+#include "mm.h"
+#include "mn.h"
+#include "mu.h"
+#include "mt.h"
+#include "mport.h"
+#include "merrhdl.h"
+#include "OptionMemory.h"
+#include "mfRdWr2DTraining.h"
+#include "Filecode.h"
+
+CODE_GROUP (G1_PEICC)
+RDATA_GROUP (G1_PEICC)
+
+#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDWR2DTRAINING_FILECODE
+/*----------------------------------------------------------------------------
+ * DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * TYPEDEFS AND STRUCTURES
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+BOOLEAN
+STATIC
+MemFRdWr2DScaleVref (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *Vref
+ );
+
+/*----------------------------------------------------------------------------
+ * EXPORTED FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+ extern MEM_PSC_FLOW_BLOCK* memPlatSpecFlowArray[];
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function initializes the 2D Read/Write Training Feature Hooks.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return BOOLEAN
+ * TRUE - Function was implemented
+ */
+
+BOOLEAN
+MemFRdWr2DTrainingInit (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ ASSERT (NBPtr != NULL);
+ NBPtr->FamilySpecificHook[RdWr2DTraining] = MemFAmdRdWr2DTraining;
+ NBPtr->FamilySpecificHook[CheckRdWr2DTrainingPerConfig] = MemFCheckRdWr2DTrainingPerConfig;
+ NBPtr->FamilySpecificHook[RdWr2DSelectIntExtVref] = MemFRdWr2DProgramIntExtVrefSelect;
+ NBPtr->FamilySpecificHook[RdWr2DProgramVref] = MemFRdWr2DProgramVref;
+ NBPtr->FamilySpecificHook[RdWr2DScaleVref] = MemFRdWr2DScaleVref;
+ NBPtr->FamilySpecificHook[RdWr2DProgramDelays] = MemFRdWr2DProgramDelays;
+ NBPtr->FamilySpecificHook[RdWr2DDataCollection] = MemFRdWr2DEyeRimSearch;
+ NBPtr->FamilySpecificHook[RdWr2DInitVictim] = MemFRdWr2DInitVictim;
+ NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] = MemFRdWr2DInitVictimChipSel;
+ NBPtr->FamilySpecificHook[RdWr2DStartVictim] = MemFRdWr2DStartVictim;
+ NBPtr->FamilySpecificHook[RdWr2DFinalizeVictim] = MemFRdWr2DFinalizeVictim;
+ NBPtr->FamilySpecificHook[RdWr2DCompareInPhase] = MemFRdWr2DCompareInPhase;
+ NBPtr->FamilySpecificHook[RdWr2DCompare180Phase] = MemFRdWr2DCompare180Phase;
+ NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] = MemFRdWr2DProgramDataPattern;
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function executes 2D training for Read DQS or Write DQ
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *OptParam - Unused
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ * FALSE - Errors occurred
+ */
+
+BOOLEAN
+MemFAmdRdWr2DTraining (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID* OptParam
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ MEM_DATA_STRUCT *MemPtr;
+ MEM_RD_WR_2D_ENTRY Data;
+ RD_WR_2D *VrefPtr;
+ PSO_TABLE *PsoTable;
+ ALLOCATE_HEAP_PARAMS AllocHeapParams;
+ UINT8 Dct;
+ UINT8 ChipSel;
+ UINT8 Lane;
+ UINT8 Vref;
+ UINT8 MaxLanes;
+ UINT8 TmpLanes;
+ UINT8 TotalDlyRange;
+ BOOLEAN Status;
+ //
+ // Initialize Pointers
+ //
+ ASSERT (NBPtr != NULL);
+ TechPtr = NBPtr->TechPtr;
+ MemPtr = NBPtr->MemPtr;
+ PsoTable = MemPtr->ParameterListPtr->PlatformMemoryConfiguration;
+ //
+ // Set environment settings before training
+ //
+ AGESA_TESTPOINT (TpProcMem2dRdDqsTraining, &(MemPtr->StdHeader));
+ MemTBeginTraining (TechPtr);
+ //
+ // Allocate heap for the 2D RdWR/Vref Data structure
+ //
+ MaxLanes = 0;
+ for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
+ NBPtr->SwitchDCT (NBPtr, Dct);
+ TmpLanes = MemFRdWr2DGetMaxLanes (NBPtr);
+ MaxLanes = MAX (MaxLanes, TmpLanes);
+ }
+
+ AllocHeapParams.RequestedBufferSize = MaxLanes * NBPtr->TotalMaxVrefRange * sizeof (RD_WR_2D);
+ AllocHeapParams.BufferHandle = AMD_MEM_2D_RD_WR_HANDLE;
+ AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
+ if (HeapAllocateBuffer (&AllocHeapParams, &MemPtr->StdHeader) == AGESA_SUCCESS) {
+ VrefPtr = (RD_WR_2D *) AllocHeapParams.BufferPtr;
+ } else {
+ SetMemError (AGESA_FATAL, NBPtr->MCTPtr);
+ PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_2D, 0, 0, 0, 0, &MemPtr->StdHeader);
+ return TRUE;
+ }
+ for (Lane = 0; Lane < MaxLanes; Lane++) {
+ Data.Lane[Lane].Vref = &VrefPtr[Lane * NBPtr->TotalMaxVrefRange];
+ }
+ //
+ // Setup hardware training engine
+ //
+ TechPtr->TrainingType = TRN_DQS_POSITION;
+ NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType);
+
+ Data.Vnom = NBPtr->TotalMaxVrefRange / 2; // Set Nominal Vref
+ TotalDlyRange = (TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalRdDQSDlyRange : NBPtr->TotalWrDatDlyRange;
+ Data.MaxRdWrSweep = TotalDlyRange / 2; // Set Max Sweep Size
+ ASSERT (TotalDlyRange <= MAX_RD_WR_DLY_ENTRIES);
+ //
+ // Execute 2d Rd DQS training for all Dcts/Chipselects
+ //
+ for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
+ IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
+ NBPtr->SwitchDCT (NBPtr, Dct);
+ NBPtr->Vref = 0xFF;
+ Status = FALSE;
+ if (NBPtr->FamilySpecificHook[CheckRdWr2DTrainingPerConfig] (NBPtr, NULL)) {
+ for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay ) {
+ if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) :
+ ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) {
+ //
+ //Initialize storage
+ //
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ for (Vref = 0; Vref < NBPtr->TotalMaxVrefRange; Vref++) {
+ Data.Lane[Lane].Vref[Vref].PosRdWrDly = 0;
+ Data.Lane[Lane].Vref[Vref].NegRdWrDly = 0;
+ }
+ }
+ TechPtr->ChipSel = ChipSel;
+ IDS_HDT_CONSOLE (MEM_FLOW,"\tChip Select: %02x \n", TechPtr->ChipSel);
+ //
+ // 1. Sample the data eyes for each channel:
+ //
+ TechPtr->RdWr2DData = &Data;
+ if (NBPtr->FamilySpecificHook[RdWr2DDataCollection] (NBPtr, NULL)) {
+ //
+ // 2. Process the array of results with a diamond convolution mask, summing the number passing sample points.
+ //
+ // Determine Diamond Mask Height
+ if (MemFRdWr2DHeight (NBPtr, &Data)) {
+ //
+ // Apply Mask
+ //
+ if (MemFRdWr2DApplyMask (NBPtr, &Data)) {
+ //
+ // Convolution
+ //
+ if (MemFRdWr2DProcessConvolution (NBPtr, &Data)) {
+ //
+ // 3. Program the final DQS delay values.
+ //
+ if (MemFRdWr2DProgramMaxDelays (NBPtr, &Data)) {
+ //
+ // Find the Smallest Positive or Negative Margin for current CS
+ //
+ if (MemFRdWr2DFindCsVrefMargin (NBPtr, &Data)) {
+ Status = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (Status == FALSE) {
+ SetMemError (AGESA_ERROR, NBPtr->MCTPtr);
+ PutEventLog (AGESA_ERROR, (TechPtr->Direction == DQS_READ_DIR) ? MEM_ERROR_2D_DQS_ERROR : MEM_ERROR_2D_WRDAT_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ }
+ }
+ }
+ //
+ // Find the Max and Min Vref values for each DCT
+ //
+ if (Status == TRUE) {
+ if (MemFRdWr2DFinalVrefMargin (NBPtr, &Data)) {
+ //
+ // Program the Max Vref value
+ //
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tProgramming Final Vref for channel\n");
+ NBPtr->FamilySpecificHook[RdWr2DProgramVref] (NBPtr, &NBPtr->ChannelPtr->MaxVref);
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tActual Vref programmed = %02x\n",
+ ( NBPtr->GetBitField (NBPtr, BFVrefDAC) >> TSEFO_END (NBPtr->NBRegTable[BFVrefDAC])) );
+ Status = TRUE;
+ } else {
+ SetMemError (AGESA_ERROR, NBPtr->MCTPtr);
+ PutEventLog (AGESA_ERROR, (TechPtr->Direction == DQS_READ_DIR) ? MEM_ERROR_2D_DQS_VREF_MARGIN_ERROR: MEM_ERROR_2D_WRDAT_VREF_MARGIN_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+ }
+ }
+ }
+ }
+ //
+ // Restore environment settings after training
+ //
+ if (HeapDeallocateBuffer (AMD_MEM_2D_RD_WR_HANDLE, &MemPtr->StdHeader) != AGESA_SUCCESS) {
+ SetMemError (AGESA_FATAL, NBPtr->MCTPtr);
+ PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_DEALLOCATE_FOR_2D, 0, 0, 0, 0, &MemPtr->StdHeader);
+ }
+ IDS_HDT_CONSOLE (MEM_STATUS, "\tEnd\n");
+ MemTEndTraining (TechPtr);
+
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\nEnd %s 2D training\n\n",(TechPtr->Direction == DQS_READ_DIR) ? "Read DQS":"Write DQ");
+ return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
+}
+/*----------------------------------------------------------------------------
+ * LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function determines whether the current configuration is a valid
+ * config for applying 2D Training
+ * @todo: Update to work for 2D WR and 2D RD training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *OptParam - Unused
+ *
+ * @return BOOLEAN
+ * TRUE - Configuration valid
+ * FALSE - Configuration invalid
+ *
+ */
+BOOLEAN
+MemFCheckRdWr2DTrainingPerConfig (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ )
+{
+ UINT8 i;
+ if (NBPtr->RefPtr->ForceTrainMode == FORCE_TRAIN_AUTO) {
+ i = 0;
+ while (memPlatSpecFlowArray[i] != NULL) {
+ if ((memPlatSpecFlowArray[i])->S2D (NBPtr, (memPlatSpecFlowArray[i])->EntryOfTables)) {
+ return TRUE;
+ }
+ i++;
+ }
+ }
+ return FALSE;
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function determines the maximum number of lanes for puposes of 2D
+ * Read or Write training.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return UINT8 - Max Number of Lanes
+ *
+ */
+UINT8
+MemFRdWr2DGetMaxLanes (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ UINT8 MaxLanes;
+
+ TechPtr = NBPtr->TechPtr;
+ if ((TechPtr->Direction == DQS_READ_DIR) && ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) != 0)) {
+ // Per Nibble
+ MaxLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && (NBPtr->IsSupported[EccByteTraining] == TRUE)) ? 18 : 16;
+ } else {
+ // Per Byte
+ MaxLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && (NBPtr->IsSupported[EccByteTraining] == TRUE)) ? 9 : 8;
+ }
+ return MaxLanes;
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs Vref to internal or external control for 2D Read
+ * or Write Training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *OptParam - Unused
+ *
+ * @return BOOLEAN
+ * TRUE - External Vref was selected
+ * FALSE - Internal Vref was selected
+ *
+ */
+BOOLEAN
+MemFRdWr2DProgramIntExtVrefSelect (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ )
+{
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ NBPtr->SetBitField (NBPtr, BFVrefSel, (NBPtr->RefPtr->ExternalVrefCtl ? 0x0000 : 0x0001));
+ }
+ return NBPtr->RefPtr->ExternalVrefCtl;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function scales Vref from the range used in Data Collection to
+ * the range that is programmed into the register.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *Vref - Pointer to UINT8 Vref Value to scale.
+ *
+ * @return BOOLEAN
+ * TRUE Function was implemented
+ *
+ */
+BOOLEAN
+STATIC
+MemFRdWr2DScaleVref (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *Vref
+ )
+{
+ *(UINT8*)Vref = *(UINT8*)Vref * 2;
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs Vref for 2D Read/Write Training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *VrefPtr - Pointer to Vref value
+ *
+ * @return BOOLEAN
+ * TRUE - Success
+ * FAIL (External Callout only)
+ *
+ */
+BOOLEAN
+MemFRdWr2DProgramVref (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *VrefPtr
+ )
+{
+ AGESA_STATUS Status;
+ MEM_DATA_STRUCT *MemPtr;
+ ID_INFO CallOutIdInfo;
+ VOLTAGE_ADJUST Va;
+ UINT8 Vref;
+
+ ASSERT (NBPtr != NULL);
+ ASSERT (VrefPtr != NULL);
+ MemPtr = NBPtr->MemPtr;
+ Vref = *(UINT8*)VrefPtr;
+ CallOutIdInfo.IdField.SocketId = NBPtr->MCTPtr->SocketId;
+ CallOutIdInfo.IdField.ModuleId = NBPtr->MCTPtr->DieId;
+ LibAmdMemCopy ((VOID *)&Va, (VOID *)MemPtr, (UINTN)sizeof (Va.StdHeader), &MemPtr->StdHeader);
+ Va.MemData = MemPtr;
+ Status = AGESA_SUCCESS;
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ if (NBPtr->RefPtr->ExternalVrefCtl == FALSE) {
+ //
+ // Internal vref control
+ //
+ // This is 1/2 VrefDAC value Sign bit is shifted into place.
+ //
+ ASSERT (Vref < 32);
+ if (Vref < 15) {
+ Vref = (31 - Vref) << 1;
+ } else {
+ Vref = (Vref - 15) << 1;
+ }
+ NBPtr->SetBitField (NBPtr, BFVrefDAC, Vref << 2);
+ } else {
+ // External vref control
+ AGESA_TESTPOINT (TpProcMemBefore2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader));
+ NBPtr->MemPtr->ParameterListPtr->ExternalVrefValue = Vref;
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n2D Read Training External CPU Vref Callout \n");
+ Va.VoltageType = VTYPE_CPU_VREF;
+ Va.AdjustValue = Vref = (Vref - 15) << 1;
+ Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va);
+ AGESA_TESTPOINT (TpProcMemAfter2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader));
+ }
+ } else {
+ //
+ // DIMM Vref Control
+ //
+ Va.VoltageType = VTYPE_DIMM_VREF;
+ //
+ // Offset by 15 and multiply by 2.
+ //
+ Va.AdjustValue = Vref = (Vref - 15) << 1;
+ Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va);
+ if (Status != AGESA_SUCCESS) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "* Dimm Vref Callout Failed *");
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ }
+ return (Status == AGESA_SUCCESS) ? TRUE : FALSE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs Read DQS or Write DQ Delay values for Read/Write
+ * Training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Delay - Pointer to UINT8 containing Delay value
+ *
+ * @return BOOLEAN
+ * TRUE
+ *
+ */
+
+BOOLEAN
+MemFRdWr2DProgramDelays (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *Delay
+ )
+{
+ UINT32 RdDqsTime;
+ UINT8 RdWrDly;
+
+ ASSERT (NBPtr != 0);
+ ASSERT (Delay != 0);
+ RdWrDly = *(UINT8*) Delay;
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ // This function should only be used for read training
+ ASSERT (NBPtr->TechPtr->Direction == DQS_READ_DIR);
+ // Program BL registers for both nibble (x4) and bytes (x8, x16)
+ RdDqsTime = 0;
+ RdDqsTime = (RdWrDly & 0x1F) << 8;
+ RdDqsTime = RdDqsTime | (RdWrDly & 0x1F);
+ if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 0) {
+ NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm0Broadcast, RdDqsTime);
+ } else if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 1) {
+ NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm1Broadcast, RdDqsTime);
+ } else if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 2) {
+ NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm2Broadcast, RdDqsTime);
+ } else if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 3) {
+ NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm3Broadcast, RdDqsTime);
+ }
+ } else {
+ MemTSetDQSDelayAllCSR (NBPtr->TechPtr, RdWrDly);
+ }
+ return TRUE;
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function stores data for 2D Read DQS and Write DQ Training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ * @param[in] *InPhaseResult[] - Array of inphase results
+ * @param[in] *PhaseResult180[] - Array of Phase 180 results
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ * FALSE - Errors ccurred
+ */
+VOID
+MemFRdWr2DStoreResult (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data,
+ IN UINT32 InPhaseResult[],
+ IN UINT32 PhaseResult180[]
+ )
+{
+ UINT8 Lane;
+ UINT8 Vref;
+ UINT8 RdWrDly;
+ UINT32 Result;
+ UINT32 Result180;
+ UINT8 Index;
+ Vref = NBPtr->Vref;
+ RdWrDly = Data->RdWrDly;
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ for (RdWrDly = 0; RdWrDly < Data->MaxRdWrSweep; RdWrDly++) {
+ if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (NBPtr->TechPtr->ChipSel >> 1))) == 0) {
+ // x8, so combine "Nibble X" and "Nibble X+1" results
+ Index = Lane * 2;
+ Result = (InPhaseResult[RdWrDly] >> Index) & 0x03;
+ Result180 = (PhaseResult180[RdWrDly] >> Index) & 0x03;
+ } else {
+ // x4, so use "Nibble" results
+ Result = (InPhaseResult[RdWrDly] >> Lane) & 0x01;
+ Result180 = (PhaseResult180[RdWrDly] >> Lane) & 0x01;
+ }
+ Data->Lane[Lane].Vref[Vref].PosRdWrDly |= (Result == 0) ? (1 << (Data->MaxRdWrSweep - 1 - RdWrDly)) : 0;
+ Data->Lane[Lane].Vref[Vref].NegRdWrDly |= (Result180 == 0) ? (1 << (Data->MaxRdWrSweep - 1 - RdWrDly)) : 0;
+ }
+ }
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function determines the height of data for 2D Read and Write training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ */
+BOOLEAN
+MemFRdWr2DHeight (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ )
+{
+ UINT8 Lane;
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ Data->Lane[Lane].HalfDiamondHeight = 0x0F;
+ }
+ IDS_HDT_CONSOLE_DEBUG_CODE (
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Lane: ");
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Lane);
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tHeight: ");
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", (2*(Data->Lane[Lane].HalfDiamondHeight) + 1));
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ );
+ return TRUE;
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function gets the width for 2D RdDQS and WrDat training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ *
+ * @return UINT8 Width
+ */
+UINT8
+MemFGetRdWr2DWidth (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ )
+{
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ return NBPtr->DiamondWidthRd;
+ } else {
+ return NBPtr->DiamondWidthWr;
+ }
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function gets the step height for the diamond mask for 2D RdDQS or
+ * WrDat Training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ * @param[in] Vref - current Vref value
+ * @param[in] Lane - current Lane
+ *
+ * @return BOOLEAN
+ * TRUE - Step found and value should be updated
+ * FALSE - Step not found and value should not be updated
+ *
+ */
+BOOLEAN
+MemFCheckRdWr2DDiamondMaskStep (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data,
+ IN UINT8 Vref,
+ IN UINT8 Lane
+ )
+{
+ UINT8 M;
+ UINT8 VrefVal;
+ UINT8 width;
+ UINT8 i;
+ BOOLEAN status;
+ // m = -1 * height/width
+ // (y-b)/m = x
+ status = FALSE;
+ if (Vref > (Data->Vnom - 1)) {
+ VrefVal = (Vref + 1) - Data->Vnom;
+ } else {
+ VrefVal = Vref;
+ }
+ width = (MemFGetRdWr2DWidth (NBPtr, Data) - 1) / 2;
+ M = Data->Lane[Lane].HalfDiamondHeight / width;
+ i = 1;
+ while (i <= Data->Lane[Lane].HalfDiamondHeight) {
+ i = i + M;
+ if (VrefVal == i) {
+ status = TRUE;
+ }
+ }
+ return status;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function applies a mask for 2D RdDQS or WrDat training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ * FALSE - Errors ccurred
+ *
+ */
+BOOLEAN
+MemFRdWr2DApplyMask (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ UINT8 RdWrDly;
+ UINT8 Lane;
+ UINT8 Height;
+ UINT8 Width;
+ UINT32 PosNegData;
+ UINT8 Vref;
+ UINT8 count;
+ UINT8 Dly;
+ UINT8 endWidth;
+ UINT8 startWidth;
+ UINT8 origEndWidth;
+ UINT8 origStartWidth;
+ UINT8 maxOverLapWidth;
+ UINT8 startOverLapWidth;
+ UINT8 TotalDlyRange;
+ BOOLEAN maxHeightExceeded;
+ BOOLEAN negVrefComplete;
+ BOOLEAN PosRdWrToNegRdWr;
+ BOOLEAN NegRdWrToPosRdWr;
+
+ TechPtr = NBPtr->TechPtr;
+ TotalDlyRange = (TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalRdDQSDlyRange : NBPtr->TotalWrDatDlyRange;
+ //
+ // Initialize Convolution
+ //
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) {
+ Data->Lane[Lane].Convolution[RdWrDly] = 0;
+ NBPtr->FamilySpecificHook[Adjust2DDelayStepSize] (NBPtr, &RdWrDly);
+ }
+ }
+ endWidth = 0;
+ startWidth = 0;
+ origEndWidth = 0;
+ origStartWidth = 0;
+ startOverLapWidth = 0;
+ maxOverLapWidth = 0;
+ maxHeightExceeded = FALSE;
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tDetermining Width");
+ //
+ // Get the Width of Diamond
+ //
+ Width = MemFGetRdWr2DWidth (NBPtr, Data);
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWidth: %02x\n", Width);
+ ASSERT (Width != 0);
+
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tExecuting convolution function\n");
+ //
+ // Perform the convolution by sweeping the mask function centered at nominal Vref. Results in a one
+ // dimensional array with FOM values at each delay for each lane. Choose the delay setting at the peak
+ // FOM value.
+ //
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ Height = Data->Lane[Lane].HalfDiamondHeight;
+ ASSERT (Height < Data->Vnom);
+ //
+ // RdWrDly is divided around "Data->MaxRdWrSweep" into positive and negative directions
+ // Positive direction -> RdWrDly = 0 to (Data->MaxRdWrSweep - 1)
+ // Negative direction -> RdWrDly = Data->MaxRdWrSweep to (TotalDlyRange - 1)
+ //
+ for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) {
+ // Vref loop is divided around "Data->Vnom - 1" into positive and negative directions
+ // Negative direction -> Vref = 0 ("Data->Vnom - 1") to Height("Data->Vnom - 1" - Height)
+ // Positive direction -> Vref = "Data->Vnom" to Height("Data->Vnom" + Height)
+ //
+ negVrefComplete = FALSE;
+ PosRdWrToNegRdWr = FALSE;
+ NegRdWrToPosRdWr = FALSE;
+ for (Vref = 0; Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) {
+ // Initial negative direction where Vref = 0 ("Data->Vnom - 1"), so we need to set
+ // initial startWidth and endWidth for +/- RdDqs
+ //
+ // Create common delay based on +/- RdDqs
+ if (RdWrDly > (Data->MaxRdWrSweep - 1)) {
+ Dly = RdWrDly - Data->MaxRdWrSweep;
+ } else {
+ Dly = RdWrDly;
+ }
+ if (Vref == 0 ) {
+ // Initialize -Vref
+ maxHeightExceeded = FALSE; // reset for start of -Vref
+ // Case 1: if +RdDqs - Check for lower bound (Width/2 > RdDqs > 0)
+ // : if -RdDqs - Check for lower bound (Width/2 + Data->MaxRdDqsSweep > RdDqs > Data->MaxRdDqsSweep)
+ if (Dly < Width / 2) {
+ endWidth = Dly + Width / 2 + 1;
+ startWidth = 0;
+ } else if ((Dly + Width / 2) > (Data->MaxRdWrSweep - 1)) {
+ // Case 2: if +RdWr - Check for upper bound ((Data->MaxRdWrSweep - 1) < RdWr < ((Data->MaxRdWrSweep - 1) - Width/2))
+ // : if -RdWr - Check for lower bound ((DatNBPtr->TotalRdWrDlyRange - 1) < RdWr < ((NBPtr->TotalRdWrDlyRange - 1) - Width/2))
+ endWidth = Data->MaxRdWrSweep;
+ startWidth = Dly - Width / 2;
+ } else {
+ // Set the initial "startWidth" and "endWidth" for +/- RdDqs
+ endWidth = Dly + Width / 2 + 1;
+ startWidth = Dly - Width / 2;
+ }
+ origEndWidth = endWidth;
+ origStartWidth = startWidth;
+ } else if (Vref == Data->Vnom) {
+ // Initialize +Vref
+ endWidth = origEndWidth;
+ startWidth = origStartWidth;
+ maxHeightExceeded = FALSE; // reset for start of +Vref
+ negVrefComplete = TRUE;
+ } else if ((Vref > (Data->Vnom + Height)) && negVrefComplete == TRUE) {
+ break; //switch to next RdDqs Dly if height exceeded for +vref and -vref complete
+ } else {
+ if (startWidth >= endWidth) {
+ if (RdWrDly == (TotalDlyRange - 1)) {
+ // Special condition for end of -RdDqs range
+ startWidth = Data->MaxRdWrSweep - 1;
+ endWidth = Data->MaxRdWrSweep;
+ } else {
+ // Width = 0, but Height not reached,
+ startWidth = Dly;
+ endWidth = Dly + 1;
+ }
+ } else {
+ // Check for Case 1 and Case 2 above
+ if ((RdWrDly + Width / 2) > (TotalDlyRange - 1)) {
+ endWidth = origEndWidth;
+ }
+ }
+ maxHeightExceeded = FALSE;
+ }
+ IDS_HDT_CONSOLE_DEBUG_CODE (
+ if (Lane == 0) {
+ if (RdWrDly == (Data->MaxRdWrSweep - (Width / 2)) ) {
+ Data->DiamondLeft[Vref] = startWidth;
+ Data->DiamondRight[Vref] = endWidth - 1;
+ }
+ }
+ );
+ //
+ // Determine the correct Delay (+/-) and Vref (+/-)direction
+ //
+ if (maxHeightExceeded == FALSE) {
+ if (RdWrDly < Data->MaxRdWrSweep) {
+ if (Vref > (Data->Vnom - 1)) {
+ PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly; // +RdWr Dly, +Vref
+ } else {
+ PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].PosRdWrDly; // +RdDqs Dly, -Vref
+ }
+ } else {
+ if (Vref > (Data->Vnom - 1)) {
+ PosNegData = Data->Lane[Lane].Vref[Vref].NegRdWrDly; // -RdWr Dly, +Vref
+ } else {
+ PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].NegRdWrDly; // -RdWr Dly, -Vref
+ }
+ }
+ //
+ // Case 1: Non-overlap condition:
+ // Count the number of passes from "startWidth" to "endWidth"
+ //
+ for (count = startWidth; count < endWidth; count++) {
+ Data->Lane[Lane].Convolution[RdWrDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdWrDly];
+ }
+ // Case 2: Overlay between +RdWr and -RdWr starting from +RdWr
+ // Count the number of passes from "startWidth" to "endWidth"
+ //
+ if ((RdWrDly <= (Data->MaxRdWrSweep - 1) && (RdWrDly > ((Data->MaxRdWrSweep - 1) - Width / 2)))) {
+ startOverLapWidth = 0;
+ if (Vref == 0 || Vref == Data->Vnom) {
+ maxOverLapWidth = (RdWrDly + Width / 2) - (Data->MaxRdWrSweep - 1); // Initial overlap max width size
+ } else if (maxOverLapWidth == 0) {
+ maxOverLapWidth = startOverLapWidth; // Stop counting after overlap region complete
+ }
+ // Ensure that +/- vref is set correctly
+ if (Vref > (Data->Vnom - 1)) {
+ PosNegData = Data->Lane[Lane].Vref[Vref].NegRdWrDly;
+ } else {
+ PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].NegRdWrDly;
+ }
+ // Need to count the number of passes when range extends from Pos RdDqs to Neg RdDqs
+ for (count = startOverLapWidth; count < maxOverLapWidth; count++) {
+ Data->Lane[Lane].Convolution[RdWrDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdWrDly];
+ }
+ if (maxOverLapWidth > 0) {
+ if (MemFCheckRdWr2DDiamondMaskStep (NBPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) {
+ maxOverLapWidth--; // Reduce overlap width outside of diamond mask
+ }
+ PosRdWrToNegRdWr = TRUE;
+ }
+ }
+ if (((RdWrDly - Data->MaxRdWrSweep) < Width / 2) && (RdWrDly > (Data->MaxRdWrSweep - 1))) {
+ //
+ // Case 3: Overlay between -RdDqs and +RdDqs starting from -RdDqs
+ // Count the number of passes from "startWidth" to "endWidth"
+ //
+ maxOverLapWidth = Data->MaxRdWrSweep;
+ if (Vref == 0 || Vref == Data->Vnom) {
+ startOverLapWidth = RdWrDly - Width / 2; // Initial overlap start point
+ } else if (startOverLapWidth > maxOverLapWidth) {
+ maxOverLapWidth = maxOverLapWidth - 1; // Continue to count until MaxHeight excceded
+ }
+ // Ensure that vref + or - is set correctly
+ if (Vref > (Data->Vnom - 1)) {
+ PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly;
+ } else {
+ PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].PosRdWrDly;
+ }
+ // Need to count the number of passes when range extends from Pos RdDqs to Neg RdDqs
+ for (count = startOverLapWidth; count < maxOverLapWidth; count++) {
+ Data->Lane[Lane].Convolution[RdWrDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdWrDly];
+ }
+ if (startOverLapWidth < maxOverLapWidth) {
+ if (MemFCheckRdWr2DDiamondMaskStep (NBPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) {
+ startOverLapWidth++; // Reduce overlap width outside of diamond mask
+ }
+ NegRdWrToPosRdWr = TRUE;
+ }
+ }
+ }
+ if (MemFCheckRdWr2DDiamondMaskStep (NBPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) {
+ if (PosRdWrToNegRdWr) {
+ startWidth++;
+ endWidth = Data->MaxRdWrSweep;
+ PosRdWrToNegRdWr = FALSE;
+ } else if (NegRdWrToPosRdWr) {
+ startWidth = 0;
+ endWidth--;
+ NegRdWrToPosRdWr = FALSE;
+ } else {
+ startWidth++;
+ endWidth--;
+ }
+ }
+ NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref);
+ }
+ NBPtr->FamilySpecificHook[Adjust2DDelayStepSize] (NBPtr, &RdWrDly);
+ }
+ }
+ IDS_HDT_CONSOLE_DEBUG_CODE (
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t Diamond Shape: \n");
+ for (Vref = 0; Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ for (RdWrDly = (Data->MaxRdWrSweep - Width); RdWrDly < Data->MaxRdWrSweep; RdWrDly++) {
+ if (Vref < (Data->Vnom - 1)) {
+ if (RdWrDly == Data->DiamondLeft[(NBPtr->TotalMaxVrefRange - 2) - Vref]) {
+ IDS_HDT_CONSOLE (MEM_FLOW, " | ");
+ } else if (RdWrDly == Data->DiamondRight[(NBPtr->TotalMaxVrefRange - 2) - Vref]) {
+ IDS_HDT_CONSOLE (MEM_FLOW, " | -> Width = %02x", (Data->DiamondRight[(NBPtr->TotalMaxVrefRange - 2) - Vref]) - (Data->DiamondLeft[(NBPtr->TotalMaxVrefRange - 2) - Vref]));
+ } else {
+ IDS_HDT_CONSOLE (MEM_FLOW, " ");
+ }
+ } else {
+ if (RdWrDly == Data->DiamondLeft[Vref - (Data->Vnom - 1)]) {
+ IDS_HDT_CONSOLE (MEM_FLOW, " | ");
+ } else if (RdWrDly == Data->DiamondRight[Vref - (Data->Vnom - 1)]) {
+ IDS_HDT_CONSOLE (MEM_FLOW, " | -> Width = %02x", (Data->DiamondRight[Vref - (Data->Vnom - 1)]) - (Data->DiamondLeft[Vref - (Data->Vnom - 1)]));
+ } else {
+ IDS_HDT_CONSOLE (MEM_FLOW, " ");
+ }
+ }
+ }
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t Convolution results after processing raw data:\n");
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t Delay: ");
+ for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, " %02x ", RdWrDly <= (Data->MaxRdWrSweep - 1) ? (Data->MaxRdWrSweep - 1) - RdWrDly : (TotalDlyRange - 1) - RdWrDly);
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\tLane: %02x\n", Lane);
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tConv: ");
+ for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "%03x ", Data->Lane[Lane].Convolution[RdWrDly]);
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ }
+ );
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function Examines the convolution function and determines the Max Delay
+ * for 2D RdDQS and WrDat training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ * FALSE - Errors ccurred
+ */
+
+BOOLEAN
+MemFRdWr2DProcessConvolution (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ UINT8 RdWrDly;
+ UINT8 Lane;
+ UINT16 MaxFOM;
+ UINT8 MaxRange;
+ UINT8 CurrRange;
+ UINT8 TotalDlyRange;
+ BOOLEAN status;
+
+ ASSERT (NBPtr != NULL);
+ TechPtr = NBPtr->TechPtr;
+ TotalDlyRange = (TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalRdDQSDlyRange : NBPtr->TotalWrDatDlyRange;
+ status = TRUE;
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tDetermining Delay based on Convolution function\n");
+ // Determine the Max RdDqs or WrDat Dly for the convolution function
+ // - Choose the delay setting at the peak FOM value.
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ // Find largest value as MaxFOM
+ MaxFOM = 0;
+ for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) {
+ if (Data->Lane[Lane].Convolution[RdWrDly] > MaxFOM) {
+ MaxFOM = Data->Lane[Lane].Convolution[RdWrDly];
+ }
+ }
+ status = MaxFOM > 0 ? TRUE : FALSE; // It is an error if all convolution points are zero
+
+ // Then find the midpoint of the largest consecutive window w/ that MaxFOM
+ // In cases of an even number of consecutive points w/ that MaxFOM exists,
+ // choose the midpoint to the right
+ // All things being equal, favor the right side of a bi-modal eye
+ // Stressful SSO patterns shift the eye right!
+ MaxRange = 0;
+ CurrRange = 0;
+ for (RdWrDly = 0; (MaxFOM > 0) && RdWrDly < TotalDlyRange; RdWrDly++) {
+ if (Data->Lane[Lane].Convolution[RdWrDly] == MaxFOM) {
+ CurrRange++;
+ if (CurrRange >= MaxRange) {
+ Data->Lane[Lane].MaxRdWrDly = RdWrDly - ((CurrRange - 1) / 2);
+ MaxRange = CurrRange;
+ }
+ } else {
+ CurrRange = 0;
+ }
+ }
+
+ if (Data->Lane[Lane].MaxRdWrDly > Data->MaxRdWrSweep) {
+ status = FALSE; // Error
+ }
+ // Set Actual register value
+ if (Data->Lane[Lane].MaxRdWrDly < Data->MaxRdWrSweep) {
+ Data->Lane[Lane].MaxRdWrDly = (Data->MaxRdWrSweep - 1) - Data->Lane[Lane].MaxRdWrDly;
+ } else {
+ status = FALSE; // Error
+ }
+ }
+
+ IDS_HDT_CONSOLE_DEBUG_CODE (
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Cs %d Lane: ", TechPtr->ChipSel);
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Lane);
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Max %s Delay: ", TechPtr->Direction == DQS_READ_DIR ? "Rd Dqs" : "Wr DQ");
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Data->Lane[Lane].MaxRdWrDly);
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t1D Trained %s Delay: ", TechPtr->Direction == DQS_READ_DIR ? "Rd Dqs" : "Wr DQ");
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ if (TechPtr->Direction == DQS_READ_DIR) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane]);
+ } else {
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", (NBPtr->ChannelPtr->WrDatDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane] - \
+ NBPtr->ChannelPtr->WrDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane]));
+ }
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n");
+ );
+
+ if (status == FALSE) {
+ SetMemError (AGESA_FATAL, NBPtr->MCTPtr);
+ PutEventLog (AGESA_FATAL, MEM_ERROR_INVALID_2D_RDDQS_VALUE, 0, 0, 0, 0, &TechPtr->NBPtr->MemPtr->StdHeader);
+ }
+ return status;
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function programs the Max Rd Dqs or Max Wr DQ for 2D training from
+ * convolution
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ * FALSE - Errors ccurred
+ */
+BOOLEAN
+MemFRdWr2DProgramMaxDelays (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ UINT8 Lane;
+ UINT8 LaneHighRdDqs2dDlys;
+ UINT8 LaneLowRdDqs2dDlys;
+ UINT8 MaxWrDatDly;
+ TechPtr = NBPtr->TechPtr;
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tProgramming Max %s Delay per Lane\n\n", TechPtr->Direction == DQS_READ_DIR ? "Rd Dqs" : "Wr DQ");
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ if ( TechPtr->Direction == DQS_WRITE_DIR || (NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) {
+ // Program Byte based for x8 and x16
+ if ( TechPtr->Direction == DQS_READ_DIR) {
+ //
+ // Read DQS Training
+ //
+ NBPtr->SetTrainDly (NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS ((TechPtr->ChipSel / NBPtr->CsPerDelay), Lane), (UINT16)Data->Lane[Lane].MaxRdWrDly);
+ NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane] = Data->Lane[Lane].MaxRdWrDly;
+ } else {
+ //
+ // Write DQ Training
+ //
+ MaxWrDatDly = (UINT8) (Data->Lane[Lane].MaxRdWrDly + TechPtr->NBPtr->ChannelPtr->WrDqsDlys[(TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) * MAX_DELAYS + Lane]);
+ NBPtr->SetTrainDly (NBPtr, AccessWrDatDly, DIMM_BYTE_ACCESS ((TechPtr->ChipSel / NBPtr->CsPerDelay), Lane), MaxWrDatDly);
+ NBPtr->ChannelPtr->WrDatDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane] = MaxWrDatDly;
+ }
+ } else {
+ ASSERT (TechPtr->Direction == DQS_READ_DIR);
+ // Program nibble based x4, so use "Nibble"
+ NBPtr->SetTrainDly (NBPtr, AccessRdDqs2dDly, DIMM_NBBL_ACCESS ((TechPtr->ChipSel / NBPtr->CsPerDelay), Lane), (UINT16)Data->Lane[Lane].MaxRdWrDly);
+ NBPtr->ChannelPtr->RdDqs2dDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_NUMBER_LANES + Lane] = Data->Lane[Lane].MaxRdWrDly;
+ // For each pair of nibbles (high (Odd Nibble) and Low (Even nibble)), find the largest and use that as the RdDqsDly value
+ if ((Lane & 0x1) == 0) {
+ LaneHighRdDqs2dDlys = Data->Lane[Lane + 1].MaxRdWrDly;
+ LaneLowRdDqs2dDlys = Data->Lane[Lane].MaxRdWrDly;
+ if (LaneHighRdDqs2dDlys > LaneLowRdDqs2dDlys) {
+ NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + (Lane >> 1)] = LaneHighRdDqs2dDlys;
+ } else {
+ NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + (Lane >> 1)] = LaneLowRdDqs2dDlys;
+ }
+ }
+ NBPtr->DctCachePtr->Is2Dx4 = TRUE;
+ }
+ }
+ return TRUE;
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function finds the Positive and negative Vref Margin for the current CS
+ * for 2D RdDQS or WrDat training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ * FALSE - Errors ccurred
+ */
+BOOLEAN
+MemFRdWr2DFindCsVrefMargin (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ )
+{
+ UINT8 SmallestMaxVrefNeg;
+ UINT8 Lane;
+ UINT8 RdWrDly;
+ UINT8 Vref;
+ UINT8 MaxVrefPositive;
+ UINT8 MaxVrefNegative;
+ UINT8 SmallestMaxVrefPos;
+ UINT32 PosNegData;
+ SmallestMaxVrefPos = 0xFF;
+ SmallestMaxVrefNeg = 0;
+ MaxVrefPositive = 0;
+ MaxVrefNegative = 0xFF;
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFinding Smallest Max Positive and Negative Vref\n\n");
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ RdWrDly = (Data->MaxRdWrSweep - 1) - Data->Lane[Lane].MaxRdWrDly;
+ for (Vref = 0; Vref < (Data->Vnom - 1); Vref++) {
+ // Neg Vref - (searching from top of array down)
+ PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly;
+ if ((UINT8) ((PosNegData >> RdWrDly) & 0x1) == 1) {
+ MaxVrefNegative = Vref;
+ break;
+ }
+ NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref);
+ }
+ for (Vref = (Data->Vnom - 1); Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) {
+ // Pos Vref - (searching from Vnom + 1 of array down)
+ PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly;
+ if ((UINT8) ((PosNegData >> RdWrDly) & 0x1) == 0) {
+ // Convert to register setting
+ MaxVrefPositive = Vref - 1;// - Data->Vnom;
+ break;
+ } else {
+ // If Vref = 1F passes, then smallest Vref = 0x1F
+ if (Vref == ((NBPtr->TotalMaxVrefRange - 1) - 1)) {
+ MaxVrefPositive = 0x1E;
+ break;
+ }
+ }
+ NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref);
+ }
+ if (MaxVrefPositive < SmallestMaxVrefPos) {
+ // Find the smallest Max Pos Vref
+ SmallestMaxVrefPos = MaxVrefPositive;
+ }
+ if (MaxVrefNegative > SmallestMaxVrefNeg) {
+ // Find the largest Max Neg Vref
+ SmallestMaxVrefNeg = MaxVrefNegative;
+ }
+ }
+ if (SmallestMaxVrefPos != (Data->Vnom - 2)) {
+ Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel] = SmallestMaxVrefPos - Data->Vnom + 1;
+ } else {
+ Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel] = 0;
+ }
+ Data->SmallestNegMaxVrefperCS[NBPtr->TechPtr->ChipSel] = (Data->Vnom - 1) - SmallestMaxVrefNeg;
+ IDS_HDT_CONSOLE_DEBUG_CODE (
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSmallest Max Positive Vref Offset from V-Nom for ChipSel %02x = + %02x\n", NBPtr->TechPtr->ChipSel, Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel]);
+ if (Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel] == 0) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSmallest Max Negative Vref Offset from V-Nom for ChipSel %02x = 00\n");
+ } else {
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSmallest Max Negative Vref Offset from V-Nom for ChipSel %02x = - %02x\n", NBPtr->TechPtr->ChipSel, Data->SmallestNegMaxVrefperCS[NBPtr->TechPtr->ChipSel]);
+ }
+ );
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function finds the final Vref Margin for 2D RdDQS or WrDat training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ *
+ * @return BOOLEAN
+ * TRUE - No Errors occurred
+ * FALSE - Errors ccurred
+ */
+BOOLEAN
+MemFRdWr2DFinalVrefMargin (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ )
+{
+ UINT8 ChipSel;
+ UINT8 SmallestMaxPosVref;
+ UINT8 SmallestMaxNegVref;
+ UINT8 OffsetFromVref;
+ UINT8 Vnom;
+ SmallestMaxNegVref = 0x7F;
+ SmallestMaxPosVref = 0x7F;
+ Vnom = (Data->Vnom - 1);
+ IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFinding Final Vref for channel\n\n");
+ for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay ) {
+ if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) :
+ ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) {
+ if (Data->SmallestPosMaxVrefperCS[ChipSel] < SmallestMaxPosVref) {
+ SmallestMaxPosVref = Data->SmallestPosMaxVrefperCS[ChipSel];
+ }
+ if (Data->SmallestNegMaxVrefperCS[ChipSel] < SmallestMaxNegVref) {
+ SmallestMaxNegVref = Data->SmallestNegMaxVrefperCS[ChipSel];
+ }
+ }
+ }
+ //
+ // Synchronize minimum and Maximums with other Vref values
+ //
+ if (NBPtr->TechPtr->Direction == DQS_WRITE_DIR) {
+ if (NBPtr->SharedPtr->CommonSmallestMaxNegVref < SmallestMaxNegVref) {
+ SmallestMaxNegVref = NBPtr->SharedPtr->CommonSmallestMaxNegVref;
+ } else {
+ NBPtr->SharedPtr->CommonSmallestMaxNegVref = SmallestMaxNegVref;
+ }
+ if (NBPtr->SharedPtr->CommonSmallestMaxPosVref < SmallestMaxPosVref) {
+ SmallestMaxPosVref = NBPtr->SharedPtr->CommonSmallestMaxPosVref;
+ } else {
+ NBPtr->SharedPtr->CommonSmallestMaxPosVref = SmallestMaxPosVref;
+ }
+ }
+ NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &SmallestMaxPosVref);
+ NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &SmallestMaxNegVref);
+ NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &Vnom);
+
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tScaled Smallest Max Positive = + %02x\n", SmallestMaxPosVref);
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tScaled Smallest Max Negative =%s%02x\n", ((SmallestMaxNegVref != 0) ? " - " : " "), SmallestMaxNegVref);
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tScaled Vnom = %02x\n", Vnom);
+
+ if (SmallestMaxPosVref > SmallestMaxNegVref) {
+ OffsetFromVref = (SmallestMaxPosVref - SmallestMaxNegVref) / 2;
+ NBPtr->ChannelPtr->MaxVref = Vnom + OffsetFromVref;
+ } else {
+ OffsetFromVref = (SmallestMaxNegVref - SmallestMaxPosVref) / 2;
+ NBPtr->ChannelPtr->MaxVref = Vnom - OffsetFromVref;
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tFinal Vref Offset From Vnom =%s%02x\n",
+ ((OffsetFromVref != 0) ? ((SmallestMaxPosVref > SmallestMaxNegVref) ? " + ":" - "):" "), OffsetFromVref);
+ return TRUE;
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function displays ther results of the 2D search
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *Data - Pointer to Result data structure
+ *
+ */
+VOID
+MemFRdWr2DDisplaySearch (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ )
+{
+ IDS_HDT_CONSOLE_DEBUG_CODE (
+ UINT8 Lane;
+ INT8 Vref;
+ // Display data collected
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDisplaying Data collected\n\n");
+ for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tLane: %02x\n", Lane);
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Vref %s\n", (NBPtr->TechPtr->Direction == DQS_READ_DIR) ? "NegRdDqs PosRdDqs" : "PosWrDat");
+ for (Vref = NBPtr->TotalMaxVrefRange - 2; Vref >= 0; Vref--) {
+ if (Vref < (Data->Vnom - 1)) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t - ");
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", ((Data->Vnom -1) - Vref));
+ } else if (Vref == (Data->Vnom - 1)) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t 00 ");
+ } else {
+ IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t + ");
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Vref - (Data->Vnom - 1));
+ }
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ IDS_HDT_CONSOLE (MEM_FLOW, "%08x", Data->Lane[Lane].Vref[Vref].NegRdWrDly);
+ } else {
+ IDS_HDT_CONSOLE (MEM_FLOW, " ");
+ }
+ IDS_HDT_CONSOLE (MEM_FLOW, "%08x \n", Data->Lane[Lane].Vref[Vref].PosRdWrDly);
+ NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref);
+ }
+ }
+ )
+}
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h
new file mode 100644
index 0000000000..a2a4b02dda
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h
@@ -0,0 +1,266 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mf2DRdWrTraining.h
+ *
+ * Common Definitions ot support 2D Read/Write Training
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/Feat/2DRdWrTraining)
+ * @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.
+ * ***************************************************************************
+ *
+ */
+
+#ifndef _MFRDWR2DTRAINING_H_
+#define _MFRDWR2DTRAINING_H_
+
+/*----------------------------------------------------------------------------
+ * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS)
+ *
+ *----------------------------------------------------------------------------
+ */
+#define MAX_2D_VREF_ENTRIES 0x20 ///< Maximum number of vref entries
+#define MAX_RD_WR_DLY_ENTRIES 0x40 ///< Maximum number of RD/WR Delay Entries
+#define VREF_ADDITIONAL_STEP_SIZE 0x0 ///< Vref Additional Step size
+#define RDDQS_ADDITIONAL_STEP_SIZE 0x0 ///< RdDqs Additional Step size
+#define MAX_2D_RD_SEED_COUNT 4 ///< Max Seed count
+#define MAX_2D_RD_WR_CS_PER_CHANNEL 8 ///< Max CS per Rd Wr delay group
+
+#define EYERIM_PARALLEL_SAMPLING TRUE
+#define EYERIM_BROADCAST_DELAYS TRUE
+
+/// Structure for RD DQS 2D training RDDQS delays.
+typedef struct {
+ UINT32 PosRdWrDly; ///< Positive RdDQS Delay
+ UINT32 NegRdWrDly; ///< Negative RdDQS Delay
+} RD_WR_2D;
+
+/// Structure for RD DQS 2D training Vref delays
+typedef struct {
+ RD_WR_2D *Vref; ///< Pointer to Vref Entries
+ UINT16 Convolution[MAX_RD_WR_DLY_ENTRIES]; ///< Total number of passes in each mask
+ UINT8 PosHeight[MAX_RD_WR_DLY_ENTRIES / 2]; ///< Positive Vref height Height per Delay
+ UINT8 NegHeight[MAX_RD_WR_DLY_ENTRIES / 2]; ///< Negative Vref height Height per Delay
+ UINT8 HalfDiamondHeight; ///< Half of the Height per BL (height of pos/neg vref)
+ UINT8 MaxRdWrDly; ///< Max RdWrDly from convolution function
+} VREF_RD_WR_2D;
+
+/// Structure for RD DQS 2D training Nibbles
+typedef struct {
+ VREF_RD_WR_2D Lane[MAX_NUMBER_LANES]; ///< Bytelane or Nibble
+ UINT8 Vnom; ///< Nominal Vref value
+ UINT8 MaxRdWrSweep; ///< Maximum RdDqs or WrDat Sweep size
+ UINT8 SmallestPosMaxVrefperCS[MAX_2D_RD_WR_CS_PER_CHANNEL]; ///< Smallest Positive Max Vref per CS
+ UINT8 SmallestNegMaxVrefperCS[MAX_2D_RD_WR_CS_PER_CHANNEL]; ///< Smallest Negative Max Vref per CS
+ UINT8 DiamondLeft[MAX_2D_VREF_ENTRIES]; ///< Left edge of Diamond for shape display
+ UINT8 DiamondRight[MAX_2D_VREF_ENTRIES]; ///< Left edge of Diamond for shape display
+ UINT8 RdWrDly; ///< RdDQS or WrDat Dly setting
+ VOID* SavedData; ///< Algorithm-specific saved data
+} MEM_RD_WR_2D_ENTRY;
+
+/// Structure used for RD WR 2D Eye Rim Search Algorithm
+typedef struct {
+ VREF_RD_WR_2D LaneSaved[MAX_NUMBER_LANES]; ///< Bytelane or Nibble that was saved
+ INT8 xMin; ///< Minimum value for RdDqs delays
+ INT8 xMax; ///< Maximum value for RdDqs delays
+ INT8 yMin; ///< Minimum value for vref delays
+ INT8 yMax; ///< Maximum value for vref delays
+ BOOLEAN ParallelSampling; ///< Flag to indicate parallel sampling feature
+ BOOLEAN BroadcastDelays; ///< Flag to indicate if delays will be broadcast
+ UINT32 SampleCount; ///< Count of samples taken
+ UINT32 VrefUpdates; ///< Count of updates to Vref
+ UINT32 RdWrDlyUpdates; ///< Count of updates to RdWrDly
+ INT8 Dirs[2]; ///< List of directions to search for each axis
+} MEM_RD_WR_2D_RIM_ENTRY;
+
+/*-----------------------------------------------------------------------------
+ * DEFINITIONS AND MACROS
+ *
+ *-----------------------------------------------------------------------------
+ */
+BOOLEAN
+MemFAmdRdWr2DTraining (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID* OptParam
+ );
+
+BOOLEAN
+MemFCheckRdWr2DTrainingPerConfig (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ );
+
+BOOLEAN
+MemFRdWr2DProgramIntExtVrefSelect (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ );
+
+BOOLEAN
+MemFRdWr2DProgramVref (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *VrefPtr
+ );
+
+BOOLEAN
+MemFRdWr2DProgramDelays (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *Delay
+ );
+
+VOID
+MemFRdWr2DStoreResult (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data,
+ IN UINT32 InPhaseResult[],
+ IN UINT32 PhaseResult180[]
+ );
+
+BOOLEAN
+MemFRdWr2DHeight (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ );
+
+UINT8
+MemFGetRdWr2DWidth (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ );
+
+BOOLEAN
+MemFCheckRdWr2DDiamondMaskStep (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data,
+ IN UINT8 Vref,
+ IN UINT8 Lane
+ );
+BOOLEAN
+MemFRdWr2DApplyMask (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ );
+
+BOOLEAN
+MemFRdWr2DProcessConvolution (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ );
+
+BOOLEAN
+MemFRdWr2DProgramMaxDelays (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ );
+
+BOOLEAN
+MemFRdWr2DFindCsVrefMargin (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ );
+
+BOOLEAN
+MemFRdWr2DFinalVrefMargin (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ );
+
+UINT8
+MemFRdWr2DGetMaxLanes (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ );
+
+BOOLEAN
+MemFRdWr2DTrainingInit (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ );
+
+BOOLEAN
+MemFRdWr2DEyeRimSearch (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *OptParam
+ );
+
+VOID
+MemFRdWr2DDisplaySearch (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN MEM_RD_WR_2D_ENTRY *Data
+ );
+
+/*******************************
+* Pattern Generation Functions
+*******************************/
+BOOLEAN
+MemFRdWr2DCompareInPhase (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ OUT VOID *Result
+ );
+
+BOOLEAN
+MemFRdWr2DCompare180Phase (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ OUT VOID *Result
+ );
+
+BOOLEAN
+MemFRdWr2DInitVictim (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ );
+
+BOOLEAN
+MemFRdWr2DInitVictimChipSel (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ );
+
+BOOLEAN
+MemFRdWr2DStartVictim (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *SeedCountPtr
+ );
+
+BOOLEAN
+MemFRdWr2DFinalizeVictim (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ );
+
+BOOLEAN
+MemFRdWr2DProgramDataPattern (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID* PatternIndexPtr
+ );
+
+#endif /* _MFRDWR2DTRAINING_H_ */