summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining')
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadDqDqs.c1207
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadDqDqs.h97
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadReceiveEnable.c1155
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadReceiveEnable.h120
4 files changed, 2579 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadDqDqs.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadDqDqs.c
new file mode 100644
index 0000000..7403971
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadDqDqs.c
@@ -0,0 +1,1207 @@
+/** @file
+ Once DQS is aligned against the clock in the receive enable training flow,
+ the second stage of the read training is the DQ/DQS training, aligning each
+ strobe with it's byte of data. The DQ/DQS training is once again using the
+ DDR read synchronization mode, in this mode a predetermined pattern is read
+ out of the DDR. The following algorithm is used to align the data sampling
+ to the best sampling point.
+
+@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 files
+//
+#include "MrcReadDqDqs.h"
+
+/**
+ Perform Read MPR Training.
+ Center read DQ-DQS with MPR pattern.
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - mrcSuccess or reason for failure.
+**/
+MrcStatus
+MrcReadMprTraining (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 ChBitMask;
+ U8 RankMask; // RankBitMask for both channels
+ U8 LoopCount;
+ S8 MPRCorrectionFactor;
+ S8 DqsDelay;
+ 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 cWidth;
+ S32 lWidth;
+ S32 Center;
+ S32 Center2;
+ BOOL Pass;
+ BOOL Lpddr;
+ U32 Offset;
+ U32 OdtSampExtendDelay;
+ MCHBAR_CH0_CR_REUT_CH_MISC_ODT_CTRL_STRUCT ReutChMiscOdtCtrl;
+ MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_STRUCT ReutGlobalCtl;
+ DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ Status = mrcSuccess;
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ LoopCount = 10;
+ OdtSampExtendDelay = 1 * HPET_MIN;
+
+ //
+ // Use basic addressing mode (open a page on a rank and keep writing/reading to it)
+ // Rotate through all 8 logical ranks
+ // LFSR and LMN disabled.
+ //
+ ChBitMask = Outputs->ValidChBitMask;
+ RankMask = Outputs->ValidRankMask;
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ //
+ // Set DQS Delay to 32
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ //
+ // Update RxDqsP & RxDqsN - leave other parameter the same; can we update in the next loop or do it per channel
+ //
+ UpdateRxT (MrcData, Channel, Rank, Byte, 5, 32);
+ }
+ }
+ }
+
+ //
+ // Clear any old state in DataTrain Offset
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ }
+
+ //
+ // Setup REUT Engine
+ //
+ SetupIOTestMPR (MrcData, ChBitMask, LoopCount, NSOE, 0, 0);
+
+ //
+ /// @todo: Start with 0 for now.
+ //
+ MPRCorrectionFactor = 0;
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (!((MRC_BIT0 << Rank) & RankMask)) {
+ continue; // Skip if both channels empty
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rank = %u\n", Rank);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel\t0 1\nByte\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"
+ );
+
+ //
+ // Program MR3 and Mask RAS/WE to prevent scheduler for issuing non-Read commands
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ SelectReutRanks (MrcData, Channel, MRC_BIT0 << Rank, 0);
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ if (!Lpddr) {
+ Status = MrcWriteMRS (MrcData, Channel, (MRC_BIT0 << Rank), mrMR3, 0x4);
+ }
+
+ 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);
+ ReutChMiscOdtCtrl.Data = MrcReadCR (MrcData, Offset);
+ ReutChMiscOdtCtrl.Bits.MPR_Train_DDR_On = 1;
+ MrcWriteCR (MrcData, Offset, ReutChMiscOdtCtrl.Data);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nDqsDelay");
+ for (DqsDelay = RMPR_DQS_START; DqsDelay < RMPR_DQS_STOP; DqsDelay += RMPR_DQS_STEP) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n% 5d\t", DqsDelay);
+
+ //
+ // Program DQS Delays and download the Reg File for the current rank.
+ //
+ Status = ChangeMargin (MrcData, RdT, DqsDelay, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileStart);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ (Channel != 0) ? "" : ((Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? " " : " ")
+ );
+ } else {
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // Force on SenseAmp
+ //
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Byte);
+ DdrCrDataControl2.Data = ChannelOut->DqControl2[Byte].Data;
+ DdrCrDataControl2.Bits.ForceBiasOn = 1;
+ DdrCrDataControl2.Bits.ForceRxOn = 1;
+ DdrCrDataControl2.Bits.LeakerComp = 0;
+ MrcWriteCR (MrcData, Offset, DdrCrDataControl2.Data);
+ }
+ //
+ // Enable RX Training mode. Turn on the ODT.
+ //
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ if (!Lpddr) {
+ DdrCrDataControl0.Bits.ForceOdtOn = 1;
+ }
+ DdrCrDataControl0.Bits.RxTrainingMode = 1;
+
+ //
+ // Need to disable EnReadPreamble
+ //
+ DdrCrDataControl0.Bits.EnReadPreamble = 0;
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ }
+ }
+
+ Status = IoReset (MrcData);
+
+
+ //
+ // Start REUT and run for 1uS
+ //
+ ReutGlobalCtl.Data = 0;
+ ReutGlobalCtl.Bits.Global_Start_Test = 1;
+ MrcWriteCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG, ReutGlobalCtl.Data);
+
+ //
+ // Wait for test to start clearing errors.
+ //
+ MrcWait (MrcData, START_TEST_DELAY);
+
+ //
+ // Clear Results for Prior Test and wait to obtain results
+ //
+ Status = IoReset (MrcData);
+ MrcWait (MrcData, IO_RESET_DELAY);
+
+ //
+ // Stop REUT
+ //
+ ReutGlobalCtl.Data = 0;
+ ReutGlobalCtl.Bits.Global_Stop_Test = 1;
+ MrcWriteCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG, ReutGlobalCtl.Data);
+
+
+ //
+ // Update results for all ch/bytes
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ 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);
+ Pass = (MrcReadCR (MrcData, Offset) == 1);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, Pass ? ". " : "# ");
+
+ if (DqsDelay == RMPR_DQS_START) {
+ if (Pass) {
+ CurrentPassingStart[Channel][Byte] = CurrentPassingEnd[Channel][Byte] = DqsDelay;
+ LargestPassingStart[Channel][Byte] = LargestPassingEnd[Channel][Byte] = DqsDelay;
+ } else {
+ CurrentPassingStart[Channel][Byte] = CurrentPassingEnd[Channel][Byte] = -33;
+ LargestPassingStart[Channel][Byte] = LargestPassingEnd[Channel][Byte] = -33;
+ }
+ } else {
+ if (Pass) {
+ if (CurrentPassingEnd[Channel][Byte] == (DqsDelay - RMPR_DQS_STEP)) {
+ CurrentPassingEnd[Channel][Byte] = DqsDelay;
+ } else {
+ CurrentPassingStart[Channel][Byte] = CurrentPassingEnd[Channel][Byte] = DqsDelay;
+ }
+ //
+ // 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 (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // Restore orginal value
+ //
+ 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);
+ }
+
+ //
+ // Clear RxTrainingMode
+ //
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ //
+ // For LPDDR need to disable OdtSampExtendEn before disabling RxTrainingMode,
+ // then re-enable OdtSampExtendEn (from the host struct)
+ //
+ DdrCrDataControl0.Bits.OdtSampExtendEn = 0;
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ MrcWait (MrcData, OdtSampExtendDelay);
+
+ DdrCrDataControl0.Bits.RxTrainingMode = 0;
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ MrcWait (MrcData, OdtSampExtendDelay);
+ }
+#endif // ULT_FLAG
+
+ MrcWriteCrMulticast (MrcData, Offset, ChannelOut->DqControl0.Data);
+ } // for Channel
+
+ Status = IoReset (MrcData);
+ } // for DqsDelay
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ //
+ // Clean Up registers.
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ //
+ // MPR_TRAIN_DDR_ON bit will force a special command so clear it before MRS command
+ //
+ 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);
+ ReutChMiscOdtCtrl.Data = MrcReadCR (MrcData, Offset);
+ ReutChMiscOdtCtrl.Bits.MPR_Train_DDR_On = 0;
+ MrcWriteCR (MrcData, Offset, ReutChMiscOdtCtrl.Data);
+
+ if (!Lpddr) {
+ Status = MrcWriteMRS (MrcData, Channel, (MRC_BIT0 << Rank), mrMR3, 0x0);
+ }
+ //
+ // Clear up after test
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%u.R%u: \tLeft\tRight\tWidth\tCenter\tRxDqsPN\n", Channel, Rank);
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ lWidth = LargestPassingEnd[Channel][Byte] - LargestPassingStart[Channel][Byte];
+
+ //
+ // Error Handler if eye not found for all bytes
+ //
+ if (lWidth == 0) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "\nERROR!! NO EYE found for Channel: %u Rank: %u Byte: %u \n",
+ Channel,
+ Rank,
+ Byte
+ );
+ return mrcReadMPRErr;
+ }
+
+ if (lWidth > RMPR_MIN_WIDTH) {
+ Center = LargestPassingStart[Channel][Byte] + lWidth / 2;
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_WARNING,
+ "\nWARNING!! lWidth <= %u for Channel: %u Rank: %u Byte: %u \n",
+ RMPR_MIN_WIDTH,
+ Channel,
+ Rank,
+ Byte
+ );
+ Center = 0;
+ }
+ //
+ // Based on previous data, the MPR center is not very good; Adjust it with a magical number
+ //
+ Center += MPRCorrectionFactor;
+ Center2 = 32 + Center;
+ ChannelOut->RxDqsP[Rank][Byte] = (U8) Center2;
+ ChannelOut->RxDqsN[Rank][Byte] = (U8) Center2;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " B%u: \t%d\t%d\t%d\t%d\t%d\n",
+ Byte,
+ LargestPassingStart[Channel][Byte],
+ LargestPassingEnd[Channel][Byte],
+ lWidth,
+ Center,
+ ChannelOut->RxDqsP[Rank][Byte]
+ );
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Center = %d, RxDqsPN[%d][%d] = %d\n", Center, Channel, Byte, ChannelOut->RxDqsP[Rank][Byte]);
+ //
+ } // for Byte
+ }
+ } // for Channel
+ } // for Rank
+
+ //
+ // Now program the DQS center values on populated ranks, data is taken from the host struct.
+ // Need to do it after all ranks are trained, because we need to keep the same DQS value on all ranks
+ // during the training.
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ }
+ }
+ }
+
+ //
+ // Clean up after Test. Download the Reg file of the last rank used.
+ //
+ Status = ChangeMargin (MrcData, RdT, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileCurrent);
+ Status = IoReset (MrcData);
+ return Status;
+}
+
+/**
+ Peform Read Timing Centering.
+ Center Rx DQS-DQ using moderate pattern with 1D eye
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded returns mrcSuccess
+**/
+MrcStatus
+MrcReadTimingCentering (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcOutput *Outputs;
+ U8 ResetPerBit;
+ U8 LoopCount;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ ResetPerBit = 1;
+
+ LoopCount = 10;
+
+ return DQTimeCentering1D (MrcData, Outputs->ValidChBitMask, RdT, ResetPerBit, LoopCount);
+}
+
+/**
+ Peform Read Timing Centering in 2D.
+ Final read 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
+MrcReadTimingCentering2D (
+ IN MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcStatus Status;
+ U8 EnPerBit;
+ U8 EnRxDutyCycle;
+ U8 ResetPerBit;
+ U8 LoopCount;
+ U8 En2D;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &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,
+ RdT,
+ EnPerBit,
+ EnRxDutyCycle,
+ ResetPerBit,
+ LoopCount,
+ En2D
+ );
+ if (mrcSuccess == Status) {
+ EnPerBit = 0;
+ EnRxDutyCycle = 1;
+ 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,
+ RdT,
+ EnPerBit,
+ EnRxDutyCycle,
+ ResetPerBit,
+ LoopCount,
+ En2D
+ );
+ }
+
+ return Status;
+}
+
+/**
+ Peform Read Voltage Centering in 2D.
+ Note: This function currently only supports param = RdV
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in,out] MarginByte - Pointer to Marging Results data structure
+ @param[in] ChBitMask - Channel bit mask.
+ @param[in] Param - {0:RcvEn, 1:RdT, 2:WrT, 3: WrDqsT, 4:RdV, 5:WrV, 6:WrLevel,
+ 8:WrTBit, 9:RdTBit, 10:RdVBit,
+ 16:RdFan2, 17:WrFan2, 32:RdFan3, 32:WrFan3}
+ ONLY RdV is allowed in this function
+ @param[in] EnPerBit - Option to enable per bit margining
+ @param[in] ResetPerBit - Option to Reset PerBit Deskew to middle value before byte training
+ @param[in] LoopCount - Loop count
+ @param[in] En2D - Option to only run center at nominal Vref point
+
+ @retval MrcStatus - If it succeded return mrcSuccess
+**/
+MrcStatus
+ReadVoltageCentering2D (
+ IN OUT MrcParameters *const MrcData,
+ IN OUT U32 MarginByte[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES],
+ IN U8 ChBitMask,
+ IN U8 Param,
+ IN U8 EnPerBit,
+ IN U8 ResetPerBit,
+ IN U8 LoopCount,
+ IN U8 En2D
+ )
+{
+ const S8 TimePoints[] = { 0, -8, 8 };
+ const U8 EHWeights[sizeof (TimePoints)] = { 1, 1, 1 };
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U8 ResultType;
+ U8 Channel;
+ U8 Byte;
+ U8 Rank;
+ U8 bit;
+ U8 lcloop;
+ U8 tim;
+ U8 paramB;
+ U8 paramT;
+ U8 BMap[MAX_SDRAM_IN_DIMM];
+ S8 SumEH;
+ S8 SumEHSign;
+ U8 MaxTscale;
+ U8 SaveLC;
+ U16 mode;
+ S32 center[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 value0[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U32 BERStats[4];
+ U32 TimScale[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 CenterSumByte[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 CenterSumBit[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS];
+ U32 marginbit[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS][MAX_EDGES];
+ U32 EyeShape[3][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES];
+ U32 Offset;
+ MCHBAR_CH0_CR_REUT_CH_ERR_CTL_STRUCT ReutChErrCtl;
+ DDRDATA0CH0_CR_RXOFFSETVDQ_STRUCT RxOffsetVdq;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ Status = mrcSuccess;
+ SumEH = 0;
+ MaxTscale = 12;
+ MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats));
+ for (lcloop = 0; lcloop < (sizeof (BMap) / sizeof (BMap[0])); lcloop++) {
+ BMap[lcloop] = lcloop;
+ }
+ //
+ // Assume rank0 is always popuplated
+ //
+ if (Param == RdV) {
+ paramB = RdVBit;
+ paramT = RdT;
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Error Handler: Unknown Margin Parameter\n");
+ Status = mrcReadVoltage2DError;
+ return Status;
+ }
+ //
+ /// @todo: Need to check if we can enable it for A0 or not
+ // Outputs->EnDumRd = 1;
+ // SOE = 10b ( Stop on All Byte Groups Error )
+ //
+ //
+ /// @todo: Will enable the DQ tests instead of basic in the future
+ // SetupIOTestDQ (MrcData, ChBitMask, LoopCount, ABGSOE, 0, 0);
+ //
+ SetupIOTestBasicVA (MrcData, ChBitMask, LoopCount, ABGSOE, 0, 0, 8);
+ //
+ // Calculate SumEH for use in weighting equations
+ //
+ for (tim = 0; tim < sizeof (TimePoints); tim++) {
+ SumEH += EHWeights[tim];
+
+ //
+ // Loop once at nominal Vref point
+ //
+ if (En2D == 0) {
+ tim = sizeof (TimePoints);
+ }
+ }
+ //
+ // SumEH is used as divisor, make sure is never 0
+ //
+ if (SumEH == 0) {
+ SumEH = 1;
+ }
+ //
+ // Reset PerBit Deskew to middle value before Byte training
+ // Amplifier voltage offset for bit[x] of the DQ Byte.
+ // {0: Most negative offset,... 8: 0 offset, ... 15: Most postive offset}
+ //
+ if (ResetPerBit == 1) {
+ //
+ // EnMultiCast=1, 0,0,0,0, UpdateHost=1, SkipWait=0
+ //
+ Status = ChangeMargin (MrcData, paramB, 0x88888888, 0, 1, 0, 0, 0, 0, 1, 0, MrcRegFileStart);
+ }
+ //
+ // Select rank for REUT test
+ //
+ ChBitMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ ChBitMask |= SelectReutRanks (MrcData, Channel, ChannelOut->ValidRankBitMask, 0);
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ //
+ // Clear any old state in DataTrain Offset
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ //
+ // ####################################################
+ // ################ Initialize EW/EH variables ######
+ // ####################################################
+ //
+ Status = GetMarginByte (MrcData, Outputs->MarginResult, paramT, 0, 0xF);
+ ResultType = GetMarginResultType (paramT);
+
+#ifdef MRC_DEBUG_PRINT
+ if (En2D) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n### Measure Eye Height, per BYTE, at ALL (2D) Timing Points\n");
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n### Measure Eye Height, per BYTE, at NOMINAL Timing\n");
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %d", Channel);
+ if (Channel == 0) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nByte ");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d ", Byte);
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nTScale\t");
+#endif // MRC_DEBUG_PRINT
+ //
+ // Update TimScale to have the appropriate eye width (read from last saved parameters)
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if (En2D > 0) {
+ TimScale[Channel][Byte] =
+ (
+ MarginByte[ResultType][0][Channel][Byte][0] +
+ MarginByte[ResultType][0][Channel][Byte][1]
+ ) /
+ 20;
+ } else {
+ TimScale[Channel][Byte] = 1;
+ }
+ //
+ // It is possible sumT is 0.
+ //
+ if (!(TimScale[Channel][Byte]) || (TimScale[Channel][Byte] > MaxTscale)) {
+ TimScale[Channel][Byte] = MaxTscale;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", TimScale[Channel][Byte]);
+ }
+ }
+
+ Status = GetMarginByte (MrcData, Outputs->MarginResult, Param, 0, 0xF);
+ ResultType = GetMarginResultType (Param);
+
+ //
+ // ####################################################
+ // ###### Measure Eye Height at all Timing Points #####
+ // ####################################################
+ //
+ //
+ // Loop through all the Time Points to Test
+ //
+ for (tim = 0; tim < sizeof (TimePoints); tim++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\nRdTime\t");
+
+ //
+ // Setup Timing Offset for this point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ value0[Channel][Byte] = (S32) (TimePoints[tim] * TimScale[Channel][Byte]) / MaxTscale;
+ Status = ChangeMargin (MrcData, paramT, value0[Channel][Byte], 0, 0, Channel, 0, Byte, 0, 1, 0, MrcRegFileCurrent);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", value0[Channel][Byte]);
+ }
+ }
+ //
+ // Run Margin Test
+ //
+ mode = 0;
+ Status = MrcGetBERMarginByte (
+ MrcData,
+ Outputs->MarginResult,
+ ChBitMask,
+ 0,
+ 0xFF,
+ Param,
+ mode,
+ BMap,
+ 1,
+ MAX_POSSIBLE_VREF,
+ 0,
+ BERStats
+ );
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nLo-Hi\t");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d %d\t",
+ MarginByte[ResultType][0][Channel][Byte][0],
+ MarginByte[ResultType][0][Channel][Byte][1]
+ );
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nCenter\t");
+#endif // MRC_DEBUG_PRINT
+ //
+ // Store Results
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ center[Channel][Byte] = (S32)(MarginByte[ResultType][0][Channel][Byte][1] -
+ MarginByte[ResultType][0][Channel][Byte][0]);
+ if (tim == 0) {
+ CenterSumByte[Channel][Byte] = 0;
+ }
+ //
+ // Calculate weight for this point
+ //
+ CenterSumByte[Channel][Byte] += EHWeights[tim] * center[Channel][Byte];
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", center[Channel][Byte] / 20);
+
+ //
+ // Record edges for use in per bit margining
+ //
+ EyeShape[tim][Channel][Byte][0] = MarginByte[ResultType][0][Channel][Byte][0];
+ EyeShape[tim][Channel][Byte][1] = MarginByte[ResultType][0][Channel][Byte][1];
+ }
+ }
+ }
+ //
+ // Loop once at nominal Vref point
+ //
+ if (En2D == 0) {
+ tim = sizeof (TimePoints);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\nWtdCntr\t");
+ //
+ // ####################################################
+ // ########### Center Results per Byte ############
+ // ####################################################
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ //
+ // Calculate CenterPoint. Round to Nearest Int
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ SumEHSign = (CenterSumByte[Channel][Byte] < 0) ? (-1) : 1;
+
+ CenterSumByte[Channel][Byte] = (CenterSumByte[Channel][Byte] + 10 * (SumEHSign * SumEH)) / (20 * SumEH);
+ MRC_DEBUG_MSG(Debug, MSG_LEVEL_NOTE,"%d\t", CenterSumByte[Channel][Byte] / 2);
+
+ //
+ // Apply new centerpoint
+ // step size for RxVref is about 7.8mv AND for RxVrefOffset is about 3.9mv
+ //
+ ChannelOut->RxVref[Byte] = (U8) ((S32) ChannelOut->RxVref[Byte] + (CenterSumByte[Channel][Byte] / 2));
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ }
+ //
+ // Update the Eye Edges
+ //
+ for (tim = 0; tim < sizeof (TimePoints); tim++) {
+ EyeShape[tim][Channel][Byte][0] = (S32) EyeShape[tim][Channel][Byte][0] + (10 * CenterSumByte[Channel][Byte]);
+ EyeShape[tim][Channel][Byte][1] = (S32) EyeShape[tim][Channel][Byte][1] - (10 * CenterSumByte[Channel][Byte]);
+
+ //
+ // Loop once at nominal Vref point
+ //
+ if (En2D == 0) {
+ tim = sizeof (TimePoints);
+ }
+ }
+ //
+ // Update MrcData for future tests (MarginResult points back to MrcData)
+ // EyeShape for Vref 0 is assumed to have the best shape for future tests.
+ //
+ MarginByte[ResultType][0][Channel][Byte][0] = EyeShape[0][Channel][Byte][0];
+ MarginByte[ResultType][0][Channel][Byte][1] = EyeShape[0][Channel][Byte][1];
+ }
+
+ //
+ // Clear up after test
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+
+ //
+ // Propagate new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, Channel, 1, 0, MrcRegFileCurrent, 0, 1, 0);
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRdVref\t");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", (S8) ChannelOut->RxVref[Byte]);
+ }
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nPerByte Margins after per BYTE Centering\nLo-Hi ");
+ //
+#endif // MRC_DEBUG_PRINT
+ //
+ // ####################################################
+ // ############ Measure Eye Height Per BIT ########
+ // ####################################################
+ //
+ if (EnPerBit) {
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE, (
+ En2D
+ ) ? "\n### Measure Eye Height, per BIT, at ALL (2D) Timing Points\n" :
+ "\n### Measure Eye Height, per BIT, at NOMINAL Timing\n"
+ );
+
+ //
+ // Stop on all lane fail
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel))
+ {
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %d", Channel);
+ if (Channel == 0) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t");
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+ //
+ // SOE = 11b ( Stop on All Lanes Error )
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_CTL_REG + ((MCHBAR_CH1_CR_REUT_CH_ERR_CTL_REG - MCHBAR_CH0_CR_REUT_CH_ERR_CTL_REG) * Channel);
+ ReutChErrCtl.Data = 0;
+
+ ReutChErrCtl.Bits.Selective_Error_Enable_Chunk = MCHBAR_CH0_CR_REUT_CH_ERR_CTL_Selective_Error_Enable_Chunk_MAX;
+ ReutChErrCtl.Bits.Selective_Error_Enable_Cacheline = MCHBAR_CH0_CR_REUT_CH_ERR_CTL_Selective_Error_Enable_Cacheline_MAX;
+ ReutChErrCtl.Bits.Stop_On_Error_Control = ALSOE;
+ ReutChErrCtl.Bits.Stop_on_Nth_Error = 1;
+ MrcWriteCR (MrcData, Offset, ReutChErrCtl.Data);
+ }
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Byte % 24d ", Byte);
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+#endif // MRC_DEBUG_PRINT
+ //
+ // Loop through all the Vref Points to Test
+ //
+ SaveLC = Outputs->DQPatLC;
+ for (tim = 0; tim < sizeof (TimePoints); tim++) {
+ //
+ // Setup Timing Offset for this point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ value0[Channel][Byte] = (S32) (TimePoints[tim] * TimScale[Channel][Byte]) / MaxTscale;
+ Status = ChangeMargin (MrcData, paramT, value0[Channel][Byte], 0, 0, Channel, 0, Byte, 0, 1, 0, MrcRegFileStart);
+
+ //
+ // Amplifier voltage offset for bit[x] of the DQ Byte.
+ // {0: Most negative offset,... 8: 0 offset, ... 15: Most postive offset}
+ //
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ marginbit[Channel][Byte][bit][0] = marginbit[Channel][Byte][bit][1] = 8;
+ }
+ }
+ }
+ }
+ //
+ // Run Margin Test
+ // Loop through 2 times. Once at low loop count and Once at high loopcount
+ // Improves runtime
+ // @todo: Need 2 loops below if not using BASICVA
+ //
+ for (lcloop = 0; lcloop < 1; lcloop++) {
+ Outputs->DQPatLC = (lcloop == 0) ? 1 : SaveLC;
+ mode = 0;
+ Status = MrcGetMarginBit (MrcData, ChBitMask, 0, marginbit, EyeShape[tim], paramB, mode, 15);
+ }
+ //
+ // Store Results
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ center[Channel][Byte] = ((marginbit[Channel][Byte][bit][1] - 8) - (8 - marginbit[Channel][Byte][bit][0]));
+ if (tim == 0) {
+ CenterSumBit[Channel][Byte][bit] = 0;
+ }
+ //
+ // Calculate weight for this point
+ //
+ CenterSumBit[Channel][Byte][bit] += EHWeights[tim] * center[Channel][Byte];
+ }
+ }
+ }
+ }
+ //
+ // Loop once at nominal Vref point
+ //
+ if (En2D == 0) {
+ tim = sizeof (TimePoints);
+ }
+ } // END OF TIM LOOP
+ //
+ // ####################################################
+ // ############ Center Result Per BIT ##############
+ // ####################################################
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nWgted Center ");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ //
+ // Calculate and apply CenterPoint. Round to Nearest Int
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ RxOffsetVdq.Data = 0;
+
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ SumEHSign = (CenterSumBit[Channel][Byte][bit] < 0) ? (-1) : 1;
+
+ CenterSumBit[Channel][Byte][bit] = (CenterSumBit[Channel][Byte][bit] + (SumEHSign * SumEH)) / (2 * SumEH);
+
+ //
+ // Centerpoint needs to be added to starting DqPb value
+ //
+ CenterSumBit[Channel][Byte][bit] += (S32) ChannelOut->RxDqVrefPb[0][Byte][bit].Center;
+
+ //
+ // Check for saturation
+ //
+ if (CenterSumBit[Channel][Byte][bit] > 15) {
+ CenterSumBit[Channel][Byte][bit] = 15;
+ } else if (CenterSumBit[Channel][Byte][bit] < 0) {
+ CenterSumBit[Channel][Byte][bit] = 0;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%x ", CenterSumBit[Channel][Byte][bit]);
+
+ //
+ // Update MrcData
+ //
+ ChannelOut->RxDqVrefPb[0][Byte][bit].Center = (U8) CenterSumBit[Channel][Byte][bit];
+
+ RxOffsetVdq.Data |= (CenterSumBit[Channel][Byte][bit] << (DDRDATA0CH0_CR_RXOFFSETVDQ_Lane0_WID * bit));
+ }
+ //
+ // Apply and propagate new centerpoint
+ //
+ Status = ChangeMargin (MrcData, RdVBit, RxOffsetVdq.Data, 0, 0, Channel, 0, Byte, 0, 0, 0, MrcRegFileCurrent);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ }
+ //
+ // Clear up after test
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ } // END PERBIT LOOP
+
+/// @attention - This is used to determine if the PerBit routines are correct. Left for sanity.
+/*
+#ifdef MRC_DEBUG_PRINT
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+
+ ChannelOut = &Outputs->Channel[Channel];
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ Offset = DDRDATA0CH0_CR_RXOFFSETVDQ_REG +
+ ((DDRDATA0CH1_CR_RXOFFSETVDQ_REG - DDRDATA0CH0_CR_RXOFFSETVDQ_REG) * Channel) +
+ ((DDRDATA1CH0_CR_RXOFFSETVDQ_REG - DDRDATA0CH0_CR_RXOFFSETVDQ_REG) * Byte);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRdVref = % 3d RdVBit = 0x%08X", (S8) ChannelOut->RxVref[Byte],
+ MrcReadCR (MrcData, Offset));
+ }
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+*/
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ //
+ // Clean up after test
+ //
+ Outputs->EnDumRd = 0;
+ Status = ChangeMargin (MrcData, paramT, 0, 0, 1, 0, 0, 0, 0, 1, 0, MrcRegFileCurrent);
+
+ return Status;
+}
+
+/**
+ Peform Read Voltage Centering in 2D.
+ Note: This function currently only supports param = RdV
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded return mrcSuccess
+**/
+MrcStatus
+MrcReadVoltageCentering2D (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcOutput *Outputs;
+ MrcStatus Status;
+ U8 EnPerBit;
+ U8 ResetPerBit;
+ U8 LoopCount;
+ U8 En2D;
+
+ Outputs = &MrcData->SysOut.Outputs;
+
+ EnPerBit = 1;
+ ResetPerBit = 1;
+ LoopCount = 15;
+ En2D = 0;
+ Status = ReadVoltageCentering2D (
+ MrcData,
+ Outputs->MarginResult,
+ Outputs->ValidChBitMask,
+ RdV,
+ EnPerBit,
+ ResetPerBit,
+ LoopCount,
+ En2D
+ );
+
+ if (mrcSuccess == Status) {
+ //
+ // EnPerBit = 0; ResetPerbit = 0; loopcount = 10; En2D=1
+ //
+ EnPerBit = 0;
+ ResetPerBit = 0;
+ LoopCount = 15;
+ En2D = 1;
+ Status = ReadVoltageCentering2D (
+ MrcData,
+ Outputs->MarginResult,
+ Outputs->ValidChBitMask,
+ RdV,
+ EnPerBit,
+ ResetPerBit,
+ LoopCount,
+ En2D
+ );
+ }
+
+ return Status;
+}
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadDqDqs.h b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadDqDqs.h
new file mode 100644
index 0000000..e0c717a
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadDqDqs.h
@@ -0,0 +1,97 @@
+/** @file
+ Read DQ/DQS training 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 _MrcReadDqDqs_h_
+#define _MrcReadDqDqs_h_
+
+#include "McAddress.h"
+#include "MrcTypes.h"
+#include "MrcCommon.h"
+#include "MrcGlobal.h"
+
+#define RMPR_DQS_START (-32)
+#define RMPR_DQS_STOP (32)
+
+#define RMPR_DQS_STEP (1)
+
+#define RMPR_MIN_WIDTH (12)
+
+/**
+@brief
+ Perform Read MPR Training.
+ Center read DQ-DQS with MPR pattern.
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - mrcSuccess or reason for failure.
+**/
+extern
+MrcStatus
+MrcReadMprTraining (
+ IN OUT MrcParameters *const MrcData
+ );
+
+/**
+@brief
+ Peform Read Timing Centering.
+ Center Rx DQS-DQ using moderate pattern with 1D eye
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded returns mrcSuccess
+**/
+extern
+MrcStatus
+MrcReadTimingCentering (
+ IN MrcParameters *const MrcData
+ );
+
+/**
+@brief
+ Peform Read Timing Centering in 2D.
+ Final read 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
+MrcReadTimingCentering2D (
+ IN MrcParameters *const MrcData
+ );
+
+/**
+@brief
+ Peform Read Voltage Centering in 2D.
+ Note: This function currently only supports param = RdV
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded return mrcSuccess
+**/
+extern
+MrcStatus
+MrcReadVoltageCentering2D (
+ IN MrcParameters *const MrcData
+ );
+
+#endif // _MrcReadDqDqs_h_
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadReceiveEnable.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadReceiveEnable.c
new file mode 100644
index 0000000..3a49188
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadReceiveEnable.c
@@ -0,0 +1,1155 @@
+/** @file
+ Implementation of the receive enable algorithm.
+ Receive enable training is made out of two stages, the first is finding the
+ DQS rising edge for each DRAM device, and the second is determining the
+ roundtrip latency by locating the preamble cycle.
+
+@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 "MrcReadReceiveEnable.h"
+
+///
+/// Local defines
+///
+
+#define RCV_EN_CENTER_LC (17)
+
+/**
+@brief
+ Perform receive enable training.
+ Optimize RcvEn timing with MPR pattern
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if succeeded, return mrcSuccess
+**/
+MrcStatus
+MrcReadLevelingTraining (
+ 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 RLStep0 = 8;
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ MrcProfile Profile;
+ U8 NumSamples;
+ U8 FineStep;
+ U8 DumArr[7];
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 ByteN; // ByteNumber
+ U8 ByteNTimes2;
+ U8 ChBitMask;
+ U8 RankMask; // RankBitMask for both channels
+ U8 Done;
+ U8 Inc;
+ U16 RLStart;
+ U16 RLStop;
+ U16 RLDelay;
+ U16 ChResult[MAX_CHANNEL];
+ U16 ChMask;
+ U32 CRValue;
+ U32 Offset;
+ U32 RtIoComp;
+ U32 RtLatency;
+ 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 IncPreAmble[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 iWidth;
+ S32 cWidth;
+ S32 lWidth;
+ S32 Center;
+ S32 Width;
+ BOOL Pass;
+ MRC_WDBPattern WDBPattern;
+ U8 Temp;
+ U16 TDqsCkDrift;
+#ifdef ULT_FLAG
+ U32 DclkPs;
+#endif // ULT_FLAG
+
+ DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2;
+ MCHBAR_CH0_CR_SC_IO_LATENCY_STRUCT ScIoLatency;
+ DDRDATA0CH0_CR_DATATRAINFEEDBACK_STRUCT DataTrainFeedback;
+
+ WDBPattern.IncRate = 32;
+ WDBPattern.Start = 0;
+ WDBPattern.Stop = 9;
+ WDBPattern.DQPat = BasicVA;
+ Status = mrcSuccess;
+ Done = 0;
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ ChBitMask = Outputs->ValidChBitMask;
+ RankMask = Outputs->ValidRankMask;
+ Profile = Inputs->MemoryProfile;
+ TDqsCkDrift = tDQSCK_DRIFT; // Pull in RcvEna by 1 QClk for Traditional.
+
+#ifdef ULT_FLAG
+#endif
+
+ MrcOemMemorySet (DumArr, 1, sizeof (DumArr));
+
+ NumSamples = 6;
+ FineStep = 1;
+
+ RtIoComp = 0;
+ RtLatency = 0;
+
+ switch (Inputs->CpuModel) {
+ case cmCRW:
+ RtIoComp = MRC_ROUND_TRIP_IO_COMPENSATION_2_CHANNEL_A0;
+ RtLatency = HW_ROUNDT_LAT_DEFAULT_VALUE_A0;
+ break;
+
+ case cmHSW_ULT:
+ RtIoComp = MRC_ROUND_TRIP_IO_COMPENSATION_2_CHANNEL_ULT_A0;
+ RtLatency = HW_ROUNDT_LAT_DEFAULT_VALUE_ULT_A0;
+ break;
+
+ case cmHSW:
+ default:
+ RtIoComp = (Inputs->CpuStepping > csHswA0) ? MRC_ROUND_TRIP_IO_COMPENSATION_2_CHANNEL_B0 :
+ MRC_ROUND_TRIP_IO_COMPENSATION_2_CHANNEL_A0;
+ RtLatency = (Inputs->CpuStepping > csHswA0) ? HW_ROUNDT_LAT_DEFAULT_VALUE_B0 :
+ HW_ROUNDT_LAT_DEFAULT_VALUE_A0;
+ break;
+ }
+
+ //
+ // CmdPat=PatRd, NumCL=2, LC=7, REUTAddress, SOE=0,
+ // WDBPattern, EnCADB=0, EnCKE=0, SubSeqWait=8
+ //
+ SetupIOTest (MrcData, ChBitMask, PatRd, 2, NumSamples + 1, &REUTAddress, 0, &WDBPattern, 0, 0, 8);
+
+ //
+ // Prepare Channel and Rank bit mask & Enable RLMode, force Odt and SAmp.
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcChannelExist (Outputs, Channel))) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Enable ReadLeveling Mode and Force On ODT and SenseAmp
+ //
+ 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);
+ DdrCrDataControl2.Data = ChannelOut->DqControl2[Byte].Data;
+ DdrCrDataControl2.Bits.ForceRxOn = 1;
+ MrcWriteCR (MrcData, Offset, DdrCrDataControl2.Data);
+ }
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+
+#ifdef ULT_FLAG
+ if (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) {
+ //
+ // W/A for b4618574 - @todo: remove for HSW ULT C0
+ // Can't have ForceOdtOn together with Leaker, disable LPDDR mode during this training step
+ // LPDDR_Mode is restored at the end of this function from the host structure.
+ //
+ DdrCrDataControl0.Bits.LPDDR_Mode = 0;
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ }
+#endif // ULT_FLAG
+
+ DdrCrDataControl0.Bits.ForceOdtOn = 1;
+ DdrCrDataControl0.Bits.RLTrainingMode = 1;
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+
+ //
+ // Set initial IO Latency and IO_COMP
+ //
+ Offset = MCHBAR_CH0_CR_SC_IO_LATENCY_REG +
+ ((MCHBAR_CH1_CR_SC_IO_LATENCY_REG - MCHBAR_CH0_CR_SC_IO_LATENCY_REG) * Channel);
+ ScIoLatency.Data = 0;
+ ScIoLatency.Bits.RT_IOCOMP = RtIoComp;
+ MrcWriteCR (MrcData, Offset, ScIoLatency.Data);
+ }
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (!((MRC_BIT0 << Rank) & RankMask)) {
+ //
+ // Skip if both channels empty
+ //
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRank %d\n", Rank);
+
+ ChBitMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChBitMask |= SelectReutRanks (MrcData, Channel, MRC_BIT0 << Rank, 0);
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ ChannelOut->IoLatency[Rank] = 0;
+ //
+ // Set initial Roundtrip Latency values -4 QCLK assumed for worst board layout
+ //
+ // Default ROUNDT_LAT = HW_ROUNDT_LAT_DEFAULT_VALUE + nMode value * 2 + (2 * tCL) + 4QCLK + PI_CLK
+ // LPDDR3 formula: HW_ROUNDT_LAT_DEFAULT_VALUE + (2 * tCL) + 4QCLK + PI_CLK + tDQSCK_max
+ // NMode = 3 during training mode
+ //
+ Temp = (Outputs->Ratio >= 2) ? Outputs->Ratio : 0;
+
+#ifdef ULT_FLAG
+ if (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) {
+ DclkPs = Outputs->Qclkps * 2;
+ CRValue = RtLatency + (2 * ChannelOut->Timing[Profile].tCL) + MAX (Temp, 4) + 1 + (tDQSCK_MAX + DclkPs - 1) / DclkPs;
+ } else
+#endif // ULT_FLAG
+ {
+ CRValue = RtLatency + (2 * 2) + (2 * ChannelOut->Timing[Profile].tCL) + MAX (Temp, 4) + 1;
+ }
+ CRValue = MIN (MCHBAR_CH0_CR_SC_ROUNDT_LAT_Lat_R0D0_MAX, CRValue);
+ Offset = MCHBAR_CH0_CR_SC_ROUNDT_LAT_REG +
+ ((MCHBAR_CH1_CR_SC_ROUNDT_LAT_REG - MCHBAR_CH0_CR_SC_ROUNDT_LAT_REG) * Channel) + Rank;
+ MrcWriteCR8 (MrcData, Offset, (U8) CRValue);
+ ChannelOut->RTLatency[Rank] = (U8) CRValue;
+ }
+ //
+ // ******************************************
+ // STEP 1 and 2: Find middle of high region
+ // ******************************************
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Step 1 and 2 Find Middle of high region\n");
+ RLStart = 256 + 24;
+ RLStop = 384 + 24;
+
+ for (RLDelay = RLStart; RLDelay < RLStop; RLDelay += RLStep0) {
+ //
+ // Program RL Delays:
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0, RLDelay);
+ }
+ }
+ //
+ // Run Test, Reset FIFOs will be done before running test
+ //
+ RunIOTest (MrcData, ChBitMask, BasicVA, DumArr, 1, 0);
+
+ //
+ // Update results for all Channels/Bytes
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ 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);
+ Pass = (MrcReadCR (MrcData, Offset) >= (U8) (MRC_BIT0 << (NumSamples - 1)));
+ if (RLDelay == RLStart) {
+ if (Pass) {
+ InitialPassingStart[Channel][Byte] = InitialPassingEnd[Channel][Byte] = RLStart;
+ CurrentPassingStart[Channel][Byte] = CurrentPassingEnd[Channel][Byte] = RLStart;
+ LargestPassingStart[Channel][Byte] = LargestPassingEnd[Channel][Byte] = RLStart;
+ } else {
+ InitialPassingStart[Channel][Byte] = InitialPassingEnd[Channel][Byte] = -RLStep0;
+ CurrentPassingStart[Channel][Byte] = CurrentPassingEnd[Channel][Byte] = -RLStep0;
+ LargestPassingStart[Channel][Byte] = LargestPassingEnd[Channel][Byte] = -RLStep0;
+ }
+ } else {
+ if (Pass) {
+ if (InitialPassingEnd[Channel][Byte] == (RLDelay - RLStep0)) {
+ InitialPassingEnd[Channel][Byte] = RLDelay;
+ }
+
+ if (CurrentPassingEnd[Channel][Byte] == (RLDelay - RLStep0)) {
+ CurrentPassingEnd[Channel][Byte] = RLDelay;
+ } else {
+ CurrentPassingStart[Channel][Byte] = CurrentPassingEnd[Channel][Byte] = RLDelay;
+ }
+ //
+ // Special case for last step: Append Initial Passing Region
+ // RLDelay should be considered a continuous range that wraps around 0
+ //
+ if ((RLDelay >= (RLStop - RLStep0)) &&
+ (InitialPassingStart[Channel][Byte] == RLStart) &&
+ (InitialPassingEnd[Channel][Byte] != RLDelay)
+ ) {
+
+ iWidth = (CurrentPassingEnd[Channel][Byte] - CurrentPassingStart[Channel][Byte]);
+ InitialPassingStart[Channel][Byte] -= (RLStep0 + iWidth);
+
+ LargestPassingStart[Channel][Byte] = InitialPassingStart[Channel][Byte];
+ LargestPassingEnd[Channel][Byte] = InitialPassingEnd[Channel][Byte];
+ continue;
+ }
+ //
+ // 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];
+ }
+ }
+ }
+ }
+ }
+ }
+ //
+ // Update RcvEn timing to be in the center of the high region.
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d.R%d: Left\tRight\tWidth\tCenter\n", Channel, Rank);
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ Center = (LargestPassingEnd[Channel][Byte] + LargestPassingStart[Channel][Byte]) / 2;
+ Width = LargestPassingEnd[Channel][Byte] - LargestPassingStart[Channel][Byte];
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " B%d: %d %d %d %d\n",
+ Byte,
+ LargestPassingStart[Channel][Byte],
+ LargestPassingEnd[Channel][Byte],
+ Width,
+ Center
+ );
+
+ //
+ // Check if center of High was found
+ //
+ if (Center > RLStop) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "\nERROR! Center of High Higher than expected for Channel: %u Byte: %u\n",
+ Channel,
+ Byte
+ );
+ return mrcReadLevelingError;
+ }
+ //
+ // Check if width is valid
+ //
+ if ((Width <= 32) || (Width >= 96)) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "\nERROR! Width region (%d) outside expected limits for Channel: %u Byte: %u\n",
+ Width,
+ Channel,
+ Byte
+ );
+ return mrcReadLevelingError;
+ }
+
+ ChannelOut->RcvEn[Rank][Byte] = (U16) Center;
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ }
+ //
+ // ******************************************************************************
+ // STEP 3: Walk Backwards
+ // ******************************************************************************
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nStep 3: Quarter Preamble - Walk Backwards\n");
+
+ if (Outputs->ValidRankMask & (MRC_BIT0 << Rank)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel 0 1\nByte ");
+ 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 (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChResult[Channel] = 0x1FF;
+ }
+
+ //
+ // 0x1FF or 0xFF
+ //
+ ChMask = (MRC_BIT0 << Outputs->SdramCount) - 1;
+ while ((ChResult[0] != 0) || (ChResult[1] != 0)) {
+ //
+ // Run Test
+ //
+ RunIOTest (MrcData, ChBitMask, BasicVA, DumArr, 1, 0);
+
+ //
+ // Update results for all Channel/Bytes
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nIOLAT =");
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChResult[Channel] = 0;
+
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ if (Channel == 0) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ (Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? " " : " "
+ );
+ }
+
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u ", ChannelOut->IoLatency[Rank]);
+
+ 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 >= (U16) (MRC_BIT0 << (NumSamples - 1)));
+ if (Pass) {
+ ChResult[Channel] |= (MRC_BIT0 << Byte);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, Pass ? "H " : "L ");
+ }
+ //
+ // Adjust Timing
+ //
+ if ((ChResult[Channel] == ChMask) && (ChannelOut->IoLatency[Rank] < 14)) {
+ //
+ // Adjust Timing globally for all Bytes - Number in Qclks
+ //
+ ChannelOut->IoLatency[Rank] = ((ChannelOut->IoLatency[Rank] + 2) & MCHBAR_CH0_CR_SC_IO_LATENCY_IOLAT_R0D0_MSK);
+
+ //
+ // @todo: Add an error check if we reach IOLAT > 10
+ // Update Value
+ //
+ ByteN = RANK_TO_DIMM_NUMBER (Rank);
+ ByteNTimes2 = ByteN * 2;
+ CRValue = (ChannelOut->IoLatency[ByteNTimes2]);
+ CRValue += (ChannelOut->IoLatency[ByteNTimes2 + 1] << MCHBAR_CH0_CR_SC_IO_LATENCY_IOLAT_R0D0_WID);
+ Offset = MCHBAR_CH0_CR_SC_IO_LATENCY_REG +
+ ((MCHBAR_CH1_CR_SC_IO_LATENCY_REG - MCHBAR_CH0_CR_SC_IO_LATENCY_REG) * Channel) + (ByteN);
+ MrcWriteCR8 (MrcData, Offset, (U8) CRValue);
+ } else {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if (ChResult[Channel] & (MRC_BIT0 << Byte)) {
+ if (ChannelOut->RcvEn[Rank][Byte] > 127) {
+ ChannelOut->RcvEn[Rank][Byte] -= 128;
+ } else {
+ //
+ // Error Handler
+ //
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "\nERROR! Channel: %u Rank: %uByte: %u - RcvEn %u/IoLat %u while walking backwards\n",
+ Channel,
+ Rank,
+ Byte,
+ ChannelOut->RcvEn[Rank][Byte],
+ ChannelOut->IoLatency[Rank]
+ );
+ return mrcReadLevelingError;
+ }
+
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ }
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%u: Preamble\n", Channel);
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " B%u: %u\n", Byte, ChannelOut->RcvEn[Rank][Byte]);
+ }
+ }
+ //
+ // ******************************************
+ // STEP 4: Add 1 qclk
+ // ******************************************
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ ChannelOut->RcvEn[Rank][Byte] += 64;
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ }
+ //
+ // ******************************************
+ // STEP 5: Walk forward
+ // ******************************************
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Step 5 Walk forward\n");
+ //
+ // Find Rising Edge
+ //
+ ChResult[0] = 0;
+ ChResult[1] = 0;
+
+ for (Inc = 0; Inc < 64; Inc += FineStep) {
+ //
+ // Run Test
+ //
+ RunIOTest (MrcData, ChBitMask, BasicVA, DumArr, 1, 0);
+
+ //
+ // Update results for all Channel/bytes
+ //
+ Done = 1;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // Skip Bytes that are already done
+ //
+ if (ChResult[Channel] & (MRC_BIT0 << Byte)) {
+ continue;
+ }
+ //
+ // Check if this byte is done
+ //
+ Offset = DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG +
+ ((DDRDATA0CH1_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * Byte);
+ Pass = (MrcReadCR (MrcData, Offset) >= (U8) (MRC_BIT0 << (NumSamples - 1)));
+ if (Pass) {
+ ChResult[Channel] |= (MRC_BIT0 << Byte);
+ } else {
+ ChannelOut->RcvEn[Rank][Byte] += FineStep;
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ IncPreAmble[Channel][Byte] = Inc;
+ }
+ }
+
+ if (ChResult[Channel] != ChMask) {
+ Done = 0;
+ }
+ }
+ //
+ // Skip additional testing if all Channel/bytes done
+ //
+ if (Done) {
+ break;
+ }
+ }
+ //
+ // Check if Edge was found for all Bytes in the channels
+ //
+ if (!Done) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "Error! Pre-amble edge not found for all Bytes with following final RcvEn results\n"
+ );
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %u Rank %u: Preamble\n", Channel, Rank);
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " Byte %u: %u %s\n",
+ Byte,
+ ChannelOut->RcvEn[Rank][Byte],
+ ((ChResult[Channel] ^ ChMask) & (1 << Byte)) ? "" : "*** ERROR! Check This Byte ***"
+ );
+ }
+ }
+
+ return mrcReadLevelingError;
+ }
+ //
+ // ******************************************
+ // STEP 6: Sub 1 qclk and Clean Up Rank
+ // ******************************************
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Step 6: Mid Preamble\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%u: Preamble Increment\n", Channel);
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // For Traditional, pull in RcvEn by 64
+ // For ULT, Take the DQS drift into account to the specified guardband: tDQSCK_DRIFT.
+ //
+ ChannelOut->RcvEn[Rank][Byte] -= TDqsCkDrift;
+
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " B%u: %u %u\n",
+ Byte,
+ ChannelOut->RcvEn[Rank][Byte],
+ IncPreAmble[Channel][Byte]
+ );
+ }
+ }
+ } // END OF RANK LOOP
+ //
+ // Clean up after Test
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcChannelExist (Outputs, Channel))) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+#ifdef ULT_FLAG
+ if (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) {
+ //
+ // W/A for b4618574 - @todo: remove for HSW ULT C0
+ // Can't have ForceOdtOn together with Leaker, disable LPDDR mode during this training step
+ // This write will disable ForceOdtOn while still keeping LPDDR_Mode disabled.
+ // Second write will restore LPDDR_Mode.
+ //
+ DdrCrDataControl0.Bits.LPDDR_Mode = 0;
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ }
+#endif // ULT_FLAG
+ MrcWriteCrMulticast (MrcData, Offset, ChannelOut->DqControl0.Data);
+
+ 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);
+ }
+ }
+
+ Status = IoReset (MrcData);
+
+ //
+ // Step 7: Try to get IO Lat the same across all ranks per channel
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Step 7: Sync IO Lat Across Ranks\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Status = MrcChangeRcvEnTiming (
+ MrcData,
+ Channel,
+ RRE_ALL_RANKS_MASK,
+ 0, // ByteMask
+ 0, // Offset
+ RRE_PI_TO_RESERVE
+ );
+ }
+ }
+
+ //
+ // Print IO Latency/RcvEn
+ //
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Adjusted Receive Enable and IO Lat Values\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ Offset = MCHBAR_CH0_CR_SC_IO_LATENCY_REG +
+ ((MCHBAR_CH1_CR_SC_IO_LATENCY_REG - MCHBAR_CH0_CR_SC_IO_LATENCY_REG) * Channel);
+ ScIoLatency.Data = MrcReadCR (MrcData, Offset);
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ MRC_DEBUG_MSG (Debug,
+ MSG_LEVEL_NOTE,
+ " C%d.R%d: IOLAT = %u RT_IOCOMP = %d\n",
+ Channel,
+ Rank,
+ ChannelOut->IoLatency[Rank],
+ ScIoLatency.Bits.RT_IOCOMP
+ );
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " B%d: %u\n", Byte, ChannelOut->RcvEn[Rank][Byte]);
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ return Status;
+}
+
+/**
+@brief
+ Apply an signed offset to all selected bytes/ ranks in a channel to RcvEn timing
+ Robustly handles any carries to/from the IO Latency vs. RcvEn FlyBy
+ PiReserve will reserve a certain number of +/- PI ticks for margin purposes
+ Routine also minimizes the difference in RcvEn settings across ranks
+
+ @param[in,out] MrcData - MRC Global Data
+ @param[in] Channel - The channel to adjust
+ @param[in] RankMask - Mask of Ranks to adjust
+ @param[in] ByteMask - Mask of Bytes to adjust by the RcvEnOffset
+ @param[in] RcvEnOffset - Amount to offset RcvEn
+ @param[in] PiReserve - The number of PiTicks to reserve on each edge of RcvEn
+
+ @retval MrcStatus - mrcSuccess if successfull
+ mrcWrongInputParameter if channel doesnt exist or a RankMask of 0 is provided
+ mrcReadLevelingError if we over/underflow RT_IOCOMP field.
+**/
+MrcStatus
+MrcChangeRcvEnTiming (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 RankMask,
+ IN const U16 ByteMask,
+ IN const S16 RcvEnOffset,
+ IN const S16 PiReserve
+ )
+{
+ MrcStatus Status;
+ MrcOutput *Outputs;
+ MrcDebug *Debug;
+ MrcChannelOut *ChannelOut;
+ MCHBAR_CH0_CR_SC_IO_LATENCY_STRUCT ScIoLatency;
+ U8 Rank;
+ U8 Byte;
+ S8 CycleOffset;
+ S8 IoGlobalOffset;
+ S8 IoLatRank[MAX_RANK_IN_CHANNEL];
+ S8 IoLatTarget;
+ S8 MaxRankLat;
+ S8 MinRankLat;
+ S16 NewRcvEn;
+ S16 MaxRcvEn;
+ S16 MinRcvEn;
+ S16 MaxRcvEnRank[MAX_RANK_IN_CHANNEL];
+ S16 MinRcvEnRank[MAX_RANK_IN_CHANNEL];
+ U32 CrOffset;
+
+ //
+ // Init variables with min and max values
+ //
+ Status = mrcSuccess;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ MaxRankLat = 0;
+ MinRankLat = 15;
+ MaxRcvEn = -4096;
+ MinRcvEn = 4096;
+ IoGlobalOffset = 0;
+ MrcOemMemorySetWord ((U16*) MaxRcvEnRank, (U16)-4096, MAX_RANK_IN_CHANNEL);
+ MrcOemMemorySetWord ((U16*) MinRcvEnRank, 4096, MAX_RANK_IN_CHANNEL);
+ MrcOemMemorySet ((U8*) IoLatRank, 0, MAX_RANK_IN_CHANNEL);
+
+ //
+ // Quick error check on parameters
+ //
+ if ((!(MrcChannelExist (Outputs, Channel))) || (RankMask == 0)) {
+ return mrcWrongInputParameter;
+ }
+
+ //
+ // Walk through all the ranks/bytes to find Max/Min RcvEn values
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((((MRC_BIT0 << Rank) & RankMask) != 0) && ((MrcRankInChannelExist (MrcData, Rank, Channel)))) {
+ //
+ // Find Max/Min for RcvEn across bytes. RcvEn is the total (RcvEnPi - 64 * IOLat)
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ NewRcvEn = (S16) ChannelOut->RcvEn[Rank][Byte] - (64 * (S16) ChannelOut->IoLatency[Rank]);
+ if (ByteMask & (MRC_BIT0 << Byte)) {
+ //
+ // Apply an offset for this byte
+ //
+ NewRcvEn += RcvEnOffset;
+ }
+
+ if (MaxRcvEnRank[Rank] < NewRcvEn) {
+ MaxRcvEnRank[Rank] = NewRcvEn;
+ }
+
+ if (MinRcvEnRank[Rank] > NewRcvEn) {
+ MinRcvEnRank[Rank] = NewRcvEn;
+ }
+ }
+ //
+ // Find Max/Min for RcvEn across ranks
+ //
+ if (MaxRcvEn < MaxRcvEnRank[Rank]) {
+ MaxRcvEn = MaxRcvEnRank[Rank];
+ }
+
+ if (MinRcvEn > MinRcvEnRank[Rank]) {
+ MinRcvEn = MinRcvEnRank[Rank];
+ }
+ }
+ }
+
+ //
+ // Determine how far we are from the ideal center point for RcvEn timing.
+ // (PiIdeal - AveRcvEn)/64 is the ideal number of cycles we should have for IOLatency
+ // Command training will reduce this by 64, so plan for that now in the ideal value
+ //
+ IoLatTarget = (S8) ((RRE_PI_IDEAL - ((MaxRcvEn + MinRcvEn) / 2) + 32) / 64); // Rnd to closest int
+
+ //
+ // Walk through all the ranks and calculate new values of IOLat
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((((MRC_BIT0 << Rank) & RankMask) != 0) && ((MrcRankInChannelExist (MrcData, Rank, Channel)))) {
+ IoLatRank[Rank] = IoLatTarget;
+
+ //
+ // Check for RcvEn underflow
+ //
+ NewRcvEn = 64 * IoLatRank[Rank] + MinRcvEnRank[Rank];
+ if (NewRcvEn < PiReserve) {
+ IoLatRank[Rank] += (U8) ((PiReserve - NewRcvEn + 63) / 64); // Ceiling
+ }
+
+ //
+ // Check for RcvEn overflow
+ //
+ NewRcvEn = 64 * IoLatRank[Rank] + MaxRcvEnRank[Rank];
+ if (NewRcvEn > (511 - PiReserve)) {
+ IoLatRank[Rank] -= (U8) ((NewRcvEn - (511 - PiReserve) + 63) / 64); // Ceiling
+ }
+ //
+ // Check for IO Latency over/underflow
+ //
+ if ((IoLatRank[Rank] - IoGlobalOffset) > 14) {
+ IoGlobalOffset = IoLatRank[Rank] - 14;
+ }
+ if ((IoLatRank[Rank] - IoGlobalOffset) < 1) {
+ IoGlobalOffset = IoLatRank[Rank] - 1;
+ }
+ //
+ // Update Byte level results
+ //
+ CycleOffset = IoLatRank[Rank] - (S8) ChannelOut->IoLatency[Rank];
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ ChannelOut->RcvEn[Rank][Byte] += 64 * (U16) CycleOffset;
+ if (ByteMask & (MRC_BIT0 << Byte)) {
+ ChannelOut->RcvEn[Rank][Byte] += (U16) RcvEnOffset;
+ }
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ }
+ }
+ //
+ // Calculate new IOComp Latency to include over/underflow
+ //
+ CrOffset = MCHBAR_CH0_CR_SC_IO_LATENCY_REG +
+ ((MCHBAR_CH1_CR_SC_IO_LATENCY_REG - MCHBAR_CH0_CR_SC_IO_LATENCY_REG) * Channel);
+ ScIoLatency.Data = MrcReadCR (MrcData, CrOffset);
+
+ //
+ // Check to see if were under/overflowing this field
+ //
+ if ((IoGlobalOffset < 0) && (ScIoLatency.Bits.RT_IOCOMP < (U8) -IoGlobalOffset)) {
+ Status = mrcReadLevelingError;
+ } else if (
+ (IoGlobalOffset > 0) &&
+ ((U8)IoGlobalOffset > (MCSCHEDS_CR_SC_IO_LATENCY_RT_IOCOMP_MAX - ScIoLatency.Bits.RT_IOCOMP))
+ ) {
+ Status = mrcReadLevelingError;
+ }
+
+ if(Status == mrcReadLevelingError) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "MrcChangeRcvEnTiming(): RT_IOCOMP %s\n IoGlobalOffset: %d\n RT_IOCOMP(6'b): %d\n",
+ (IoGlobalOffset < 0) ? "underflowed" : "overflowed",
+ IoGlobalOffset,
+ ScIoLatency.Bits.RT_IOCOMP
+ );
+ }
+ ScIoLatency.Bits.RT_IOCOMP += IoGlobalOffset;
+ ChannelOut->RTIoComp = ScIoLatency.Bits.RT_IOCOMP;
+
+ //
+ // Walk through all ranks to program new IO Latency values
+ //
+ ScIoLatency.Data &= ~(
+ MCHBAR_CH0_CR_SC_IO_LATENCY_IOLAT_R0D0_MSK +
+ MCHBAR_CH0_CR_SC_IO_LATENCY_IOLAT_R1D0_MSK +
+ MCHBAR_CH0_CR_SC_IO_LATENCY_IOLAT_R0D1_MSK +
+ MCHBAR_CH0_CR_SC_IO_LATENCY_IOLAT_R1D1_MSK
+ );
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((((MRC_BIT0 << Rank) & RankMask) != 0) && ((MrcRankInChannelExist (MrcData, Rank, Channel)))) {
+ ChannelOut->IoLatency[Rank] = IoLatRank[Rank] - IoGlobalOffset;
+ }
+ ScIoLatency.Data |= (
+ (MCHBAR_CH0_CR_SC_IO_LATENCY_IOLAT_R0D0_MSK & (ChannelOut->IoLatency[Rank])) <<
+ (Rank * MCHBAR_CH0_CR_SC_IO_LATENCY_IOLAT_R0D0_WID)
+ );
+ }
+
+ //
+ // Program new IO Latency
+ //
+ MrcWriteCR (MrcData, CrOffset, ScIoLatency.Data);
+
+ return Status;
+}
+
+/**
+@brief
+ Once the DQS high phase has been found (for each DRAM) the next stage is to find out the round trip latency,
+ by locating the preamble cycle. This is achieved by trying smaller and smaller roundtrip
+ values until the strobe sampling is done on the preamble cycle.
+ The following algorithm is used to find the preamble cycle:
+
+ @param[in] MrcData - all the global data
+
+ @retval Nothing.
+**/
+MrcStatus
+MrcRoundTripLatency (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcStatus Status;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDebug *Debug;
+ U8 Channel;
+ U8 Rank;
+ U8 OptParam;
+ U8 RankMask;
+ U8 TestList[1];
+ S8 ClkShifts[1];
+ U8 Start;
+ U8 Stop;
+ U8 LoopCount;
+ U8 Update;
+ U8 MinIoLat;
+ U8 MaxRankRtl;
+ S8 DeltaLimitRtl;
+ U8 DeltaRtl;
+ MCHBAR_CH0_CR_TC_BANK_RANK_A_STRUCT TcBankRankA;
+
+ Status = mrcSuccess;
+ TestList[0] = RdT; // Test based on read eye width
+ ClkShifts[0] = 25; // Delay by 25 pi ticks to guardband for delay drift/jitter
+ LoopCount = 10;
+ Update = 1; // Apply the optimal settings
+ MaxRankRtl = 0;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ ControllerOut = &Outputs->Controller[0];
+ OptParam = rtl; // Which parameter to optimize for
+
+ //
+ // Train timing separately for each rank
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = (MRC_BIT0 << Rank);
+ if (!(RankMask & Outputs->ValidRankMask)) {
+ continue;
+ }
+ //
+ // Pick starting and stopping points
+ //
+ Stop = 0;
+ Start = 0;
+ MinIoLat = 15;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ if (Stop < ChannelOut->RTLatency[Rank]) {
+ Stop = ChannelOut->RTLatency[Rank];
+ }
+
+ if (MinIoLat > ChannelOut->IoLatency[Rank]) {
+ MinIoLat = ChannelOut->IoLatency[Rank];
+ }
+
+ Start = Stop - MinIoLat;
+ }
+
+ if ((S8) Start < 0) {
+ Start = 0;
+ }
+ //
+ // Find optimal answer
+ //
+ Status = TrainDDROptParamCliff (
+ MrcData,
+ OptParam,
+ TestList,
+ sizeof (TestList),
+ Start,
+ Stop,
+ LoopCount,
+ Update,
+ Outputs->MarginResult,
+ ClkShifts,
+ sizeof (ClkShifts),
+ Rank,
+ RankMask,
+ 0
+ );
+ if (Status == mrcFail) {
+ return mrcRoundTripLatencyError;
+ }
+ }
+
+ //
+ // Limit the RTL delta across the ranks present.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nLimit the delta between Rank's RTL value.\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %d\n", Channel);
+ ChannelOut = &ControllerOut->Channel[Channel];
+ TcBankRankA.Data = ChannelOut->MchbarBANKRANKA;
+ DeltaLimitRtl = MAX ((S8) TcBankRankA.Bits.tRDRD_dr, (S8) TcBankRankA.Bits.tRDRD_dd);
+ //
+ // TA Times are in dclks. Must convert to qclks and subtract the burst length.
+ // Ensure we do not underflow the variable.
+ //
+ DeltaLimitRtl = ((2 * DeltaLimitRtl) - 8);
+ DeltaLimitRtl = MAX (DeltaLimitRtl, 0);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "RTL Delta Limit: %d\n", DeltaLimitRtl);
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ MaxRankRtl = MAX (MaxRankRtl, ChannelOut->RTLatency[Rank]);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Rank %u RTL: %u\n", Rank, ChannelOut->RTLatency[Rank]);
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "MaxRankRtl: %u\n", MaxRankRtl);
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ DeltaRtl = MaxRankRtl - ChannelOut->RTLatency[Rank];
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Rank %d: DeltaRtl: %u\tDeltaLimitRtl: %u%s",
+ Rank,
+ DeltaRtl,
+ DeltaLimitRtl,
+ (DeltaRtl > DeltaLimitRtl) ? "\tNew RTL: " : ""
+ );
+ if (DeltaRtl > DeltaLimitRtl) {
+ UpdateTAParamOffset (MrcData, Channel, 0, OptParam, MaxRankRtl - DeltaLimitRtl, 1, 0, 1 << Rank);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Perform Receive Enable Timing Centering.
+ Center Receive Enable using moderate pattern with 1D eye.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - If successful, returns mrcSuccess.
+**/
+MrcStatus
+MrcReceiveEnTimingCentering (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcOutput *Outputs;
+
+ Outputs = &MrcData->SysOut.Outputs;
+
+ return DQTimeCentering1D (MrcData, Outputs->ValidChBitMask, RcvEnaX, 0, RCV_EN_CENTER_LC);
+} \ No newline at end of file
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadReceiveEnable.h b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadReceiveEnable.h
new file mode 100644
index 0000000..4984d2a
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining/MrcReadReceiveEnable.h
@@ -0,0 +1,120 @@
+/** @file
+ Read receive enable training 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 _MrcReadReceiveEnable_h_
+#define _MrcReadReceiveEnable_h_
+
+#include "MrcTypes.h"
+#include "MrcCommon.h"
+#include "MrcCrosser.h"
+#include "MrcGlobal.h"
+
+#define HW_ROUNDT_LAT_DEFAULT_VALUE_A0 (20) ///< HSW HW specific default value
+#define MRC_ROUND_TRIP_IO_COMPENSATION_2_CHANNEL_A0 (25) // Roundtrip - IO compensation for 2 channel
+
+#define HW_ROUNDT_LAT_DEFAULT_VALUE_B0 (16) ///< HSW HW specific default value
+#define MRC_ROUND_TRIP_IO_COMPENSATION_2_CHANNEL_B0 (21) // Roundtrip - IO compensation for 2 channel
+
+#define HW_ROUNDT_LAT_DEFAULT_VALUE_ULT_A0 (18) // Roundtrip Latency
+#define MRC_ROUND_TRIP_IO_COMPENSATION_2_CHANNEL_ULT_A0 (23) // Roundtrip - IO compensation for 2 channel
+
+///
+/// ReadReceiveEnable (RRE) parameters
+/// Command training will reduce this by 64, so plan for that now in the ideal value
+///
+#define RRE_PI_IDEAL (256 + 64)
+#define RRE_ALL_RANKS_MASK (0x0F)
+#define RRE_PI_TO_RESERVE (64)
+
+/**
+@brief
+ Perform receive enable training.
+ Optimize RcvEn timing with MPR pattern
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if succeeded, return mrcSuccess
+**/
+extern
+MrcStatus
+MrcReadLevelingTraining (
+ IN OUT MrcParameters *const MrcData
+ );
+
+/**
+@brief
+ Apply an signed offset to all selected bytes/ ranks in a channel to RcvEn timing
+ Robustly handles any carries to/from the IO Latency vs. RcvEn FlyBy
+ PiReserve will reserve a certain number of +/- PI ticks for margin purposes
+ Routine also minimizes the difference in RcvEn settings across ranks
+
+ @param[in,out] MrcData - MRC Global Data
+ @param[in] Channel - The channel to adjust
+ @param[in] RankMask - Mask of Ranks to adjust
+ @param[in] ByteMask - Mask of Bytes to adjust by the RcvEnOffset
+ @param[in] RcvEnOffset - Amount to offset RcvEn
+ @param[in] PiReserve - The number of PiTicks to reserve on each edge of RcvEn
+
+ @retval MrcStatus - mrcSuccess if successfull
+ mrcWrongInputParameter if channel doesnt exist or a RankMask of 0 is provided
+ mrcReadLevelingError if we over/underflow RT_IOCOMP field.
+**/
+MrcStatus
+MrcChangeRcvEnTiming (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 RankMask,
+ IN const U16 ByteMask,
+ IN const S16 RcvEnOffset,
+ IN const S16 PiReserve
+ );
+
+/**
+@brief
+ Once the DQS high phase has been found (for each DRAM) the next stage is to find out the round trip latency,
+ by locating the preamble cycle. This is achieved by trying smaller and smaller roundtrip
+ values until the strobe sampling is done on the preamble cycle.
+ The following algorithm is used to find the preamble cycle:
+
+ @param[in] MrcData - all the global data
+
+ @retval Nothing.
+**/
+extern
+MrcStatus
+MrcRoundTripLatency (
+ IN MrcParameters *const MrcData
+ );
+
+/**
+ Perform Receive Enable Timing Centering.
+ Center Receive Enable using moderate pattern with 1D eye
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded returns mrcSuccess
+**/
+MrcStatus
+MrcReceiveEnTimingCentering (
+ IN MrcParameters *const MrcData
+ );
+
+#endif // _MrcReadReceiveEnable_h_