summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining')
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteDqDqs.c703
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteDqDqs.h108
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteLeveling.c1079
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteLeveling.h85
4 files changed, 1975 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteDqDqs.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteDqDqs.c
new file mode 100644
index 0000000..49b35e4
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteDqDqs.c
@@ -0,0 +1,703 @@
+/** @file
+ The third stage of the write training is determining the PI setting for
+ each byte strobe to make sure that data is sent at the optimal location.
+ In order to do that a pattern of alternating zeros and ones is written to
+ a block of the memory, and then read out. By identifying the location
+ where it is farthest away from where errors are shown the DQS will be
+ aligned to the center of the eye.
+
+@copyright
+ Copyright (c) 1999 - 2012 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement.
+
+**/
+
+//
+// Include files
+//
+#include "MrcWriteDqDqs.h"
+
+/**
+@brief
+ this function executes the write timing centering.
+ Center Tx DQS-DQ using moderate pattern with 1D eye.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded returns mrcSuccess
+**/
+MrcStatus
+MrcWriteTimingCentering (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcOutput *Outputs;
+ U8 ResetPerbit;
+ U8 LoopCount;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ ResetPerbit = 1;
+
+ LoopCount = 10;
+
+ return DQTimeCentering1D (MrcData, Outputs->ValidChBitMask, WrT, ResetPerbit, LoopCount);
+}
+
+/**
+ @brief
+ this function executes the write timing centering in 2D.
+ Final write timing centering using 2D algorithm and per bit optimization.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded returns mrcSuccess
+
+ **/
+MrcStatus
+MrcWriteTimingCentering2D (
+ IN MrcParameters *const MrcData
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcStatus Status;
+ U8 EnPerBit;
+ U8 EnRxDutyCycle;
+ U8 ResetPerBit;
+ U8 LoopCount;
+ U8 En2D;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ EnPerBit = 1;
+ EnRxDutyCycle = 0;
+ ResetPerBit = 1;
+ LoopCount = 15;
+ En2D = 0;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\n\nCalling with EnRxDutyCycle = %d, EnPerBit = %d, ResetPerBit = %d En2D = %d\n",
+ EnRxDutyCycle,
+ EnPerBit,
+ ResetPerBit,
+ En2D
+ );
+
+ Status = DataTimeCentering2D (
+ MrcData,
+ Outputs->MarginResult,
+ Outputs->ValidChBitMask,
+ WrT,
+ EnPerBit,
+ EnRxDutyCycle,
+ ResetPerBit,
+ LoopCount,
+ En2D
+ );
+
+ if (mrcSuccess == Status) {
+ EnPerBit = 0;
+ ResetPerBit = 0;
+ En2D = 1;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\n\nCalling with EnRxDutyCycle = %d, EnPerBit = %d, ResetPerBit = %d En2D = %d\n",
+ EnRxDutyCycle,
+ EnPerBit,
+ ResetPerBit,
+ En2D
+ );
+
+ Status = DataTimeCentering2D (
+ MrcData,
+ Outputs->MarginResult,
+ Outputs->ValidChBitMask,
+ WrT,
+ EnPerBit,
+ EnRxDutyCycle,
+ ResetPerBit,
+ LoopCount,
+ En2D
+ );
+ }
+
+ return Status;
+}
+
+/**
+@brief
+ Rank Margin Tool - Measure margins across various parameters
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - mrcSuccess if succeded
+**/
+MrcStatus
+MrcRankMarginTool (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const U16 mode = 0;
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcCommandMargin *CommandOut;
+ MrcRecvEnMargin *RecvEnOut;
+ MrcWrLevelMargin *WrLevelOut;
+ MrcStatus Status;
+ U32 BERStats[4];
+ U32 Offset;
+ U8 Rank;
+ U8 Param;
+ U8 RankMask;
+ U8 Controller;
+ U8 Channel;
+ U8 byte;
+ U8 bit;
+ U8 chBitMask;
+ U8 MaxMargin;
+ U8 DqLoopCount;
+ U8 CmdLoopCount;
+ S8 VrefOffsets[2];
+ BOOL Lpddr;
+ BOOL SkipVref;
+ BOOL SkipPrint;
+ MrcPower PwrChRank[MAX_CHANNEL][MAX_RANK_IN_CHANNEL];
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ Status = mrcSuccess;
+ MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats));
+ MrcOemMemorySet ((U8 *) VrefOffsets, 0, sizeof (VrefOffsets));
+ MrcOemMemorySet ((U8 *) PwrChRank, 0, sizeof (PwrChRank));
+
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+
+ DqLoopCount = 17;
+ CmdLoopCount = (Lpddr) ? 10 : 17;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rank Margin Testing: DQ LC = %d, Cmd LC = %d\n\n", DqLoopCount, CmdLoopCount);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Margin\nParams: RcvEna\tWrLevel\tRdT\tWrT\tRdV\tWrV\tCmdT\tCmdV\tDimmPwr\tCpuPwr\tTotPwr\n"
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tLft Rgt Lft Rgt Lft Rgt Lft Rgt Low Hi Low Hi Lft Rgt Low Hi\t[mW]\t[mW]\t[mW]\n"
+ );
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ //
+ // Select rank for REUT test
+ //
+ RankMask = 1 << Rank;
+ chBitMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ chBitMask |= SelectReutRanks (MrcData, Channel, RankMask, 0);
+ if ((MRC_BIT0 << Channel) & chBitMask) {
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ //
+ // Clear any old state in DataOffsetTrain
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ }
+ //
+ // Continue with next rank if this rank is not present on any channel
+ //
+ if (!(chBitMask)) {
+ continue;
+ }
+ //
+ // Setup Test
+ // SOE=1, EnCADB=0, EnCKE=0 SOE=1 sets bit12 of REUT_CH_ERR_CTL
+ //
+ SetupIOTestBasicVA (MrcData, chBitMask, DqLoopCount, NSOE, 0, 0, 8);
+ for (Param = RcvEna; Param <= WrLevel; Param++) {
+ if (Param == WrDqsT) {
+ continue;
+ }
+
+ //
+ // For Write/Read timing margining, we want to run traffic with Rd->Rd turnaround times of 4 and 5.
+ // This statement depends on the order of MRC_MarginTypes. If this enum's order changes, this
+ // statement must change.
+ //
+ if (Param == RdT) {
+ Outputs->DQPat = RdRdTA;
+ } else if (Param == RdV) {
+ Outputs->DQPat = BasicVA;
+ }
+
+ MaxMargin = ((Param == RdV) || (Param == WrV)) ? MAX_POSSIBLE_VREF : MAX_POSSIBLE_TIME;
+
+ //
+ // Run test for different Params
+ //
+ Status = MrcGetBERMarginCh (
+ MrcData,
+ Outputs->MarginResult,
+ chBitMask,
+ 0xFF,
+ Rank,
+ Param,
+ mode,
+ 1,
+ MaxMargin,
+ 0,
+ BERStats
+ );
+ }
+
+ //
+ // Use CADB test for Cmd to match Late Command Training
+ //
+ SetupIOTestCADB (MrcData, chBitMask, CmdLoopCount, NSOE, 1, 0);
+
+ //
+ // Run test for Cmd Timing
+ //
+ SkipVref = TRUE;
+ SkipPrint = TRUE;
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ CmdLinearFindEdgesLpddr (MrcData, MrcIterationClock, chBitMask, RankMask, !SkipPrint);
+ } else
+#endif // ULT_FLAG
+ {
+ CmdLinearFindEdges (
+ MrcData,
+ MrcIterationClock,
+ chBitMask,
+ 0xFF,
+ 3,
+ -64,
+ 64,
+ 1,
+ VrefOffsets,
+ SkipPrint,
+ SkipVref
+ );
+ }
+
+ //
+ // Restore centered value
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationClock, 0xFF, 3, 0, 0);
+ }
+
+ Status = MrcResetSequence (MrcData);
+
+ //
+ // Run test for Cmd Voltage
+ //
+ Status = MrcGetBERMarginCh (
+ MrcData,
+ Outputs->MarginResult,
+ chBitMask,
+ 0xFF,
+ Rank,
+ CmdV,
+ mode,
+ 0,
+ MAX_POSSIBLE_VREF,
+ 0,
+ BERStats
+ );
+ Status = MrcResetSequence (MrcData);
+
+ CalcSysPower(MrcData, PwrChRank);
+
+#ifdef MRC_DEBUG_PRINT
+ //
+ // Print test results
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%dR%d:\t", Channel, Rank);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%2d %2d\t%2d %2d\t%2d %2d\t%2d %2d\t%2d %2d\t%2d %2d\t%2d %2d\t%2d %2d\t%2d.%d\t%2d.%d\t%2d.%d\n",
+ Outputs->MarginResult[LastRcvEna][Rank][Channel][0][0] / 10,
+ Outputs->MarginResult[LastRcvEna][Rank][Channel][0][1] / 10,
+ Outputs->MarginResult[LastWrLevel][Rank][Channel][0][0] / 10,
+ Outputs->MarginResult[LastWrLevel][Rank][Channel][0][1] / 10,
+ Outputs->MarginResult[LastRxT][Rank][Channel][0][0] / 10,
+ Outputs->MarginResult[LastRxT][Rank][Channel][0][1] / 10,
+ Outputs->MarginResult[LastTxT][Rank][Channel][0][0] / 10,
+ Outputs->MarginResult[LastTxT][Rank][Channel][0][1] / 10,
+ Outputs->MarginResult[LastRxV][Rank][Channel][0][0] / 10,
+ Outputs->MarginResult[LastRxV][Rank][Channel][0][1] / 10,
+ Outputs->MarginResult[LastTxV][Rank][Channel][0][0] / 10,
+ Outputs->MarginResult[LastTxV][Rank][Channel][0][1] / 10,
+ Outputs->MarginResult[LastCmdT][Rank][Channel][0][0] / 10,
+ Outputs->MarginResult[LastCmdT][Rank][Channel][0][1] / 10,
+ Outputs->MarginResult[LastCmdV][Rank][Channel][0][0] / 10,
+ Outputs->MarginResult[LastCmdV][Rank][Channel][0][1] / 10,
+ PwrChRank[Channel][Rank].DimmPwr / 10,
+ PwrChRank[Channel][Rank].DimmPwr % 10,
+ PwrChRank[Channel][Rank].CpuPower / 10,
+ PwrChRank[Channel][Rank].CpuPower % 10,
+ PwrChRank[Channel][Rank].TotPwr / 10,
+ PwrChRank[Channel][Rank].TotPwr % 10
+ );
+ }
+ }
+#endif
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ CommandOut = &ChannelOut->Command[Rank];
+ CommandOut->Left = (U8) (Outputs->MarginResult[LastCmdT][Rank][Channel][0][0] / 10);
+ CommandOut->Right = (U8) (Outputs->MarginResult[LastCmdT][Rank][Channel][0][1] / 10);
+ CommandOut->Low = (U8) (Outputs->MarginResult[LastCmdV][Rank][Channel][0][0] / 10);
+ CommandOut->High = (U8) (Outputs->MarginResult[LastCmdV][Rank][Channel][0][1] / 10);
+ RecvEnOut = &ChannelOut->ReceiveEnable[Rank];
+ RecvEnOut->Left = (U8) (Outputs->MarginResult[LastRcvEna][Rank][Channel][0][0] / 10);
+ RecvEnOut->Right = (U8) (Outputs->MarginResult[LastRcvEna][Rank][Channel][0][1] / 10);
+ WrLevelOut = &ChannelOut->WriteLevel[Rank];
+ WrLevelOut->Left = (U8) (Outputs->MarginResult[LastWrLevel][Rank][Channel][0][0] / 10);
+ WrLevelOut->Right = (U8) (Outputs->MarginResult[LastWrLevel][Rank][Channel][0][1] / 10);
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ ChannelOut->RxDqPb[Rank][byte][bit].Left = (U8) (Outputs->MarginResult[LastRxT][Rank][Channel][0][0] / 10);
+ ChannelOut->RxDqPb[Rank][byte][bit].Right = (U8) (Outputs->MarginResult[LastRxT][Rank][Channel][0][1] / 10);
+ ChannelOut->TxDqPb[Rank][byte][bit].Left = (U8) (Outputs->MarginResult[LastTxT][Rank][Channel][0][0] / 10);
+ ChannelOut->TxDqPb[Rank][byte][bit].Right = (U8) (Outputs->MarginResult[LastTxT][Rank][Channel][0][1] / 10);
+ ChannelOut->RxDqVrefPb[Rank][byte][bit].Low = (U8) (Outputs->MarginResult[LastRxV][Rank][Channel][0][0] / 10);
+ ChannelOut->RxDqVrefPb[Rank][byte][bit].High = (U8) (Outputs->MarginResult[LastRxV][Rank][Channel][0][1] / 10);
+ ChannelOut->TxDqVrefPb[Rank][byte][bit].Low = (U8) (Outputs->MarginResult[LastTxV][Rank][Channel][0][0] / 10);
+ ChannelOut->TxDqVrefPb[Rank][byte][bit].High = (U8) (Outputs->MarginResult[LastTxV][Rank][Channel][0][1] / 10);
+ }
+ }
+ }
+ }
+ }
+ } // for Rank
+
+ //
+ // Disable CADB Deselects after RMT
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_CTRL_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, 0);
+ }
+ }
+ return Status;
+}
+
+/**
+@brief
+ Peform Read Voltage Centering in 2D.
+ Note: This function currently only supports param = WrV
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if t succeded return mrcSuccess
+ @todo - Need option for loopcount
+**/
+MrcStatus
+MrcWriteVoltageCentering2D (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U32 (*marginch)[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES];
+
+ U8 ResultType;
+ U8 ResultTypeT;
+ U8 loopcount;
+ U8 param;
+ U8 paramT;
+ U8 Channel;
+ U8 byte;
+ U8 tim;
+ U8 chBitMask;
+ U8 MaxTscale;
+ U8 SkipWait;
+ S8 SumEH;
+ S8 SumEHSign;
+ S8 TimePoints[3];
+ U8 EHWeights[sizeof (TimePoints)];
+ U16 mode;
+ S32 center;
+ S32 height;
+ U32 value0[MAX_CHANNEL];
+ U32 BERStats[4];
+ U32 TimScale[MAX_CHANNEL];
+ S32 centersum[MAX_CHANNEL];
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ marginch = &Outputs->MarginResult;
+ Status = mrcSuccess;
+ loopcount = 17;
+ MaxTscale = 12;
+ SumEH = 0;
+ MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats));
+ MrcOemMemorySet ((U8 *) EHWeights, 1, sizeof (EHWeights));
+ TimePoints[0] = -4;
+ TimePoints[1] = 0;
+ TimePoints[2] = 4;
+
+ //
+ // No input for param so set it to RdV
+ //
+ param = WrV;
+
+ //
+ // Assume rank0 is always popuplated
+ //
+ if (param == WrV) {
+ paramT = WrT;
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Error Handler: Unknown Margin Parameter\n");
+ Status = mrcFail;
+ return Status;
+ }
+
+ ResultType = GetMarginResultType (param);
+ ResultTypeT = GetMarginResultType (paramT);
+
+ //
+ /// @todo: Need to check if we can enable it for A0 or not
+ // Outputs->EnDumRd = 1;
+ // SOE = 00b(No Stop on error), EnCADB=0, EnCKE=0
+ //
+ //
+ /// @todo: Will enable the DQ tests instead of basic in the future
+ // SetupIOTestDQ (MrcData,Outputs->ValidChBitMask, loopcount, NSOE, 0, 0);
+ //
+ SetupIOTestBasicVA (MrcData, Outputs->ValidChBitMask, loopcount, NSOE, 0, 0, 8);
+
+ //
+ // Calculate SumEH for use in weighting equations
+ //
+ for (tim = 0; tim < sizeof (TimePoints); tim++) {
+ SumEH += EHWeights[tim];
+ }
+ //
+ // Select rank for REUT test
+ //
+ chBitMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ chBitMask |= SelectReutRanks (MrcData, Channel, ChannelOut->ValidRankBitMask, 0);
+
+ //
+ // Clear any old state in DataTrain Offset
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ //
+ // ####################################################
+ // ############# Initialize EW/EH variables ########
+ // ####################################################
+ //
+ Status = GetMarginCh (MrcData, Outputs->MarginResult, paramT, 0xF);
+
+ //
+ // Update TimScale with results
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ TimScale[Channel] = 0;
+ if (!(chBitMask & (1 << Channel))) {
+ continue;
+ }
+
+ TimScale[Channel] = ((*marginch)[ResultTypeT][0][Channel][0][0] + (*marginch)[ResultTypeT][0][Channel][0][1]) / 20;
+ //
+ // It is possible TimScale[Channel] is 0.
+ //
+ if (!TimScale[Channel] || (TimScale[Channel] > MaxTscale)) {
+ TimScale[Channel] = MaxTscale;
+ }
+ }
+
+ Status = GetMarginCh (MrcData, Outputs->MarginResult, param, 0xF);
+
+ //
+ // ####################################################
+ // ###### Measure Eye Height at all Timing Points #####
+ // ####################################################
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TimScale{0] is %d, TimScale{1] is %d\n", TimScale[0], TimScale[1]);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel\t0\t\t\t\t1\n");
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "WrTime\tLow\tHigh\tHeight\tCenter\t");
+ }
+ //
+ // Initialize parameters to 0
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ centersum[Channel] = 0;
+ value0[Channel] = 0;
+ }
+ //
+ // Loop through all the Time Points to Test
+ //
+ for (tim = 0; tim < sizeof (TimePoints); tim++) {
+ //
+ // Setup Timing Offset for this point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(chBitMask & (1 << Channel))) {
+ continue;
+ }
+
+ value0[Channel] = (S32) (TimePoints[tim] * TimScale[Channel]) / MaxTscale;
+
+ //
+ // There is no multicast per channel...
+ //
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ Status = ChangeMargin (MrcData, paramT, value0[Channel], 0, 0, Channel, 0, byte, 0, 1, 0, MrcRegFileStart);
+ }
+ }
+ //
+ // Run Margin Test
+ //
+ mode = 0;
+ Status = MrcGetBERMarginCh (
+ MrcData,
+ Outputs->MarginResult,
+ chBitMask,
+ 0xFF,
+ 0,
+ param,
+ mode,
+ 1,
+ MAX_POSSIBLE_VREF,
+ 0,
+ BERStats
+ );
+
+ //
+ // Store Results
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(chBitMask & (1 << Channel))) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t");
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", value0[Channel]);
+
+ height = ((*marginch)[ResultType][0][Channel][0][1] + (*marginch)[ResultType][0][Channel][0][0]) / 10;
+ center = (S32) ((*marginch)[ResultType][0][Channel][0][1] - (*marginch)[ResultType][0][Channel][0][0]);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d\t%d\t%d\t%d\t",
+ (*marginch)[ResultType][0][Channel][0][0] / 10,
+ (*marginch)[ResultType][0][Channel][0][1] / 10,
+ height,
+ center / 20
+ );
+ if (tim == 0) {
+ centersum[Channel] = 0;
+ }
+ //
+ // Calculate weight for this point
+ //
+ centersum[Channel] += EHWeights[tim] * center;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "+----->centersum[%d] = %d, \n", Channel, centersum[Channel]);
+ //
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nWtdCntr\t");
+ //
+ // ####################################################
+ // ########### Center Results per Ch #############
+ // ####################################################
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(chBitMask & (1 << Channel))) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t");
+ continue;
+ }
+
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ //
+ // Calculate CenterPoint. Round to Nearest Int
+ //
+ SumEHSign = (centersum[Channel] < 0) ? (-1) : 1;
+
+ centersum[Channel] = (centersum[Channel] + 10 * SumEH * SumEHSign) / (20 * SumEH);
+
+ //
+ // Apply new centerpoint
+ // Only Byte 7 on Channel 1 is needed to update DIMM Vref
+ // Skip if there are more channels
+ //
+ SkipWait = (chBitMask >> (Channel + 1));
+ Status = ChangeMargin (MrcData, param, centersum[Channel], 0, 0, Channel, 0, 7, 0, 1, SkipWait, MrcRegFileCurrent);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t\t\t\t", centersum[Channel]);
+
+ //
+ // Update MrcData for future tests
+ //
+ (*marginch)[ResultType][0][Channel][0][0] = (S32) ((*marginch)[ResultType][0][Channel][0][0]) + (10 * (centersum[Channel]));
+ (*marginch)[ResultType][0][Channel][0][1] = (S32) ((*marginch)[ResultType][0][Channel][0][1]) - (10 * (centersum[Channel]));
+
+ //
+ // Clean up after test
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ //
+ // Clean up
+ //
+ Status = ChangeMargin (MrcData, paramT, 0, 0, 1, 0, 0, 0, 0, 1, 0, MrcRegFileCurrent);
+
+ Outputs->EnDumRd = 0;
+ return Status;
+}
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteDqDqs.h b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteDqDqs.h
new file mode 100644
index 0000000..d021aed
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteDqDqs.h
@@ -0,0 +1,108 @@
+/** @file
+ The third stage of the write training is determining the PI setting for each
+ byte strobe to make sure that data is sent at the optimal location.
+ In order to do that a pattern of alternating zeros and ones is written to a block of the memory, and then read out.
+ By identifying the location where it is farthest away from where errors are shown the DQS will be aligned to the
+ center of the eye.
+
+@copyright
+ Copyright (c) 1999 - 2012 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement.
+**/
+
+#ifndef _MrcWriteDqDqs_h_
+#define _MrcWriteDqDqs_h_
+
+#include "MrcTypes.h"
+#include "McAddress.h"
+#include "MrcApi.h"
+#include "MrcCommandTraining.h"
+#include "MrcCommon.h"
+#include "MrcGlobal.h"
+#include "MrcReset.h"
+
+/**
+ @brief
+ this function executes the write timing centering in 2D.
+ Final write timing centering using 2D algorithm and per bit optimization.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded returns mrcSuccess
+
+ **/
+extern
+MrcStatus
+MrcWriteTimingCentering (
+ IN MrcParameters *const MrcData
+ );
+
+/**
+ @brief
+ this function executes the write timing centering in 2D.
+ Final write timing centering using 2D algorithm and per bit optimization.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded returns mrcSuccess
+
+ **/
+extern
+MrcStatus
+MrcWriteTimingCentering2D (
+ IN MrcParameters *const MrcData
+ );
+
+/**
+@brief
+ Rank Margin Tool - Measure margins across various parameters
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - mrcSuccess if succeded
+**/
+extern
+MrcStatus
+MrcRankMarginTool (
+ IN OUT MrcParameters *const MrcData
+ );
+
+/**
+@brief
+ this function execute the write timing centering in 2D.
+ Final write timing centering using 2D algorithm and per bit optimization
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded returns mrcSuccess
+**/
+/**
+@brief
+ Peform Read Voltage Centering in 2D.
+ Note: This function currently only supports param = WrV
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded return mrcSuccess
+ @todo - Need option for loopcount
+**/
+extern
+MrcStatus
+MrcWriteVoltageCentering2D (
+ IN OUT MrcParameters *const MrcData
+ );
+
+#endif // _MrcWriteDqDqs_h_
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteLeveling.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteLeveling.c
new file mode 100644
index 0000000..8b71620
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteLeveling.c
@@ -0,0 +1,1079 @@
+/** @file
+ The write leveling flow is the first part of the write training.
+ In this stage the memory controller needs to synchronize its DQS sending
+ with the clock for each DRAM. The DRAM can be put in a mode where for a
+ write command it responds by sampling the clock using DQS and sending it
+ back as the data. The IO can receive this and tune the DQS alignment so
+ it will appear in sync with the clock at the DRAM side.
+ The following algorithm is used for the write leveling flow:
+
+@copyright
+ Copyright (c) 1999 - 2013 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement.
+
+**/
+#include "MrcWriteLeveling.h"
+
+/**
+@brief
+ this function execute the Jedec write leveling Cleanup.
+ Center TxDQS-CLK timing
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded return mrcSuccess
+**/
+MrcStatus
+MrcJedecWriteLevelingCleanUp (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MRC_REUTAddress REUTAddress = {{0, 0, 0, 0}, // Start
+ {0, 0, 0, 1023}, // Stop
+ {0, 0, 0, 0}, // Order
+ {0, 0, 0, 0}, // IncRate
+ {0, 0, 0, 1}}; // IncValue
+ const U8 DumArr[7] = {1, 1, 1, 1, 1, 1, 1};
+ const U8 DqOffsetMax = 7;
+ const S8 DqOffsets[7] = {0, -10, 10, -5, 5, -15, 15};
+ const S8 Offsets[5] = {0, 1, -1, 2, 3};
+ const U8 PMaskConst[8] = {0, 0, 1, 1, 1, 1, 0, 0};
+ const U32 CleanUpSeeds[MRC_WDB_NUM_MUX_SEEDS] = {0xAAAAAA, 0xCCCCCC, 0xF0F0F0};
+ const U32 NormalSeeds[MRC_WDB_NUM_MUX_SEEDS] = {0xA10CA1, 0xEF0D08, 0xAD0A1E};
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 offset;
+ U8 ChBitMask;
+ U8 RankMask; // RankBitMask for both channels
+ U8 ValidRankMask;
+ U8 Pattern[4][2];
+ U8 PMask[sizeof (PMaskConst)];
+ U8 AllGood;
+ U8 AllGoodLoops;
+ U8 DqOffset;
+ U8 RankDouble;
+ U8 RankHalf;
+ U8 RankMod2;
+ U8 Start;
+ S8 ByteOff[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Passing offset for each ch/byte.
+ S8 ByteSum[MAX_CHANNEL]; // Sum of passing offsets for a ch
+ S8 TargetOffset;
+ U16 ByteMask;
+ U16 ValidByteMask;
+ U16 Result;
+ U16 SkipMe;
+ U16 BytePass[MAX_CHANNEL]; // Bit mask indicating which ch/byte has passed
+ S16 GlobalByteOff;
+ U32 CRValue;
+ U32 Offset;
+ U32 CRAddDelay[MAX_CHANNEL];
+ S32 LocalOffset;
+ BOOL Done;
+#ifdef ULT_FLAG
+ BOOL Lpddr;
+#endif //ULT_FLAG
+ MRC_WDBPattern WDBPattern;
+
+ MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_CFG_STRUCT ReutChPatWdbClMuxCfg;
+ DDRDATA0CH0_CR_TXTRAINRANK0_STRUCT CrTxTrainRank;
+#ifdef MRC_DEBUG_PRINT
+ U32 ErrLower[MAX_CHANNEL];
+ U32 ErrUpper[MAX_CHANNEL];
+#endif // MRC_DEBUG_PRINT
+
+ //
+ // Setup REUT Pattern
+ // Use 0x00FFC33C pattern to keep DQ-DQS simple but detect any failures
+ // Same Pattern as NHM/WSM
+ //
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Debug = &Inputs->Debug;
+ Status = mrcSuccess;
+ LocalOffset = 0;
+ Done = TRUE;
+ Pattern[0][0] = 0x00;
+ Pattern[0][1] = 0xFF;
+ Pattern[1][0] = 0xFF;
+ Pattern[1][1] = 0x00;
+ Pattern[2][0] = 0xC3;
+ Pattern[2][1] = 0x3C;
+ Pattern[3][0] = 0x3C;
+ Pattern[3][1] = 0xC3;
+ WDBPattern.IncRate = 1;
+ WDBPattern.Start = 0;
+ WDBPattern.Stop = 3;
+ WDBPattern.DQPat = BasicVA;
+ MrcOemMemoryCpy (PMask, (U8 *) PMaskConst, sizeof (PMask));
+ MrcOemMemorySet ((U8 *) CRAddDelay, 0, sizeof (CRAddDelay));
+#ifdef MRC_DEBUG_PRINT
+ MrcOemMemorySet ((U8 *) ErrLower, 0, sizeof (ErrLower));
+ MrcOemMemorySet ((U8 *) ErrUpper, 0, sizeof (ErrUpper));
+#endif // MRC_DEBUG_PRINT
+
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+#endif // ULT_FLAG
+ //
+ // Spread = 8, Start = 0, 1, 2 and 3
+ //
+ for (Start = 0; Start < (sizeof (Pattern) / sizeof (Pattern[0])); Start++) {
+ WriteWDBFixedPattern (MrcData, Pattern[Start], PMask, 8, Start);
+ }
+
+ //
+ // Set LSFR Seed to be sequential
+ //
+ MrcProgramLFSR (MrcData, CleanUpSeeds);
+
+ //
+ // Set Channel and Rank bit masks
+ //
+ ChBitMask = Outputs->ValidChBitMask;
+ ValidRankMask = Outputs->ValidRankMask;
+ ValidByteMask = (MRC_BIT0 << Outputs->SdramCount) - 1; // 0x1FF or 0xFF
+ //
+ // Setip IO test CmdPat=PatWrRd, NumCL=4, LC=4, REUTAddress, SOE=3,
+ // WDBPattern, EnCADB=0, EnCKE=0, SubSeqWait=0 )
+ //
+ SetupIOTest (MrcData, ChBitMask, PatWrRd, 2, 4, &REUTAddress, NSOE, &WDBPattern, 0, 0, 0);
+
+ //
+ // Progam BITBUFFER for JWLT
+ //
+ ReutChPatWdbClMuxCfg.Data = 0;
+ ReutChPatWdbClMuxCfg.Bits.Mux0_Control = BTBUFFER;
+ ReutChPatWdbClMuxCfg.Bits.Mux1_Control = BTBUFFER;
+ ReutChPatWdbClMuxCfg.Bits.Mux2_Control = BTBUFFER;
+ ReutChPatWdbClMuxCfg.Bits.ECC_Data_Source_Sel = 1;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_CFG_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_WDB_CL_MUX_CFG_REG - MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_CFG_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ReutChPatWdbClMuxCfg.Data);
+ }
+ }
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ //
+ // Select Rank for REUT test
+ //
+ ChBitMask = 0;
+ RankMask = MRC_BIT0 << Rank;
+ RankDouble = Rank * 2;
+ RankHalf = Rank / 2;
+ RankMod2 = Rank % 2;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChBitMask |= SelectReutRanks (MrcData, Channel, (RankMask), 0);
+ BytePass[Channel] = ByteSum[Channel] = 0;
+ }
+ //
+ // Skip if both channels empty
+ //
+ if (!(RankMask & ValidRankMask)) {
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRank %d\n", Rank);
+
+ //
+ // *************************************************
+ // Sweep through the cycle offsets until we find a value that passes
+ // *************************************************
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Sweep through the cycle offsets until we find a value that passes\n");
+
+ if (RankMask & ValidRankMask) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel 0 1\nDelay DqOffset Byte \t");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE, (
+ Outputs->SdramCount == MAX_SDRAM_IN_DIMM
+ ) ? "0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8" : "0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7"
+ );
+ }
+
+ for (offset = 0; offset < sizeof (Offsets); offset++) {
+ //
+ // Program new delay offsets to DQ/DQS timing:
+ //
+ /// @todo - Address debuging switches or remove.
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Program new delay offsets to DQ/DQS timing %d\n", Offsets[offset]);
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Calculate offsets
+ //
+ GlobalByteOff = 0;
+ if (Offsets[offset] > MAX_ADD_DELAY) {
+ CRAddDelay[Channel] = MrcBitSwap (CRAddDelay[Channel], MAX_ADD_DELAY, RankDouble, 2);
+ GlobalByteOff = 128 * (Offsets[offset] - MAX_ADD_DELAY);
+ } else if (Offsets[offset] < 0) {
+ CRAddDelay[Channel] = MrcBitSwap (CRAddDelay[Channel], 0, RankDouble, 2);
+ GlobalByteOff = 128 * Offsets[offset];
+ } else {
+ CRAddDelay[Channel] = MrcBitSwap (CRAddDelay[Channel], Offsets[offset], RankDouble, 2);
+ }
+ //
+ // Write Tx DQ/DQS Flyby delays
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Add GlobalByteOff = %d to TxDQS Flyby delay: Ch %d \n", GlobalByteOff, Channel);
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ CRValue = ChannelOut->TxDqs[Rank][Byte] + GlobalByteOff;
+ CrTxTrainRank.Data = 0;
+ CrTxTrainRank.Bits.TxDqDelay = CRValue + 32;
+ CrTxTrainRank.Bits.TxDqsDelay = CRValue;
+ CrTxTrainRank.Bits.TxEqualization = ChannelOut->TxEq[Rank][Byte];
+ UpdateTxT (MrcData, Channel, Rank, Byte, 0x3, CrTxTrainRank.Data);
+ //
+ /// @todo - Address debuging switches or remove.
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CRValue = 0x%x \n", CrTxTrainRank.Data);
+ //
+ }
+ //
+ // Write Wr ADD Delays
+ //
+ Offset = MCHBAR_CH0_CR_SC_WR_ADD_DELAY_REG + ((MCHBAR_CH1_CR_SC_WR_ADD_DELAY_REG - MCHBAR_CH0_CR_SC_WR_ADD_DELAY_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, CRAddDelay[Channel]);
+ //
+ /// @todo - Address debuging switches or remove.
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CRAddDelay[%d] = 0x%x \n", Channel, CRAddDelay[Channel]);
+ //
+ }
+
+#ifdef ULT_FLAG
+ if (!Lpddr) {
+#endif // ULT_FLAG
+ //
+ // Reset FIFOs & Reset DRAM DLL (Micron WorkAround). Wait 1uS for test to complete
+ //
+ Status = IoReset (MrcData);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ Status = MrcWriteMRS (
+ MrcData,
+ Channel,
+ RankMask,
+ mrMR0,
+ ChannelOut->Dimm[RankHalf].Rank[RankMod2].MR[mrMR0] | (MRC_BIT0 << 8)
+ );
+ }
+ }
+
+ MrcWait (MrcData, (1 * HPET_1US));
+#ifdef ULT_FLAG
+ }
+#endif // ULT_FLAG
+
+ //
+ // Run Test across all DqOffsets points
+ //
+ for (DqOffset = 0; DqOffset < DqOffsetMax; DqOffset++) {
+ //
+ // Update Offset
+ //
+ ChangeMargin (MrcData, WrT, DqOffsets[DqOffset], 0, 1, 0, Rank, 0, 0, 0, 0, MrcRegFileRank);
+
+ //
+ // Run Test
+ // DQPat = BasicVA, DumArr, ClearErrors = 1, mode = 0
+ //
+ RunIOTest (MrcData, ChBitMask, BasicVA, DumArr, 1, 0);
+
+ //
+ // Update results for all ch/bytes
+ //
+ Done = TRUE;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n% 3d\t% 3d\t \t", Offsets[offset], DqOffsets[DqOffset]);
+
+ //
+ // Update results for all ch/bytes
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ if (Channel == 0) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ (Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? " " : " "
+ );
+ }
+
+ continue;
+ }
+ //
+ // Read out per byte error results and check for any byte error
+ //
+ Offset = 4 + MCHBAR_CH0_CR_REUT_CH_ERR_ECC_CHUNK_RANK_BYTE_NTH_STATUS_REG +
+ (
+ (
+ MCHBAR_CH1_CR_REUT_CH_ERR_ECC_CHUNK_RANK_BYTE_NTH_STATUS_REG -
+ MCHBAR_CH0_CR_REUT_CH_ERR_ECC_CHUNK_RANK_BYTE_NTH_STATUS_REG
+ ) * Channel
+ );
+ Result = (U16) MrcReadCR (MrcData, Offset);
+ SkipMe = (Result & ValidByteMask) | BytePass[Channel];
+
+#ifdef MRC_DEBUG_PRINT
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_DATA_STATUS_REG + ((MCHBAR_CH1_CR_REUT_CH_ERR_DATA_STATUS_REG - MCHBAR_CH0_CR_REUT_CH_ERR_DATA_STATUS_REG) * Channel);
+ ErrLower[Channel] = MrcReadCR (MrcData, Offset);
+ //
+ // Lower 32 bits
+ //
+ ErrUpper[Channel] = MrcReadCR (MrcData, Offset + 4);
+ //
+ // Upper 32 bits
+ //
+#endif // MRC_DEBUG_PRINT
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ ByteMask = MRC_BIT0 << Byte;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ ((Result & ValidByteMask) & ByteMask) ?
+ "# " : //Fail
+ ". " // Pass
+ );
+ //
+ // If this byte has failed or previously passed, nothing to do
+ //
+ if (SkipMe & ByteMask) {
+ continue;
+ }
+
+ BytePass[Channel] |= ByteMask;
+ ByteOff[Channel][Byte] = Offsets[offset];
+ ByteSum[Channel] += Offsets[offset];
+ }
+ //
+ /// @todo - Address debuging switches or remove.
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "For DqOffsets %d: BytePass[%d] = 0x%X, Result = 0x%x, SkipMe = 0x%x\n", DqOffsets[DqOffset], Channel, BytePass[Channel], Result, SkipMe);
+ //
+ if (BytePass[Channel] != ValidByteMask) {
+ Done = FALSE;
+ }
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "0x%08x%08x ",
+ ErrUpper[Channel],
+ ErrLower[Channel]
+ );
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+ //
+ // Jump out of the for DqOffset loop if everybody is passing
+ //
+ if (Done == TRUE) {
+ break;
+ }
+ }
+ //
+ // Jump out of the for offset loop if everybody is passing
+ //
+ if (Done == TRUE) {
+ break;
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "\n");
+
+ //
+ // Walk through and find the correct value for each ch/byte
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ if (Done == FALSE) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "Error! Write Leveling CleanUp - Couldn't find a passing value for all bytes on Channel %u Rank %u:\nBytes - ",
+ Channel,
+ Rank
+ );
+#ifdef MRC_DEBUG_PRINT
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, ((BytePass[Channel] ^ ValidByteMask) & (1 << Byte)) ? "%d " : "", Byte);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "\n");
+#endif
+ return mrcWriteLevelingError;
+ }
+ //
+ // Calculate the average offset, rounding up
+ // Apply that offset to the global MC CRAddDelay register
+ //
+ TargetOffset = (ByteSum[Channel] + (S8) (Outputs->SdramCount / 2)) / (S8) Outputs->SdramCount;
+ //
+ /// @todo - Address debuging switches or remove.
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TargetOffset = 0x%x, ByteSum[%d] = 0x%x \n", TargetOffset, Channel, ByteSum[Channel]);
+ //
+ AllGood = 0;
+ AllGoodLoops = 0;
+ GlobalByteOff = 0;
+ while (AllGood == 0) {
+ //
+ // Update CRAddDelay and GlobalByteOff
+ //
+ GlobalByteOff = 0;
+ if (TargetOffset > MAX_ADD_DELAY) {
+ CRAddDelay[Channel] = MrcBitSwap (CRAddDelay[Channel], MAX_ADD_DELAY, RankDouble, 2);
+ GlobalByteOff = 128 * (TargetOffset - MAX_ADD_DELAY);
+ } else if (TargetOffset < 0) {
+ CRAddDelay[Channel] = MrcBitSwap (CRAddDelay[Channel], 0, RankDouble, 2);
+ GlobalByteOff = 128 * TargetOffset;
+ } else {
+ CRAddDelay[Channel] = MrcBitSwap (CRAddDelay[Channel], TargetOffset, RankDouble, 2);
+ }
+ //
+ /// @todo - Address debuging switches or remove.
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "GlobalByteOff = 0x%x, CRAddDelay[%d] = 0x%x \n", GlobalByteOff, Channel, CRAddDelay[Channel]);
+ // Refine TargetOffset to make sure it works for all byte lanes
+ //
+ AllGood = 1;
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ LocalOffset = GlobalByteOff + 128 * (ByteOff[Channel][Byte] - TargetOffset);
+ if ((ChannelOut->TxDq[Rank][Byte] + LocalOffset) > (511 - 64)) {
+ AllGood = 0;
+ AllGoodLoops += 1;
+ TargetOffset += 1;
+ break;
+ }
+
+ if ((ChannelOut->TxDqs[Rank][Byte] + LocalOffset) < 96) {
+ AllGood = 0;
+ AllGoodLoops += 1;
+ TargetOffset -= 1;
+ break;
+ }
+ //
+ /// @todo - Address debuging switches or remove.
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TargetOffset = 0x%x, ByteOff[%d][%d] = 0x%x \n", TargetOffset, Channel, Byte, ByteOff[Channel][Byte]);
+ //
+ }
+ //
+ // Avoid an infinite loop
+ //
+ if (AllGoodLoops > 3) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Error Handler1: JWL CleanUp - TargetOffset refining failed \n");
+ return mrcFail;
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d.R%d: Offset\tFinalEdge\n", Channel, Rank);
+ //
+ // Program the final settings to the DQ bytes and update MrcData
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ LocalOffset = GlobalByteOff + 128 * (ByteOff[Channel][Byte] - TargetOffset);
+ ChannelOut->TxDq[Rank][Byte] += (S16) LocalOffset;
+ ChannelOut->TxDqs[Rank][Byte] += (S16) LocalOffset;
+ UpdateTxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " B%d: %d\t%d\n", Byte, LocalOffset, ChannelOut->TxDqs[Rank][Byte]);
+ }
+ //
+ /// @todo - Address debuging switches or remove.
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "LocalOffset = 0x%x \n", LocalOffset);
+ // Update MC Delay
+ //
+ Offset = MCHBAR_CH0_CR_SC_WR_ADD_DELAY_REG + ((MCHBAR_CH1_CR_SC_WR_ADD_DELAY_REG - MCHBAR_CH0_CR_SC_WR_ADD_DELAY_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, CRAddDelay[Channel]);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CRAddDelay[%d] = 0x%x \n", Channel, CRAddDelay[Channel]);
+
+#ifdef ULT_FLAG
+ if (!Lpddr) {
+#endif // ULT_FLAG
+ //
+ // Clean up after Test
+ //
+ Status = MrcWriteMRS (
+ MrcData,
+ Channel,
+ RankMask,
+ mrMR0,
+ ChannelOut->Dimm[RankHalf].Rank[RankMod2].MR[mrMR0] | (MRC_BIT0 << 8)
+ );
+ MrcWait (MrcData, (1 * HPET_1US));
+#ifdef ULT_FLAG
+ }
+#endif // ULT_FLAG
+ }
+ }
+ //
+ // Clean up after Test
+ // Restore WDB - VicRot=8, Start=0 and restore default seed
+ //
+ WriteWDBVAPattern (MrcData, 0, BASIC_VA_PATTERN_SPRED_8, 8, 0);
+ MrcProgramLFSR (MrcData, NormalSeeds);
+
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_DDRCRDATAOFFSETTRAIN_REG, 0);
+ Status = IoReset (MrcData);
+
+ return Status;
+}
+
+/**
+@brief
+ this function execute the Jedec write leveling training.
+ Center TxDQS-CLK timing
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succedes return mrcSuccess
+**/
+MrcStatus
+MrcJedecWriteLevelingTraining (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ TOdtValue *OdtTableIndex;
+ MrcStatus Status;
+ U8 Channel;
+ U8 Rank;
+ U8 RankDouble;
+ U8 RankHalf;
+ U8 RankMod2;
+ U8 Byte;
+ U8 refbyte;
+ U8 ChBitMask;
+ U8 RankMask; // RankBitMask for both channels
+ U8 ValidRankMask;
+ U8 OtherDimm;
+ U8 OdtMatrix;
+ U16 WLStart;
+ U16 WLStop;
+ U16 WLDelay;
+ U8 WLStep;
+ U32 WaitTime;
+ U32 CRValue;
+ U32 Offset;
+ U32 DqsToggleTime;
+ S32 InitialPassingStart[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 InitialPassingEnd[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 CurrentPassingStart[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 CurrentPassingEnd[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 LargestPassingStart[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 LargestPassingEnd[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 iWidth;
+ S32 cWidth;
+ S32 lWidth;
+ S32 ByteWidth;
+ BOOL Pass;
+ BOOL RankIsx16;
+ BOOL SavedRestoreMRS;
+#ifdef ULT_FLAG
+ BOOL Lpddr;
+#endif //ULT_FLAG
+
+ DDR3_MODE_REGISTER_1_STRUCT Ddr3ModeRegister1;
+ DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2;
+ MCHBAR_CH0_CR_REUT_CH_MISC_ODT_CTRL_STRUCT ReutChMiscOdtCtrl;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrData0Control0;
+ DDRDATA0CH0_CR_DATATRAINFEEDBACK_STRUCT DataTrainFeedback;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Debug = &Inputs->Debug;
+ Status = mrcSuccess;
+ OdtTableIndex = NULL;
+ CRValue = 0;
+ ChBitMask = Outputs->ValidChBitMask;
+ ValidRankMask = Outputs->ValidRankMask;
+
+ // Save the flag and force MRS recalculation
+ SavedRestoreMRS = Outputs->RestoreMRs;
+ Outputs->RestoreMRs = FALSE;
+
+ DqsToggleTime = 1024;
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ if (Lpddr) {
+ DqsToggleTime = 2048;
+ }
+#endif // ULT_FLAG
+
+ //
+ // Enabling WLmode causes DQS to toggle for 1024 qclk. Wait for this to stop
+ // Round up to nearest uS
+ //
+
+ WaitTime = (Outputs->Qclkps * DqsToggleTime + 500000) / 1000000;
+ //
+ // Propagate delay values (without a write command) and Set Qoff on all ranks.
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Propagate delay values from rank 0 to prevent assertion failures in RTL
+ //
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ DdrCrDataControl0.Bits.ReadRFRd = 0;
+ DdrCrDataControl0.Bits.ReadRFWr = 1;
+ DdrCrDataControl0.Bits.ReadRFRank = 0;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+
+ //
+ // Set ForceBiasOn and make sure ForceRxAmpOn is cleared
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DdrCrDataControl2.Data = ChannelOut->DqControl2[Byte].Data;
+ DdrCrDataControl2.Bits.ForceBiasOn = 1;
+ DdrCrDataControl2.Bits.ForceRxOn = 0;
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ DdrCrDataControl2.Bits.WlLongDelEn = 1;
+ }
+#endif // ULT_FLAG
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Byte);
+ MrcWriteCR (MrcData, Offset, DdrCrDataControl2.Data);
+ }
+ }
+ }
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = MRC_BIT0 << Rank;
+ if (!(RankMask & ValidRankMask)) {
+ //
+ // Skip if all channels empty
+ //
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRank %d\n", Rank);
+ RankDouble = Rank * 2;
+ RankHalf = Rank / 2;
+ RankMod2 = Rank % 2;
+ //
+ // Program MR1: Set A7 to enter Write Leveling mode
+ // Write MaskRasWe to prevent scheduler from issuing non-Read commands
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+#ifdef ULT_FLAG
+ //
+ // Enable WL mode in MR2[7]
+ //
+ if (Lpddr) {
+ CRValue = (ChannelOut->Dimm[RankHalf].Rank[RankMod2].MR[mrMR2]);
+ Status = MrcIssueMrw (MrcData, Channel, Rank, mrMR2, (CRValue | MRC_BIT7), FALSE, FALSE);
+ } else
+#endif // ULT_FLAG
+ {
+ Ddr3ModeRegister1.Data = ChannelOut->Dimm[RankHalf].Rank[RankMod2].MR[mrMR1];
+ Ddr3ModeRegister1.Bits.WriteLeveling = 1;
+
+ OdtTableIndex = GetOdtTableIndex (MrcData, Channel, RANK_TO_DIMM_NUMBER (Rank));
+ if (OdtTableIndex == NULL) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: OdtTableIndex is NULL!\n");
+ return mrcFail;
+ }
+#ifdef ULT_FLAG
+ if (Inputs->CpuModel == cmHSW_ULT) { // DDR3L case
+ Ddr3ModeRegister1 = UpdateRttNomValue (MrcData, OdtTableIndex->RttWr, Ddr3ModeRegister1);
+ }
+#endif // ULT_FLAG
+
+ //
+ // In write leveling mode Rtt_Nom = Rtt_Wr ONLY for 2DPC
+ //
+ if (ChannelOut->DimmCount == 2) {
+ Ddr3ModeRegister1 = UpdateRttNomValue (MrcData, OdtTableIndex->RttWr, Ddr3ModeRegister1);
+ }
+ Status = MrcWriteMRS (MrcData, Channel, RankMask, mrMR1, (U16) Ddr3ModeRegister1.Data);
+ }
+
+ //
+ // Assert ODT for myself
+ //
+ OdtMatrix = RankMask;
+ if (ChannelOut->DimmCount == 2) {
+ //
+ // Calculate non-target DIMM
+ //
+ OtherDimm = ((Rank + 2) / 2) & MRC_BIT0;
+ //
+ // Assert ODT for non-target DIMM
+ //
+ OdtMatrix |= 1 << (2 * OtherDimm);
+ }
+
+ ReutChMiscOdtCtrl.Data = 0;
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ //
+ // Only one ODT pin for ULT
+ //
+ ReutChMiscOdtCtrl.Bits.ODT_On = 1;
+ ReutChMiscOdtCtrl.Bits.ODT_Override = 1;
+ }
+#endif // ULT_FLAG
+#ifdef TRAD_FLAG
+ if ((Inputs->CpuModel == cmHSW) || (Inputs->CpuModel == cmCRW)) {
+ ReutChMiscOdtCtrl.Bits.ODT_On = OdtMatrix;
+ ReutChMiscOdtCtrl.Bits.ODT_Override = MCHBAR_CH0_CR_REUT_CH_MISC_ODT_CTRL_ODT_Override_MAX;
+ }
+#endif // TRAD_FLAG
+ Offset = MCHBAR_CH0_CR_REUT_CH_MISC_ODT_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_MISC_ODT_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_MISC_ODT_CTRL_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ReutChMiscOdtCtrl.Data);
+ }
+ } // for Channel
+
+ //
+ // ******************************************
+ // STEP 1 and 2: Find middle of high region
+ // ******************************************
+ //
+ WLStart = 192;
+ WLStop = 320;
+ WLStep = 2;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\n\tCh0\t\t\t\t\t\t\t\t%sCh1\n",
+ (Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? "\t" : ""
+ );
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "WLDelay%s%s",
+ (Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? "\t0\t1\t2\t3\t4\t5\t6\t7\t8" : "\t0\t1\t2\t3\t4\t5\t6\t7",
+ (Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? "\t0\t1\t2\t3\t4\t5\t6\t7\t8" : "\t0\t1\t2\t3\t4\t5\t6\t7"
+ );
+
+ for (WLDelay = WLStart; WLDelay < WLStop; WLDelay += WLStep) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n %d:", WLDelay);
+ //
+ // Program WL DQS Delays:
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Enable Write Level Mode in DDR and Propagate delay values (without a write command).
+ // Stay in WLMode.
+ //
+ DdrCrData0Control0.Data = ChannelOut->DqControl0.Data;
+ DdrCrData0Control0.Bits.WLTrainingMode = 1;
+ DdrCrData0Control0.Bits.TxPiOn = 1;
+ DdrCrData0Control0.Bits.ReadRFRd = 0;
+ DdrCrData0Control0.Bits.ReadRFWr = 1;
+ DdrCrData0Control0.Bits.ReadRFRank = Rank;
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ UpdateTxT (MrcData, Channel, Rank, Byte, 1, WLDelay);
+
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL0_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL0_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL0_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL0_REG) * Byte);
+ MrcWriteCR (MrcData, Offset, DdrCrData0Control0.Data);
+ }
+ }
+ }
+
+ if (WLDelay == WLStart) {
+ MrcWait (MrcData, (WaitTime * HPET_1US)); // Wait for the first burst to finish
+ }
+
+ Status = IoReset (MrcData);
+
+ MrcWait (MrcData, (WaitTime * HPET_1US));
+
+ //
+ // Update results for all Channels/Bytes
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\t\t\t\t\t\t\t\t%s",
+ (Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? "\t" : ""
+ );
+ continue;
+ }
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ Offset = DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG +
+ ((DDRDATA0CH1_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * Byte);
+ DataTrainFeedback.Data = MrcReadCR (MrcData, Offset);
+ Pass = (DataTrainFeedback.Bits.DataTrainFeedback >= 16);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t%c%d", Pass ? '.' : '#', DataTrainFeedback.Data);
+ if (WLDelay == WLStart) {
+ if (Pass) {
+ InitialPassingStart[Channel][Byte] = InitialPassingEnd[Channel][Byte] = WLStart;
+ CurrentPassingStart[Channel][Byte] = CurrentPassingEnd[Channel][Byte] = WLStart;
+ LargestPassingStart[Channel][Byte] = LargestPassingEnd[Channel][Byte] = WLStart;
+ } else {
+ InitialPassingStart[Channel][Byte] = InitialPassingEnd[Channel][Byte] = -WLStep;
+ CurrentPassingStart[Channel][Byte] = CurrentPassingEnd[Channel][Byte] = -WLStep;
+ LargestPassingStart[Channel][Byte] = LargestPassingEnd[Channel][Byte] = -WLStep;
+ }
+ } else {
+ if (Pass) {
+ if (InitialPassingEnd[Channel][Byte] == (WLDelay - WLStep)) {
+ InitialPassingEnd[Channel][Byte] = WLDelay;
+ }
+
+ if (CurrentPassingEnd[Channel][Byte] == (WLDelay - WLStep)) {
+ CurrentPassingEnd[Channel][Byte] = WLDelay;
+ } else {
+ CurrentPassingStart[Channel][Byte] = CurrentPassingEnd[Channel][Byte] = WLDelay;
+ }
+ //
+ // Special case for last step: Append Initial Passing Region
+ // WLDelay should be considered a continuous range that wraps around 0
+ //
+ if ((WLDelay >= (WLStop - WLStep)) && (InitialPassingStart[Channel][Byte] == WLStart)) {
+ iWidth = (InitialPassingEnd[Channel][Byte] - InitialPassingStart[Channel][Byte]);
+ CurrentPassingEnd[Channel][Byte] += (WLStep + iWidth);
+ }
+ //
+ // Update Largest variables
+ //
+ cWidth = CurrentPassingEnd[Channel][Byte] - CurrentPassingStart[Channel][Byte];
+ lWidth = LargestPassingEnd[Channel][Byte] - LargestPassingStart[Channel][Byte];
+ if (cWidth > lWidth) {
+ LargestPassingStart[Channel][Byte] = CurrentPassingStart[Channel][Byte];
+ LargestPassingEnd[Channel][Byte] = CurrentPassingEnd[Channel][Byte];
+ }
+ }
+ }
+ } // for Byte
+ } // for Channel
+ } // for WLDelay
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\tInitPassSt\tInitPassEn\tCurrPassSt\tCurrPassEn\tLargPassSt\tLargPassEn\n");
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d\n", Channel);
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " B%d:\t\t%d\t\t%d\t\t%d\t\t%d\t\t%d\t\t%d\n",
+ Byte,
+ InitialPassingStart[Channel][Byte],
+ InitialPassingEnd[Channel][Byte],
+ CurrentPassingStart[Channel][Byte],
+ CurrentPassingEnd[Channel][Byte],
+ LargestPassingStart[Channel][Byte],
+ LargestPassingEnd[Channel][Byte]
+ );
+ }
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+
+ //
+ // Clean up after step
+ // Very coarsely adjust for any cycle errors
+ // Program values for TxDQS
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Check if rank is a x16
+ //
+ RankIsx16 = (ChannelOut->Dimm[RankHalf].SdramWidth == 16 ? TRUE : FALSE);
+
+ //
+ // Clear ODT before MRS (JEDEC Spec)
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_MISC_ODT_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_MISC_ODT_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_MISC_ODT_CTRL_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0);
+
+#ifdef ULT_FLAG
+ //
+ // Restore MR2 values
+ //
+ if (Lpddr) {
+ CRValue = (ChannelOut->Dimm[RankHalf].Rank[RankMod2].MR[mrMR2]);
+ Status = MrcIssueMrw (MrcData, Channel, Rank, mrMR2, CRValue, FALSE, FALSE);
+ } else
+#endif // ULT_FLAG
+ {
+ //
+ // Restore Write Leveling mode and Rtt_Nom for this rank
+ //
+ CRValue = (ChannelOut->Dimm[RankHalf].Rank[RankMod2].MR[mrMR1]);
+ Status = MrcWriteMRS (MrcData, Channel, RankMask, mrMR1, (U16) CRValue);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d.R%d: LftEdge Width\n", Channel, Rank);
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ ByteWidth = LargestPassingEnd[Channel][Byte] - LargestPassingStart[Channel][Byte];
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " B%d: %d\t%d\n",
+ Byte,
+ LargestPassingStart[Channel][Byte],
+ ByteWidth
+ );
+
+ //
+ // Check if width is valid
+ //
+ if ((ByteWidth <= 32) || (ByteWidth >= 96)) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "\nERROR! Width region outside expected limits for Channel: %u Rank %u Byte: %u\n",
+ Channel,
+ Rank,
+ Byte
+ );
+ return mrcWriteLevelingError;
+ }
+ //
+ // Align byte pairs if DIMM is x16
+ //
+ if (RankIsx16 && (Byte & MRC_BIT0)) {
+ //
+ // If odd byte number (1, 3, 5 or 7)
+ //
+ refbyte = Byte - 1;
+ if (LargestPassingStart[Channel][Byte] > (LargestPassingStart[Channel][refbyte] + 64)) {
+ LargestPassingStart[Channel][Byte] -= 128;
+ }
+
+ if (LargestPassingStart[Channel][Byte] < (LargestPassingStart[Channel][refbyte] - 64)) {
+ LargestPassingStart[Channel][Byte] += 128;
+ }
+ }
+
+#ifdef ULT_FLAG
+ if (Inputs->CpuModel == cmHSW_ULT) {
+ //
+ // Fix for b4618067 - need to add 1 QCLK to DqsPi
+ //
+ LargestPassingStart[Channel][Byte] += 64;
+ }
+#endif // ULT_FLAG
+
+ ChannelOut->TxDqs[Rank][Byte] = (U16) LargestPassingStart[Channel][Byte];
+ ChannelOut->TxDq[Rank][Byte] = (U16) (LargestPassingStart[Channel][Byte] + 32);
+ UpdateTxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ }
+ }
+ }
+ //
+ // Clean up after Test
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, ChannelOut->DqControl0.Data);
+
+ //
+ // Restore DqControl2 values.
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Byte);
+ MrcWriteCR (MrcData, Offset, ChannelOut->DqControl2[Byte].Data);
+ }
+ }
+ }
+
+#ifdef ULT_FLAG
+ if (!Lpddr)
+#endif // ULT_FLAG
+ {
+ //
+ // DLLEnable=0, Dic=0, Al=0, Level=0, Tdqs=0, Qoff=0
+ //
+ Status = MrcSetMR1 (MrcData, 0, DIMMRON, 0, 0, 0, 0);
+ if (Status != mrcSuccess) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "ERROR: RESET FAIL - WLT\n");
+ return Status;
+ }
+ }
+
+ // Restore flag
+ Outputs->RestoreMRs = SavedRestoreMRS;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nJedec Write Leveling CLEANUP\n");
+ Status = MrcJedecWriteLevelingCleanUp (MrcData);
+
+ return Status;
+}
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteLeveling.h b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteLeveling.h
new file mode 100644
index 0000000..bbb6ceb
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/WriteTraining/MrcWriteLeveling.h
@@ -0,0 +1,85 @@
+/** @file
+ The write leveling training algorithm definitions.
+
+@copyright
+ Copyright (c) 1999 - 2012 Intel Corporation. All rights reserved.
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement.
+**/
+
+#ifndef _MrcWriteLeveling_h_
+#define _MrcWriteLeveling_h_
+
+#include "MrcTypes.h"
+#include "MrcApi.h"
+#include "McAddress.h"
+#include "MrcCommon.h"
+#include "MrcReset.h"
+#include "MrcDdr3.h"
+#include "MrcIoControl.h"
+#include "MrcReadReceiveEnable.h"
+#include "MrcOem.h"
+
+///
+/// This defines the maximum ADD delay that can be programmed to the register. It may change in the future
+///
+#define MAX_ADD_DELAY (2)
+
+/**
+@brief
+ this function execute the Jedec write leveling Cleanup.
+ Center TxDQS-CLK timing
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded return mrcSuccess
+**/
+extern
+MrcStatus
+MrcJedecWriteLevelingCleanUp (
+ IN OUT MrcParameters *const MrcData
+ );
+
+/**
+@brief
+ this function execute the functional write leveling training.
+ Center TxDQS-CLK timing
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded return mrcSuccess
+**/
+extern
+MrcStatus
+MrcWriteLevelingTraining (
+ IN OUT MrcParameters *const MrcData
+ );
+
+/**
+@brief
+ this function execute the Jedec write leveling training.
+ Center TxDQS-CLK timing
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succedes return mrcSuccess
+**/
+extern
+MrcStatus
+MrcJedecWriteLevelingTraining (
+ IN OUT MrcParameters *const MrcData
+ );
+
+#endif // _MrcWriteLeveling_h_