blob: 532c04083b1eec7e259f16fe9aa579f3c3a1d3ca [file] [log] [blame]
/* $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: 60556 $ @e \$Date: 2011-10-17 20:19:58 -0600 (Mon, 17 Oct 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 "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 (G2_PEI)
#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);
NBPtr->FamilySpecificHook[ProgramFence2RxDll] (NBPtr, &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 (GetMaxDimmsPerChannel (NBPtr->RefPtr->PlatformMemoryConfiguration,
NBPtr->MCTPtr->SocketId,
NBPtr->ChannelPtr->ChannelID) == 3) {
if (FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_NO_LRDIMM_CS67_ROUTING, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0, NULL, NULL) == 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;
INT16 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 + ((INT16) 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) || (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)) ? (NBPtr->IsSupported[WLNegativeDelay] || (Field < 0xA0)) : TRUE);
} 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] 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
MemNcmnGetSetTrainDlyUnb (
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;
UINT8 DimmNibble;
Dimm = DRBN_DIMM (DrbnVar);
Rank = DRBN_RANK (DrbnVar);
Byte = DRBN_BYTE (DrbnVar);
Nibble = DRBN_NBBL (DrbnVar);
DimmNibble = DRBN_DIMM_NBBL (DrbnVar);
ASSERT (Dimm < 4);
ASSERT (Byte <= ECC_DLY);
if ((Byte == ECC_DLY) && !NBPtr->MCTPtr->Status[SbEccDimms]) {
// When ECC is not enabled
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 AccessRdDqs__Dly:
Index = 0x00;
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);
Address = Index;
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);
Address = Index;
break;
case AccessRdDqs__Dly:
Address = 0x0D0F0000;
Index += (DimmNibble >> 1) * 0x100;
Index += 0x20;
Index = Index + Dimm;
Offset = 4 * ((DimmNibble & 0x01) * 2);
Address += Index;
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];
}
if (TrnDly != AccessRdDqs__Dly) {
Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF) << Offset)));
} else {
Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) 0x1F << 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 {
if (TrnDly != AccessRdDqs__Dly) {
Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF);
} else {
Value = (Value >> Offset) & (UINT32) (0x1F);
}
}
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
)
{
UINT8 MinRdDataeye;
UINT8 MinWrDataeye;
UINT8 *MinRdWrDataeyePtr;
MinRdWrDataeyePtr = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MIN_RD_WR_DATAEYE_WIDTH, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0,
&(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
if (MinRdWrDataeyePtr != NULL) {
MinRdDataeye = MinRdWrDataeyePtr[0];
return MinRdDataeye;
} else {
return MIN_RD_DATAEYE_WIDTH_NB;
}
} else {
if (MinRdWrDataeyePtr != NULL) {
MinWrDataeye = MinRdWrDataeyePtr[1];
return MinWrDataeye;
} 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;
BIT_FIELD_NAME BFEnd;
UINT16 BFValue;
UINT16 RegValue;
BFValue = (UINT16) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage) << 3;
BFEnd = NBPtr->IsSupported[ProgramCsrComparator] ? BFCsrComparator : BFCmpVioLvl;
for (BitField = BFDataRxVioLvl; BitField <= BFEnd; BitField++) {
RegValue = BFValue;
if (BitField == BFCsrComparator) {
RegValue >>= (3 - 2);
// Setting this bit in DCT0 adjusts the comparator for DCT0 and DCT1. Setting this bit in DCT1 has no effect.
NBPtr->SwitchDCT (NBPtr, 0);
MemNSetBitFieldNb (NBPtr, BitField, RegValue);
break;
} else if (BitField == BFCmpVioLvl) {
RegValue <<= (14 - 3);
// Must set this bit on DCT0 even when DCT0 has no memory
NBPtr->SwitchDCT (NBPtr, 0);
MemNSetBitFieldNb (NBPtr, BitField, RegValue);
}
MemNBrdcstSetNb (NBPtr, BitField, RegValue);
}
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* 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 INT16 *Value16
)
{
*Value16 += 2; //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[2]: slew rate for VDDIO 1.25V
// array[x][y]: slew rate for a certain frequency
// array[x][y][0]: frequency mask for current entry
CONST STATIC UINT16 TxPrePNDataDqs[3][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}},
{{ (UINT16) DDR800, 0xFF6, 0xDAD, 0xDAD, 0x924},
{ (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
{ (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}
};
CONST STATIC UINT16 TxPrePNCmdAddr[3][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}},
{{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492},
{ (UINT16) (DDR1066 + DDR1333), 0xDAD, 0x924, 0x6DB, 0x492},
{ (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xDAD, 0xB64, 0xB64}}
};
CONST STATIC UINT16 TxPrePNClock[3][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}},
{{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924},
{ (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
{ (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}
};
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) CONVERT_VDDIO_TO_ENCODED (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");
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tMin. Critical Delay: %x\n", TechPtr->WLCriticalDelay);
// We've saved the entire negative delay value, so take the ABS and convert to GrossDly.
WrDqDqsEarly = (UINT8) (0x00FF &((((ABS (TechPtr->WLCriticalDelay)) + 0x1F) / 0x20)));
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tWrDqDqsEarly : %02x\n\n", WrDqDqsEarly);
//
// Loop through All WrDqsDlys on all DIMMs
//
for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
if ((NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << Dimm)) != 0) :
((DCTPtr->Timings.CsEnabled & ((UINT16) 3 << (Dimm << 1))) != 0)) {
//
// If LRDIMMs, only include the physical dimms, not logical Dimms
//
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);
return TRUE;
}
/*-----------------------------------------------------------------------------
*
*
* This function forces phy to M0 state
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] *OptParam - Optional parameter
*
* @return FALSE - always
* ----------------------------------------------------------------------------
*/
BOOLEAN
MemNForcePhyToM0Unb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN OUT VOID *OptParam
)
{
// 1. Program D18F2x9C_x0D0F_E013_dct[1:0] = 0118h.
MemNBrdcstSetNb (NBPtr, BFPllRegWaitTime, 0x118);
// 2. Force the phy to M0 with the following sequence:
// A. Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 190h. Restore the default PLL lock time.
MemNBrdcstSetNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
// B. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80800000h.
MemNBrdcstSetNb (NBPtr, BFDramPhyStatusReg, 0x80800000);
NBPtr->SwitchDCT (NBPtr, 0);
// C. Program D18F2x9C_x0D0F_E018_dct[0][PhyPSMasterChannel] = 0.
MemNSetBitFieldNb (NBPtr, BFPhyPSMasterChannel, 0);
// D. Program D18F2x9C_x0000_000B_dct[0] = 40000000h. CH0 only;
MemNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x40000000);
// E. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80000000h.
MemNBrdcstSetNb (NBPtr, BFDramPhyStatusReg, 0x80000000);
return FALSE;
}
/*-----------------------------------------------------------------------------
*
*
* This function sets SkewMemClk before enabling MemClk
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] *OptParam - Optional parameter
*
* @return TRUE - always
* ----------------------------------------------------------------------------
*/
BOOLEAN
MemNSetSkewMemClkUnb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN OUT VOID *OptParam
)
{
UINT8 Dct;
// SkewMemClk is set to 1 if all DCTs are enabled, else 0.
for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
MemNSwitchDCTNb (NBPtr, Dct);
if (NBPtr->DCTPtr->Timings.DctMemSize == 0) {
break;
}
}
MemNSwitchDCTNb (NBPtr, 0);
if (Dct == NBPtr->DctCount) {
MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0x10);
} else {
MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0);
}
return TRUE;
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function masks the RdDqsDly Bit 0 before writing to register for UNB.
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] *Offset - Bit offset of the field to be programmed
*
* @return TRUE
*/
BOOLEAN
MemNAdjustRdDqsDlyOffsetUnb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN OUT VOID *Offset
)
{
*(UINT16*) Offset = *(UINT16*) Offset + 1;
return TRUE;
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function delays MEMCLK to prevent WrDqs skew due to negative PRE result.
*
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] OptParam - Optional parameter
*
* @return TRUE
* ----------------------------------------------------------------------------
*/
BOOLEAN
MemNCalcWrDqDqsEarlyClientNb (
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 NewClkDllDelay;
UINT16 ClkDllFineDly;
UINT32 AddrCmdTmg;
TechPtr = NBPtr->TechPtr;
ChannelPtr = NBPtr->ChannelPtr;
DCTPtr = NBPtr->DCTPtr;
ASSERT (NBPtr != NULL);
ASSERT (ChannelPtr != NULL);
ASSERT (DCTPtr != NULL);
if (NBPtr->IsSupported[WLNegativeDelay]) {
if (TechPtr->WLCriticalDelay < 0) {
NewClkDllDelay = (UINT8) ABS (TechPtr->WLCriticalDelay);
// Prepare new delay for MEMCLK
ClkDllFineDly = (UINT16) ((MemNGetBitFieldNb (NBPtr, BFPhyClkDllFine0) & 0xBF60) | NewClkDllDelay);
// Program bit 7(FenceBit) = 1 if NewClkDllDelay >= > F2x9C[FenceThresholdTxPad], else 0.
ClkDllFineDly |= (NewClkDllDelay >= MemNGetBitFieldNb (NBPtr, BFPhyFence)) ? 0x80 : 0;
// Apply new delay to both chiplets
MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine0, ClkDllFineDly | 0x4000);
MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine0, ClkDllFineDly);
MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine1, ClkDllFineDly | 0x4000);
MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine1, ClkDllFineDly);
IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tShift MemClk, AddrCmd, CsOdt, Cke by %d to eliminate negative WL\n", NewClkDllDelay);
//
// Adjust AddrCmd/CsOdt/Cke timing by amount MemClk was delayed
//
AddrCmdTmg = MemNGetBitFieldNb (NBPtr, BFAddrTmgControl);
AddrCmdTmg += (NewClkDllDelay << 16) | (NewClkDllDelay << 8) | NewClkDllDelay;
AddrCmdTmg &= 0x003F3F3F;
MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, AddrCmdTmg);
//
// Adjust all WrDqsDlys on all DIMMs of the current channel
//
for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
if ((DCTPtr->Timings.CsEnabled & ((UINT16)3 << (Dimm << 1))) != 0) {
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tCS%d\n\t\t\tWrDqs:", Dimm << 1);
WrDqsDlysPtr = &(ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ())]);
for (ByteLane = 0; ByteLane < 8; ByteLane++) {
WrDqsDlysPtr[ByteLane] = (UINT8) (WrDqsDlysPtr[ByteLane] + NewClkDllDelay);
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");
}
}
}
}
return TRUE;
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function initializes RxEn Delays for RxEn seedless training
*
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] OptParam - Optional parameter
*
* @return TRUE
* ----------------------------------------------------------------------------
*/
BOOLEAN
MemNInitializeRxEnSeedlessTrainingUnb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN OUT VOID *OptParam
)
{
UINT8 ByteLane;
// Save original PRE based RxEnDly for RxEn Seedless training
for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
NBPtr->TechPtr->RxOrig[ByteLane] = NBPtr->ChannelPtr->RcvEnDlys[(NBPtr->TechPtr->ChipSel >> 1) * NBPtr->TechPtr->DlyTableWidth () + ByteLane];
}
return TRUE;
}
/*-----------------------------------------------------------------------------
*
*
* This function checks each bytelane for no window error.
*
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] OptParam - Optional parameter
*
* @return TRUE
* ----------------------------------------------------------------------------
*/
BOOLEAN
MemNTrackRxEnSeedlessRdWrNoWindBLErrorUnb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN OUT VOID *OptParam
)
{
MemTTrackRxEnSeedlessRdWrNoWindBLError (NBPtr->TechPtr, OptParam);
return TRUE;
}
/*-----------------------------------------------------------------------------
*
*
* This function checks each bytelane for small window error.
*
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] OptParam - Optional parameter
*
* @return TRUE
* ----------------------------------------------------------------------------
*/
BOOLEAN
MemNTrackRxEnSeedlessRdWrSmallWindBLErrorUnb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN OUT VOID *OptParam
)
{
MemTTrackRxEnSeedlessRdWrSmallWindBLError (NBPtr->TechPtr, OptParam);
return TRUE;
}
/*-----------------------------------------------------------------------------
*
*
* This function initializes a ByteLaneError error.
*
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] OptParam - Optional parameter
*
* @return TRUE
* ----------------------------------------------------------------------------
*/
BOOLEAN
MemNInitialzeRxEnSeedlessByteLaneErrorUnb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN OUT VOID *OptParam
)
{
UINT8 ByteLane;
for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
NBPtr->TechPtr->ByteLaneError[ByteLane] = FALSE; // All Bytelanes have no errors
}
return TRUE;
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function sets phy power saving related settings in different MPstate context.
*
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
*
* @return none
* ----------------------------------------------------------------------------
*/
VOID
MemNPhyPowerSavingMPstateUnb (
IN OUT MEM_NB_BLOCK *NBPtr
)
{
STATIC UINT8 Sequence[] = {8, 4, 3, 5, 2, 6, 1, 7, 0};
UINT16 DllPower[9];
UINT8 NumLanes;
UINT8 DllWakeTime;
UINT8 MaxRxStggrDly;
UINT8 MinRcvEnGrossDly;
UINT8 MinWrDatGrossDly;
UINT8 dRxStggrDly;
UINT8 dTxStggrDly;
UINT8 TempStggrDly;
UINT8 MaxTxStggrDly;
UINT8 Tcwl;
UINT8 i;
IDS_HDT_CONSOLE (MEM_FLOW, "Start Phy power saving setting for memory Pstate %d\n", NBPtr->MemPstate);
// 4. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyU] = 1b.
// 5. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyL] = 1b.
// 6. D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0][RxDqsUDllPowerDown] = 1.
MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13, MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) | 0x83);
// 7. D18F2x9C_x0D0F_812F_dct[1:0][PARTri] = ~D18F2x90_dct[1:0][ParEn].
// 8. D18F2x9C_x0D0F_812F_dct[1:0][Add17Tri, Add16Tri] = {1b, 1b}
if (NBPtr->MemPstate == MEMORY_PSTATE0) {
MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 0xA1);
}
// 9. IF (DimmsPopulated == 1)&& ((D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==010b) ||
// (D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==011b)) THEN THEN
// program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 1
// ELSE program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 0 ENDIF.
if ((NBPtr->ChannelPtr->Dimms == 1) && ((MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 2) || (MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 3))) {
MemNSetBitFieldNb (NBPtr, BFReserved00C, 0x100);
}
// 10. Program D18F2x9C_x0D0F_0[F,7:0][50,10]_dct[1:0][EnRxPadStandby] = IF
// (D18F2x94_dct[1:0][MemClkFreq] <= 800 MHz) THEN 1 ELSE 0 ENDIF.
MemNSetBitFieldNb (NBPtr, BFEnRxPadStandby, (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) ? 0x1000 : 0);
// 11. Program D18F2x9C_x0000_000D_dct[1:0]_mp[1:0] as follows:
// If (DDR rate < = 1600) TxMaxDurDllNoLock = RxMaxDurDllNoLock = 8h
// else TxMaxDurDllNoLock = RxMaxDurDllNoLock = 7h.
if (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) {
MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 8);
MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 8);
} else {
MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 7);
MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 7);
}
// TxCPUpdPeriod = RxCPUpdPeriod = 011b.
MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 3);
MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 3);
// TxDLLWakeupTime = RxDLLWakeupTime = 11b.
MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 3);
MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 3);
if (NBPtr->IsSupported[DllStaggerEn]) {
// 12. Program D18F2x9C_x0D0F_0[F,7:0][5C,1C]_dct[1:0] as follows.
// Let Numlanes = 8. = 9 with ECC.
NumLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8;
// RxDllStggrEn = TxDllStggrEn = 1.
for (i = 0; i < 9; i ++) {
DllPower[i] = 0x8080;
}
// If (DDR rate > = 1866) DllWakeTime = 1, Else DllWakeTime = 0.
DllWakeTime = (NBPtr->DCTPtr->Timings.Speed >= DDR1866_FREQUENCY) ? 1 : 0;
// Let MaxRxStggrDly = (Tcl*2) + MIN(DqsRcvEnGrossDelay for all byte lanes (see D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4.
MinRcvEnGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessRcvEnDly, FALSE);
ASSERT ((NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly) >= 4);
MaxRxStggrDly = NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly - 4;
// Let (real) dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (Numlanes - 1).
ASSERT (MaxRxStggrDly >= DllWakeTime);
dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (NumLanes - 1);
IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum RcvEnGrossDly: 0x%02x MaxRxStggrDly: 0x%02x dRxStggrDly: 0x%02x\n", MinRcvEnGrossDly, MaxRxStggrDly, dRxStggrDly);
// For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program RxDllStggrDly[5:0] = an
// increasing value, starting with 0 for the first byte lane in the sequence and increasing at a rate of dRxStggrDly
// for each subsequent byte lane. Convert the real to integer by rounding down or using C (int) typecast after linearization.
i = 9 - NumLanes;
TempStggrDly = 0;
for (; i < 9; i ++) {
DllPower[Sequence[i]] |= ((TempStggrDly & 0x3F) << 8);
TempStggrDly = TempStggrDly + dRxStggrDly;
}
// Let MaxTxStggrDly = (Tcwl*2) + MIN(MIN (WrDatGrossDly for all byte lanes (see
// D18F2x9C_x0000_0[3:0]0[2:1]_dct[1:0]_mp[1:0])), MIN(DqsRcvEnGrossDelay for all byte lanes (see
// D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4.
Tcwl = (UINT8) MemNGetBitFieldNb (NBPtr, BFTcwl);
MinWrDatGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessWrDatDly, FALSE);
MaxTxStggrDly = Tcwl * 2 + MIN (MinRcvEnGrossDly, MinWrDatGrossDly) - 4;
// Let dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (Numlanes - 1).
ASSERT (MaxTxStggrDly >= DllWakeTime);
dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (NumLanes - 1);
// For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program TxDllStggrDly[5:0] = an
// increasing integer value, starting with 0 for the first byte lane in the sequence and increasing at a rate of
// dTxStggrDly for each subsequent byte lane.
IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum WrDatGrossDly: 0x%02x MaxTxStggrDly: 0x%02x dTxStggrDly: 0x%02x\n", MinWrDatGrossDly, MaxTxStggrDly, dTxStggrDly);
i = 9 - NumLanes;
TempStggrDly = 0;
for (; i < 9; i ++) {
DllPower[Sequence[i]] |= (TempStggrDly & 0x3F);
TempStggrDly = TempStggrDly + dTxStggrDly;
}
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tByte Lane : ECC 07 06 05 04 03 02 01 00\n");
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tDll Power : %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
DllPower[8], DllPower[7], DllPower[6], DllPower[5], DllPower[4], DllPower[3], DllPower[2], DllPower[1], DllPower[0]);
for (i = 0; i < NumLanes; i ++) {
MemNSetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i, (MemNGetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i) & 0x4040) | DllPower[i]);
}
}
// 13. Program D18F2x248_dct[1:0]_mp[1:0] and then D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0] as follows:
// For M1 context program RxChMntClkEn=RxSsbMntClkEn=0.
// For M0 context program RxChMntClkEn=RxSsbMntClkEn=1.
if (NBPtr->MemPstate == MEMORY_PSTATE1) {
MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 0);
MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0);
} else {
MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 1);
MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0x100);
}
IDS_OPTION_HOOK (IDS_PHY_DLL_STANDBY_CTRL, NBPtr, &NBPtr->MemPtr->StdHeader);
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function resets RxFifo pointer during Read DQS training
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] *OptParam - Optional parameter
*
* @return TRUE
*/
BOOLEAN
MemNResetRxFifoPtrClientNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN OUT VOID *OptParam
)
{
if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
MemNSetBitFieldNb (NBPtr, BFRxPtrInitReq, 1);
MemNPollBitFieldNb (NBPtr, BFRxPtrInitReq, 0, PCI_ACCESS_TIMEOUT, FALSE);
}
return TRUE;
}
/* -----------------------------------------------------------------------------*/
/**
* This function adjusts the Phase Mask based on ECC.
*
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in,out] OptParam - Optional parameter
*
* @return TRUE
* ----------------------------------------------------------------------------
*/
BOOLEAN
MemNAdjust2DPhaseMaskBasedOnEccUnb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN OUT VOID *OptParam
)
{
NBPtr->PhaseLaneMask = NBPtr->PhaseLaneMask & (UINT32) (NBPtr->MCTPtr->Status[SbEccDimms] ? 0x0FFFF : 0x3FFFF);
return TRUE;
}