AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / Recovery / Mem / NB / mrndct.c
diff --git a/src/vendorcode/amd/agesa/f15/Proc/Recovery/Mem/NB/mrndct.c b/src/vendorcode/amd/agesa/f15/Proc/Recovery/Mem/NB/mrndct.c
new file mode 100644 (file)
index 0000000..2333be7
--- /dev/null
@@ -0,0 +1,1577 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mrndct.c
+ *
+ * Northbridge common DCT support for Recovery
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Proc/Recovery/Mem/NB)
+ * @e \$Revision: 50454 $ @e \$Date: 2011-04-10 21:20:37 -0600 (Sun, 10 Apr 2011) $
+ *
+ **/
+/*****************************************************************************
+*
+* Copyright (C) 2012 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 "OptionMemory.h"
+#include "PlatformMemoryConfiguration.h"
+#include "Ids.h"
+#include "mrport.h"
+#include "cpuFamRegisters.h"
+#include "mm.h"
+#include "mn.h"
+#include "mt.h"
+#include "mru.h"
+#include "ma.h"
+#include "Filecode.h"
+#define FILECODE PROC_RECOVERY_MEM_NB_MRNDCT_FILECODE
+/*----------------------------------------------------------------------------
+ *                          DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+#define RECDEF_CSMASK_REG         0x00083FE0
+#define RECDEF_DRAM_BASE_REG      0x00000003
+
+
+/*----------------------------------------------------------------------------
+ *                           TYPEDEFS AND STRUCTURES
+ *
+ *----------------------------------------------------------------------------
+ */
+/// Type of an entry for processing phy init compensation for client NB
+typedef struct {
+  BIT_FIELD_NAME IndexBitField;          ///< Bit field on which the value is decided
+  BIT_FIELD_NAME StartTargetBitField;    ///< First bit field to be modified
+  BIT_FIELD_NAME EndTargetBitField;      ///< Last bit field to be modified
+  UINT16 ExtraValue;                     ///< Extra value needed to be written to bit field
+  CONST UINT16 (*TxPrePN)[4];         ///< Pointer to slew rate table
+} REC_PHY_COMP_INIT_CLIENTNB;
+
+/*----------------------------------------------------------------------------
+ *                        PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+VOID
+STATIC
+MemRecTCtlOnDimmMirrorNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       BOOLEAN SetFlag
+  );
+
+VOID
+STATIC
+MemRecNSwapBitsNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  );
+
+VOID
+STATIC
+MemRecNProgNbPstateDependentRegClientNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  );
+
+VOID
+STATIC
+MemRecNTrainPhyFenceNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  );
+
+VOID
+STATIC
+MemRecNCommonReadWritePatternUnb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       UINT8  CmdType,
+  IN       UINT16 ClCount
+  );
+
+/*----------------------------------------------------------------------------
+ *                            EXPORTED FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *      This function programs the memory controller with configuration parameters
+ *
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ *     @return          TRUE - An Error value lower than AGESA_ERROR may have occurred
+ *     @return          FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred
+ */
+
+BOOLEAN
+MemRecNAutoConfigNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  UINT8 Dimm;
+  UINT8 Dct;
+  UINT8 ChipSel;
+  UINT32 CSBase;
+  DCT_STRUCT *DCTPtr;
+  CH_DEF_STRUCT *ChannelPtr;
+  UINT16 i;
+
+  Dct = NBPtr->Dct;
+  DCTPtr = NBPtr->DCTPtr;
+  ChannelPtr = NBPtr->ChannelPtr;
+
+  //Prepare variables for future usage.
+  for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
+    if ((ChannelPtr->ChDimmValid & (UINT8) 1 << Dimm) != 0) {
+      DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2);
+      if (((ChannelPtr->DimmDrPresent & (UINT8) 1 << Dimm) == 0) && ((ChannelPtr->DimmQrPresent & (UINT8) 1 << Dimm) == 0)) {
+        continue;
+      } else {
+        DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2 + 1);
+      }
+    }
+  }
+
+  Dimm = NBPtr->DimmToBeUsed;
+
+  //Temporarily set all CS Base/Limit registers (corresponding to Dimms exist on a channel) with 256MB size for WL training.
+  CSBase = 0;
+  for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
+    if (DCTPtr->Timings.CsPresent & (UINT8) 1 << ChipSel) {
+
+      CSBase &= (UINT32) ~0x08; //Clear OnDimmMirror bit.
+      if (((ChipSel & 1) != 0) && ((ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0)) {
+        CSBase |= (UINT32) 0x08; //Set OnDimmMirror bit.
+      }
+      MemRecNSetBitFieldNb (NBPtr, (BFCSBaseAddr0Reg + ChipSel), (CSBase | 0x01));
+      CSBase += 0x100000;
+      if ((ChipSel & 1) == 0) {
+        MemRecNSetBitFieldNb (NBPtr, (BFCSMask0Reg + (ChipSel >> 1)), RECDEF_CSMASK_REG);
+      }
+    }
+  }
+  MemRecNSetBitFieldNb (NBPtr, BFDramBaseReg0, RECDEF_DRAM_BASE_REG);
+  MemRecNSetBitFieldNb (NBPtr, BFDramLimitReg0, 0x70000);
+
+  // Disable the other DCT
+  NBPtr->MemRecNSwitchDctNb (NBPtr, Dct ^ 0x01);
+  MemRecNSetBitFieldNb (NBPtr, BFDisDramInterface, 1);
+  NBPtr->MemRecNSwitchDctNb (NBPtr, Dct);
+  if (Dct != 0) {
+    // If DCT 1, set DctSelBase registers
+    MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseAddrReg, 0x00000003);
+    MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseOffsetReg, 0x00000000);
+  }
+
+  // Use default values for common registers
+  i = 0;
+  while (NBPtr->RecModeDefRegArray[i] != NULL) {
+    MemRecNSetBitFieldNb (NBPtr, NBPtr->RecModeDefRegArray[i], NBPtr->RecModeDefRegArray[i + 1]);
+    i += 2;
+  }
+
+  // Other specific settings
+  MemRecNSetBitFieldNb (NBPtr, BFX4Dimm, ChannelPtr->Dimmx4Present );
+
+  if ((ChannelPtr->RegDimmPresent  == 0) && (ChannelPtr->SODimmPresent == 0)) {
+    MemRecNSetBitFieldNb (NBPtr, BFUnBuffDimm, 1);
+  }
+
+  if ((NBPtr->ChannelPtr->RegDimmPresent != 0) && (NBPtr->ChannelPtr->TechType == DDR3_TECHNOLOGY)) {
+    MemRecNSetBitFieldNb (NBPtr, BFSubMemclkRegDly, 1);
+  }
+  MemRecNSetBitFieldNb (NBPtr, BFOdtSwizzle, 1);
+
+  return TRUE;
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *   This function gets platform specific config/timing values from the interface layer and
+ *   programs them into DCT.
+ *
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ *     @return          TRUE - An Error value lower than AGESA_ERROR may have occurred
+ *     @return          FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred
+ */
+
+BOOLEAN
+MemRecNPlatformSpecNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  UINT8 p;
+
+  p = 0;
+  for (p = 0; p < MAX_PLATFORM_TYPES; p++) {
+    if (NBPtr->MemPtr->GetPlatformCfg[p] (NBPtr->MemPtr, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr) == AGESA_SUCCESS) {
+      MemRecNSetBitFieldNb (NBPtr, BFODCControl, NBPtr->ChannelPtr->DctOdcCtl);
+      MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, NBPtr->ChannelPtr->DctAddrTmg);
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *   This function  reads MemClkFreqVal bit to see if the DIMMs are present in this node.
+ *  If the DIMMs are present then set the DRAM Enable bit for this node.
+ *
+ *  Setting dram init starts up the DCT state machine, initializes the
+ *  dram devices with MRS commands, and kicks off any
+ *  HW memory clear process that the chip is capable of.  The sooner
+ *  that dram init is set for all nodes, the faster the memory system
+ *  initialization can complete.  Thus, the init loop is unrolled into
+ *  two loops so as to start the processes for non BSP nodes sooner.
+ *  This procedure will not wait for the process to finish.  Synchronization is
+ *  handled elsewhere.
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemRecNStartupDCTNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  // 1. Ensure F2x[1, 0]9C_x08[DisAutoComp] = 1.
+  // 2. BIOS waits 5 us for the disabling of the compensation engine to complete.
+  // ------- Done in InitPhyComp_Nb -------
+  //
+  MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 1);
+  MemRecUWait10ns (500, NBPtr->MemPtr);
+
+  //MemRecNSetBitFieldNb (NBPtr, BFInitDram, 1);    // HW Dram init
+  AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
+  NBPtr->TechPtr->DramInit (NBPtr->TechPtr);
+
+  // 7. Program F2x[1, 0]9C_x08[DisAutoComp] = 0.
+  // 8. BIOS must wait 750 us for the phy compensation engine
+  //    to reinitialize.
+  //
+  MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 0);
+  MemRecUWait10ns (75000, NBPtr->MemPtr);
+
+  while (MemRecNGetBitFieldNb (NBPtr, BFDramEnabled) == 0);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function initializes the DRAM devices on all DCTs at the same time
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemRecNStartupDCTClientNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", NBPtr->Dct);
+
+  // Program D18F2x[1,0]9C_x0000_000B = 80000000h. #109999.
+  MemRecNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x80000000);
+
+  // Program D18F2x[1,0]9C_x0D0F_E013[PllRegWaitTime] = 0118h. #193770.
+  MemRecNSetBitFieldNb (NBPtr, BFPllRegWaitTime, 0x118);
+
+  // Phy Voltage Level Programming
+  MemRecNPhyVoltageLevelNb (NBPtr);
+
+  // Run frequency change sequence
+  MemRecNSetBitFieldNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
+  MemRecNSetBitFieldNb (NBPtr, BFMemClkFreq, 6);
+  MemRecNProgNbPstateDependentRegClientNb (NBPtr);
+  MemRecNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 1);
+  MemRecNSetBitFieldNb (NBPtr, BFPllLockTime, 0x000F);
+
+  IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClkAlign=0\n");
+  IDS_HDT_CONSOLE (MEM_FLOW, "\tEnDramInit = 1 for DCT%d\n", NBPtr->Dct);
+  MemRecNSetBitFieldNb (NBPtr, BFDbeGskMemClkAlignMode, 0);
+  MemRecNSetBitFieldNb (NBPtr, BFEnDramInit, 1);
+
+  // Run DramInit sequence
+  AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
+  NBPtr->TechPtr->DramInit (NBPtr->TechPtr);
+  IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq: %d MHz\n", DDR800_FREQUENCY);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *   This function  sets the maximum round-trip latency in the system from the processor to the DRAM
+ *   devices and back.
+
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *     @param[in]     MaxRcvEnDly - Maximum receiver enable delay value
+ *
+ */
+
+VOID
+MemRecNSetMaxLatencyNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       UINT16 MaxRcvEnDly
+  )
+{
+  UINT16 SubTotal;
+
+  AGESA_TESTPOINT (TpProcMemRcvrCalcLatency, &(NBPtr->MemPtr->StdHeader));
+
+  // Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs UINTs.
+  SubTotal = 6 * 2;
+
+  // If registered DIMMs are being used then add 1 MEMCLK to the sub-total.
+  if (MemRecNGetBitFieldNb (NBPtr, BFUnBuffDimm) == 0) {
+    SubTotal += 2;
+  }
+
+  // if (AddrCmdSetup || CsOdtSetup || CkeSetup) then K := K + 2;
+  SubTotal += 2;
+
+  // If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs,
+  // then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total.
+  //
+  SubTotal += 8 - 5;
+
+  // Add the maximum (worst case) delay value of DqsRcvEnGrossDelay
+  // that exists across all DIMMs and byte lanes.
+  //
+  SubTotal += MaxRcvEnDly >> 5;
+
+  // Add 5.5 to the sub-total. 5.5 represents part of the processor
+  // specific constant delay value in the DRAM clock domain.
+  //
+  SubTotal += 5;             // add 5.5 1/2MemClk
+
+  // Convert the sub-total (in 1/2 MEMCLKs) to northbridge clocks (NCLKs)
+  // as follows (assuming DDR400 and assuming that no P-state or link speed
+  // changes have occurred).
+  //
+  //     Simplified formula:
+  //     SubTotal *= (Fn2xD4[NBFid]+4)/4
+  //
+  SubTotal = SubTotal * ((UINT16) MemRecNGetBitFieldNb (NBPtr, BFNbFid) + 4);
+  SubTotal /= 4;
+
+  // Add 5 NCLKs to the sub-total. 5 represents part of the processor
+  // specific constant value in the northbridge clock domain.
+  //
+  SubTotal += 5;
+
+  // Program the F2x[1, 0]78[MaxRdLatency] register with the total delay value
+  MemRecNSetBitFieldNb (NBPtr, BFMaxLatency, SubTotal);
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *   Set Dram ODT for mission mode and write leveling mode.
+ *
+ *     @param[in,out]   *NBPtr     - Pointer to the MEM_NB_BLOCK
+ *     @param[in]       OdtMode    - Mission mode or write leveling mode
+ *     @param[in]       ChipSelect - Chip select number
+ *     @param[in]       TargetCS   - Chip select number that is being trained
+ *
+ */
+
+VOID
+MemRecNSetDramOdtNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       ODT_MODE OdtMode,
+  IN       UINT8 ChipSelect,
+  IN       UINT8 TargetCS
+  )
+{
+  UINT8 DramTerm;
+  UINT8 DramTermDyn;
+
+  DramTerm = NBPtr->ChannelPtr->Reserved[0];
+  DramTermDyn = NBPtr->ChannelPtr->Reserved[1];
+
+  if (OdtMode == WRITE_LEVELING_MODE) {
+    if (ChipSelect == TargetCS) {
+      DramTerm = DramTermDyn;
+      MemRecNSetBitFieldNb (NBPtr, BFWrLvOdt, NBPtr->ChannelPtr->PhyWLODT[TargetCS >> 1]);
+    }
+  }
+  MemRecNSetBitFieldNb (NBPtr, BFDramTerm, DramTerm);
+  MemRecNSetBitFieldNb (NBPtr, BFDramTermDyn, DramTermDyn);
+}
+
+/*----------------------------------------------------------------------------
+ *                              LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *   This function sends an MRS command
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemRecNSendMrsCmdNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  BOOLEAN ClearODM;
+  ClearODM = FALSE;
+  if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
+    ClearODM = FALSE;
+    if ((NBPtr->MCTPtr->LogicalCpuid.Revision & AMD_F10_C0) != 0) {
+      if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
+        if (MemRecNGetBitFieldNb (NBPtr, BFEnDramInit) == 0) {
+          // For C0, if EnDramInit bit is cleared, ODM needs to be cleared before sending MRS
+          MemRecTCtlOnDimmMirrorNb (NBPtr, FALSE);
+          ClearODM = TRUE;
+        }
+      }
+    }
+  }
+
+  MemRecNSwapBitsNb (NBPtr);
+
+  IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS%d MR%d %04x\n",
+              MemRecNGetBitFieldNb (NBPtr, BFMrsChipSel),
+              MemRecNGetBitFieldNb (NBPtr, BFMrsBank),
+              MemRecNGetBitFieldNb (NBPtr, BFMrsAddress));
+
+  // 1.Set SendMrsCmd=1
+  MemRecNSetBitFieldNb (NBPtr, BFSendMrsCmd, 1);
+
+  // 2.Wait for SendMrsCmd=0
+  while (MemRecNGetBitFieldNb (NBPtr, BFSendMrsCmd)) {}
+
+  if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
+    if (ClearODM) {
+      // Restore ODM if necessary
+      MemRecTCtlOnDimmMirrorNb (NBPtr, TRUE);
+    }
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *   This function sends the ZQCL command
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemRecNSendZQCmdNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  // 1.Program MrsAddress[10]=1
+  MemRecNSetBitFieldNb (NBPtr, BFMrsAddress, (UINT32) 1 << 10);
+
+  // 2.Set SendZQCmd=1
+  MemRecNSetBitFieldNb (NBPtr, BFSendZQCmd, 1);
+
+  // 3.Wait for SendZQCmd=0
+  while (MemRecNGetBitFieldNb (NBPtr, BFSendZQCmd)) {}
+
+  // 4.Wait 512 MEMCLKs
+  MemRecUWait10ns (128, NBPtr->MemPtr);   // 512*2.5ns=1280, wait 1280ns
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *      This function disables/enables F2x[1, 0][5C:40][OnDimmMirror]
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *     @param[in]   SetFlag   - Enable or disable flag - TRUE - Enable, FALSE - DISABLE
+ *
+ */
+
+VOID
+STATIC
+MemRecTCtlOnDimmMirrorNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       BOOLEAN SetFlag
+  )
+{
+  UINT8 Chipsel;
+  UINT32 CSBaseAddrReg;
+
+  for (Chipsel = 0; Chipsel < MAX_CS_PER_CHANNEL; Chipsel += 2) {
+    CSBaseAddrReg = MemRecNGetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel);
+    if ((CSBaseAddrReg & 1) == 1) {
+      if (SetFlag && ((NBPtr->ChannelPtr->DimmMirrorPresent & ((UINT8) 1 << (Chipsel >> 1))) != 0)) {
+        CSBaseAddrReg |= ((UINT32) 1 << BFOnDimmMirror);
+      } else {
+        CSBaseAddrReg &= ~((UINT32) 1 << BFOnDimmMirror);
+      }
+      MemRecNSetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel, CSBaseAddrReg);
+    }
+  }
+}
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ *   This function swaps bits for OnDimmMirror support
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+STATIC
+MemRecNSwapBitsNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  UINT8 ChipSel;
+  UINT32 MRSReg;
+
+  ChipSel = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFMrsChipSel);
+  if ((ChipSel & 1) != 0) {
+    MRSReg = MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg);
+    if ((NBPtr->ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0) {
+      MRSReg = (MRSReg & 0xFFFCFE07) | ((MRSReg&0x100A8) << 1) | ((MRSReg&0x20150) >> 1);
+      MemRecNSetBitFieldNb (NBPtr, BFDramInitRegReg, MRSReg);
+    }
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ *   This function gets the total of sync components for Max Read Latency calculation
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ *     @return      Total in 1/2 MEMCLKs
+ */
+
+UINT32
+MemRecNTotalSyncComponentsClientNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  UINT32 T;
+  UINT32 P;
+  UINT32 AddrTmgCtl;
+  UINT32 MemClkPeriod;
+
+  AGESA_TESTPOINT (TpProcMemRcvrCalcLatency , &(NBPtr->MemPtr->StdHeader));
+
+  // P = P + ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16) where RdPtrInitMin = RdPtrInit
+  P = 0;
+
+  AddrTmgCtl = MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl);
+  if (((AddrTmgCtl >> 16) & 0x20) != (AddrTmgCtl & 0x20)) {
+    P += 1;
+  }
+
+  // IF (DbeGskMemClkAlignMode==01b || (DbeGskMemClkAlignMode==00b && !(AddrCmdSetup==CsOdtSetup==CkeSetup)))
+  // THEN P = P + 1
+
+  // IF (SlowAccessMode==1) THEN P = P + 2
+
+  // T = T + (0.5 * MemClkPeriod) - 786 ps
+  MemClkPeriod = 1000000 / DDR800_FREQUENCY;
+  T = MemClkPeriod / 2 - 768;
+
+  // If (AddrCmdSetup==0 && CsOdtSetup==0 && CkeSetup==0)
+  // then P = P + 1
+  // else P = P + 2
+  if ((AddrTmgCtl & 0x0202020) == 0) {
+    P += 1;
+  } else {
+    P += 2;
+  }
+
+  // P = P + (2 * (D18F2x[1,0]88[Tcl] clocks - 1))
+  P += 2 * 5;  // Tcl = 6 clocks
+
+  // (DisCutThroughMode = 0), so P = P + 3
+  P += 3;
+
+  return ((P * MemClkPeriod + 1) / 2) + T;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *     This function programs the phy registers according to the desired phy VDDIO voltage level
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemRecNPhyVoltageLevelNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  BIT_FIELD_NAME BitField;
+  UINT16 Value;
+  UINT16 Mask;
+
+  Mask = 0xFFE7;
+  Value = (UINT16) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage) << 3;
+
+  for (BitField = BFDataRxVioLvl; BitField <= BFCmpVioLvl; BitField++) {
+    if (BitField == BFCmpVioLvl) {
+      Mask = 0x3FFF;
+      Value <<= (14 - 3);
+    }
+    MemRecNSetBitFieldNb (NBPtr, BitField, ((MemRecNGetBitFieldNb (NBPtr, BitField) & Mask)) | Value);
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ *   This function executes Phy fence training
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+STATIC
+MemRecNTrainPhyFenceNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  UINT8 Byte;
+  UINT16 Avg;
+  UINT8 PREvalue;
+
+  if (MemRecNGetBitFieldNb (NBPtr, BFDisDramInterface)) {
+    return;
+  }
+
+  // 1. BIOS first programs a seed value to the phase recovery
+  //    engine registers.
+  //
+  IDS_HDT_CONSOLE (MEM_FLOW, "\t\tSeeds: ");
+  for (Byte = 0; Byte < 9; Byte++) {
+    // This includes ECC as byte 8
+    MemRecNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19);
+    IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", 19);
+  }
+
+  IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tPhyFenceTrEn = 1");
+  // 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1.
+  MemRecNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1);
+
+  MemRecUWait10ns (5000, NBPtr->MemPtr);
+
+  // 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0.
+  MemRecNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 0);
+
+  // 5. BIOS reads the phase recovery engine registers
+  //    F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52.
+  // 6. Calculate the average value of the fine delay and subtract 8.
+  //
+  Avg = 0;
+  for (Byte = 0; Byte < 9; Byte++) {
+    // This includes ECC as byte 8
+    PREvalue = (UINT8) (0x1F & MemRecNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte)));
+    Avg = Avg + ((UINT16) PREvalue);
+  }
+  Avg = ((Avg + 8) / 9);    // round up
+  Avg -= 6;
+
+  // 7. Write the value to F2x[1, 0]9C_x0C[PhyFence].
+  MemRecNSetBitFieldNb (NBPtr, BFPhyFence, Avg);
+
+  // 8. BIOS rewrites F2x[1, 0]9C_x04, DRAM Address/Command Timing Control
+  //    Register delays for both channels. This forces the phy to recompute
+  //    the fence.
+  //
+  MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl));
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function calculates and programs NB P-state dependent registers
+ *
+ *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+STATIC
+MemRecNProgNbPstateDependentRegClientNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr
+  )
+{
+  UINT8 i;
+  UINT8 NclkFid;
+  UINT16 MemClkDid;
+  UINT8 PllMult;
+  UINT8 NclkDiv;
+  UINT8 RdPtrInit;
+  UINT32 NclkPeriod;
+  UINT32 MemClkPeriod;
+  INT32 PartialSum2x;
+  INT32 PartialSumSlotI2x;
+  INT32 RdPtrInitRmdr2x;
+
+  NclkFid = (UINT8) (MemRecNGetBitFieldNb (NBPtr, BFMainPllOpFreqId) + 0x10);
+  MemClkDid = 2; //BKDG recommended value for DDR800
+  PllMult = 16;  //BKDG recommended value for DDR800
+  NclkDiv = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFNbPs0NclkDiv);
+
+  NclkPeriod = (2500 * NclkDiv) / NclkFid;
+  MemClkPeriod = 1000000 / DDR800_FREQUENCY;
+  NBPtr->NBClkFreq = ((UINT32) NclkFid * 400) / NclkDiv;
+
+  IDS_HDT_CONSOLE (MEM_FLOW, "\n\tNB P%d  Freq: %dMHz\n", 0, NBPtr->NBClkFreq);
+  IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClk Freq: %dMHz\n", DDR800_FREQUENCY);
+
+  // D18F2x[1,0]78[RdPtrInit] = IF (D18F2x[1,0]94[MemClkFreq] >= 667 MHz) THEN 7 ELSE 8 ENDIF (Llano)
+  //                                                                      THEN 2 ELSE 3 ENDIF (Ontario)
+  RdPtrInit = NBPtr->FreqChangeParam->RdPtrInitLower667;
+  MemRecNSetBitFieldNb (NBPtr, BFRdPtrInit, RdPtrInit);
+  IDS_HDT_CONSOLE (MEM_FLOW, "\t\tRdPtr: %d\n", RdPtrInit);
+
+  // Program D18F2x[1,0]F4_x30[DbeGskFifoNumerator] and D18F2x[1,0]F4_x31[DbeGskFifoDenominator].
+  MemRecNSetBitFieldNb (NBPtr, BFDbeGskFifoNumerator, NclkFid * MemClkDid * 16);
+  MemRecNSetBitFieldNb (NBPtr, BFDbeGskFifoDenominator, PllMult * NclkDiv);
+
+  IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoNumerator: %d\n", NclkFid * MemClkDid * 16);
+  IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoDenominator: %d\n", PllMult * NclkDiv);
+
+  // Program D18F2x[1,0]F4_x32[DataTxFifoSchedDlyNegSlot1, DataTxFifoSchedDlySlot1,
+  // DataTxFifoSchedDlyNegSlot0, DataTxFifoSchedDlySlot0].
+  //   PartialSum = ((7 * NclkPeriod) + (1.5 * MemClkPeriod) + 520ps)*MemClkFrequency - tCWL -
+  //   CmdSetup - PtrSeparation - 1. (Llano)
+  //   PartialSum = ((5 * NclkPeriod) + MemClkPeriod) + 520ps)*MemClkFrequency - tCWL -
+  //   CmdSetup - PtrSeparation - 1. (Ontario)
+  PartialSum2x = NBPtr->FreqChangeParam->NclkPeriodMul2x * NclkPeriod;
+  PartialSum2x += NBPtr->FreqChangeParam->MemClkPeriodMul2x * MemClkPeriod;
+  PartialSum2x += 520 * 2;
+
+  // PtrSeparation = ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16)/2 + RdPtrInitRmdr
+  // RdPtrInitRmdr = (((2.25 * MemClkPeriod) - 1520ps) MOD MemClkPeriod)/MemClkPeriod
+  RdPtrInitRmdr2x = ((NBPtr->FreqChangeParam->SyncTimeMul4x * MemClkPeriod) / 2) - 2 * (NBPtr->FreqChangeParam->TDataPropLower800 + 520);
+  RdPtrInitRmdr2x %= MemClkPeriod;
+  PartialSum2x -= RdPtrInitRmdr2x;
+  PartialSum2x = (PartialSum2x + MemClkPeriod - 1) / MemClkPeriod;  // round-up here
+  PartialSum2x -= 2 * 5;  //Tcwl + 5
+
+  if ((MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl) & 0x0202020) == 0) {
+    PartialSum2x -= 1;
+  } else {
+    PartialSum2x -= 2;
+  }
+  PartialSum2x -= 2;
+
+  // If PartialSumSlotN is positive:
+  //   DataTxFifoSchedDlySlotN=CEIL(PartialSumSlotN).
+  //   DataTxFifoSchedDlyNegSlotN=0.
+  // Else if PartialSumSlotN is negative:
+  //   DataTxFifoSchedDlySlotN=ABS(CEIL(PartialSumSlotN*MemClkPeriod/NclkPeriod)).
+  //   DataTxFifoSchedDlyNegSlotN=1.
+  for (i = 0; i < 2; i++) {
+    PartialSumSlotI2x = PartialSum2x;
+    if (i == 0) {
+    PartialSumSlotI2x += 2;
+    }
+    if (PartialSumSlotI2x > 0) {
+      MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 0);
+      MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, (PartialSumSlotI2x + 1) / 2);
+      IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: %d\n", i, (PartialSumSlotI2x + 1) / 2);
+    } else {
+      MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 1);
+      PartialSumSlotI2x = ((-PartialSumSlotI2x) * MemClkPeriod) / (2 * NclkPeriod);
+      MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, PartialSumSlotI2x);
+      IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: -%d\n", i, PartialSumSlotI2x);
+    }
+  }
+  // Program ProcOdtAdv
+  MemRecNSetBitFieldNb (NBPtr, BFProcOdtAdv, 0);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ *      This function reads cache lines continuously using TCB CPG engine
+ *
+ *     @param[in,out] NBPtr  - Pointer to the MEM_NB_BLOCK
+ *     @param[in,out] Buffer - Array of bytes to be filled with data read from DRAM
+ *     @param[in]     Address - System Address [47:16]
+ *     @param[in] ClCount - Number of cache lines
+ *
+ */
+
+VOID
+MemRecNContReadPatternClientNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       UINT8 Buffer[],
+  IN       UINT32 Address,
+  IN       UINT16 ClCount
+  )
+{
+  // 1. Program D18F2x1C0[RdDramTrainMode]=1.
+  MemRecNSetBitFieldNb (NBPtr, BFRdDramTrainMode, 1);
+
+  // 2. Program D18F2x1C0[TrainLength] to the appropriate number of cache lines.
+  MemRecNSetBitFieldNb (NBPtr, BFTrainLength, ClCount);
+
+  // 3. Program the DRAM training address as follows:
+  MemRecNSetBitFieldNb (NBPtr, BFWrTrainAdrPtrLo, (Address >> 6));
+
+  // 4. Program D18F2x1D0[WrTrainBufAddr]=000h
+  MemRecNSetBitFieldNb (NBPtr, BFWrTrainBufAddr, 0);
+
+  // 5. Program D18F2x1C0[RdTrainGo]=1.
+  MemRecNSetBitFieldNb (NBPtr, BFRdTrainGo, 1);
+
+  // 6. Wait for D18F2x1C0[RdTrainGo]=0.
+  while (MemRecNGetBitFieldNb (NBPtr, BFRdTrainGo) != 0) {}
+
+  // 7. Read D18F2x1E8[TrainCmpSts] and D18F2x1E8[TrainCmpSts2].
+
+  // 8. Program D18F2x1C0[RdDramTrainMode]=0.
+  MemRecNSetBitFieldNb (NBPtr, BFRdDramTrainMode, 0);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *     This is function sets the platform specific settings for the systems with UDIMMs configuration
+ *
+ *     @param[in,out]   *MemData           Pointer to MEM_DATA_STRUCTURE
+ *     @param[in]       SocketID        Socket number
+ *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
+ *
+ *     @return          AGESA_SUCCESS
+ *     @return          CurrentChannel->DctAddrTmg        Address Command Timing Settings for specified channel
+ *     @return          CurrentChannel->DctOdcCtl         Drive Strength settings for specified channel
+ *     @return          CurrentChannel->Reserved[0]       Dram Term for specified channel
+ *     @return          CurrentChannel->Reserved[1]       Dynamic Dram Term for specified channel
+ *     @return          CurrentChannel->PhyWLODT[0]       WL ODT for DIMM0
+ *     @return          CurrentChannel->PhyWLODT[1]       WL ODT for DIMM1
+ *     @return          CurrentChannel->PhyWLODT[2]       WL ODT for DIMM2
+ *     @return          CurrentChannel->PhyWLODT[3]       WL ODT for DIMM3
+ *
+ */
+AGESA_STATUS
+MemRecNGetPsCfgUDIMM3Nb (
+  IN OUT   MEM_DATA_STRUCT *MemData,
+  IN       UINT8 SocketID,
+  IN OUT   CH_DEF_STRUCT *CurrentChannel
+  )
+{
+  UINT32 AddrTmgCTL;
+  UINT32 DctOdcCtl;
+  UINT8 Dimms;
+  UINT8  MaxDimmPerCH;
+  UINT8 DramTerm;
+  UINT8 DramTermDyn;
+
+  if ((CurrentChannel->RegDimmPresent != 0) || (CurrentChannel->SODimmPresent != 0)) {
+    return AGESA_UNSUPPORTED;
+  }
+
+  Dimms = CurrentChannel->Dimms;
+  MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
+
+  if (MaxDimmPerCH == 1) {
+    return AGESA_UNSUPPORTED;
+  } else {
+    DctOdcCtl = 0x20223323;
+    AddrTmgCTL = 0x00390039;
+    if (Dimms == 1) {
+      DctOdcCtl = 0x20113222;
+      AddrTmgCTL = 0x00390039;
+      if (CurrentChannel->Loads == 16) {
+        AddrTmgCTL = 0x003B0000;
+      }
+    }
+  }
+  CurrentChannel->DctAddrTmg = AddrTmgCTL;
+  CurrentChannel->DctOdcCtl = DctOdcCtl;
+
+  // ODT
+  if (Dimms == 1) {
+    DramTerm = 1; // 60 ohms
+    DramTermDyn = 0; // Disable
+    if ((MaxDimmPerCH == 3) && (CurrentChannel->DimmDrPresent != 0)) {
+      DramTermDyn = 1; // 60 ohms
+    }
+  } else {
+    DramTerm = 3; // 40 ohms
+    DramTermDyn = 2; // 120 ohms
+  }
+  CurrentChannel->Reserved[0] = DramTerm;
+  CurrentChannel->Reserved[1] = DramTermDyn;
+
+  // WL ODT
+  if (Dimms == 1) {
+    CurrentChannel->PhyWLODT[0] = 0;
+    CurrentChannel->PhyWLODT[1] = (CurrentChannel->DimmDrPresent != 0) ? 8 : 2;
+  } else {
+    CurrentChannel->PhyWLODT[0] = 3;
+    CurrentChannel->PhyWLODT[1] = 3;
+  }
+  CurrentChannel->PhyWLODT[2] = 0;
+  CurrentChannel->PhyWLODT[3] = 0;
+
+  return AGESA_SUCCESS;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *     This is function sets the platform specific settings for the systems with SODIMMs configuration
+ *
+ *     @param[in,out]   *MemData           Pointer to MEM_DATA_STRUCTURE
+ *     @param[in]       SocketID        Socket number
+ *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
+ *
+ *     @return          AGESA_SUCCESS
+ *     @return          CurrentChannel->DctAddrTmg        Address Command Timing Settings for specified channel
+ *     @return          CurrentChannel->DctOdcCtl         Drive Strength settings for specified channel
+ *     @return          CurrentChannel->Reserved[0]       Dram Term for specified channel
+ *     @return          CurrentChannel->Reserved[1]       Dynamic Dram Term for specified channel
+ *     @return          CurrentChannel->PhyWLODT[0]       WL ODT for DIMM0
+ *     @return          CurrentChannel->PhyWLODT[1]       WL ODT for DIMM1
+ *     @return          CurrentChannel->PhyWLODT[2]       WL ODT for DIMM2
+ *     @return          CurrentChannel->PhyWLODT[3]       WL ODT for DIMM3
+ *
+ */
+AGESA_STATUS
+MemRecNGetPsCfgSODIMM3Nb (
+  IN OUT   MEM_DATA_STRUCT *MemData,
+  IN       UINT8 SocketID,
+  IN OUT   CH_DEF_STRUCT *CurrentChannel
+  )
+{
+  UINT32 AddrTmgCTL;
+  UINT32 DctOdcCtl;
+  UINT8  MaxDimmPerCH;
+  UINT8 Dimms;
+  UINT8 DramTerm;
+  UINT8 DramTermDyn;
+
+  if (CurrentChannel->SODimmPresent != CurrentChannel->ChDimmValid) {
+    return AGESA_UNSUPPORTED;
+  }
+
+  Dimms = CurrentChannel->Dimms;
+  MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
+
+  if (MaxDimmPerCH == 1) {
+    DctOdcCtl = 0x00113222;
+    AddrTmgCTL = 0;
+  } else {
+    DctOdcCtl = 0x00223323;
+    AddrTmgCTL = 0x00000039;
+    if (Dimms == 1) {
+      DctOdcCtl = 0x00113222;
+      AddrTmgCTL = 0;
+    }
+  }
+  CurrentChannel->DctAddrTmg = AddrTmgCTL;
+  CurrentChannel->DctOdcCtl = DctOdcCtl;
+
+  // ODT
+  if (Dimms == 1) {
+    DramTerm = 2; // 120 ohms
+    DramTermDyn = 0; // Disable
+    if (MaxDimmPerCH == 2) {
+      DramTerm = 1; // 60 ohms
+    }
+  } else {
+    DramTerm = 3; // 40 ohms
+    DramTermDyn = 2; // 120 ohms
+  }
+  CurrentChannel->Reserved[0] = DramTerm;
+  CurrentChannel->Reserved[1] = DramTermDyn;
+
+  // WL ODT
+  if (Dimms == 1) {
+    if (MaxDimmPerCH == 1) {
+      CurrentChannel->PhyWLODT[0] = (CurrentChannel->DimmDrPresent != 0) ? 4 : 1;
+      CurrentChannel->PhyWLODT[1] = 0;
+    } else {
+      CurrentChannel->PhyWLODT[0] = 0;
+      CurrentChannel->PhyWLODT[1] = (CurrentChannel->DimmDrPresent != 0) ? 8 : 2;
+    }
+  } else {
+    CurrentChannel->PhyWLODT[0] = 3;
+    CurrentChannel->PhyWLODT[1] = 3;
+  }
+  CurrentChannel->PhyWLODT[2] = 0;
+  CurrentChannel->PhyWLODT[3] = 0;
+
+  return AGESA_SUCCESS;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *     This is function sets the platform specific settings for the systems with RDIMMs configuration
+ *
+ *     @param[in,out]   *MemData           Pointer to MEM_DATA_STRUCTURE
+ *     @param[in]       SocketID        Socket number
+ *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
+ *
+ *     @return          AGESA_SUCCESS
+ *     @return          CurrentChannel->DctAddrTmg        Address Command Timing Settings for specified channel
+ *     @return          CurrentChannel->DctOdcCtl         Drive Strength settings for specified channel
+ *     @return          CurrentChannel->Reserved[0]       Dram Term for specified channel
+ *     @return          CurrentChannel->Reserved[1]       Dynamic Dram Term for specified channel
+ *     @return          CurrentChannel->PhyWLODT[0]       WL ODT for DIMM0
+ *     @return          CurrentChannel->PhyWLODT[1]       WL ODT for DIMM1
+ *     @return          CurrentChannel->PhyWLODT[2]       WL ODT for DIMM2
+ *     @return          CurrentChannel->PhyWLODT[3]       WL ODT for DIMM3
+ *
+ */
+
+AGESA_STATUS
+MemRecNGetPsCfgRDIMM3Nb (
+  IN OUT   MEM_DATA_STRUCT *MemData,
+  IN       UINT8 SocketID,
+  IN OUT   CH_DEF_STRUCT *CurrentChannel
+  )
+{
+  STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg2DIMMsWlODT[] = {
+    {SR_DIMM0,            {0x01, 0x00, 0x00, 0x00}, 1},
+    {DR_DIMM0,            {0x04, 0x00, 0x00, 0x00}, 1},
+    {QR_DIMM0,            {0x05, 0x00, 0x00, 0x00}, 1},
+    {SR_DIMM1,            {0x00, 0x02, 0x00, 0x00}, 1},
+    {DR_DIMM1,            {0x00, 0x08, 0x00, 0x00}, 1},
+    {QR_DIMM1,            {0x00, 0x0A, 0x00, 0x00}, 1},
+    {SR_DIMM0 + DR_DIMM0 + SR_DIMM1 + DR_DIMM1, {0x03, 0x03, 0x00, 0x00}, 2},
+    {SR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
+    {DR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
+    {QR_DIMM0 + SR_DIMM1, {0x03, 0x07, 0x06, 0x00}, 2},
+    {QR_DIMM0 + DR_DIMM1, {0x03, 0x07, 0x06, 0x00}, 2},
+    {QR_DIMM0 + QR_DIMM1, {0x0B, 0x07, 0x0E, 0x0D}, 2}
+  };
+  STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg3DIMMsWlODT[] = {
+    {SR_DIMM2 + DR_DIMM2, {0x00, 0x00, 0x04, 0x00}, 1},
+    {SR_DIMM0 + DR_DIMM0, {0x01, 0x02, 0x00, 0x00}, 1},
+    {SR_DIMM0 + DR_DIMM0 + SR_DIMM2 + DR_DIMM2, {0x05, 0x00, 0x05, 0x00}, 2},
+    {SR_DIMM0 + DR_DIMM0 + SR_DIMM1 + DR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x07, 0x07, 0x07, 0x00}, 3},
+    {QR_DIMM1, {0x00, 0x0A, 0x00, 0x0A}, 1},
+    {QR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x00, 0x06, 0x0E, 0x0C}, 2},
+    {SR_DIMM0 + DR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
+    {SR_DIMM0 + DR_DIMM0 + QR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x0F, 0x07, 0x0F, 0x0D}, 3}
+  };
+  STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg4DIMMsWlODT[] = {
+    {ANY_DIMM3, {0x00, 0x00, 0x00, 0x08}, 1},
+    {ANY_DIMM2 + ANY_DIMM3, {0x00, 0x00, 0x0C, 0x0C}, 2},
+    {ANY_DIMM1 + ANY_DIMM2 + ANY_DIMM3, {0x00, 0x0E, 0x0E, 0x0E}, 3},
+    {ANY_DIMM0 + ANY_DIMM1 + ANY_DIMM2 + ANY_DIMM3, {0x0F, 0x0F, 0x0F, 0x0F}, 4}
+  };
+
+  UINT8 i;
+  UINT8 j;
+  UINT8 Dimms;
+  UINT8 DimmQrPresent;
+  UINT32 AddrTmgCTL;
+  UINT32 DctOdcCtl;
+  UINT8 PhyWLODT[4];
+  UINT8 DramTerm;
+  UINT8 DramTermDyn;
+  UINT16 DIMMRankType;
+  UINT16 _DIMMRankType_;
+  UINT8 DimmTpMatch;
+  UINT8  MaxDimmPerCH;
+  UINT8 PSCfgWlODTSize;
+  CONST ADV_R_PSCFG_WL_ODT_ENTRY *PSCfgWlODTPtr;
+
+  if (CurrentChannel->RegDimmPresent != CurrentChannel->ChDimmValid) {
+    return AGESA_UNSUPPORTED;
+  }
+
+  DIMMRankType = MemRecNGetPsRankType (CurrentChannel);
+  MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
+  Dimms = CurrentChannel->Dimms;
+  PSCfgWlODTPtr = RecPSCfg2DIMMsWlODT;
+  PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg2DIMMsWlODT);
+  PhyWLODT[0] = PhyWLODT[1] = PhyWLODT[2] = PhyWLODT[3] = 0xFF;
+  DimmQrPresent = CurrentChannel->DimmQrPresent;
+
+  if (MaxDimmPerCH == 4) {
+    AddrTmgCTL = (Dimms > 2) ? 0x002F0000 : 0;
+    DctOdcCtl = (Dimms == 1) ? 0x20113222 : 0x20223222;
+    PSCfgWlODTPtr = RecPSCfg4DIMMsWlODT;
+    PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg4DIMMsWlODT);
+  } else  if (MaxDimmPerCH == 3) {
+    AddrTmgCTL = 0;
+    DctOdcCtl = 0x20223222;
+    if (Dimms == 3) {
+      AddrTmgCTL = 0x00380038;
+      DctOdcCtl = 0x20113222;
+    }
+    if (Dimms == 1) {
+      DctOdcCtl = 0x20113222;
+    }
+    PSCfgWlODTPtr = RecPSCfg3DIMMsWlODT;
+    PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg3DIMMsWlODT);
+  } else if (MaxDimmPerCH == 2) {
+    AddrTmgCTL = 0;
+    DctOdcCtl = 0x20223222;
+    if ((Dimms == 1) && (DimmQrPresent == 0)) {
+      DctOdcCtl = 0x20113222;
+    }
+  } else {
+    AddrTmgCTL = 0;
+    DctOdcCtl = (DimmQrPresent == 0) ? 0x20113222 : 0x20223222;
+  }
+  CurrentChannel->DctAddrTmg = AddrTmgCTL;
+  CurrentChannel->DctOdcCtl = DctOdcCtl;
+
+  // ODT
+  if (Dimms == 1) {
+    DramTerm = 1; // 60 ohms
+    DramTermDyn = 0; // Disable
+    if (DimmQrPresent != 0) {
+      DramTermDyn = 2; // 120 ohms
+    }
+  } else {
+    DramTerm = 3; // 40 ohms
+    DramTermDyn = 2; // 120 ohms
+    if (DimmQrPresent != 0) {
+      DramTerm = 1; // 60 ohms
+    }
+  }
+  CurrentChannel->Reserved[0] = DramTerm;
+  CurrentChannel->Reserved[1] = DramTermDyn;
+
+  // WL ODT
+  for (i = 0; i  < PSCfgWlODTSize; i++, PSCfgWlODTPtr++) {
+    if (Dimms != PSCfgWlODTPtr->Dimms) {
+      continue;
+    }
+    DimmTpMatch = 0;
+    _DIMMRankType_ = DIMMRankType & PSCfgWlODTPtr->DIMMRankType;
+    for (j = 0; j < MAX_DIMMS_PER_CHANNEL; j++) {
+      if ((_DIMMRankType_ & (UINT16) 0x0F << (j << 2)) != 0) {
+        DimmTpMatch++;
+      }
+    }
+    if (DimmTpMatch == PSCfgWlODTPtr->Dimms) {
+      PhyWLODT[0] = PSCfgWlODTPtr->PhyWrLvOdt[0];
+      PhyWLODT[1] = PSCfgWlODTPtr->PhyWrLvOdt[1];
+      PhyWLODT[2] = PSCfgWlODTPtr->PhyWrLvOdt[2];
+      PhyWLODT[3] = PSCfgWlODTPtr->PhyWrLvOdt[3];
+      break;
+    }
+  }
+  CurrentChannel->PhyWLODT[0] = PhyWLODT[0];
+  CurrentChannel->PhyWLODT[1] = PhyWLODT[1];
+  CurrentChannel->PhyWLODT[2] = PhyWLODT[2];
+  CurrentChannel->PhyWLODT[3] = PhyWLODT[3];
+
+  return AGESA_SUCCESS;
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ *      This function returns the max dimms for a given memory channel on a given
+ *  processor.  It first searches the platform override table for the max dimms
+ *  value.  If it is not provided, the AGESA default value is returned. The target
+ *  socket must be a valid present socket.
+ *
+ *     @param[in] PlatformMemoryConfiguration - Platform config table
+ *     @param[in] SocketID  - ID of the processor that owns the channel
+ *     @param[in] ChannelID - Channel to get max dimms for
+ *
+ *
+ *     @return UINT8 - Max Number of Dimms for that channel
+ */
+UINT8
+RecGetMaxDimmsPerChannel (
+  IN       PSO_TABLE *PlatformMemoryConfiguration,
+  IN       UINT8 SocketID,
+  IN       UINT8 ChannelID
+  )
+{
+  UINT8  *DimmsPerChPtr;
+  UINT8  MaxDimmPerCH;
+
+  DimmsPerChPtr = MemRecFindPSOverrideEntry (PlatformMemoryConfiguration, PSO_MAX_DIMMS, SocketID, ChannelID, 0);
+  if (DimmsPerChPtr != NULL) {
+    MaxDimmPerCH = *DimmsPerChPtr;
+  } else {
+    MaxDimmPerCH = 2;
+  }
+
+  return MaxDimmPerCH;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *    This is the default return function of the ARDK block. The function always
+ *    returns   AGESA_UNSUPPORTED
+ *
+ *     @param[in,out]   *MemData           Pointer to MEM_DATA_STRUCTURE
+ *     @param[in]       SocketID        Socket number
+ *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
+ *
+ *     @return          AGESA_UNSUPPORTED  AGESA status indicating that default is unsupported
+ *
+ */
+
+AGESA_STATUS
+MemRecNGetPsCfgDef (
+  IN OUT   MEM_DATA_STRUCT *MemData,
+  IN       UINT8 SocketID,
+  IN OUT   CH_DEF_STRUCT *CurrentChannel
+  )
+{
+  return AGESA_UNSUPPORTED;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *    This function returns the rank type map of a channel.
+ *
+ *     @param[in]       *CurrentChannel       Pointer to CH_DEF_STRUCT
+ *
+ *     @return          UINT16 - The map of rank type.
+ *
+ */
+UINT16
+MemRecNGetPsRankType (
+  IN       CH_DEF_STRUCT *CurrentChannel
+  )
+{
+  UINT8 i;
+  UINT16 DIMMRankType;
+
+  DIMMRankType = 0;
+  for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i++) {
+    if ((CurrentChannel->DimmQrPresent & (UINT8) 1 << i) != 0) {
+      if (i < 2) {
+        DIMMRankType |= (UINT16) 4 << (i << 2);
+      }
+    } else if ((CurrentChannel->DimmDrPresent & (UINT8) 1 << i) != 0) {
+      DIMMRankType |= (UINT16) 2 << (i << 2);
+    } else if ((CurrentChannel->DimmSRPresent & (UINT8) 1 << i) != 0) {
+      DIMMRankType |= (UINT16) 1 << (i << 2);
+    }
+  }
+  return DIMMRankType;
+}
+
+UINT32
+MemRecNcmnGetSetTrainDlyClientNb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       UINT8 IsSet,
+  IN       TRN_DLY_TYPE TrnDly,
+  IN       DRBN DrbnVar,
+  IN       UINT16 Field
+  )
+{
+  UINT16 Index;
+  UINT16 Offset;
+  UINT32 Value;
+  UINT32 Address;
+  UINT8 Dimm;
+  UINT8 Byte;
+
+  Dimm = DRBN_DIMM (DrbnVar);
+  Byte = DRBN_BYTE (DrbnVar);
+
+  ASSERT (Dimm < 2);
+  ASSERT (Byte <= ECC_DLY);
+
+  if ((Byte > 7)) {
+    // LN and ON do not support ECC delay, so:
+    if (IsSet) {
+      // On write, ignore
+      return 0;
+    } else {
+      // On read, redirect to byte 0 to correct fence averaging
+      Byte = 0;
+    }
+  }
+
+  switch (TrnDly) {
+  case AccessRcvEnDly:
+    Index = 0x10;
+    break;
+  case AccessWrDqsDly:
+    Index = 0x30;
+    break;
+  case AccessWrDatDly:
+    Index = 0x01;
+    break;
+  case AccessRdDqsDly:
+    Index = 0x05;
+    break;
+  case AccessPhRecDly:
+    Index = 0x50;
+    break;
+  default:
+    Index = 0;
+    IDS_ERROR_TRAP;
+  }
+
+  switch (TrnDly) {
+  case AccessRcvEnDly:
+  case AccessWrDqsDly:
+    Index += (Dimm * 3);
+    if (Byte & 0x04) {
+      // if byte 4,5,6,7
+      Index += 0x10;
+    }
+    if (Byte & 0x02) {
+      // if byte 2,3,6,7
+      Index++;
+    }
+    Offset = 16 * (Byte % 2);
+    break;
+
+  case AccessRdDqsDly:
+  case AccessWrDatDly:
+    Index += (Dimm * 0x100);
+    // break is not being used here because AccessRdDqsDly and AccessWrDatDly also need
+    // to run AccessPhRecDly sequence.
+  case AccessPhRecDly:
+    Index += (Byte / 4);
+    Offset = 8 * (Byte % 4);
+    break;
+  default:
+    Offset = 0;
+    IDS_ERROR_TRAP;
+  }
+
+  Address = Index;
+  MemRecNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
+  Value = MemRecNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
+
+  if (IsSet) {
+    if (TrnDly == AccessPhRecDly) {
+      Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
+    }
+
+    Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
+    MemRecNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
+    Address |= DCT_ACCESS_WRITE;
+    MemRecNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
+
+    if (TrnDly == AccessPhRecDly) {
+      NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
+    }
+    // Gross WrDatDly and WrDqsDly cannot be larger than 4
+    ASSERT (((TrnDly == AccessWrDatDly) || (TrnDly == AccessWrDqsDly)) ? (NBPtr->IsSupported[WLNegativeDelay] || (Field < 0xA0)) : TRUE);
+  } else {
+    Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF);
+  }
+
+  return Value;
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ *      This function reads cache lines continuously using PRBS engine
+ *
+ *     @param[in,out] NBPtr  - Pointer to the MEM_NB_BLOCK
+ *     @param[in,out] Buffer - Array of bytes to be filled with data read from DRAM
+ *     @param[in]     Address - System Address [47:16]
+ *     @param[in] ClCount - Number of cache lines
+ *
+ */
+
+VOID
+MemRecNContReadPatternUnb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       UINT8 Buffer[],
+  IN       UINT32 Address,
+  IN       UINT16 ClCount
+  )
+{
+  MemRecNCommonReadWritePatternUnb (NBPtr, CMD_TYPE_READ, ClCount);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *     This function generates a continuous stream of writes to DRAM using the
+ *       Unified Northbridge Reliable Read/Write Engine.
+ *
+ *     @param[in,out] NBPtr   - Pointer to the MEM_NB_BLOCK
+ *     @param[in,out] Address - Unused by this function
+ *     @param[in]     Pattern - Unused by this function
+ *     @param[in]     ClCount - Number of cache lines to write
+ *
+ */
+
+VOID
+MemRecNContWritePatternUnb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       UINT32 Address,
+  IN       UINT8 Pattern[],
+  IN       UINT16 ClCount
+  )
+{
+  MemRecNCommonReadWritePatternUnb (NBPtr, CMD_TYPE_WRITE, ClCount);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *     This function generates either read or write DRAM cycles for training
+ *     using PRBS engine
+ *
+ *     @param[in,out] NBPtr   - Pointer to the MEM_NB_BLOCK
+ *     @param[in]     CmdType - Read/Write
+ *     @param[in]     ClCount - Number of cache lines to write
+ *
+ */
+
+VOID
+STATIC
+MemRecNCommonReadWritePatternUnb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       UINT8  CmdType,
+  IN       UINT16 ClCount
+  )
+{
+  MEM_TECH_BLOCK *TechPtr;
+
+  TechPtr = NBPtr->TechPtr;
+
+  // Enable PRBS, also set ResetAllErr
+  MemRecNSetBitFieldNb (NBPtr, BFCmdTestEnable, 3);
+
+  // Send activate command
+  MemRecNSetBitFieldNb (NBPtr, BFDramCmd2Reg, (UINT32) 1 << (TechPtr->ChipSel + 22) | ((UINT32) 1 << 31));
+  MemRecUWait10ns (750, NBPtr->MemPtr);
+
+  // Setup test address
+  MemRecNSetBitFieldNb (NBPtr, BFTgtChipSelectA, TechPtr->ChipSel);
+  MemRecNSetBitFieldNb (NBPtr, BFDataPrbsSeed, PRBS_SEED_256);
+  MemRecNSetBitFieldNb (NBPtr, BFCmdCount, ClCount);
+
+  // Select read or write command
+  MemRecNSetBitFieldNb (NBPtr, BFCmdType, CmdType);
+
+  // Send command and wait for completion
+  MemRecNSetBitFieldNb (NBPtr, BFSendCmd, 1);
+  while (MemRecNGetBitFieldNb (NBPtr, BFTestStatus) == 0) {}
+  while (MemRecNGetBitFieldNb (NBPtr, BFCmdSendInProg) != 0) {}
+  MemRecNSetBitFieldNb (NBPtr, BFSendCmd, 0);
+
+  // Send precharge all command
+  MemRecNSetBitFieldNb (NBPtr, BFDramCmd2Reg, (UINT32) 1 << (TechPtr->ChipSel + 22) | ((UINT32) 1 << 30));
+  MemRecUWait10ns (750, NBPtr->MemPtr);
+
+  // Disable PRBS
+  MemRecNSetBitFieldNb (NBPtr, BFCmdTestEnable, 0);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *     This function checks the Error status bits for comparison results using PRBS
+ *
+ *     @param[in,out]   *NBPtr    - Pointer to the MEM_NB_BLOCK
+ *     @param[in]       Buffer[]  -  Not used in this implementation
+ *     @param[in]       Pattern[] - Not used in this implementation
+ *     @param[in]       ByteCount - Not used in this implementation
+ *
+ *     @return  PASS - Bitmap of results of comparison
+ */
+
+UINT16
+MemRecNCompareTestPatternUnb (
+  IN OUT   MEM_NB_BLOCK *NBPtr,
+  IN       UINT8 Buffer[],
+  IN       UINT8 Pattern[],
+  IN       UINT16 ByteCount
+  )
+{
+  UINT16 i;
+  UINT16 Pass;
+  UINT32 NibbleErrSts;
+
+  NibbleErrSts = MemRecNGetBitFieldNb (NBPtr, BFNibbleErrSts);
+
+  Pass = 0;
+  for (i = 0; i < 8; i++) {
+    Pass |= ((NibbleErrSts & 0x03) > 0 ) ? (1 << i) : 0;
+    NibbleErrSts >>= 2;
+  }
+  Pass = ~Pass;
+  return Pass;
+}
+