diff options
author | raywu <raywu0301@gmail.com> | 2018-06-15 00:00:50 +0800 |
---|---|---|
committer | raywu <raywu0301@gmail.com> | 2018-06-15 00:00:50 +0800 |
commit | b7c51c9cf4864df6aabb99a1ae843becd577237c (patch) | |
tree | eebe9b0d0ca03062955223097e57da84dd618b9a /ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining | |
download | zprj-master.tar.xz |
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/ReadTraining')
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_ |