summaryrefslogtreecommitdiff
path: root/src/vendorcode/amd/agesa/Proc/Mem/NB/mnphy.c
diff options
context:
space:
mode:
authorFrank Vibrans <frank.vibrans@amd.com>2011-02-14 18:30:54 +0000
committerMarc Jones <marc.jones@amd.com>2011-02-14 18:30:54 +0000
commit2b4c831b4d16b55a7abdea20bce82cccd168232c (patch)
tree95a35c737d16119f1dfa9c1c9d7700710d8a04f7 /src/vendorcode/amd/agesa/Proc/Mem/NB/mnphy.c
parent74ad66cdc143e04f976ba21e538e02b20362d7e6 (diff)
downloadcoreboot-2b4c831b4d16b55a7abdea20bce82cccd168232c.tar.xz
Add AMD Agesa and AMD CIMx SB800 code. Patch 1 of 8.
This code currently generates many warnings that are functionally benign. These are being addressed, but the wheels of bureaucracy turn slowly. This drop supports AMD cpu families 10h and 14h. Only Family 14h is used as an example in this set of patches. Other cpu families are supported by the infrastructure, but their specific support is not included herein. This patch is functionally independent of the other patches in this set. Signed-off-by: Frank Vibrans <frank.vibrans@amd.com> Acked-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Acked-by: Marc Jones <marcj303@gmail.com> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@6344 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/vendorcode/amd/agesa/Proc/Mem/NB/mnphy.c')
-rw-r--r--src/vendorcode/amd/agesa/Proc/Mem/NB/mnphy.c1377
1 files changed, 1377 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/Proc/Mem/NB/mnphy.c b/src/vendorcode/amd/agesa/Proc/Mem/NB/mnphy.c
new file mode 100644
index 0000000000..bcf2f6e4c1
--- /dev/null
+++ b/src/vendorcode/amd/agesa/Proc/Mem/NB/mnphy.c
@@ -0,0 +1,1377 @@
+/* $NoKeywords:$ */
+/**
+ * @file
+ *
+ * mnphy.c
+ *
+ * Common Northbridge Phy support
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/NB)
+ * @e \$Revision: 38306 $ @e \$Date: 2010-09-22 01:51:51 +0800 (Wed, 22 Sep 2010) $
+ *
+ **/
+/*
+ *****************************************************************************
+ *
+ * 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"
+CODE_GROUP (G1_PEICC)
+RDATA_GROUP (G1_PEICC)
+#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
+MemNPhyFenceTrainingUnb (
+ 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.
+ // 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 (MEM_FLOW, "\t\tFenceThresholdTxDll\n");
+ MemNTrainPhyFenceNb (NBPtr);
+ FenceThresholdTxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
+ NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxDll);
+
+ // 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.
+ // 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 (MEM_FLOW, "\n\t\tFenceThresholdRxDll\n");
+ MemNTrainPhyFenceNb (NBPtr);
+ FenceThresholdRxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
+ NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdRxDll);
+
+ // 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.
+ // 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 (MEM_FLOW, "\n\t\tFenceThresholdTxPad\n");
+ MemNTrainPhyFenceNb (NBPtr);
+ FenceThresholdTxPad = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
+ NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxPad);
+
+ // 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);
+
+ if (NBPtr->MCTPtr->Status[SbLrdimms]) {
+ // 18. If motherboard routing requires CS[7:6] to adopt address timings, e.g. 3 LRDIMMs/ch with CS[7:6]
+ // routed across all DIMM sockets, BIOS performs the following:
+ if (FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_NO_LRDIMM_CS67_ROUTING, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID) != NULL) {
+ // A. Program D18F2xA8_dct[1:0][CSTimingMux67] = 1.
+ MemNSetBitFieldNb (NBPtr, BFCSTimingMux67, 1);
+ // B. Program D18F2x9C_x0D0F_8021_dct[1:0]:
+ // - DiffTimingEn = 1.
+ // - IF (D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay] >=
+ // D18F2x9C_x0D0F_E008_dct[1:0][FenceValue]) THEN Fence = 1 ELSE Fence = 0.
+ // - Delay = D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay].
+ //
+ MemNSetBitFieldNb (NBPtr, BFDiffTimingEn, 1);
+ MemNSetBitFieldNb (NBPtr, BFFence, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay) >= MemNGetBitFieldNb (NBPtr, BFFenceValue)) ? 1 : 0);
+ MemNSetBitFieldNb (NBPtr, BFDelay, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay)));
+ }
+ }
+
+ // 19. 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 (MEM_FLOW, "\t\tSeeds: ");
+ for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) {
+ // This includes ECC as byte 8
+ MemNSetTrainDlyNb (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.
+ MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1);
+
+ if (!NBPtr->IsSupported[UnifiedNbFence]) {
+ // 3. Wait 200 MEMCLKs.
+ MemNWaitXMemClksNb (NBPtr, 200);
+ } else {
+ // 3. Wait 2000 MEMCLKs.
+ MemNWaitXMemClksNb (NBPtr, 2000);
+ }
+
+ // 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 (MEM_FLOW, "\n\t\t PRE: ");
+ for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) {
+ //
+ // This includes ECC as byte 8. ECC Byte lane (8) is ignored by MemNGetTrainDlyNb function where
+ // ECC is not supported.
+ //
+ PREvalue = (UINT8) (0x1F & MemNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte)));
+ Avg = Avg + ((UINT16) PREvalue);
+ IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", PREvalue);
+ }
+ Avg = ((Avg + 8) / 9); // round up
+
+ Avg -= 8;
+ NBPtr->MemNPFenceAdjustNb (NBPtr, &Avg);
+
+ IDS_HDT_CONSOLE (MEM_FLOW, "\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;
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------------*/
+/**
+ *
+ * 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
+MemNGetTrainDlyParmsClientNb (
+ 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) {
+ Parms->Max = 0x3E;
+ Parms->Mask = 0x03E;
+ }
+}
+/*-----------------------------------------------------------------------------*/
+/**
+ *
+ * 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
+MemNGetTrainDlyParmsUnb (
+ 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) {
+ 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 (TrnDly == AccessRdDqsDly) {
+ NBPtr->FamilySpecificHook[AdjustRdDqsDlyOffset] (NBPtr, &Offset);
+ }
+
+ 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);
+
+ ASSERT (Dimm < 2);
+ ASSERT (Byte <= ECC_DLY);
+
+ if ((Byte > 7)) {
+ // Llano does 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;
+ 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) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
+ MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
+ Address |= DCT_ACCESS_WRITE;
+ MemNSetBitFieldNb (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)) ? (Field < 0xA0) : TRUE);
+ } else {
+ Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 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;
+ TRAIN_PATTERN TrainPattern;
+ AGESA_STATUS Status;
+
+ TechPtr = NBPtr->TechPtr;
+ TrainPattern = 0;
+ //
+ // Check the training type
+ //
+ if (TechPtr->TrainingType == TRN_DQS_POSITION) {
+ //
+ // DQS Position Training
+ //
+ if (NBPtr->PosTrnPattern == POS_PATTERN_256B) {
+ //
+ // 256 Bit pattern
+ //
+ 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;
+ }
+ }
+ } else if (TechPtr->TrainingType == TRN_MAX_READ_LATENCY) {
+ //
+ // Max Read Latency Training
+ //
+ TrainPattern = TestPatternML;
+ TechPtr->PatternLength = (NBPtr->MCTPtr->Status[Sb128bitmode]) ? 6 : 3;
+ } else {
+ //
+ // Error - TechPtr->Training Type must be set to one of the types handled in this function
+ //
+ ASSERT (FALSE);
+ }
+ //
+ // 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 determined the settings for the Reliable Read/Write engine
+ * for each specific type of training
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in] *OptParam - Pointer to an Enum of TRAINING_TYPE
+ *
+ * @return TRUE
+ */
+
+BOOLEAN
+MemNSetupHwTrainingEngineUnb (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN VOID *OptParam
+ )
+{
+ TRAINING_TYPE TrnType;
+ RRW_SETTINGS *Rrw;
+
+ TrnType = *(TRAINING_TYPE*) OptParam;
+ Rrw = &NBPtr->RrwSettings;
+ //
+ // Common Settings
+ //
+ Rrw->TgtBankAddressA = CPG_BANK_ADDRESS_A;
+ Rrw->TgtRowAddressA = CPG_ROW_ADDRESS_A;
+ Rrw->TgtColAddressA = CPG_COL_ADDRESS_A;
+ Rrw->TgtBankAddressB = CPG_BANK_ADDRESS_B;
+ Rrw->TgtRowAddressB = CPG_ROW_ADDRESS_B;
+ Rrw->TgtColAddressB = CPG_COL_ADDRESS_B;
+ Rrw->CompareMaskHigh = CPG_COMPARE_MASK_HI;
+ Rrw->CompareMaskLow = CPG_COMPARE_MASK_LOW;
+ Rrw->CompareMaskEcc = CPG_COMPARE_MASK_ECC;
+
+ switch (TrnType) {
+ case TRN_RCVR_ENABLE:
+ //
+ // Receiver Enable Training
+ //
+ NBPtr->TechPtr->PatternLength = 192;
+ break;
+ case TRN_MAX_READ_LATENCY:
+ //
+ // Max Read Latency Training
+ //
+ Rrw->CmdTgt = CMD_TGT_A;
+ NBPtr->TechPtr->PatternLength = 32;
+ Rrw->DataPrbsSeed = PRBS_SEED_32;
+ break;
+ case TRN_DQS_POSITION:
+ //
+ // Read/Write DQS Position training
+ //
+ Rrw->CmdTgt = CMD_TGT_AB;
+ NBPtr->TechPtr->PatternLength = 256;
+ Rrw->DataPrbsSeed = PRBS_SEED_256;
+ break;
+ default:
+ ASSERT (FALSE);
+ }
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ * 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
+MemNPhyVoltageLevelNb (
+ IN OUT MEM_NB_BLOCK *NBPtr
+ )
+{
+ BIT_FIELD_NAME BitField;
+ UINT16 Value;
+
+ Value = (UINT16) NBPtr->RefPtr->DDR3Voltage << 3;
+
+ for (BitField = BFDataRxVioLvl; BitField <= BFCmpVioLvl; BitField++) {
+ if (BitField == BFCmpVioLvl) {
+ Value <<= (14 - 3);
+ }
+ MemNBrdcstSetNb (NBPtr, BitField, 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
+MemNPFenceAdjustUnb (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT UINT16 *Value16
+ )
+{
+ *Value16 += 2; //for LN,ON,and OR, 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, 0x924, 0x924, 0x924, 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, 0x924, 0x924, 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;
+ UINT8 Dct;
+
+ Dct = NBPtr->Dct;
+ NBPtr->SwitchDCT (NBPtr, 0);
+ // 1. Program D18F2x[1,0]9C_x0D0F_E003[DisAutoComp, DisablePreDriverCal] = {1b, 1b}.
+ MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 0x6000);
+ NBPtr->SwitchDCT (NBPtr, Dct);
+
+ 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);
+ }
+
+ NBPtr->FamilySpecificHook[ForceAutoComp] (NBPtr, NBPtr);
+}
+
+/*-----------------------------------------------------------------------------
+ *
+ *
+ * This function re-enable phy compensation.
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] OptParam - Optional parameter
+ *
+ * @return TRUE
+ * ----------------------------------------------------------------------------
+ */
+BOOLEAN
+MemNReEnablePhyCompNb (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ )
+{
+ UINT8 Dct;
+
+ Dct = NBPtr->Dct;
+
+ NBPtr->SwitchDCT (NBPtr, 0);
+ // Clear DisableCal and set DisablePredriverCal
+ MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 0x2000);
+ NBPtr->SwitchDCT (NBPtr, Dct);
+
+ return TRUE;
+}
+
+/*-----------------------------------------------------------------------------
+ *
+ *
+ * This function calculates the value of WrDqDqsEarly and programs it into
+ * the DCT and adds it to the WrDqsGrossDelay of each byte lane on each
+ * DIMM of the channel.
+ *
+ *
+ * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
+ * @param[in,out] OptParam - Optional parameter
+ *
+ * @return TRUE
+ * ----------------------------------------------------------------------------
+ */
+BOOLEAN
+MemNCalcWrDqDqsEarlyUnb (
+ IN OUT MEM_NB_BLOCK *NBPtr,
+ IN OUT VOID *OptParam
+ )
+{
+ MEM_TECH_BLOCK *TechPtr;
+ DCT_STRUCT *DCTPtr;
+ CH_DEF_STRUCT *ChannelPtr;
+ UINT8 Dimm;
+ UINT8 ByteLane;
+ UINT8 *WrDqsDlysPtr;
+ UINT8 WrDqDqsEarly;
+
+ ASSERT ((NBPtr->IsSupported[WLSeedAdjust]) && (NBPtr->IsSupported[WLNegativeDelay]));
+
+ TechPtr = NBPtr->TechPtr;
+ ChannelPtr = NBPtr->ChannelPtr;
+ DCTPtr = NBPtr->DCTPtr;
+
+ ASSERT (NBPtr != NULL);
+ ASSERT (ChannelPtr != NULL);
+ ASSERT (DCTPtr != NULL);
+ //
+ // For each DIMM:
+ // - The Critical Gross Delay (CGD) is the minimum GrossDly of all byte lanes and all DIMMs.
+ // - If (CGD < 0) Then
+ // - D18F2xA8_dct[1:0][WrDqDqsEarly] = ABS(CGD)
+ // - WrDqsGrossDly = GrossDly + WrDqDqsEarly
+ // - Else
+ // - D18F2xA8_dct[1:0][WrDqDqsEarly] = 0.
+ // - WrDqsGrossDly = GrossDly
+ //
+ WrDqDqsEarly = 0;
+ if (TechPtr->WLCriticalDelay < 0) {
+ IDS_HDT_CONSOLE(MEM_FLOW, "\t\tCalculating WrDqDqsEarly, adjusting WrDqs.\n");
+ // We've saved the entire negative delay value, so take the ABS and convert to GrossDly.
+ WrDqDqsEarly = (UINT8) (0x00FF &((((ABS (TechPtr->WLCriticalDelay)) + 0x1F) / 0x20)));
+ //
+ // Loop through All WrDqsDlys on all DIMMs
+ //
+ for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
+ if ((DCTPtr->Timings.CsEnabled & ((UINT16)3 << (Dimm << 1))) != 0) {
+ IDS_HDT_CONSOLE(MEM_FLOW, "\t\t\tDimm %x:",Dimm);
+ WrDqsDlysPtr = &(ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ())]);
+ for (ByteLane = 0; ByteLane < TechPtr->DlyTableWidth (); ByteLane++) {
+ WrDqsDlysPtr[ByteLane] += (WrDqDqsEarly << 5);
+ NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDlysPtr[ByteLane]);
+ IDS_HDT_CONSOLE(MEM_FLOW, " %02x",WrDqsDlysPtr[ByteLane]);
+ }
+ IDS_HDT_CONSOLE(MEM_FLOW, "\n");
+ }
+ }
+ }
+ MemNSetBitFieldNb (NBPtr, BFWrDqDqsEarly, WrDqDqsEarly);
+ IDS_HDT_CONSOLE(MEM_FLOW, "\t\tWrDqDqsEarly : %02x\n",WrDqDqsEarly);
+ return TRUE;
+}