aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/amd/agesa/f10/Proc/Mem/NB/mnphy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendorcode/amd/agesa/f10/Proc/Mem/NB/mnphy.c')
-rwxr-xr-xsrc/vendorcode/amd/agesa/f10/Proc/Mem/NB/mnphy.c1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/f10/Proc/Mem/NB/mnphy.c b/src/vendorcode/amd/agesa/f10/Proc/Mem/NB/mnphy.c
new file mode 100755
index 0000000000..7c188e0e28
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f10/Proc/Mem/NB/mnphy.c
@@ -0,0 +1,1086 @@
+/**
+ * @file
+ *
+ * mnphy.c
+ *
+ * Common Northbridge Phy support
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/NB)
+ * @e \$Revision: 6789 $ @e \$Date: 2008-07-17 15:56:25 -0500 (Thu, 17 Jul 2008) $
+ *
+ **/
+/*****************************************************************************
+*
+* Copyright (c) 2011, 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 "mport.h"
+#include "mm.h"
+#include "mn.h"
+#include "mt.h"
+#include "mu.h"
+#include "PlatformMemoryConfiguration.h"
+#include "heapManager.h"
+#include "merrhdl.h"
+#include "Filecode.h"
+#define FILECODE PROC_MEM_NB_MNPHY_FILECODE
+/*----------------------------------------------------------------------------
+ * DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+#define UNUSED_CLK 4
+
+/*----------------------------------------------------------------------------
+ * 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)[3][5]; ///< Pointer to slew rate table
+} PHY_COMP_INIT_CLIENTNB;
+
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+/*----------------------------------------------------------------------------
+ * EXPORTED FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function gets a delay value a PCI register during training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] TrnDly - type of delay to be set
+ * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
+ * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
+ *
+ * @return Value read
+ */
+
+UINT32
+MemNGetTrainDlyNb (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN TRN_DLY_TYPE TrnDly,
+ IN DRBN DrbnVar
+ )
+{
+ return NBPtr->MemNcmnGetSetTrainDly (NBPtr, 0, TrnDly, DrbnVar, 0);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function sets a delay value a PCI register during training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] TrnDly - type of delay to be set
+ * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
+ * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
+ * @param[in] Field - Value to be programmed
+ *
+ */
+
+VOID
+MemNSetTrainDlyNb (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN TRN_DLY_TYPE TrnDly,
+ IN DRBN DrbnVar,
+ IN UINT16 Field
+ )
+{
+ NBPtr->MemNcmnGetSetTrainDly (NBPtr, 1, TrnDly, DrbnVar, Field);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function executes prototypical Phy fence training function.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemNPhyFenceTrainingNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ NBPtr->MemPPhyFenceTrainingNb (NBPtr);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function executes prototypical Phy fence training function.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemNPhyFenceTrainingClientNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ UINT8 FenceThresholdTxDll;
+ UINT8 FenceThresholdRxDll;
+ UINT8 FenceThresholdTxPad;
+ UINT16 Fence2Data;
+
+ // 1. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=10b.
+ // 2. Perform phy fence training. See 2.10.3.2.3.1 [Phy Fence Training].
+ // 3. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxDll].
+ MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 2);
+ MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 30, 26, BFPhyFence);
+ IDS_HDT_CONSOLE ("\t\tFenceThresholdTxDll\n");
+ MemNTrainPhyFenceNb (NBPtr);
+ FenceThresholdTxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
+
+ // 4. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=001b.
+ MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x1000);
+
+ // 5. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=01b.
+ // 6. Perform phy fence training. See 2.10.3.2.3.1 [Phy Fence Training].
+ // 7. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdRxDll].
+ MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 1);
+ MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 25, 21, BFPhyFence);
+ IDS_HDT_CONSOLE ("\n\t\tFenceThresholdRxDll\n");
+ MemNTrainPhyFenceNb (NBPtr);
+ FenceThresholdRxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
+
+ // 8. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=000b.
+ MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x0000);
+
+ // 9. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=11b.
+ // 10. Perform phy fence training. See 2.10.3.2.3.1 [Phy Fence Training].
+ // 11. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxPad].
+ MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 3);
+ MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 20, 16, BFPhyFence);
+ IDS_HDT_CONSOLE ("\n\t\tFenceThresholdTxPad\n");
+ MemNTrainPhyFenceNb (NBPtr);
+ FenceThresholdTxPad = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
+
+ // Program Fence2 threshold for Clk, Cmd, and Addr
+ if (FenceThresholdTxPad < 16) {
+ MemNSetBitFieldNb (NBPtr, BFClkFence2, FenceThresholdTxPad | 0x10);
+ MemNSetBitFieldNb (NBPtr, BFCmdFence2, FenceThresholdTxPad | 0x10);
+ MemNSetBitFieldNb (NBPtr, BFAddrFence2, FenceThresholdTxPad | 0x10);
+ } else {
+ MemNSetBitFieldNb (NBPtr, BFClkFence2, 0);
+ MemNSetBitFieldNb (NBPtr, BFCmdFence2, 0);
+ MemNSetBitFieldNb (NBPtr, BFAddrFence2, 0);
+ }
+
+ // Program Fence2 threshold for data
+ Fence2Data = 0;
+ if (FenceThresholdTxPad < 16) {
+ Fence2Data |= FenceThresholdTxPad | 0x10;
+ }
+ if (FenceThresholdRxDll < 16) {
+ Fence2Data |= (FenceThresholdRxDll | 0x10) << 10;
+ }
+ if (FenceThresholdTxDll < 16) {
+ Fence2Data |= (FenceThresholdTxDll | 0x10) << 5;
+ }
+ MemNSetBitFieldNb (NBPtr, BFDataFence2, Fence2Data);
+
+ // Reprogram F2x9C_04.
+ MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl));
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function executes Phy fence training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemNTrainPhyFenceNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ UINT8 Byte;
+ UINT16 Avg;
+ UINT8 PREvalue;
+
+ if (MemNGetBitFieldNb (NBPtr, BFDisDramInterface)) {
+ return;
+ }
+
+ // 1. BIOS first programs a seed value to the phase recovery
+ // engine registers.
+ //
+ IDS_HDT_CONSOLE ("\t\tSeeds: ");
+ for (Byte = 0; Byte < 9; Byte++) {
+ // This includes ECC as byte 8
+ MemNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19);
+ IDS_HDT_CONSOLE ("%02x ", 19);
+ }
+
+ IDS_HDT_CONSOLE ("\n\t\tPhyFenceTrEn = 1");
+ // 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1.
+ MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1);
+
+ if (!NBPtr->IsSupported[ClientNbFence]) {
+ // 3. Wait 200 MEMCLKs.
+ MemUWait10ns (500, NBPtr->MemPtr);
+ } else {
+ // 3. Wait 2000 MEMCLKs.
+ MemUWait10ns (5000, NBPtr->MemPtr);
+ }
+
+ // 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0.
+ MemNSetBitFieldNb (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;
+ IDS_HDT_CONSOLE ("\n\t\t PRE: ");
+ for (Byte = 0; Byte < 9; Byte++) {
+ // This includes ECC as byte 8
+ PREvalue = (UINT8) (0x1F & MemNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte)));
+ Avg = Avg + ((UINT16) PREvalue);
+ IDS_HDT_CONSOLE ("%02x ", PREvalue);
+ }
+ Avg = ((Avg + 8) / 9); // round up
+
+ Avg -= 8;
+ NBPtr->MemNPFenceAdjustNb (NBPtr, &Avg);
+
+ IDS_HDT_CONSOLE ("\n\t\tFence: %02x\n", Avg);
+
+ // 7. Write the value to F2x[1, 0]9C_x0C[PhyFence].
+ MemNSetBitFieldNb (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.
+ //
+ MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl));
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function initializes the DDR phy compensation logic
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemNInitPhyCompNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ CONST UINT8 TableCompRiseSlew20x[] = {7, 3, 2, 2};
+ CONST UINT8 TableCompRiseSlew15x[] = {7, 7, 3, 2};
+ CONST UINT8 TableCompFallSlew20x[] = {7, 5, 3, 2};
+ CONST UINT8 TableCompFallSlew15x[] = {7, 7, 5, 3};
+ UINT8 i;
+ UINT8 j;
+ UINT8 CurrDct;
+ UINT8 CurrChannel;
+ BOOLEAN MarginImprv;
+ MarginImprv = FALSE;
+ CurrDct = NBPtr->Dct;
+ CurrChannel = NBPtr->Channel;
+ if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) {
+ if (NBPtr->MCTPtr->GangedMode == FALSE) {
+ for (i = 0; i < NBPtr->DctCount; i++) {
+ MemNSwitchDCTNb (NBPtr, i);
+ for (j = 0; j < NBPtr->ChannelCount; j++) {
+ NBPtr->SwitchChannel (NBPtr, j);
+ if ((NBPtr->ChannelPtr->Dimms == 4) && ((NBPtr->DCTPtr->Timings.Speed == DDR533_FREQUENCY) || (NBPtr->DCTPtr->Timings.Speed == DDR667_FREQUENCY))) {
+ MarginImprv = TRUE;
+ }
+ }
+ }
+ MemNSwitchDCTNb (NBPtr, CurrDct);
+ NBPtr->SwitchChannel (NBPtr, CurrChannel);
+ }
+ }
+
+ // 1. BIOS disables the phy compensation register by programming F2x9C_x08[DisAutoComp]=1
+ // 2. BIOS waits 5 us for the disabling of the compensation engine to complete.
+ // DisAutoComp will be cleared after Dram init has completed
+ //
+ MemNSwitchDCTNb (NBPtr, 0);
+ MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 1);
+ MemUWait10ns (500, NBPtr->MemPtr);
+ MemNSwitchDCTNb (NBPtr, CurrDct);
+
+ // 3. For each normalized driver strength code read from
+ // F2x[1, 0]9C_x00[AddrCmdDrvStren], program the
+ // corresponding 3 bit predriver code in F2x9C_x0A[D3Cmp1NCal, D3Cmp1PCal].
+ //
+ // 4. For each normalized driver strength code read from
+ // F2x[1, 0]9C_x00[DataDrvStren], program the corresponding
+ // 3 bit predriver code in F2x9C_x0A[D3Cmp0NCal, D3Cmp0PCal, D3Cmp2NCal,
+ // D3Cmp2PCal].
+ //
+ j = (UINT8) MemNGetBitFieldNb (NBPtr, BFAddrCmdDrvStren);
+ i = (UINT8) MemNGetBitFieldNb (NBPtr, BFDataDrvStren);
+
+ MemNSwitchDCTNb (NBPtr, 0);
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp1NCal, TableCompRiseSlew20x[j]);
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp1PCal, TableCompFallSlew20x[j]);
+
+ if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) {
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]);
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]);
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]);
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]);
+ }
+ if (NBPtr->IsSupported[CheckSlewWithoutMarginImprv]) {
+ ASSERT (i <= 3);
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, TableCompRiseSlew15x[i]);
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, TableCompFallSlew15x[i]);
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, TableCompRiseSlew15x[i]);
+ MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, TableCompFallSlew15x[i]);
+ }
+ MemNSwitchDCTNb (NBPtr, CurrDct);
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This is a general purpose function that executes before DRAM training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemNBeforeDQSTrainingNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ UINT8 Dct;
+ UINT8 ChipSel;
+ UINT32 TestAddrRJ16;
+ UINT32 RealAddr;
+
+ MemTBeginTraining (NBPtr->TechPtr);
+
+ for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
+ MemNSwitchDCTNb (NBPtr, Dct);
+ if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
+ for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel += 2) {
+ if (MemNGetMCTSysAddrNb (NBPtr, ChipSel, &TestAddrRJ16)) {
+
+ RealAddr = MemUSetUpperFSbase (TestAddrRJ16, NBPtr->MemPtr);
+
+ MemUDummyCLRead (RealAddr);
+
+ MemNSetBitFieldNb (NBPtr, BFErr350, 0x8000);
+ MemUWait10ns (60, NBPtr->MemPtr); // Wait 300ns
+ MemNSetBitFieldNb (NBPtr, BFErr350, 0x0000);
+ MemUWait10ns (400, NBPtr->MemPtr); // Wait 2us
+ MemUProcIOClFlush (TestAddrRJ16, 1, NBPtr->MemPtr);
+ break;
+ }
+ }
+ }
+ if (NBPtr->IsSupported[CheckEccDLLPwrDnConfig]) {
+ if (!NBPtr->MCTPtr->Status[SbEccDimms]) {
+ MemNSetBitFieldNb (NBPtr, BFEccDLLPwrDnConf, 0x0010);
+ }
+ if (NBPtr->DCTPtr->Timings.Dimmx4Present == 0) {
+ MemNSetBitFieldNb (NBPtr, BFEccDLLConf, 0x0080);
+ }
+ }
+ }
+
+ MemTEndTraining (NBPtr->TechPtr);
+}
+
+/*-----------------------------------------------------------------------------*/
+/**
+ *
+ * Returns the parameters for a requested delay value to be used in training
+ * The correct Min, Max and Mask are determined based on the type of Delay,
+ * and the frequency
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] TrnDly - Type of delay
+ * @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct
+ *
+ */
+
+VOID
+MemNGetTrainDlyParmsNb (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN TRN_DLY_TYPE TrnDly,
+ IN OUT TRN_DLY_PARMS *Parms
+ )
+{
+ Parms->Min = 0;
+
+ if (TrnDly == AccessWrDatDly) {
+ Parms->Max = 0x1F;
+ Parms->Mask = 0x01F;
+ } else if (TrnDly == AccessRdDqsDly) {
+ if ( (NBPtr->IsSupported[CheckMaxRdDqsDlyPtr]) && (NBPtr->DCTPtr->Timings.Speed > DDR667_FREQUENCY) ) {
+ Parms->Max = 0x3E;
+ Parms->Mask = 0x03E;
+ } else {
+ Parms->Max = 0x1F;
+ Parms->Mask = 0x01F;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function gets or set DQS timing during training.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] TrnDly - type of delay to be set
+ * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
+ * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
+ * @param[in] Field - Value to be programmed
+ * @param[in] IsSet - Indicates if the function will set or get
+ *
+ * @return value read, if the function is used as a "get"
+ */
+
+UINT32
+MemNcmnGetSetTrainDlyNb (
+ 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 Rank;
+ UINT8 Byte;
+ UINT8 Nibble;
+
+ Dimm = DRBN_DIMM (DrbnVar);
+ Rank = DRBN_RANK (DrbnVar);
+ Byte = DRBN_BYTE (DrbnVar);
+ Nibble = DRBN_NBBL (DrbnVar);
+
+ ASSERT (Dimm < 4);
+ ASSERT (Byte <= ECC_DLY);
+
+ 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++;
+ }
+ if (Byte > 7) {
+ Index += 2;
+ }
+ Offset = 16 * (Byte % 2);
+ Index |= (Rank << 8);
+ Index |= (Nibble << 9);
+ break;
+
+ case AccessRdDqsDly:
+ case AccessWrDatDly:
+
+ if (NBPtr->IsSupported[DimmBasedOnSpeed]) {
+ if (NBPtr->DCTPtr->Timings.Speed < DDR800_FREQUENCY) {
+ // if DDR speed is below 800, use DIMM 0 delays for all DIMMs.
+ Dimm = 0;
+ }
+ }
+
+ Index += (Dimm * 0x100);
+ if (Nibble) {
+ if (Rank) {
+ Index += 0xA0;
+ } else {
+ Index += 0x70;
+ }
+ } else if (Rank) {
+ Index += 0x60;
+ }
+ // 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;
+ MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
+ MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
+ Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
+
+ if (IsSet) {
+ if (TrnDly == AccessPhRecDly) {
+ Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
+ }
+
+ Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
+ MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
+ Address |= DCT_ACCESS_WRITE;
+ MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
+ MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
+
+ if (TrnDly == AccessPhRecDly) {
+ NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
+ }
+ } else {
+ Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF);
+ }
+
+ return Value;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function gets or set DQS timing during training.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] IsSet - Indicates if the function will set or get
+ * @param[in] TrnDly - type of delay to be set
+ * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
+ * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
+ * @param[in] Field - Value to be programmed
+ *
+ * @return value read, if the function is used as a "get"
+ */
+UINT32
+MemNcmnGetSetTrainDlyClientNb (
+ 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);
+
+ if ((Dimm > 1) || (Byte > 7)) {
+ // LN only support DIMM 0 and 8 byte lanes.
+ return 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;
+ MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
+ Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
+
+ if (IsSet) {
+ if (TrnDly == AccessPhRecDly) {
+ Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
+ }
+
+ Value = ((UINT32)Field << Offset) | (Value & (~((UINT32)0xFF << Offset)));
+ MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
+ Address |= DCT_ACCESS_WRITE;
+ MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
+
+ if (TrnDly == AccessPhRecDly) {
+ NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
+ }
+ } else {
+ Value = (Value >> Offset) & 0xFF;
+ }
+
+ return Value;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function initializes the training pattern.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return AGESA_STATUS - Result
+ * AGESA_SUCCESS - Training pattern is ready to use
+ * AGESA_ERROR - Unable to initialize the pattern.
+ */
+
+AGESA_STATUS
+MemNTrainingPatternInitNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ ALLOCATE_HEAP_PARAMS AllocHeapParams;
+ POS_TRN_PATTERN_TYPE TrainPattern;
+ AGESA_STATUS Status;
+
+ TechPtr = NBPtr->TechPtr;
+ //
+ // Determine pattern to be used
+ //
+ if (NBPtr->PosTrnPattern == POS_PATTERN_256B) {
+ if (NBPtr->MCTPtr->Status[Sb128bitmode]) {
+ TrainPattern = TestPatternJD256B;
+ TechPtr->PatternLength = 64;
+ } else {
+ TrainPattern = TestPatternJD256A;
+ TechPtr->PatternLength = 32;
+ }
+ } else {
+ //
+ // 72 bit pattern will be used if PosTrnPattern is not specified
+ //
+ if (NBPtr->MCTPtr->Status[Sb128bitmode]) {
+ TrainPattern = TestPatternJD1B;
+ TechPtr->PatternLength = 18;
+ } else {
+ TrainPattern = TestPatternJD1A;
+ TechPtr->PatternLength = 9;
+ }
+ }
+ //
+ // Allocate training buffer
+ //
+ AllocHeapParams.RequestedBufferSize = (TechPtr->PatternLength * 64 * 2) + 16;
+ AllocHeapParams.BufferHandle = AMD_MEM_TRAIN_BUFFER_HANDLE;
+ AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
+ Status = HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader);
+ ASSERT (Status == AGESA_SUCCESS);
+ if (Status != AGESA_SUCCESS) {
+ return Status;
+ }
+ TechPtr->PatternBufPtr = AllocHeapParams.BufferPtr;
+ AlignPointerTo16Byte (&TechPtr->PatternBufPtr);
+ TechPtr->TestBufPtr = TechPtr->PatternBufPtr + (TechPtr->PatternLength * 64);
+
+ // Prepare training pattern
+ MemUFillTrainPattern (TrainPattern, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
+
+ return Status;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function finalizes the training pattern.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] Index - Index of Write Data Delay Value
+ * @param[in,out] *Value - Write Data Delay Value
+ * @return BOOLEAN - TRUE - Use the value returned.
+ * FALSE - No more values in table.
+ */
+
+BOOLEAN
+MemNGetApproximateWriteDatDelayNb (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN UINT8 Index,
+ IN OUT UINT8 *Value
+ )
+{
+ CONST UINT8 WriteDatDelayValue[] = {0x10, 0x4, 0x8, 0xC, 0x14, 0x18, 0x1C, 0x1F};
+ if (Index < GET_SIZE_OF (WriteDatDelayValue)) {
+ *Value = WriteDatDelayValue[Index];
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function finalizes the training pattern.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return AGESA_STATUS - Result
+ * AGESA_SUCCESS - Training pattern has been finalized.
+ * AGESA_ERROR - Unable to initialize the pattern.
+ */
+
+AGESA_STATUS
+MemNTrainingPatternFinalizeNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ AGESA_STATUS Status;
+ //
+ // Deallocate training buffer
+ //
+ Status = HeapDeallocateBuffer (AMD_MEM_TRAIN_BUFFER_HANDLE, &NBPtr->MemPtr->StdHeader);
+ ASSERT (Status == AGESA_SUCCESS);
+ return Status;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function returns the number of chipselects per channel.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return
+ */
+
+UINT8
+MemNCSPerChannelNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ return MAX_CS_PER_CHANNEL;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function returns the number of Chipselects controlled by each set
+ * of Delay registers under current conditions.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return
+ */
+
+UINT8
+MemNCSPerDelayNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ return MAX_CS_PER_DELAY;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * This function returns the minimum data eye width in 32nds of a UI for
+ * the type of data eye(Rd/Wr) that is being trained. This value will
+ * be the minimum number of consecutive delays that yield valid data.
+ * Uses TechPtr->Direction to determine read or write.
+ *
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ * @return
+ */
+
+UINT8
+MemNMinDataEyeWidthNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
+ return MIN_RD_DATAEYE_WIDTH_NB;
+ } else {
+ return MIN_WR_DATAEYE_WIDTH_NB;
+ }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * 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
+MemNPhyVoltageLevelClientNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ BIT_FIELD_NAME BitField;
+ UINT16 Value;
+ UINT16 Mask;
+
+ Mask = 0xFFE7;
+ Value = (UINT16) NBPtr->RefPtr->DDR3Voltage << 3;
+
+ /// @todo: Need to verify if the following logic can work on real hardware or not
+ for (BitField = BFDataRxVioLvl; BitField <= BFCmpVioLvl; BitField++) {
+ if (BitField == BFCmpVioLvl) {
+ Mask = 0x3FFF;
+ Value <<= (14 - 3);
+ }
+ MemNBrdcstSetNb (NBPtr, BitField, ((MemNGetBitFieldNb (NBPtr, BitField) & Mask)) | Value);
+ }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function adjusts Avg PRE value of Phy fence training according to specific CPU family.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] *Value16 - Pointer to the value that we want to adjust
+ *
+ */
+VOID
+MemNPFenceAdjustClientNb (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT UINT16 *Value16
+ )
+{
+ *Value16 += 2; //for LN, the Avg PRE value is subtracted by 6 only.
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function initializes the DDR phy compensation logic
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ *
+ */
+
+VOID
+MemNInitPhyCompClientNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ // Slew rate table array [x][y][z]
+ // array[0]: slew rate for VDDIO 1.5V
+ // array[1]: slew rate for VDDIO 1.35V
+ // array[x][y]: slew rate for a certain frequency
+ // array[x][y][0]: frequency mask for current entry
+ CONST STATIC UINT16 TxPrePNDataDqs[2][3][5] = {
+ {{ (UINT16) DDR800, 0xFF6, 0xDAD, 0xDAD, 0x924},
+ { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
+ { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}},
+ {{ (UINT16) DDR800, 0xFF6, 0xB6D, 0xB6D, 0x924},
+ { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
+ { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}
+ };
+ CONST STATIC UINT16 TxPrePNCmdAddr[2][3][5] = {
+ {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492},
+ { (UINT16) (DDR1066 + DDR1333), 0x6DB, 0x6DB, 0x6DB, 0x6DB},
+ { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0xB6D, 0xB6D}},
+ {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492},
+ { (UINT16) (DDR1066 + DDR1333), 0x924, 0x6DB, 0x6DB, 0x6DB},
+ { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0x924, 0x924}}
+ };
+ CONST STATIC UINT16 TxPrePNClock[2][3][5] = {
+ {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924},
+ { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xB6D},
+ { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}},
+ {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924},
+ { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xDAD},
+ { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xDAD}}
+ };
+
+ CONST PHY_COMP_INIT_CLIENTNB PhyCompInitBitField[] = {
+ // 3. Program TxPreP/TxPreN for Data and DQS according toTable 14 if VDDIO is 1.5V or Table 15 if 1.35V.
+ // A. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0[A,6]={0000b, TxPreP, TxPreN}.
+ // B. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]02={1000b, TxPreP, TxPreN}.
+ {BFDqsDrvStren, BFDataByteTxPreDriverCal2Pad1, BFDataByteTxPreDriverCal2Pad1, 0, TxPrePNDataDqs},
+ {BFDataDrvStren, BFDataByteTxPreDriverCal2Pad2, BFDataByteTxPreDriverCal2Pad2, 0, TxPrePNDataDqs},
+ {BFDataDrvStren, BFDataByteTxPreDriverCal, BFDataByteTxPreDriverCal, 8, TxPrePNDataDqs},
+ // 4. Program TxPreP/TxPreN for Cmd/Addr according toTable 16 if VDDIO is 1.5V or Table 17 if 1.35V.
+ // A. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0][12,0E,0A,06]={0000b, TxPreP, TxPreN}.
+ // B. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0]02={1000b, TxPreP, TxPreN}.
+ {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCal2Pad1, BFCmdAddr0TxPreDriverCal2Pad2, 0, TxPrePNCmdAddr},
+ {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCal2Pad1, BFAddrTxPreDriverCal2Pad4, 0, TxPrePNCmdAddr},
+ {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCalPad0, BFCmdAddr0TxPreDriverCalPad0, 8, TxPrePNCmdAddr},
+ {BFCkeDrvStren, BFAddrTxPreDriverCalPad0, BFAddrTxPreDriverCalPad0, 8, TxPrePNCmdAddr},
+ {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCalPad0, BFCmdAddr1TxPreDriverCalPad0, 8, TxPrePNCmdAddr},
+ // 5. Program TxPreP/TxPreN for Clock according toTable 18 if VDDIO is 1.5V or Table 19 if 1.35V.
+ // A. Program D18F2x[1,0]9C_x0D0F_2[1:0]02={1000b, TxPreP, TxPreN}.
+ {BFClkDrvStren, BFClock0TxPreDriverCalPad0, BFClock1TxPreDriverCalPad0, 8, TxPrePNClock}
+ };
+
+ BIT_FIELD_NAME CurrentBitField;
+ UINT16 SpeedMask;
+ CONST UINT16 (*TxPrePNArray)[5];
+ UINT8 Voltage;
+ UINT8 i;
+ UINT8 j;
+ UINT8 k;
+
+ // 1. Program D18F2x[1,0]9C_x0000_0008[DisAutoComp] = 1.
+ MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 1);
+ // 2. Program D18F2x[1,0]9C_x0D0F_E003[DisalbePredriverCal]=1
+ MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, (1 << 13));
+
+ SpeedMask = (UINT16) 1 << (NBPtr->DCTPtr->Timings.Speed / 66);
+ Voltage = (UINT8) NBPtr->RefPtr->DDR3Voltage;
+
+ for (j = 0; j < GET_SIZE_OF (PhyCompInitBitField); j ++) {
+ i = (UINT8) MemNGetBitFieldNb (NBPtr, PhyCompInitBitField[j].IndexBitField);
+ TxPrePNArray = PhyCompInitBitField[j].TxPrePN[Voltage];
+ for (k = 0; k < 3; k ++) {
+ if ((TxPrePNArray[k][0] & SpeedMask) != 0) {
+ for (CurrentBitField = PhyCompInitBitField[j].StartTargetBitField; CurrentBitField <= PhyCompInitBitField[j].EndTargetBitField; CurrentBitField ++) {
+ MemNSetBitFieldNb (NBPtr, CurrentBitField, ((PhyCompInitBitField[j].ExtraValue << 12) | TxPrePNArray[k][i + 1]));
+ }
+ break;
+ }
+ }
+ ASSERT (k < 3);
+ }
+
+ // 6. Program D18F2x[1,0]9C_x0000_0008[DisAutoComp] = 0.
+ MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 0);
+} \ No newline at end of file