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