summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services')
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCommandTraining.c4743
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCommon.c8010
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCrosser.c9860
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcDdr3.c1572
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcIoControl.c62
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcMcConfiguration.c1405
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcMemoryMap.c528
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcReset.c629
8 files changed, 26809 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCommandTraining.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCommandTraining.c
new file mode 100644
index 0000000..3bc5ef6
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCommandTraining.c
@@ -0,0 +1,4743 @@
+/** @file
+ Implementation of the command training algorithm.
+ The algorithm finds the N mode for the current board and also the correct
+ CLK CMD CTL pi setting.
+
+@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 "MrcCommandTraining.h"
+
+#define MRC_CADB_PB_LENGTH 16
+
+/**
+@brief
+ This function performs early command training.
+ Center CTL-CLK timing to allow subsequent steps to work
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - mrcSuccess if it succeeded
+**/
+MrcStatus
+MrcEarlyCommandTraining (
+ IN MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ S32 *IPStart;
+ S32 *IPEnd;
+ S32 *CPStart;
+ S32 *CPEnd;
+ S32 *LPStart;
+ S32 *LPEnd;
+ MrcStatus Status;
+ BOOL Pass;
+ BOOL Done;
+ DDRCLK_CR_DDRCRCLKPICODE_STRUCT DdrCrClkPiCode;
+ MCHBAR_CH0_CR_REUT_CH_MISC_ODT_CTRL_STRUCT ReutChMiscOdtCtrl;
+ MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_STRUCT ReutGlobalCtl;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2;
+ U32 CRValue;
+ S32 cWidth;
+ S32 lWidth;
+ S32 InitialPassingStart[MAX_CHANNEL][MAX_RANK_IN_CHANNEL];
+ S32 InitialPassingEnd[MAX_CHANNEL][MAX_RANK_IN_CHANNEL];
+ S32 CurrentPassingStart[MAX_CHANNEL][MAX_RANK_IN_CHANNEL];
+ S32 CurrentPassingEnd[MAX_CHANNEL][MAX_RANK_IN_CHANNEL];
+ S32 LargestPassingStart[MAX_CHANNEL][MAX_RANK_IN_CHANNEL];
+ S32 LargestPassingEnd[MAX_CHANNEL][MAX_RANK_IN_CHANNEL];
+ U32 DqsDoneMask;
+ U32 bytePass[MAX_CHANNEL];
+ U32 byteFail[MAX_CHANNEL];
+ U32 Offset;
+ U8 Channel;
+ U8 Rank;
+ U8 byte;
+ U8 chBitMask;
+ U8 RankMask;
+ U8 ValidRankMask;
+ U8 clkDelay;
+ U8 clkDelayArray;
+ U8 PiCode;
+ S8 DqsDelay;
+ S8 LastDqsRan[ECT_CLK_LOOPS][MAX_CHANNEL][MAX_RANK_IN_CHANNEL];
+
+ MrcOemMemorySet ((U8 *) LastDqsRan, ECT_DQS_STOP, sizeof (LastDqsRan));
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ //
+ // RankBitMask for both channels
+ //
+ ValidRankMask = Outputs->ValidRankMask;
+ //
+ // Channel bit mask
+ //
+ chBitMask = Outputs->ValidChBitMask;
+
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ if (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) {
+ return EarlyCommandTrainingLpddr (MrcData);
+ }
+#endif // ULT_FLAG
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->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)) && (Inputs->SetRxDqs32 == TRUE)) {
+ //
+ // 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
+ // LC = 10, SOE = 0 (NSOE), EnCADB = 0, EnCKE = 0
+ //
+ SetupIOTestMPR (MrcData, chBitMask, 10, NSOE, 0, 0);
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nChannel\t");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%u\t\t\t\t", Channel);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRank\t");
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%u", Rank);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nClock");
+#endif // MRC_DEBUG_PRINT
+ for (clkDelay = ECT_CLK_START; clkDelay < ECT_CLK_STOP; clkDelay += ECT_CLK_STEP) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n% 5u\t", clkDelay);
+ clkDelayArray = clkDelay / ECT_CLK_STEP;
+ //
+ // Program Clock Delays
+ //
+ DdrCrClkPiCode.Data = 0;
+ DdrCrClkPiCode.Bits.PiSettingRank0 =
+ DdrCrClkPiCode.Bits.PiSettingRank1 =
+ DdrCrClkPiCode.Bits.PiSettingRank2 =
+ DdrCrClkPiCode.Bits.PiSettingRank3 = clkDelay;
+ MrcWriteCrMulticast (MrcData, DDRCLK_CR_DDRCRCLKPICODE_REG, DdrCrClkPiCode.Data);
+
+ //
+ // Reset FIFOs and Reset all DIMM/all channels after changing PI codes
+ //
+ Status = MrcResetSequence (MrcData);
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = MRC_BIT0 << Rank;
+ //
+ // Skip ranks that are not populated
+ //
+ if ((ValidRankMask & RankMask) == 0) {
+ continue;
+ }
+ //
+ // Program MR3 and Mask RAS/WE to prevent scheduler from issuing non-Read commands
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ SelectReutRanks (MrcData, Channel, RankMask, 0);
+ bytePass[Channel] = 0;
+ byteFail[Channel] = 0;
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ Status = MrcWriteMRS (MrcData, Channel, RankMask, mrMR3, 4);
+
+ 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);
+ }
+ }
+ //
+ // Run ReadDQS Test
+ //
+ DqsDoneMask = (MRC_BIT0 << Outputs->SdramCount) - 1;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRank %u DqsDelay for clkDelay = %u", Rank, clkDelay);
+ //
+ for (DqsDelay = ECT_DQS_START; DqsDelay < ECT_DQS_STOP; DqsDelay += ECT_DQS_STEP) {
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n %d\t", DqsDelay);
+ //
+ // Write DqsDelay
+ //
+ 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 = &ControllerOut->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 = DDRDATA0CH0_CR_DDRCRDATACONTROL2_ForceBiasOn_MAX;
+ DdrCrDataControl2.Bits.ForceRxOn = DDRDATA0CH0_CR_DDRCRDATACONTROL2_ForceRxOn_MAX;
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl2.Data);
+ }
+ //
+ // Enable RX Training mode. Turn on Odt
+ //
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ DdrCrDataControl0.Bits.ForceOdtOn = DDRDATACH0_CR_DDRCRDATACONTROL0_ForceOdtOn_MAX;
+ DdrCrDataControl0.Bits.RxTrainingMode = DDRDATACH0_CR_DDRCRDATACONTROL0_RxTrainingMode_MAX;
+ MrcWriteCR (MrcData, Offset, DdrCrDataControl0.Data);
+ }
+ }
+ //
+ // Clear Results for Prior Test and wait to obtain results
+ //
+ Status = IoReset (MrcData);
+
+ //
+ // Start REUT and run for 1uS
+ //
+ ReutGlobalCtl.Data = 0;
+ ReutGlobalCtl.Bits.Global_Clear_Errors = 1;
+ 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);
+
+ //
+ // Get Results for all ch/bytes
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ 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 ? ". " : "# ");
+ //
+ CRValue = (MRC_BIT0 << byte);
+ if (Pass) {
+ bytePass[Channel] |= CRValue;
+ } else {
+ byteFail[Channel] |= CRValue;
+ }
+ }
+ //
+ // Save DqsDelay where all bytes passed
+ //
+ if ((bytePass[Channel] == DqsDoneMask) && (LastDqsRan[clkDelayArray][Channel][Rank] > DqsDelay)) {
+ LastDqsRan[clkDelayArray][Channel][Rank] = DqsDelay;
+ }
+ }
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ChannelOut = &ControllerOut->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);
+ }
+
+ Status = IoReset (MrcData);
+
+ //
+ // Clear RX Mode
+ //
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, ChannelOut->DqControl0.Data);
+ }
+ }
+ //
+ // Are We done yet?
+ //
+ Done = TRUE;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ if ((bytePass[Channel] != DqsDoneMask) || (byteFail[Channel] != DqsDoneMask)) {
+ Done = FALSE;
+ break;
+ }
+ }
+ }
+ //
+ // If we re done, we passed or failed for all bytes
+ //
+ if (Done == TRUE) {
+ break;
+ }
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ //
+ // Update results for all channel at this rank
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ Pass = ((bytePass[Channel] == DqsDoneMask) && (byteFail[Channel] == DqsDoneMask));
+
+ //
+ // Check if we have a valid pass
+ //
+ if (Pass &&
+ (clkDelay != ECT_CLK_START) &&
+ ((LastDqsRan[clkDelayArray][Channel][Rank] - LastDqsRan[clkDelayArray - 1][Channel][Rank]) > 16)
+ ) {
+ Pass = FALSE;
+ }
+
+ IPStart = &InitialPassingStart[Channel][Rank];
+ IPEnd = &InitialPassingEnd[Channel][Rank];
+ CPStart = &CurrentPassingStart[Channel][Rank];
+ CPEnd = &CurrentPassingEnd[Channel][Rank];
+ LPStart = &LargestPassingStart[Channel][Rank];
+ LPEnd = &LargestPassingEnd[Channel][Rank];
+ if (clkDelay == ECT_CLK_START) {
+ if (Pass) {
+ *IPStart = clkDelay;
+ *IPEnd = clkDelay;
+ *CPStart = clkDelay;
+ *CPEnd = clkDelay;
+ *LPStart = clkDelay;
+ *LPEnd = clkDelay;
+ } else {
+ *IPStart = -ECT_CLK_STEP;
+ *IPEnd = -ECT_CLK_STEP;
+ *CPStart = -ECT_CLK_STEP;
+ *CPEnd = -ECT_CLK_STEP;
+ *LPStart = -ECT_CLK_STEP;
+ *LPEnd = -ECT_CLK_STEP;
+ }
+ } else {
+ if (Pass) {
+ //
+ // Update Initial variables
+ //
+ if (*IPEnd == clkDelay - ECT_CLK_STEP) {
+ *IPEnd = clkDelay; // In passing region
+ }
+ //
+ // Update Current variables
+ //
+ if (*CPEnd == clkDelay - ECT_CLK_STEP) {
+ *CPEnd = clkDelay; // In passing region
+ } else {
+ *CPStart = clkDelay; // New region
+ *CPEnd = clkDelay;
+ }
+ //
+ // Special case for last step: Append Initial Passing Region
+ // clkDelay should be considered a continuous range that wraps around 0
+ //
+ if (clkDelay == 128 - ECT_CLK_STEP && *IPStart == ECT_CLK_START && *IPEnd != clkDelay) {
+ *CPEnd += ECT_CLK_STEP + (*IPEnd -*IPStart);
+ }
+ //
+ // Update Largest variables
+ //
+ cWidth = *CPEnd - *CPStart;
+ lWidth = *LPEnd - *LPStart;
+ if (cWidth > lWidth) {
+ *LPStart = *CPStart;
+ *LPEnd = *CPEnd;
+ }
+ }
+ }
+ }
+ }
+ //
+ // Clean up registers. No need to clear MR3 since DIMM will be reset
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ 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);
+ }
+ }
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ CPEnd = &CurrentPassingEnd[Channel][Rank];
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ (LastDqsRan[clkDelayArray][Channel][Rank] < ECT_DQS_STOP) ? ". " : "# "
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "(% 3d)", LastDqsRan[clkDelayArray][Channel][Rank]);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ }
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\tLeft\tRight\tWidth\tClkDelay\n");
+ //
+ // Find largest passing region and Update PICodes
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ DdrCrClkPiCode.Data = 0;
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ LPStart = &LargestPassingStart[Channel][Rank];
+ LPEnd = &LargestPassingEnd[Channel][Rank];
+ lWidth = *LPEnd - *LPStart;
+
+ //
+ // 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\n", Channel, Rank);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Channel %u Rank %u:\t%d\t%d\t%d\t%d\n",
+ Channel,
+ Rank,
+ *LPStart,
+ *LPEnd,
+ lWidth,
+ 0
+ );
+ return mrcReadMPRErr;
+ }
+
+ if (lWidth > ECT_MIN_WIDTH) {
+ PiCode = (U8) (*LPStart + lWidth / 2);
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_WARNING,
+ "\nWARNING!! lWidth <= %u for Channel %u Rank %u \n",
+ ECT_MIN_WIDTH,
+ Channel,
+ Rank
+ );
+ PiCode = 64;
+ }
+ //
+ // Update Host Structure with new PiCode
+ //
+ switch (Rank) {
+ case 0:
+ DdrCrClkPiCode.Bits.PiSettingRank0 = PiCode;
+ break;
+
+ case 1:
+ DdrCrClkPiCode.Bits.PiSettingRank1 = PiCode;
+ break;
+
+ case 2:
+ DdrCrClkPiCode.Bits.PiSettingRank2 = PiCode;
+ break;
+
+ case 3:
+ DdrCrClkPiCode.Bits.PiSettingRank3 = PiCode;
+ break;
+
+ default:
+ break;
+ }
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "C%u.R%u:\t%d\t%d\t%d\t%d\n",
+ Channel,
+ Rank,
+ *LPStart,
+ *LPEnd,
+ lWidth,
+ PiCode
+ );
+ }
+ }
+
+ Offset = DDRCLKCH0_CR_DDRCRCLKPICODE_REG +
+ ((DDRCLKCH1_CR_DDRCRCLKPICODE_REG - DDRCLKCH0_CR_DDRCRCLKPICODE_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, DdrCrClkPiCode.Data);
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ }
+
+ Status = ChangeMargin (MrcData, RdT, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileCurrent);
+ Status = MrcResetSequence (MrcData);
+
+ return Status;
+}
+
+/**
+@brief
+ This function performs Late command training.
+ Center CMD/CTL-CLK timing using complex patterns.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - If it's a success return mrcSuccess
+**/
+MrcStatus
+MrcLateCommandTraining (
+ MrcParameters *const MrcData
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ MrcProfile Profile;
+ U32 MinCode;
+ U32 Offset;
+ U8 Cmd2N;
+ U8 Channel;
+ U8 ChBitMask;
+ U8 RankMask;
+ U8 Rank;
+ U8 Ranks;
+ U8 CmdPiCode[MAX_CHANNEL];
+ U8 CtlPiCode[MAX_CHANNEL];
+#ifdef ULT_FLAG
+ BOOL Lpddr;
+ U8 MidPointCke[MAX_CHANNEL];
+ U8 MidPointCmdN[MAX_CHANNEL];
+ U8 MidPointCmdS[MAX_CHANNEL];
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_ORDER_CARRY_INVERT_CTL_MCMAIN_0_STRUCT ReutChSeqBaseAddrOrderCarryInvertCtl;
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_STRUCT ReutChSeqBaseAddrIncCtl;
+ MCHBAR_CH0_CR_REUT_CH_PAT_CADB_CTRL_STRUCT ReutChPatCadbCtrl;
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_STRUCT ReutChSeqBaseAddrWrap;
+#endif //ULT_FLAG
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Profile = MrcData->SysIn.Inputs.MemoryProfile;
+
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+#endif // ULT_FLAG
+
+ ChBitMask = Outputs->ValidChBitMask;
+ RankMask = Outputs->ValidRankMask;
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ SetupIOTestCADB (MrcData, ChBitMask, 10, NTHSOE, 1, 0); // LC = 10
+ } else
+#endif //ULT_FLAG
+ {
+ Cmd2N = FALSE;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Cmd2N = (ControllerOut->Channel[Channel].Timing[Profile].NMode == 2) ? TRUE : FALSE; // All channels have same NMode
+ break;
+ }
+ }
+ CmdPiCode[0] = CmdPiCode[1] = (Cmd2N == TRUE) ? 85 : 64;
+ CtlPiCode[0] = CtlPiCode[1] = 64;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " Cmd2N %d, CmdPiCode %d, ChBitMask = 0x%x\n",
+ Cmd2N,
+ CmdPiCode[0],
+ ChBitMask
+ );
+
+ //
+ // Setup REUT
+ // LC= 10, SOE = 1 (NTHSOE), EnCADB = 1, EnCKE = 0
+ //
+ SetupIOTestCADB (MrcData, ChBitMask, 10, NTHSOE, 1, 0);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcChannelExist (Outputs, Channel))) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Shift everything to the right. To get DQ timing right, program Clk to 0
+ //
+ ShiftPIforCmdTraining (
+ MrcData,
+ Channel,
+ MrcIterationClock,
+ ChannelOut->ValidRankBitMask,
+ 1,
+ 0 - ChannelOut->ClkPiCode[0],
+ 1
+ );
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, ChannelOut->ValidRankBitMask, 1, CmdPiCode[Channel], 1);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdN, ChannelOut->ValidRankBitMask, 1, CmdPiCode[Channel], 1);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCtl, ChannelOut->ValidRankBitMask, 1, CtlPiCode[Channel], 1);
+ }
+ }
+
+#ifdef ULT_FLAG
+
+ if (Lpddr) {
+ //
+ // Center Command Timing
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** ECT results\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ MidPointCke[Channel] = (U8) ChannelOut->CkeCmdPiCode[0];
+ MidPointCmdS[Channel] = (U8) ChannelOut->CmdsCmdPiCode[0];
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "C%u: CAA: CKE fub: %d, CmdS fub: %d\n",
+ Channel,
+ MidPointCke[Channel],
+ MidPointCmdS[Channel]
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** Center CAA[5,6,7,8,9] Timing using CKE fub\n");
+ CmdTimingCentering (MrcData, MrcIterationCke, RankMask, 1, MidPointCke);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** Center CAA[0,1,2,3,4] Timing using CmdS fub\n");
+ CmdTimingCentering (MrcData, MrcIterationCmdS, RankMask, 1, MidPointCmdS);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** ECT results\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ MidPointCmdS[Channel] = (U8)ChannelOut->CmdsCmdPiCode[1];
+ MidPointCmdN[Channel] = (U8)ChannelOut->CmdnCmdPiCode[1];
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "C%u: CAB: CmdS fub: %d, CmdN fub: %d\n",
+ Channel,
+ MidPointCmdS[Channel],
+ MidPointCmdN[Channel]
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** Center CAB[5,8] Timing using CmdS fub\n");
+ CmdTimingCentering (MrcData, MrcIterationCmdS, RankMask, 2, MidPointCmdS);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** Center CAB[0,1,2,3,4,6,7,9] Timing using CmdN fub\n");
+ CmdTimingCentering (MrcData, MrcIterationCmdN, RankMask, 2, MidPointCmdN);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** Center Control Timing\n");
+
+ //
+ // @todo Reinitialize registers to CAS-centric training (no CADB) ?
+ //
+
+ //
+ // Modify the differences
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ Offset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_ORDER_CARRY_INVERT_CTL_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_ORDER_CARRY_INVERT_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_ORDER_CARRY_INVERT_CTL_MCMAIN_0_REG) * Channel);
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Data = 0;
+ MrcWriteCR (MrcData, Offset, ReutChSeqBaseAddrOrderCarryInvertCtl.Data);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d ReutChSeqBaseAddrOrderCarryInvertCtl: 0x%08X\n", Channel, ReutChSeqBaseAddrOrderCarryInvertCtl.Data);
+
+ Offset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_REG) * Channel);
+ ReutChSeqBaseAddrIncCtl.Data = 0;
+ ReutChSeqBaseAddrIncCtl.Bits.Rank_Base_Address_Update_Scale = 1;
+ ReutChSeqBaseAddrIncCtl.Bits.Bank_Base_Address_Update_Scale = 1;
+ ReutChSeqBaseAddrIncCtl.Bits.Row_Base_Address_Update_Scale = 1;
+ ReutChSeqBaseAddrIncCtl.Bits.Column_Base_Address_Update_Scale = 1;
+ ReutChSeqBaseAddrIncCtl.Bits.Column_Base_Address_Increment = 1;
+ MrcWriteCR64 (MrcData, Offset, ReutChSeqBaseAddrIncCtl.Data);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d ReutChSeqBaseAddrIncCtl: 0x%08X%08X\n", Channel, ReutChSeqBaseAddrIncCtl.Data32[1],
+ ReutChSeqBaseAddrIncCtl.Data32[0]);
+
+ 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);
+ ReutChPatCadbCtrl.Data = 0;
+ ReutChPatCadbCtrl.Bits.Lane_Deselect_Enable = 0xB; // All, except CMD
+ ReutChPatCadbCtrl.Bits.CMD_Deselect_Start = 2; // Start on RD
+ MrcWriteCR (MrcData, Offset, ReutChPatCadbCtrl.Data);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d ReutChPatCadbCtrl: 0x%08X\n", Channel, ReutChPatCadbCtrl.Data);
+
+ Offset = MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_MASK_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_MASK_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_MASK_MCMAIN_0_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, 0xFF);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d ReutChSeqDummyreadMask Offset:0x%X Value:0x%X\n", Channel, Offset, 0xFF);
+
+ //
+ // Start from logical Rank 0
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG) * Channel);
+ MrcWriteCR64 (MrcData, Offset, 0);
+
+ //
+ // Wrap at column 127
+ // Logical Rank Wrap address will be updated in SelectReutRanks() later on.
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG) * Channel);
+ ReutChSeqBaseAddrWrap.Data = 0;
+ ReutChSeqBaseAddrWrap.Bits.Column_Address = 0x7F;
+ MrcWriteCR64 (MrcData, Offset, ReutChSeqBaseAddrWrap.Data);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d ReutChSeqBaseAddrWrap: 0x%08X%08X\n", Channel, ReutChSeqBaseAddrWrap.Data32[1],
+ ReutChSeqBaseAddrWrap.Data32[0]);
+ } // for Channel
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ Ranks = (1 << Rank);
+ if ((Ranks & RankMask) == 0) {
+ continue;
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** ECT results\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ CtlPiCode[Channel] = ControllerOut->Channel[Channel].CtlPiCode[Rank];
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%uR%u: CTL: %u\n", Channel, Rank, CtlPiCode[Channel]);
+ } else {
+ CtlPiCode[Channel] = 0;
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nCentering CTL on Rank %d\n", Rank);
+ CmdTimingCentering (MrcData, MrcIterationCtl, Ranks, Ranks, CtlPiCode);
+ }
+ } else // not Lpddr
+#endif //ULT_FLAG
+ {
+ //
+ // Center Clock Timing in the global eye
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** Center Clock Timing in the Global eye\n");
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ALL Ranks - RankBitMask = %d\n", RankMask);
+ CmdTimingCentering (MrcData, MrcIterationClock, RankMask, 1, NULL);
+
+ //
+ // Center Command Timing
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n*** Center Command S Timing\n");
+ CmdTimingCentering (MrcData, MrcIterationCmdS, RankMask, 1, CmdPiCode);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n*** Center Command N Timing\n");
+ CmdTimingCentering (MrcData, MrcIterationCmdN, RankMask, 1, CmdPiCode);
+
+ //
+ // Center Control Timing. For control pins, CKE PI is shared between Rank 2 and 3
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n*** Center Control Timing. CKE PI is shared between Rank 2 and 3");
+ for (Rank = 0; Rank < (MAX_RANK_IN_CHANNEL - 1); Rank++) {
+ Ranks = (1 << Rank);
+
+ if (Rank == 2) {
+ Ranks = 0xC;
+ }
+
+ Ranks = Ranks & RankMask;
+
+ if (Ranks) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n Rank %d\n", Rank);
+ CmdTimingCentering (MrcData, MrcIterationCtl, Ranks, 1, CtlPiCode);
+ }
+ }
+ }
+
+ //
+ // Normalize timing back to 0 to improve performance
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n*** Normalize timing back to 0\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Find the minimum PI Code across all relevant CMD and CTL fubs
+ //
+ MinCode = ChannelOut->CkeCmdPiCode[0];
+ MinCode = MIN (MinCode, ChannelOut->CmdsCmdPiCode[0]);
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ MinCode = MIN (MinCode, ChannelOut->CmdsCmdPiCode[1]);
+ MinCode = MIN (MinCode, ChannelOut->CmdnCmdPiCode[1]);
+ } else
+#endif //ULT_FLAG
+ {
+ MinCode = MIN (MinCode, ChannelOut->CmdnCmdPiCode[0]);
+ }
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ MinCode = MIN (MinCode, ChannelOut->CkePiCode[Rank]);
+ MinCode = MIN (MinCode, ChannelOut->CtlPiCode[Rank]);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d: shifting all PI settings by Min PI Code = %d\n", Channel, MinCode);
+ ShiftChannelTiming (MrcData, Channel, (-1) * MinCode, 1);
+ } // for Channel
+
+ //
+ // Disable CADB Deselects after Command Training
+ //
+ 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);
+ MrcWriteCR (MrcData, Offset, 0);
+ }
+ }
+
+ //
+ // Finish Training with JEDEC Reset / Init
+ //
+ Status = MrcResetSequence (MrcData);
+ return Status;
+}
+
+/**
+@brief
+ Perform Command Voltage Centering.
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded returns mrcSuccess
+**/
+MrcStatus
+MrcCmdVoltageCentering (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const U16 mode = 0;
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U32 BERStats[4];
+ U32 Offset;
+ U8 LoopCount;
+ U8 ValidRankMask;
+ U8 Channel;
+ U8 Rank;
+ U8 chBitMask;
+ U8 RankMask;
+ U32 MinChLow;
+ U32 MinChHigh;
+ BOOL Lpddr;
+#ifdef MRC_DEBUG_PRINT
+ U32 Low;
+ U32 High;
+ U32 Height;
+#endif
+ S32 Center;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ValidRankMask = Outputs->ValidRankMask;
+ Status = mrcSuccess;
+ MinChLow = 0xFFFFFFFF;
+ MinChHigh = 0xFFFFFFFF;
+ MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats));
+
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ LoopCount = (Lpddr) ? 6 : 10;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Cmd Vref Training with LC = %d\n\nMargin\nParams: CmdV\n\tLow\tHigh\tHeight\tCenter\n",
+ LoopCount
+ );
+
+ //
+ // Use CADB test for Cmd to match Late Command Training
+ //
+ SetupIOTestCADB (MrcData, Outputs->ValidChBitMask, LoopCount, NSOE, 1, 0);
+
+ //
+ // 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 ((MRC_BIT0 << Channel) & chBitMask) {
+ //
+ // Clear any old state in DataTrain Offset
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ }
+
+ //
+ // Run test for Cmd Voltage
+ //
+ Status = MrcGetBERMarginCh (
+ MrcData,
+ Outputs->MarginResult,
+ chBitMask,
+ 0xFF,
+ 0,
+ CmdV,
+ mode,
+ 0,
+ MAX_POSSIBLE_VREF,
+ 0,
+ BERStats
+ );
+ Status = MrcResetSequence (MrcData);
+
+ //
+ // Find center value and update Vref.
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ MinChLow = MIN (MinChLow, Outputs->MarginResult[LastCmdV][0][Channel][0][0]);
+ MinChHigh = MIN (MinChHigh, Outputs->MarginResult[LastCmdV][0][Channel][0][1]);
+ }
+ }
+ Center = ((S32) (MinChHigh - MinChLow)) / 2;
+ UpdateVrefWaitTilStable (MrcData, 2, 1, Center / 10, 0);
+ Status = MrcResetSequence (MrcData);
+
+#ifdef MRC_DEBUG_PRINT
+ //
+ // Print test results
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Low = Outputs->MarginResult[LastCmdV][0][Channel][0][0] / 10;
+ High = Outputs->MarginResult[LastCmdV][0][Channel][0][1] / 10;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "C%u:\t%u\t%u\t%u\t%d\n",
+ Channel,
+ Low,
+ High,
+ Low + High,
+ ((S32) (High - Low)) / 2
+ );
+ }
+ }
+ Height = MinChHigh + MinChLow;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Sys:\t%u\t%u\t%u\t%d\n",
+ MinChLow / 10,
+ MinChHigh / 10,
+ Height / 10,
+ Center / 10
+ );
+#endif
+
+ //
+ // Update MrcData for future tests
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = MRC_BIT0 << Rank;
+ if (!(RankMask & ValidRankMask)) {
+ //
+ // Skip if all channels empty
+ //
+ continue;
+ }
+ Outputs->MarginResult[LastCmdV][Rank][0][0][0] = MinChLow + Center;
+ Outputs->MarginResult[LastCmdV][Rank][0][0][1] = MinChHigh - Center;
+ }
+ //
+ // Disable CADB Deselects
+ //
+ 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
+ Centers Command Timing around a MidPoint
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Iteration - Determines which PI to shift
+ @param[in] Ranks - Valid Rank bit mask
+ @param[in] GroupMask - which LPDDR groups to work on for CMD/CLK/CKE; not used for DDR3
+ @param[in] MidPoint - The MidPoint to center around (per channel)
+
+ @retval Nothing
+**/
+void
+CmdTimingCentering (
+ IN MrcParameters *const MrcData,
+ IN U8 Iteration,
+ IN U8 Ranks,
+ IN U8 GroupMask,
+ IN U8 MidPoint[MAX_CHANNEL]
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ U8 Ledge[MAX_CHANNEL];
+ U8 Redge[MAX_CHANNEL];
+ U8 Mid[MAX_CHANNEL];
+ U8 Low[MAX_CHANNEL];
+ U8 High[MAX_CHANNEL];
+ U8 MidValue;
+ S8 VrefOffsets[2];
+ U8 Center;
+ U8 ChBitMask;
+ U8 RankMask;
+ U8 Channel;
+ U8 MinWidth;
+ U8 lWidth;
+ BOOL SkipVref;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ VrefOffsets[0] = -8;
+ VrefOffsets[1] = 8;
+ MinWidth = 18;
+ MrcOemMemorySet (Ledge, 0, sizeof (Ledge));
+ MrcOemMemorySet (Redge, 0, sizeof (Redge));
+
+ if ((Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) && (Iteration != MrcIterationClock)) {
+ //
+ // Limit the binary search to +/- 32 PI ticks from the ECT midpoint, for LPDDR3 Command/Control
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ MidValue = MidPoint[Channel];
+ Low[Channel] = (MidValue > 32) ? (MidValue - 32) : 0;
+ High[Channel] = (MidValue < 127 - 32) ? (MidValue + 32) : 127;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Ch%d: search range is [%d..%d]\n", Channel, Low[Channel], High[Channel]);
+ }
+ } else {
+ //
+ // Binary search will use the full PI range of [0..127]
+ //
+ MrcOemMemorySet (Low, 0, sizeof (Low));
+ MrcOemMemorySet (High, 127, sizeof (High));
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel\t\t\t0 1\n");
+
+ //
+ // Setup REUT Test to iteration through appropriate ranks during test
+ //
+ ChBitMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChBitMask |= SelectReutRanks (MrcData, Channel, Ranks, 0);
+ if (MidPoint != NULL) {
+ Mid[Channel] = MidPoint[Channel];
+ }
+ }
+ //
+ // MRC_DEBUG_MSG (&MrcData->Inputs.Debug, MSG_LEVEL_NOTE, "**** CmdTimingCentering, Iteration = %d, ChBitMask = 0x%x\n", Iteration,ChBitMask);
+ //
+ if (Iteration == MrcIterationClock) {
+ //
+ // Use a linear search to center clock and Update Clock Delay/Host
+ // Allow wrap around since this is clock
+ // CmdLinearFindEdges also programs the new values
+ //
+ SkipVref = FALSE;
+ CmdLinearFindEdges (MrcData, Iteration, ChBitMask, Ranks, GroupMask, Low[0], High[0], 1, VrefOffsets, FALSE, SkipVref);
+ } else {
+ CmdBinaryFindEdge (MrcData, Iteration, ChBitMask, Ranks, GroupMask, Low, Mid, 0, VrefOffsets);
+ Ledge[0] = Mid[0];
+ Ledge[1] = Mid[1]; // CountUp is 0 so return High.
+ if (MidPoint == NULL) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Midpoint[] is NULL and MrcIterationClock not selected!\n");
+ } else {
+ Mid[0] = MidPoint[0];
+ Mid[1] = MidPoint[1]; //Mid Modified by CmdBinaryFindEdge
+ }
+ CmdBinaryFindEdge (MrcData, Iteration, ChBitMask, Ranks, GroupMask, Mid, High, 1, VrefOffsets);
+ Redge[0] = Mid[0];
+ Redge[1] = Mid[1]; // CountUp is 1 so return Low.
+ //
+ // Update Variables:
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nCH\tLeft\tRight\tWidth\tCenter");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((1 << Channel) & ChBitMask) == 0) {
+ continue;
+ }
+ RankMask = Ranks & ControllerOut->Channel[Channel].ValidRankBitMask;
+ lWidth = Redge[Channel] - Ledge[Channel];
+
+ if ((Redge[Channel] == 127) && (Ledge[Channel] == 0)) {
+ //
+ // No errors found
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "\nNo Errors Found for C%u!\n", Channel);
+ Center = MidPoint[Channel];
+ } else {
+ Center = (Ledge[Channel] + Redge[Channel] + 1) / 2;
+ if (lWidth < MinWidth) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "\nEye < %u for C%u!\n", MinWidth, Channel);
+ }
+ }
+
+ ShiftPIforCmdTraining (MrcData, Channel, Iteration, RankMask, GroupMask, Center, 1);
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\n %d\t%d\t%d\t%d\t%d",
+ Channel,
+ Ledge[Channel],
+ Redge[Channel],
+ lWidth,
+ Center
+ );
+ } // for Channel
+ }
+
+ return;
+}
+
+/**
+@brief
+ Use a linear search to find the edges between Low and High
+ if WrapAround = 0: Look for largest passing region between low and high
+ if WrapAround = 1: Look for largest passing region, including wrapping from high to low
+
+ @param[in, out] MrcData - Include all MRC global data.
+ @param[in] Iteration - Determines which PI to shift
+ @param[in] chBitMask - Valid Channel bit mask
+ @param[in] Ranks - Valid Rank bit mask
+ @param[in] GroupMask - which LPDDR groups to work on for CMD/CLK/CKE; not used for DDR3
+ @param[in] Low - Low limit
+ @param[in] High - High limit
+ @param[in] WrapAllowed - Determines the search region
+ @param[in] VrefOffsets - Array of Vref offsets
+ @param[in] SkipPrint - Switch to enable or disable debug printing
+ @param[in] SkipVref - Skip changing CMD Vref offsets, only run test once at the current Vref.
+
+ @retval MrcStatus - If it succeeds return mrcSuccess
+**/
+void
+CmdLinearFindEdges (
+ IN OUT MrcParameters *const MrcData,
+ IN U8 Iteration,
+ IN U8 chBitMask,
+ IN U8 Ranks,
+ IN U8 GroupMask,
+ IN S8 Low,
+ IN U8 High,
+ IN U8 WrapAllowed,
+ IN S8 *VrefOffsets,
+ IN BOOL SkipPrint,
+ IN BOOL SkipVref
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ S32 *IPStart;
+ S32 *IPEnd;
+ S32 *CPStart;
+ S32 *CPEnd;
+ S32 *LPStart;
+ S32 *LPEnd;
+ MrcStatus Status;
+ BOOL Pass;
+ BOOL Lpddr;
+ S32 InitialPassingStart[MAX_CHANNEL];
+ S32 InitialPassingEnd[MAX_CHANNEL];
+ S32 CurrentPassingStart[MAX_CHANNEL];
+ S32 CurrentPassingEnd[MAX_CHANNEL];
+ S32 LargestPassingStart[MAX_CHANNEL];
+ S32 LargestPassingEnd[MAX_CHANNEL];
+ S32 lWidth;
+ S32 iWidth;
+ S32 cWidth;
+ S32 Center;
+ S16 LCTDelay;
+ U8 Channel;
+ U8 ChannelMask;
+ U8 RankMask;
+ U8 Rank;
+ U8 LCTStep;
+ U8 LastStep;
+ U8 Vloop;
+ U8 ChError;
+ U8 DumArr[7];
+ S8 Vref;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ lWidth = 0;
+ iWidth = 0;
+ cWidth = 0;
+ MrcOemMemorySet (DumArr, 1, sizeof (DumArr));
+
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ LCTStep = (Lpddr) ? 2 : 6;
+
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "**** CmdLinearFindEdges, Iteration = %d, Low = %d, High = %d\n", Iteration, Low, High);
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (!SkipPrint) ? "CLkDlay" : "");
+
+ for (LCTDelay = Low; LCTDelay <= High; LCTDelay += LCTStep) {
+ //
+ // Update Timing
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((1 << Channel) & chBitMask) {
+ RankMask = Ranks & ControllerOut->Channel[Channel].ValidRankBitMask;
+ ShiftPIforCmdTraining (MrcData, Channel, Iteration, RankMask, GroupMask, LCTDelay, 0);
+ }
+ }
+ //
+ // Reset DDR
+ //
+ Status = MrcResetSequence (MrcData);
+
+ //
+ // Run REUT until both channels fail or we finish all Vref points
+ //
+ if (SkipVref) {
+ ChError = RunIOTest (MrcData, chBitMask, Outputs->DQPat, DumArr, 1, 0);
+ } else {
+ ChError = 0;
+ for (Vloop = 0; Vloop < 2; Vloop++) {
+ Vref = VrefOffsets[Vloop];
+ UpdateVrefWaitTilStable (MrcData, 2, 0, Vref, 0);
+
+ ChError |= RunIOTest (MrcData, chBitMask, Outputs->DQPat, DumArr, 1, 0);
+
+ if (ChError == chBitMask) {
+ break;
+ }
+
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (!SkipPrint) ? "\n %d\t\t\t" : "", LCTDelay);
+
+ //
+ // Update Passing Variables
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelMask = MRC_BIT0 << Channel;
+ if (!(ChannelMask & chBitMask)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (!SkipPrint && (Channel == 0)) ? " " : "");
+ continue;
+ }
+
+ Pass = !(ChError & ChannelMask);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (!SkipPrint) ? (Pass ? ". " : "# ") : "");
+
+ IPStart = &InitialPassingStart[Channel];
+ IPEnd = &InitialPassingEnd[Channel];
+ CPStart = &CurrentPassingStart[Channel];
+ CPEnd = &CurrentPassingEnd[Channel];
+ LPStart = &LargestPassingStart[Channel];
+ LPEnd = &LargestPassingEnd[Channel];
+
+ if (LCTDelay == (S16) Low) {
+ if (Pass) {
+ *IPStart = *IPEnd = *CPStart = *CPEnd = *LPStart = *LPEnd = Low;
+ } else {
+ *IPStart = *IPEnd = *CPStart = *CPEnd = *LPStart = *LPEnd = Low - LCTStep;
+ }
+ } else {
+ if (Pass) {
+ //
+ // Update Initial variables
+ //
+ if (*IPEnd == (LCTDelay - LCTStep)) {
+ *IPEnd = LCTDelay; // In passing region
+ }
+ //
+ // Update Current variables
+ //
+ if (*CPEnd == (LCTDelay - LCTStep)) {
+ *CPEnd = LCTDelay; // In passing region
+ } else {
+ *CPStart = *CPEnd = LCTDelay;
+ }
+ //
+ // Special case for last step: Append Initial Passing Region
+ // LCTDelay should be considered a continuous range that wraps around 0
+ //
+ LastStep = High - LCTStep;
+ if ((LCTDelay >= LastStep) && (*IPStart == Low) && WrapAllowed) {
+ iWidth = *IPEnd -*IPStart;
+ *CPEnd += (LCTStep + iWidth);
+ }
+ //
+ // Update Largest variables
+ //
+ cWidth = *CPEnd - *CPStart;
+ lWidth = *LPEnd - *LPStart;
+ if (cWidth > lWidth) {
+ *LPStart = *CPStart;
+ *LPEnd = *CPEnd;
+ }
+ }
+ }
+ } // for Channel
+
+
+ } // for LCTDelay
+
+ if (!SkipPrint) {
+ //
+ // Update Variables:
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nCH\tLeft\tRight\tWidth\tCenter\n");
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((MRC_BIT0 << Channel) & chBitMask) {
+ LPStart = &LargestPassingStart[Channel];
+ LPEnd = &LargestPassingEnd[Channel];
+ //
+ // Handle any corner cases
+ //
+ lWidth = *LPEnd - *LPStart;
+ if ((lWidth < (3 * LCTStep)) || (lWidth >= (High - Low))) {
+ //
+ // @todo: Pass a default center parameter instead of line below.
+ //
+ Center = (Low + High) / 2;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "\nError Handler! Found Bad command Eye\n");
+ } else {
+ Center = (*LPEnd + *LPStart) / 2;
+ }
+ RankMask = Ranks & ControllerOut->Channel[Channel].ValidRankBitMask;
+ if (!SkipPrint) {
+ //
+ // Shift Timing
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, Iteration, RankMask, GroupMask, Center, 1);
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " %d\t%d\t%d\t%d\t%d\n",
+ Channel,
+ *LPStart,
+ *LPEnd,
+ lWidth,
+ Center
+ );
+ }
+ //
+ // Determine in which rank to save the margins...
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((RankMask >> Rank) & MRC_BIT0) {
+ Outputs->MarginResult[LastCmdT][Rank][Channel][0][0] = 10 * ABS (*LPStart);
+ Outputs->MarginResult[LastCmdT][Rank][Channel][0][1] = 10 * ABS (*LPEnd);
+ }
+ }
+ }
+ }
+ //
+ // Clean Up
+ //
+ if (!SkipVref) {
+ UpdateVrefWaitTilStable (MrcData, 2, 0, 0, 0);
+ }
+
+ Status = MrcResetSequence (MrcData);
+ return;
+}
+
+/**
+@brief
+ Use a binary search to find the edge between Low and High
+ High and Low track passing points
+ if CountUp: Low is a passing point and need to count up to find a failing point
+ if CountDn: High is a passing point and need to count dn to find a failing point
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Iteration - Determines which PI to shift
+ @param[in] ChBitMask - Valid Channel bit mask
+ @param[in] Ranks - Valid Rank bit mask
+ @param[in] GroupMask - which LPDDR groups to work on for CMD/CLK/CKE; not used for DDR3
+ @param[in, out] Low - Low limit
+ @param[in, out] High - High limit
+ @param[in] CountUp - The direction to search
+ @param[in] VrefOffsets - Array of Vref offsets
+
+ @retval Nothing
+**/
+void
+CmdBinaryFindEdge (
+ IN MrcParameters *const MrcData,
+ IN U8 Iteration,
+ IN U8 ChBitMask,
+ IN U8 Ranks,
+ IN U8 GroupMask,
+ IN OUT U8 *Low,
+ IN OUT U8 *High,
+ IN U8 CountUp,
+ IN S8 *VrefOffsets
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcStatus Status;
+ U8 Target[MAX_CHANNEL];
+ U8 Done;
+ U8 ChError;
+ U8 DumArr[7];
+ S8 Vref;
+ U8 Channel;
+ U8 ChannelMask;
+ U8 RankMask;
+ U8 Group;
+ U8 Fail;
+ U8 Vloop;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Done = 0;
+ ChError = 0;
+ MrcOemMemorySet (Target, 0, sizeof (Target));
+ MrcOemMemorySet (DumArr, 1, sizeof (DumArr));
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CmdTgt\nCh0G0\tCh0G1\tCh1G0\tCh1G1\n");
+
+ while (!Done) {
+ //
+ // Update Timing
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((1 << Channel) & ChBitMask) == 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ } else {
+ Target[Channel] = (High[Channel] + Low[Channel] + CountUp) / 2; // CountUp gets rounding correct
+ RankMask = Ranks & ControllerOut->Channel[Channel].ValidRankBitMask;
+ for (Group = 0; Group < 2; Group++) {
+ if (((1 << Group) & GroupMask) == 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ } else {
+ ShiftPIforCmdTraining (MrcData, Channel, Iteration, RankMask, 1 << Group, Target[Channel], 0);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Target[Channel]);
+ }
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+
+ //
+ // Reset DDR
+ //
+ Status = MrcResetSequence (MrcData);
+
+ //
+ // Run REUT until both channels fail or we finish all Vref points
+ //
+ ChError = 0;
+ for (Vloop = 0; Vloop < 2; Vloop++) {
+ Vref = VrefOffsets[Vloop];
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "**** Run REUT until both channels fail or we finish all Vref points, Vref = %d\n", Vref);
+ //
+ UpdateVrefWaitTilStable (MrcData, 2, 0, Vref, 0);
+
+ ChError |= RunIOTest (MrcData, ChBitMask, Outputs->DQPat, DumArr, 1, 0);
+ if (ChError == ChBitMask) {
+ break;
+ }
+
+ }
+
+ //
+ // Update High/Low
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelMask = 1 << Channel;
+ if (!(ChannelMask & ChBitMask)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (Channel == 0) ? " " : "");
+ } else {
+ Fail = (ChError & ChannelMask);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, Fail ? "# " : ". ");
+
+ //
+ // Skip if this channel is done
+ //
+ if (High[Channel] > Low[Channel]) {
+ if (CountUp) {
+ if (Fail) {
+ High[Channel] = Target[Channel] - 1;
+ } else {
+ Low[Channel] = Target[Channel];
+ }
+ } else {
+ if (Fail) {
+ Low[Channel] = Target[Channel] + 1;
+ } else {
+ High[Channel] = Target[Channel];
+ }
+ }
+ }
+ }
+ }
+ //
+ // Update Done
+ //
+ Done = 1;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((1 << Channel) & ChBitMask) {
+ if (High[Channel] > Low[Channel]) {
+ Done = 0;
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+ //
+ // Clean Up
+ //
+ UpdateVrefWaitTilStable (MrcData, 2, 0, 0, 0);
+ MrcResetSequence (MrcData);
+ return;
+}
+
+/**
+@brief
+ Shift the CLK/CMD/CTL Timing by the given PI setting value
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel to shift
+ @param[in] Offset - Offset to shift by
+ @param[in] UpdateHost - Switch to update the host structure
+
+ @retval Nothing
+**/
+void
+ShiftChannelTiming (
+ IN MrcParameters *const MrcData,
+ IN U8 Channel,
+ IN S32 Offset,
+ IN U8 UpdateHost
+ )
+{
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+ const MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ S32 NewCode;
+ U8 Rank;
+ U8 RankBit;
+#ifdef ULT_FLAG
+ U8 Group;
+ BOOL Lpddr;
+#endif // ULT_FLAG
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ ChannelIn = &Inputs->Controller[0].Channel[Channel];
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %d:\n", Channel);
+
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3);
+#endif // ULT_FLAG
+
+ //
+ // Shift the CLK/CTL Timing
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ RankBit = 1 << Rank;
+ NewCode = ChannelOut->ClkPiCode[Rank] + Offset;
+#ifdef ULT_FLAG
+ if (!Lpddr)
+#endif // ULT_FLAG
+ {
+ //
+ // CLK is per Rank in DDR3
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationClock, RankBit, RankBit, Offset, UpdateHost);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " R%d New CLK value = %d\n", Rank, NewCode);
+ }
+
+ NewCode = ChannelOut->CtlPiCode[Rank] + Offset;
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCtl, RankBit, 1, NewCode, UpdateHost);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " R%d New CTL value = %d\n", Rank, NewCode);
+ }
+ }
+
+ //
+ // Shift the CMD Timing
+ //
+ NewCode = ChannelOut->CmdsCmdPiCode[0] + Offset;
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, ChannelOut->ValidRankBitMask, 1, NewCode, UpdateHost);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " New CMDS[0] value = %d\n", NewCode);
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ //
+ // CLK is per Group in LPDDR3
+ //
+ for (Group = 0; Group < 2; Group++) {
+ if (ChannelIn->DQByteMap[MrcIterationClock][Group] != 0) {
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationClock, 0, 1 << Group, Offset, UpdateHost);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " New CLK%d value = %d\n", Group, ChannelOut->ClkPiCode[Group]);
+ }
+ }
+
+ NewCode = ChannelOut->CkeCmdPiCode[0] + Offset;
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCke, ChannelOut->ValidRankBitMask, 1, NewCode, UpdateHost);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " New CKE[0] value = %d\n", NewCode);
+
+ NewCode = ChannelOut->CmdsCmdPiCode[1] + Offset;
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, ChannelOut->ValidRankBitMask, 2, NewCode, UpdateHost);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " New CMDS[1] value = %d\n", NewCode);
+
+ NewCode = ChannelOut->CmdnCmdPiCode[1] + Offset;
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdN, ChannelOut->ValidRankBitMask, 2, NewCode, UpdateHost);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " New CMDN[1] value = %d\n", NewCode);
+ } else
+#endif // ULT_FLAG
+ {
+ NewCode = ChannelOut->CmdnCmdPiCode[0] + Offset;
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdN, ChannelOut->ValidRankBitMask, 1, NewCode, UpdateHost);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " New CMDN[0] value = %d\n", NewCode);
+ }
+ return;
+}
+
+/**
+@brief
+ This function updtes Command Mode register, tXP and Round trip latency
+
+ @param[in, out] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel to perform update to
+ @param[in] OldN - Old N Mode value
+ @param[in] NewN - New N mode value
+
+ @retval Nothing
+**/
+void
+UpdateCmdNTiming (
+ IN OUT MrcParameters *const MrcData,
+ IN U8 Channel,
+ IN U8 OldN,
+ IN U8 NewN
+ )
+{
+ const U8 CmdStretch[1 << MCHBAR_CH0_CR_TC_BANK_RANK_A_CMD_stretch_WID] = {
+ 0,
+ 2,
+ 3,
+ MCHBAR_CH0_CR_TC_BANK_RANK_A_CMD_stretch_DEF
+ };
+
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ U8 *RtLatency;
+ MCHBAR_CH0_CR_TC_BANK_RANK_A_STRUCT TcBankRankA;
+ MCHBAR_CH0_CR_TC_BANK_RANK_C_STRUCT TcBankRankC;
+ MCHBAR_CH0_CR_SC_ROUNDT_LAT_STRUCT ScRoundtLat;
+ MrcProfile Profile;
+ U32 Offset;
+ U32 Scratch;
+ U8 Rank;
+ S8 Diff;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ Profile = MrcData->SysIn.Inputs.MemoryProfile;
+
+ //
+ // Update CmdN timing
+ //
+ Offset = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * Channel);
+ TcBankRankA.Data = MrcReadCR (MrcData, Offset);
+ TcBankRankA.Bits.CMD_stretch = CmdStretch[ChannelOut->Timing[Profile].NMode - 1];
+ MrcWriteCR (MrcData, Offset, TcBankRankA.Data);
+ ChannelOut->MchbarBANKRANKA = TcBankRankA.Data;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d_TC_BANK_RANK_A = 0x%x\n", Channel, TcBankRankA.Data);
+
+ //
+ // Adjust tXP value
+ //
+ Offset = MCHBAR_CH0_CR_TC_BANK_RANK_C_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_C_REG - MCHBAR_CH0_CR_TC_BANK_RANK_C_REG) * Channel);
+ TcBankRankC.Data = MrcReadCR (MrcData, Offset);
+ Scratch = tXPValue (Outputs->DdrType, Outputs->Frequency, (U8) ChannelOut->Timing[Profile].NMode);
+ TcBankRankC.Bits.tXP = MIN (Scratch, MCHBAR_CH0_CR_TC_BANK_RANK_C_tXP_MAX);
+ MrcWriteCR (MrcData, Offset, TcBankRankC.Data);
+ ChannelOut->MchbarBANKRANKC = TcBankRankC.Data;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "C%d_TC_BANK_RANK_C = 0x%x\n", Channel, TcBankRankC.Data);
+
+ //
+ // Adjust RT values to compensate.
+ //
+ Diff = (NewN - OldN);
+ ScRoundtLat.Data = 0;
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ RtLatency = &ChannelOut->RTLatency[Rank];
+ *RtLatency = (U8) (*RtLatency + Diff);
+ switch (Rank) {
+ case 0:
+ ScRoundtLat.Bits.Lat_R0D0 = *RtLatency;
+ break;
+
+ case 1:
+ ScRoundtLat.Bits.Lat_R1D0 = *RtLatency;
+ break;
+
+ case 2:
+ ScRoundtLat.Bits.Lat_R0D1 = *RtLatency;
+ break;
+
+ case 3:
+ ScRoundtLat.Bits.Lat_R1D1 = *RtLatency;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ Offset = MCHBAR_CH0_CR_SC_ROUNDT_LAT_REG +
+ ((MCHBAR_CH1_CR_SC_ROUNDT_LAT_REG - MCHBAR_CH0_CR_SC_ROUNDT_LAT_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ScRoundtLat.Data);
+ return;
+}
+
+#ifdef ULT_FLAG
+
+/**
+@brief
+ Enter / exit LPDDR CA training modes.
+ Main flow:
+ 1. Force CKE high.
+ 2. Send MRW 41, 48 or 42.
+ 3. Force CKE low for MRW 41 or 48
+
+ @param[in] MrcData - The MRC global data.
+ @param[in] ChBitMask - channels to work on.
+ @param[in] RankBitMask - ranks to work on.
+ @param[in] Mode - CA training mode.
+
+ @retval mrcSuccess if succeeded
+**/
+MrcStatus
+LpddrCommandTrainingMode (
+ IN MrcParameters * const MrcData,
+ IN U8 ChBitMask,
+ IN U8 RankBitMask,
+ IN MrcCaTrainingMode Mode
+ )
+{
+ MrcStatus Status;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ U8 Channel;
+ U8 Rank;
+ U32 Offset;
+ U32 Address;
+ U32 Data;
+ BOOL InitMrw;
+ BOOL ChipSelect2N;
+ MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_STRUCT MiscCkeCtrl;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+
+ InitMrw = TRUE;
+ ChipSelect2N = FALSE;
+
+ switch (Mode) {
+ case CaTrainingMode41:
+ Address = 0x29;
+ Data = 0xA4; // Data is selected so that High and Low phases of CA[9:0] are equal
+ break;
+
+ case CaTrainingMode48:
+ Address = 0x30;
+ Data = 0xC0;
+ break;
+
+ case CaTrainingMode42:
+ Address = 0x2A;
+ Data = 0xA8;
+ ChipSelect2N = FALSE;
+ break;
+
+ default:
+ return mrcFail;
+ }
+
+ //
+ // Send the MRW41 command to populated ranks
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((1 << Channel) & ChBitMask) == 0) {
+ continue;
+ }
+
+ //
+ // Force CKE high
+ //
+ MiscCkeCtrl.Data = 0;
+ MiscCkeCtrl.Bits.CKE_Override = 0x0F;
+ MiscCkeCtrl.Bits.CKE_On = ControllerOut->Channel[Channel].ValidCkeBitMask;
+ Offset = MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG +
+ (MCHBAR_CH1_CR_REUT_CH_MISC_CKE_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG) * Channel;
+ MrcWriteCR (MrcData, Offset, MiscCkeCtrl.Data);
+
+ //
+ // Wait for CKE to become effective
+ //
+ MrcWait (MrcData, 1 * HPET_MIN);
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank ++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ if (((1 << Rank) & RankBitMask) != 0) {
+ Status = MrcIssueMrw (MrcData, Channel, Rank, Address, Data, InitMrw, ChipSelect2N);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ }
+ }
+
+ //
+ // Force CKE Low for MRW 41 or 48
+ //
+ if (Mode != CaTrainingMode42) {
+ //
+ // Wait tCACKEL = 10 tCK
+ //
+ MrcWait (MrcData, 1 * HPET_MIN);
+
+ //
+ // Force CKE low, tCACKEL after MRW41 issued
+ //
+ MiscCkeCtrl.Bits.CKE_On = 0;
+ MrcWriteCR (MrcData, Offset, MiscCkeCtrl.Data);
+ }
+ } // for Channel
+ return mrcSuccess;
+}
+
+/**
+ Program CADB Pattern Buffers with given values
+
+ @param[in] MrcData - The MRC global data.
+ @param[in] Channel - channel to work on.
+ @param[in] PatBuf0 - Pattern Buffer 0 value
+ @param[in] PatBuf1 - Pattern Buffer 1 value
+ @param[in] PatBuf2 - Pattern Buffer 2 value
+
+ @retval none
+**/
+void
+SetCadbPatternBuffers (
+ IN MrcParameters * const MrcData,
+ IN U8 Channel,
+ IN U32 PatBuf0,
+ IN U32 PatBuf1,
+ IN U32 PatBuf2
+ )
+{
+ U32 Offset;
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_MUX_PB_0_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_0_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, PatBuf0);
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_1_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_MUX_PB_1_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_1_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, PatBuf1);
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_2_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_MUX_PB_2_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_2_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, PatBuf2);
+}
+
+//
+// Sets up 3 CADB lines that will be used to send out a CS pattern.
+//
+// -----------------------
+// CADB Phase Phase CS
+// Line High Low
+// -----------------------
+// 0 0x000 0x000 Off
+// 1 0x3FF 0x3FF Off
+// 2 0x2AA 0x2AA On
+// 3 0x155 0x155 On
+//
+// The CS pattern uses Pattern Buffer and hence has 16 lines, with CS active for one line only.
+// This will send a command every 16 DCLKs.
+//
+// Pattern Buffer details:
+// The line order is: 0, 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+// or different command: 0, 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+//
+// 000
+// 000
+// 000
+// 000
+//
+// 000
+// 000
+// 000
+// 000
+//
+// 000
+// 000
+// 000
+// 000
+//
+// 001
+// 001
+// 010 or 011
+// 000
+// ----
+// 000 --> PB[0] = 0x3000 or 0x7000
+// 000 PB[1] = 0x4000
+// 000 PB[2] = 0x0000
+// 043 or 047
+//
+CADB_LINE CadbLinesCs[] = {
+ { 0x000, 0x000, 0 },
+ { 0x3FF, 0x3FF, 0 },
+ { 0x2AA, 0x2AA, 1 },
+ { 0x155, 0x155, 1 }
+};
+
+/**
+ Setup the CADB for CS or CA training.
+
+ @param[in] MrcData - The MRC global data.
+ @param[in] Channel - channel to work on
+ @param[in] Rank - rank to work on
+ @param[in] CadbLines - CADB lines to program
+ @param[in] CadbCount - Number of CADB lines to program
+ @param[in] PatBuf0 - Pattern Buffer 0 value
+ @param[in] PatBuf1 - Pattern Buffer 1 value
+ @param[in] PatBuf2 - Pattern Buffer 2 value
+ @retval none
+**/
+void
+SetupCaTrainingCadb (
+ IN MrcParameters * const MrcData,
+ IN U8 Channel,
+ IN U8 Rank,
+ IN CADB_LINE *CadbLines,
+ IN U32 CadbCount,
+ IN U32 PatBuf0,
+ IN U32 PatBuf1,
+ IN U32 PatBuf2
+)
+{
+ U32 Offset;
+ U32 MA;
+ U32 BA;
+ U32 CMD;
+ U32 i;
+
+ MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_CTRL_STRUCT ReutChPatCadbMuxCtrl;
+ MCHBAR_CH0_CR_REUT_CH_PAT_CADB_PROG_STRUCT ReutChPatCadbProg;
+
+ //
+ // Set Mux0/1/2 to Pattern Buffer mode
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_MUX_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_CTRL_REG) * Channel);
+ ReutChPatCadbMuxCtrl.Data = MrcReadCR (MrcData, Offset);
+ ReutChPatCadbMuxCtrl.Bits.Mux0_Control = 1;
+ ReutChPatCadbMuxCtrl.Bits.Mux1_Control = 1;
+ ReutChPatCadbMuxCtrl.Bits.Mux2_Control = 1;
+ MrcWriteCR (MrcData, Offset, ReutChPatCadbMuxCtrl.Data);
+
+ //
+ // Program Pattern Buffers for a specific progression over CADB,
+ // according to the given Pattern Buffer values
+ //
+ SetCadbPatternBuffers (MrcData, Channel, PatBuf0, PatBuf1, PatBuf2);
+
+ //
+ // Start writing at CADB row 0
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_WRITE_POINTER_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_WRITE_POINTER_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_WRITE_POINTER_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0);
+
+ ReutChPatCadbProg.Data = 0;
+ ReutChPatCadbProg.Bits.CADB_Data_ODT = (0 << Rank);
+ ReutChPatCadbProg.Bits.CADB_Data_CKE = (0 << Rank);
+
+ //
+ // Program the CADB lines
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_PROG_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_PROG_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_PROG_REG) * Channel);
+ for (i = 0; i < CadbCount ; i++) {
+ MrcConvertLpddr2Ddr (CadbLines[i].CaHigh, CadbLines[i].CaLow, &MA, &BA, &CMD);
+ ReutChPatCadbProg.Bits.CADB_Data_Address = MA;
+ ReutChPatCadbProg.Bits.CADB_Data_Bank = BA;
+ ReutChPatCadbProg.Bits.CADB_Data_Control = CMD;
+ ReutChPatCadbProg.Bits.CADB_Data_CS = 0x0F & ~(CadbLines[i].ChipSelect << Rank);
+
+ //
+ // Write CADB line. It is auto incremented after every write
+ //
+ MrcWriteCR64 (MrcData, Offset, ReutChPatCadbProg.Data);
+ }
+}
+
+/**
+ Program DESWIZZLE_HIGH/LOW registers for MR4 decoding
+
+ @param[in] MrcData - The MRC global data.
+
+ @retval none
+**/
+void
+ProgramDeswizzleRegisters (
+ IN MrcParameters * const MrcData
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ MrcControllerOut *ControllerOut;
+ U8 Channel;
+ U32 Byte;
+ U8 Bit;
+ U32 Offset;
+ MCHBAR_CH0_CR_DESWIZZLE_LOW_STRUCT DeswizzleLow;
+ MCHBAR_CH0_CR_DESWIZZLE_HIGH_STRUCT DeswizzleHigh;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ ChannelIn = &Inputs->Controller[0].Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ DeswizzleLow.Data = 0;
+ DeswizzleHigh.Data = 0;
+
+ for (Byte = 0; Byte <= 7; Byte++) {
+ //
+ // DqsMapCpu2Dram maps CPU bytes to DRAM, we need to find the reverse mapping here
+ //
+ switch (ChannelIn->DqsMapCpu2Dram[Byte]) {
+ case 0:
+ DeswizzleLow.Bits.Byte_0 = Byte;
+ break;
+ case 2:
+ DeswizzleLow.Bits.Byte_2 = Byte;
+ break;
+ case 4:
+ DeswizzleHigh.Bits.Byte_4 = Byte;
+ break;
+ case 6:
+ DeswizzleHigh.Bits.Byte_6 = Byte;
+ break;
+ }
+ }
+
+ for (Bit = 0; Bit <= 7; Bit++) {
+ //
+ // DqMapCpu2Dram maps CPU DQ pins to DRAM, we need to find the reverse mapping here
+ //
+ Byte = DeswizzleLow.Bits.Byte_0;
+ switch (ChannelIn->DqMapCpu2Dram[Byte][Bit]) {
+ case 0:
+ DeswizzleLow.Bits.Bit_0 = Bit;
+ break;
+ case 1:
+ DeswizzleLow.Bits.Bit_1 = Bit;
+ break;
+ case 2:
+ DeswizzleLow.Bits.Bit_2 = Bit;
+ break;
+ }
+
+ Byte = DeswizzleLow.Bits.Byte_2;
+ switch (ChannelIn->DqMapCpu2Dram[Byte][Bit]) {
+ case 16:
+ DeswizzleLow.Bits.Bit_16 = Bit;
+ break;
+ case 17:
+ DeswizzleLow.Bits.Bit_17 = Bit;
+ break;
+ case 18:
+ DeswizzleLow.Bits.Bit_18 = Bit;
+ break;
+ }
+
+ Byte = DeswizzleHigh.Bits.Byte_4;
+ switch (ChannelIn->DqMapCpu2Dram[Byte][Bit]) {
+ case 32:
+ DeswizzleHigh.Bits.Bit_32 = Bit;
+ break;
+ case 33:
+ DeswizzleHigh.Bits.Bit_33 = Bit;
+ break;
+ case 34:
+ DeswizzleHigh.Bits.Bit_34 = Bit;
+ break;
+ }
+
+ Byte = DeswizzleHigh.Bits.Byte_6;
+ switch (ChannelIn->DqMapCpu2Dram[Byte][Bit]) {
+ case 48:
+ DeswizzleHigh.Bits.Bit_48 = Bit;
+ break;
+ case 49:
+ DeswizzleHigh.Bits.Bit_49 = Bit;
+ break;
+ case 50:
+ DeswizzleHigh.Bits.Bit_50 = Bit;
+ break;
+ }
+ } // for Bit
+
+ if (ChannelOut->Dimm[dDIMM0].SdramWidth == 32) {
+ //
+ // Bytes 2 and 6 (and their bits) are irrelevant for x32 devices - copy from Bytes 0 and 4 instead
+ //
+ DeswizzleLow.Bits.Byte_2 = DeswizzleLow.Bits.Byte_0;
+ DeswizzleLow.Bits.Bit_16 = DeswizzleLow.Bits.Bit_0;
+ DeswizzleLow.Bits.Bit_17 = DeswizzleLow.Bits.Bit_1;
+ DeswizzleLow.Bits.Bit_18 = DeswizzleLow.Bits.Bit_2;
+
+ DeswizzleHigh.Bits.Byte_6 = DeswizzleHigh.Bits.Byte_4;
+ DeswizzleHigh.Bits.Bit_48 = DeswizzleHigh.Bits.Bit_32;
+ DeswizzleHigh.Bits.Bit_49 = DeswizzleHigh.Bits.Bit_33;
+ DeswizzleHigh.Bits.Bit_50 = DeswizzleHigh.Bits.Bit_34;
+ }
+
+ Offset = MCHBAR_CH0_CR_DESWIZZLE_LOW_REG +
+ (MCHBAR_CH1_CR_DESWIZZLE_LOW_REG - MCHBAR_CH0_CR_DESWIZZLE_LOW_REG) * Channel;
+ MrcWriteCR (MrcData, Offset, DeswizzleLow.Data);
+
+ Offset = MCHBAR_CH0_CR_DESWIZZLE_HIGH_REG +
+ (MCHBAR_CH1_CR_DESWIZZLE_HIGH_REG - MCHBAR_CH0_CR_DESWIZZLE_HIGH_REG) * Channel;
+ MrcWriteCR (MrcData, Offset, DeswizzleHigh.Data);
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Ch%d DESWIZZLE_HIGH=%08X, DESWIZZLE_LOW=%08X\n",
+ Channel,
+ DeswizzleHigh.Data,
+ DeswizzleLow.Data
+ );
+ } // for Channel
+}
+
+/**
+ Sweep both CS and CMD PI and print the feedback.
+
+ @param[in] MrcData - The MRC global data.
+
+ @retval none
+**/
+MrcStatus
+Ca2DMargins (
+ IN MrcParameters * const MrcData,
+ U8 Rank
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcStatus Status;
+ U8 Channel;
+ U8 ChannelMask;
+ U8 Byte;
+ U8 CaStart = 32;
+ U8 CaStop = 127;
+ S8 CaStep = 6;
+ U8 CsStart = 0;
+ U8 CsStop = 127;
+ S8 CsStep = 8;
+ U8 CaPiCode;
+ U8 CsPiCode;
+ U32 DelayCadb;
+ U32 Offset;
+ char PassFail;
+ DDRDATA0CH0_CR_DATATRAINFEEDBACK_STRUCT DataTrainFeedback;
+
+ Status = mrcSuccess;
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+
+ DelayCadb = 1 * HPET_1US;
+
+ ChannelMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ChannelMask |= (1 << Channel);
+ }
+ }
+
+ for (CsPiCode = CsStart; CsPiCode < CsStop; CsPiCode += CsStep) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ //
+ // Shift the CS PI on Rank.
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCtl, 1 << Rank, 1, CsPiCode, 0);
+ }
+
+ for (CaPiCode = CaStart; CaPiCode < CaStop; CaPiCode += CaStep) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t%d\t", CsPiCode, CaPiCode);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ //
+ // Shift the Command PI on both CAA and CAB groups
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdN, 1 << Rank, 3, CaPiCode, 0);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, 1 << Rank, 3, CaPiCode, 0);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCke, 1 << Rank, 3, CaPiCode, 0);
+ }
+
+ //
+ // Perform Jedec Reset ONLY
+ //
+ MrcJedecResetLpddr3 (MrcData);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ //
+ // Put the current Rank in CA training mode using MRW41.
+ //
+ Status = LpddrCommandTrainingMode (MrcData, 1 << Channel, 1 << Rank, CaTrainingMode41);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ }
+
+ //
+ // Run CADB pattern on selected channels at the same time
+ //
+ ShortRunCADB (MrcData, ChannelMask);
+ MrcWait (MrcData, DelayCadb);
+
+ //
+ // Read and process the results
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Byte Feedback\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ 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) & 0xFF); // Get only DQ bits, not DQS
+
+ //
+ // If we don't see 4 ones in the byte, then the command was not aligned properly
+ //
+ if (MrcCountBitsEqOne (DataTrainFeedback.Data) != 4) {
+ PassFail = '#';
+ } else {
+ PassFail = '.';
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%c%02X\t", PassFail, DataTrainFeedback.Data);
+ } // for Byte
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ } // for Channel
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+ } // while not done
+
+ return Status;
+}
+
+/**
+ Sweep CMD PI up or down and find edges for all bytes.
+ Main flow:
+ 1.
+ 2.
+ 3.
+
+ @param[in] MrcData - The MRC global data.
+
+ @retval none
+**/
+void
+EarlyCaFindEdge (
+ IN MrcParameters * const MrcData,
+ U8 Rank,
+ U8 Start,
+ U8 Stop,
+ S8 Step,
+ U8 Limit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM]
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerIn *ControllerIn;
+ MrcControllerOut *ControllerOut;
+ MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ U8 Channel;
+ U8 ChannelMask;
+ U8 Byte;
+ U8 ByteMask;
+ U8 DramByte;
+ U8 ByteDoneMask[MAX_CHANNEL];
+ U8 PiCode;
+ U32 DelayCadb;
+ U32 Offset;
+ BOOL Done;
+ char *BytesHeader;
+ char PassFail;
+ DDRDATA0CH0_CR_DATATRAINFEEDBACK_STRUCT DataTrainFeedback;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerIn = &Inputs->Controller[0];
+ ControllerOut = &Outputs->Controller[0];
+
+ DelayCadb = 1 * HPET_1US;
+
+ MrcOemMemorySet (ByteDoneMask, 0, sizeof (ByteDoneMask));
+
+ PiCode = Start;
+ Done = FALSE;
+
+ ChannelMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ChannelMask |= (1 << Channel);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t Ch0\t\t\t\t Ch1\n");
+ BytesHeader = "0 1 2 3 4 5 6 7 ";
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CMD PI\t %s%s\n", BytesHeader, BytesHeader);
+
+ while (!Done) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d: \t", PiCode);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ //
+ // Shift the Command PI on both CAA and CAB groups
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdN, 1 << Rank, 3, PiCode, 0);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, 1 << Rank, 3, PiCode, 0);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCke, 1 << Rank, 3, PiCode, 0);
+ }
+
+ //
+ // Run CADB pattern on selected channels at the same time
+ //
+ ShortRunCADB (MrcData, ChannelMask);
+ MrcWait (MrcData, DelayCadb);
+
+ //
+ // Read and process the results
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ByteDoneMask[Channel] = 0xFF;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ continue;
+ }
+ if (ByteDoneMask[Channel] == 0xFF) { // All bytes failed on this channel, no need to sweep anymore
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ continue;
+ }
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ ByteMask = (1 << Byte);
+ DramByte = ChannelIn->DqsMapCpu2Dram[Byte];
+ if (ChannelOut->Dimm[dDIMM0].SdramWidth == 32) {
+ if ((DramByte & 0x02) != 0) {
+ //
+ // Ignore upper 16 bits on x32 devices in MRW41 feedback - DRAM bytes 2, 3, 6 and 7
+ //
+ ByteDoneMask[Channel] |= ByteMask;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ continue;
+ }
+ }
+ 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) & 0xFF); // Get only DQ bits, not DQS
+
+ PassFail = '#';
+ if ((ByteDoneMask[Channel] & ByteMask) == 0) {
+ //
+ // If we don't see 4 ones in the byte, then the command was not aligned properly
+ //
+ if (MrcCountBitsEqOne (DataTrainFeedback.Data) != 4) {
+ Limit[Channel][Rank][Byte] = PiCode;
+ ByteDoneMask[Channel] |= ByteMask;
+ } else {
+ PassFail = '.';
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%c%02X ", PassFail, DataTrainFeedback.Data);
+ } // for Byte
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ } // for Channel
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ if ((ByteDoneMask[0] == 0xFF) && (ByteDoneMask[1] == 0xFF)) {
+ // Found the limit on all bytes on both channels - no need to sweep Pi any longer
+ break;
+ }
+
+ PiCode += Step;
+ if (Step > 0) {
+ // Sweep up
+ Done = (PiCode > Stop);
+ } else {
+ // Sweep down
+ Done = (((S8) PiCode) < Stop);
+ }
+ } // while not done
+}
+
+/**
+ Process the results of the early LPDDR3 CMD training and find the best PI settings for CmdS/CmdN/Cke.
+ Flow:
+ 1. Find the worst case Right and Left limits for each channel
+ 2. Find the Center for each channel
+
+ @param[in] MrcData - The MRC global data.
+ @param[in] LeftLimit - array of left edge values per channel, rank and CPU byte
+ @param[in] RightLimit - array of right edge values per channel, rank and CPU byte
+ @param[out] BestCs - array of best CMD PI settings, per channel
+
+ @retval mrcSuccess if succeeded
+**/
+MrcStatus
+FindBestCmdPi (
+ IN MrcParameters * const MrcData,
+ IN U8 LeftLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM],
+ IN U8 RightLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM],
+ OUT U8 BestCmd[MAX_CHANNEL][2] // per Channel and per group (CAA and CAB)
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcStatus Status;
+ MrcChannelIn *ChannelIn;
+ MrcControllerIn *ControllerIn;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 CaGroup;
+ U8 CmdLeftLimit[MAX_CHANNEL][2]; // Per ch and group
+ U8 CmdRightLimit[MAX_CHANNEL][2]; // Per ch and group
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerIn = &Inputs->Controller[0];
+
+ Status = mrcSuccess;
+
+ MrcOemMemorySet ((U8 *) CmdRightLimit, 127, sizeof (CmdRightLimit));
+ MrcOemMemorySet ((U8 *) CmdLeftLimit, 0, sizeof (CmdLeftLimit));
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Finding best CMD PIs:\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ ChannelIn = &ControllerIn->Channel[Channel];
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel: %d\t\tLeft\tRight\tCenter\n", Channel);
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ //
+ // Find the worst case Right and Left limits for all ranks, for bytes from the particular CA group
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // CmdS CAA goes to Bytes[3:0], CmdS CAB goes to Byte[7:4]
+ //
+ if ((1 << Byte) & ChannelIn->DQByteMap[MrcIterationCmdS][0]) {
+ CaGroup = 0;
+ } else {
+ CaGroup = 1;
+ }
+ CmdRightLimit[Channel][CaGroup] = MIN (CmdRightLimit[Channel][CaGroup], RightLimit[Channel][Rank][Byte]);
+ CmdLeftLimit[Channel][CaGroup] = MAX (CmdLeftLimit[Channel][CaGroup], LeftLimit[Channel][Rank][Byte]);
+ }
+ } // for Rank
+
+ //
+ // Find the Center for each group, worst case of all ranks
+ //
+ BestCmd[Channel][0] = (CmdRightLimit[Channel][0] + CmdLeftLimit[Channel][0]) / 2;
+ BestCmd[Channel][1] = (CmdRightLimit[Channel][1] + CmdLeftLimit[Channel][1]) / 2;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "CA%c\t\t\t%d\t%d\t%d\n",
+ 'A',
+ CmdLeftLimit[Channel][0],
+ CmdRightLimit[Channel][0],
+ BestCmd[Channel][0]
+ );
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "CA%c\t\t\t%d\t%d\t%d\n",
+ 'B',
+ CmdLeftLimit[Channel][1],
+ CmdRightLimit[Channel][1],
+ BestCmd[Channel][1]
+ );
+ } // for Channel
+
+ return Status;
+}
+
+/**
+ Update DqMapCpu2Dram array
+
+ @param[in] MrcData - The MRC global data.
+ @param[in] Channel - the channel to work on
+ @param[in] Feedback - array of DATATRAINFEEDBACK values for all 8 bytes
+ @param[in] Bit - The DQ bit the should be set in each DRAM byte
+
+ @retval none
+**/
+void
+FillCA2DQMapResult (
+ IN OUT MrcParameters * const MrcData,
+ IN const U8 Channel,
+ IN const U8 Feedback[8],
+ IN const U8 Bit
+ )
+{
+ MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ MrcControllerOut *ControllerOut;
+ MrcControllerIn *ControllerIn;
+ MrcChannelIn *ChannelIn;
+ U8 Byte;
+ U8 Temp;
+ U8 CpuBit;
+ S8 BitNumber;
+ BOOL BitFound;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ ControllerIn = &Inputs->Controller[0];
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ ChannelIn = &ControllerIn->Channel[Channel];
+
+ BitNumber = -1;
+
+ //
+ // Loop on CPU bytes
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if (Feedback[Byte] == 0) {
+ continue;
+ }
+ Temp = Feedback[Byte];
+ BitNumber = 0;
+ CpuBit = 0;
+ BitFound = FALSE;
+ while (Temp > 0) {
+ if (Temp & 1) {
+ if (!BitFound) {
+ CpuBit = BitNumber;
+ BitFound = TRUE;
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Ch%d: ERROR: More than one DQ pin toggled while looking for DQ%d in Byte%d, Feedback=0x%X\n",
+ Channel,
+ Bit,
+ Byte,
+ Feedback[Byte]
+ );
+ break;
+ }
+ }
+ Temp >>= 1;
+ BitNumber++;
+ }
+ ChannelIn->DqMapCpu2Dram[Byte][CpuBit] = ChannelIn->DqsMapCpu2Dram[Byte] * 8 + Bit;
+ } // for Byte
+}
+
+/**
+ Rotate a given number left by a specified number of bits.
+
+ @param[in] Value - The input value
+ @param[in] BitLength - How many bits to rotate in the input value.
+ @param[in] RotateBy - Number of bits to rotate by.
+
+ @retval The rotated number
+**/
+U32
+RotateLeft (
+ IN const U32 Value,
+ IN const U8 BitLength, // should be >1 and <32, tested for 16
+ IN const U8 RotateBy
+ )
+{
+ U32 Mask;
+ U32 Lsb;
+ U32 Result;
+ U8 i;
+
+ Result = Value;
+ Mask = (1 << BitLength) - 1;
+
+ for (i = 0; i < RotateBy; i++) {
+ Lsb = 1 & (((Result) & (1 << (BitLength - 1))) >> (BitLength-1)); // The MSB value needs to move to LSB
+ Result = (Mask & (Result << 1)) | Lsb; // Shift Left once and add the new LSB
+ }
+
+ return Result;
+}
+
+/**
+ Calculate 3 Pattern Buffers values for the given CADB sequence.
+
+ @param[in] MrcData - The MRC global data.
+ @param[in] CadbSequence - CADB line numbers in the order of transmission.
+ Example: 0,1,0,0,...0 for DQ mapping, 0,0,2,1,1,0,0,...0 for CS training
+ @param[out] CadbPatternBuffers - Array of 3 Pattern Buffer values
+
+ @retval none
+**/
+void
+CalculateCadbPB (
+ IN MrcParameters * const MrcData,
+ IN const U8 CadbSequence[MRC_CADB_PB_LENGTH],
+ OUT U32 CadbPatternBuffers[3]
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ U8 i;
+ U8 j;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+
+ MrcOemMemorySet ((U8 *) CadbPatternBuffers, 0, 3 * sizeof (CadbPatternBuffers[0]));
+
+ for (i = 0; i < MRC_CADB_PB_LENGTH; i++) {
+ for (j = 0; j < 3; j++) {
+ CadbPatternBuffers[j] = RotateLeft (CadbPatternBuffers[j], MRC_CADB_PB_LENGTH, 1) |
+ ((CadbSequence[i] & (1 << j)) >> j);
+ }
+ }
+
+ for (j = 0; j < 3; j++) {
+ CadbPatternBuffers[j] = RotateLeft (CadbPatternBuffers[j], MRC_CADB_PB_LENGTH, 1);
+ }
+}
+
+/**
+ Map CA to DQ Pins for CA training and MR4 bit swizzling settings for LPDDR.
+ Main flow:
+ Repeat for each of the 8 bits per DQ byte (total 8 iterations for both channels, for rank0 only):
+ Transmit single CA phase expected to appear on a known DQ pin
+ One CA phase per byte, 2 different CA phases for Even and Odd bytes in parallel
+ Locate the single DQ in each byte based on DATATRAINFEEDBACK
+ Report error if more than one DQ pin toggles
+ Report error if no active DQ pin found
+ Ignore Byte2 and Byte3 for x32 devices if they don't return feedback (only DQ[15:0] must return feedback per JEDEC)
+ Update the DQ mapping data structure.
+
+ Assumption: runs on stable and correct CLK, CS and CA PI settings (either guaranteed by design or pre-trained)
+
+ @param[in] MrcData - The MRC global data.
+
+ @retval mrcSuccess if succeeded
+**/
+MrcStatus
+MapCA2DQPins (
+ IN MrcParameters * const MrcData
+)
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerIn *ControllerIn;
+ MrcControllerOut *ControllerOut;
+ MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 DramByte;
+ U8 Bit;
+ DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2;
+ U32 Offset;
+ U32 CaPattern;
+ U8 Feedback[8];
+ CADB_LINE CadbLinesDqMapping[] = {
+ { 0x000, 0x000, 0 },
+ { 0x001, 0x000, 1 }
+ };
+ U32 CadbPatternBuffers[3];
+ U8 CadbSequence[MRC_CADB_PB_LENGTH];
+#ifdef MRC_DEBUG_PRINT
+ U8 DramBit;
+ U8 i;
+#endif
+
+ Status = mrcSuccess;
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerIn = &Inputs->Controller[0];
+ ControllerOut = &Outputs->Controller[0];
+ Rank = 0;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "MapCA2DQPins started\n");
+ if (ControllerOut->Channel[0].Dimm[dDIMM0].SdramWidth == 32) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "x32 DRAM devices - skipping Bytes 2,3 in each DRAM!\n");
+ }
+
+ MrcOemMemorySet (CadbSequence, 0, sizeof (CadbSequence));
+ CadbSequence[1] = 1; // The 2nd PB entry is the 2nd CADB line with active CS. The rest are 0.
+
+ //
+ // Calculate the Pattern Buffers values for the given CADB sequence
+ //
+ CalculateCadbPB (MrcData, CadbSequence, CadbPatternBuffers);
+
+ //
+ // Enable the following bits because we will use DATATRAINFEEDBACK to read back CA values on DQ pins:
+ // DataControl0.SenseampTrainingMode and ForceOdtOn
+ // DataControl2.ForceRxOn and ForceBiasOn
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, 0, Channel)) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+ MrcOemMemorySet (
+ (U8 *) (ControllerIn->Channel[Channel].DqMapCpu2Dram),
+ 0xFF,
+ sizeof (ControllerIn->Channel[0].DqMapCpu2Dram)
+ );
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DdrCrDataControl2.Data = ChannelOut->DqControl2[Byte].Data;
+ DdrCrDataControl2.Bits.ForceBiasOn = 1;
+ DdrCrDataControl2.Bits.ForceRxOn = 1;
+ 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);
+ }
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ DdrCrDataControl0.Bits.ForceOdtOn = 1;
+ DdrCrDataControl0.Bits.SenseampTrainingMode = 1;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+
+ //
+ // Put Rank 0 in CA training mode using MRW41.
+ //
+ Status = LpddrCommandTrainingMode (MrcData, 1 << Channel, 1 << Rank, CaTrainingMode41);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ }
+
+ for (Bit = 0; Bit < MAX_BITS; Bit++) {
+ //
+ // Create CA patterns for high and low phases,
+ // such that only one DQ bit should toggle on each phase, per DRAM byte
+ //
+ CaPattern = (1 << (Bit / 2)) | (1 << (Bit / 2 + 5));
+ CadbLinesDqMapping[1].CaHigh = ((Bit % 2) == 0) ? CaPattern : 0;
+ CadbLinesDqMapping[1].CaLow = ((Bit % 2) == 1) ? CaPattern : 0;
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRunning Bit %d\n", Bit);
+ for (i = 0; i < sizeof (CadbLinesDqMapping) / sizeof (CadbLinesDqMapping[0]); i++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tCADB[%d] CaHigh=0x%03X\tCaLow=0x%03X\tCS=0x%03X\n",
+ i,
+ CadbLinesDqMapping[i].CaHigh,
+ CadbLinesDqMapping[i].CaLow,
+ CadbLinesDqMapping[i].ChipSelect
+ );
+ }
+#endif //MRC_DEBUG_PRINT
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, 0, Channel)) {
+ continue;
+ }
+
+ SetupCaTrainingCadb (
+ MrcData,
+ Channel,
+ Rank,
+ CadbLinesDqMapping,
+ sizeof (CadbLinesDqMapping) / sizeof (CadbLinesDqMapping[0]),
+ CadbPatternBuffers[0],
+ CadbPatternBuffers[1],
+ CadbPatternBuffers[2]
+ );
+ }
+
+ //
+ // Run CADB pattern on both channels at the same time
+ //
+ ShortRunCADB (MrcData, 0x3);
+
+ //
+ // Get Results for all ch/bytes
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, 0, Channel)) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+ ChannelIn = &ControllerIn->Channel[Channel];
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DramByte = ChannelIn->DqsMapCpu2Dram[Byte];
+ if (ChannelOut->Dimm[dDIMM0].SdramWidth == 32) {
+ if ((DramByte & 0x02) != 0) {
+ //
+ // Ignore upper 16 bits (in DRAM terms) on x32 devices in MRW41 feedback - DRAM bytes 2, 3, 6 and 7
+ //
+ Feedback[Byte] = 0;
+ continue;
+ }
+ }
+ Offset = DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG +
+ ((DDRDATA0CH1_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * Byte);
+ Feedback[Byte] = (U8) (MrcReadCR (MrcData, Offset) & 0xFF);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\t Channel %d CPU Byte %d DRAM Byte %d => Feedback = %02X - %s feedback\n",
+ Channel,
+ Byte,
+ DramByte,
+ Feedback[Byte],
+ (MrcCountBitsEqOne (Feedback[Byte]) == 1) ? "Good" : "Bad"
+ );
+ } // for Byte
+ //
+ // Store results in ChannelIn->DqMapCpu2Dram
+ //
+ FillCA2DQMapResult (MrcData, Channel, Feedback, Bit);
+ } // for Channel
+ } // for Bit
+
+ //
+ // Exit CA training mode on rank 0 on both channels
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, 0, Channel)) {
+ continue;
+ }
+ Status = LpddrCommandTrainingMode (MrcData, 1 << Channel, 1 << Rank, CaTrainingMode42);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, 0, Channel)) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+ ChannelIn = &ControllerIn->Channel[Channel];
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\nMapCA2DQPins Results for Ch%d (\"-1\" for skipped Bytes, DRAM DQ pins offsets):\n%s",
+ Channel,
+ "CPU Bit: \t[0]\t[1]\t[2]\t[3]\t[4]\t[5]\t[6]\t[7]\n"
+ );
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CPU Byte%d:", Byte);
+ for (Bit = 0; Bit < MAX_BITS; Bit++) {
+ DramBit = ChannelIn->DqMapCpu2Dram[Byte][Bit];
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t%d", (DramBit == 255) ? -1: DramBit);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+ }
+#endif //MRC_DEBUG_PRINT
+
+ return Status;
+}
+
+/**
+ Sweep the given PI up or down and find the edge.
+
+ @param[in] MrcData - The MRC global data.
+ @param[in] Iteration - Determines which PI to shift
+ @param[in] ChannelMask - Valid Channel bit mask
+ @param[in] RankMask - Valid Rank bit mask
+ @param[in] Stop - End of the PI range
+ @param[in] Step - PI step for the sweep
+ @param[out] Limit - array of edge values (per channel), filled by this function
+ @param[in] DebugPrint - Print debug messages or not
+
+ @retval none
+**/
+void
+CaFindEdge (
+ IN MrcParameters * const MrcData,
+ IN U8 Iteration,
+ IN U8 ChannelMask,
+ IN U8 RankMask,
+ IN S16 Stop,
+ IN S16 Step,
+ OUT U8 Limit[MAX_CHANNEL],
+ IN BOOL DebugPrint
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ U8 Channel;
+ U8 ChannelBit;
+ U8 ChError;
+ U8 DumArr[7];
+ S16 PiOffset;
+ BOOL Pass;
+ BOOL Done;
+ BOOL ChannelDone[MAX_CHANNEL];
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+
+ PiOffset = 0;
+ Done = FALSE;
+ ChannelDone[0] = ChannelDone[1] = FALSE;
+ MrcOemMemorySet (DumArr, 1, sizeof (DumArr));
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (DebugPrint) ? "\t0 1\n" : "");
+
+ while (!Done) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (DebugPrint) ? "%d:\t" : "", PiOffset);
+ //
+ // Update Timing
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((1 << Channel) & ChannelMask) {
+ if (!ChannelDone[Channel]) {
+ ShiftPIforCmdTraining (MrcData, Channel, Iteration, RankMask, 3, PiOffset, 0);
+ }
+ }
+ }
+ //
+ // Reset DDR after changing the CLK PI
+ //
+ MrcResetSequence (MrcData);
+
+ //
+ // Run CPGC test on both channels
+ //
+ ChError = RunIOTest (MrcData, ChannelMask, Outputs->DQPat, DumArr, 1, 0);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelBit = (1 << Channel);
+ if (((ChannelBit & ChannelMask) == 0) || (ChannelDone[Channel])) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (DebugPrint && (Channel == 0)) ? " " : "");
+ continue;
+ }
+
+ Pass = !(ChError & ChannelBit);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (DebugPrint) ? (Pass ? ". " : "# ") : "");
+
+ if (Pass) {
+ Limit[Channel] = (U8) (ABS (PiOffset));
+ } else {
+ ChannelDone[Channel] = TRUE;
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (DebugPrint) ? "\n" : "");
+
+ PiOffset += Step;
+ if (Step > 0) {
+ // Sweep up
+ Done = (PiOffset > Stop);
+ } else {
+ // Sweep down
+ Done = (PiOffset < Stop);
+ }
+
+ if (ChannelDone[0] && ChannelDone[1]) {
+ // Found the limit on both channels - no need to sweep PI any longer
+ Done = TRUE;
+ }
+ } // while not done
+}
+
+/**
+@brief
+ Sweep right and left from the current point to find the margins.
+
+ @param[in, out] MrcData - Include all MRC global data.
+ @param[in] Iteration - Determines which PI to shift
+ @param[in] ChannelMask - Valid Channel bit mask
+ @param[in] RankMask - Valid Rank bit mask
+ @param[in] DebugPrint - Print debug messages or not
+
+ @retval MrcStatus - If it succeeds return mrcSuccess
+**/
+void
+CmdLinearFindEdgesLpddr (
+ IN OUT MrcParameters *const MrcData,
+ IN U8 Iteration,
+ IN U8 ChannelMask,
+ IN U8 RankMask,
+ IN BOOL DebugPrint
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ U8 Channel;
+ U8 Rank;
+ S16 PiLow;
+ S16 PiHigh;
+ S16 PiStep;
+ U8 RightLimit[MAX_CHANNEL];
+ U8 LeftLimit[MAX_CHANNEL];
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+
+ //
+ // We are going to sweep clock 32 PI ticks to the left and to the right
+ //
+ PiLow = -32;
+ PiHigh = 32;
+
+ PiStep = 1;
+
+ //
+ // Initialize to zero margin
+ //
+ MrcOemMemorySet ((U8 *) RightLimit, 0, sizeof (RightLimit));
+ MrcOemMemorySet ((U8 *) LeftLimit, 0, sizeof (LeftLimit));
+
+ //
+ // Find right and left margins
+ //
+ CaFindEdge (MrcData, Iteration, ChannelMask, RankMask, PiHigh, PiStep, RightLimit, DebugPrint);
+ CaFindEdge (MrcData, Iteration, ChannelMask, RankMask, PiLow, -PiStep, LeftLimit, DebugPrint);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((1 << Channel) & ChannelMask) {
+ //
+ // Save margins for RMT
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((1 << Rank) & RankMask) {
+ Outputs->MarginResult[LastCmdT][Rank][Channel][0][0] = 10 * LeftLimit[Channel];
+ Outputs->MarginResult[LastCmdT][Rank][Channel][0][1] = 10 * RightLimit[Channel];
+ }
+ }
+ }
+ }
+}
+
+/**
+ Early CMD / CLK training for LPDDR.
+
+ @param[in] MrcData - The MRC global data.
+
+ @retval mrcSuccess if succeeded
+**/
+MrcStatus
+EarlyCaTraining (
+ IN MrcParameters * const MrcData
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerIn *ControllerIn;
+ MrcControllerOut *ControllerOut;
+ MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U8 Channel;
+ U8 Rank;
+ U8 RankBit;
+ U8 Byte;
+ U8 DramByte;
+ U8 RankMask;
+ U32 Offset;
+ U8 PiLow;
+ U8 PiHigh;
+ U8 PiMiddle;
+ U8 PiStep;
+ U8 RightLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U8 LeftLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U8 BestCmd[MAX_CHANNEL][2]; // per Channel and per group (CAA and CAB)
+ DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerIn = &Inputs->Controller[0];
+ ControllerOut = &Outputs->Controller[0];
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "EarlyCaTraining started\n");
+
+ Status = mrcSuccess;
+ RankMask = Outputs->ValidRankMask;
+
+ PiLow = 0;
+ PiHigh = 127;
+ PiMiddle = 96;
+ PiStep = 2;
+
+ MrcOemMemorySet ((U8 *) RightLimit, PiHigh, sizeof (RightLimit));
+ MrcOemMemorySet ((U8 *) LeftLimit, PiLow, sizeof (LeftLimit));
+
+ //
+ // Enable the following bits because we will use DATATRAINFEEDBACK to read back CA values on DQ pins:
+ // DataControl0.SenseampTrainingMode and ForceOdtOn
+ // DataControl2.ForceRxOn and ForceBiasOn
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DdrCrDataControl2.Data = ChannelOut->DqControl2[Byte].Data;
+ DdrCrDataControl2.Bits.ForceBiasOn = 1;
+ DdrCrDataControl2.Bits.ForceRxOn = 1;
+ 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);
+ }
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ DdrCrDataControl0.Bits.ForceOdtOn = 1;
+ DdrCrDataControl0.Bits.SenseampTrainingMode = 1;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ }
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { // @todo Do we have to do this per rank, or rank 0 is enough ?
+ RankBit = 1 << Rank;
+ if ((RankBit & RankMask) == 0) {
+ continue;
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+
+ //
+ // Put the current Rank in CA training mode using MRW41.
+ //
+ Status = LpddrCommandTrainingMode (MrcData, 1 << Channel, RankBit, CaTrainingMode41);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+
+ SetupCaTrainingCadb (
+ MrcData,
+ Channel,
+ Rank,
+ CadbLinesCs,
+ sizeof (CadbLinesCs) / sizeof (CadbLinesCs[0]),
+ 0x3000,
+ 0x4000,
+ 0x0000
+ );
+ } // for Channel
+
+ //
+ // Sweep CMD PI up and down from the middle, on both channels at the same time
+ //
+// Ca2DMargins (MrcData, Rank); // This is used for test
+ EarlyCaFindEdge (MrcData, Rank, PiMiddle, PiHigh, PiStep, RightLimit);
+ EarlyCaFindEdge (MrcData, Rank, PiMiddle, PiLow, -PiStep, LeftLimit);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CA training data Ch%d Rank%d\nCPU Byte\tLeft\tRight\n", Channel, Rank);
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DramByte = ChannelIn->DqsMapCpu2Dram[Byte];
+ if (ChannelOut->Dimm[dDIMM0].SdramWidth == 32) {
+ if ((DramByte & 0x02) != 0) {
+ //
+ // Ignore upper 16 bits on x32 devices in MRW41 feedback - DRAM bytes 2, 3, 6 and 7
+ //
+ continue;
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t\t%d\t%d\n",
+ Byte, LeftLimit[Channel][Rank][Byte], RightLimit[Channel][Rank][Byte]);
+ }
+
+ //
+ // Put the CMD PI back to middle for MRW42 command
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, RankBit, 3, PiMiddle, 1);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCke, RankBit, (1 << 0), PiMiddle, 1);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdN, RankBit, (1 << 1), PiMiddle, 1);
+
+ //
+ // Exit CA training mode on the current rank
+ //
+ Status = LpddrCommandTrainingMode (MrcData, 1 << Channel, RankBit, CaTrainingMode42);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ } // for Channel
+ } // for Rank
+
+ //
+ // Restore original DataControl0 and DataControl2 values
+ //
+ 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);
+ 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);
+ }
+ }
+
+ //
+ // Select optimal CMD timings for both channels
+ //
+ FindBestCmdPi (MrcData, LeftLimit, RightLimit, BestCmd);
+
+ //
+ // Apply the new CmdN, CmdS and CKE command PI settings
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // CAA is controlled by CMDS.CmdPi0Code and CKE.CmdPi0Code
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, RankMask, (1 << 0), BestCmd[Channel][0], 1);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCke, RankMask, (1 << 0), BestCmd[Channel][0], 1);
+
+ //
+ // CAB is controlled by CMDS.CmdPi1Code and CMDN.CmdPi1Code
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, RankMask, (1 << 1), BestCmd[Channel][1], 1);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdN, RankMask, (1 << 1), BestCmd[Channel][1], 1);
+ } // for Channel
+
+ return Status;
+}
+
+/**
+ Early CA / CS training for LPDDR.
+ Main flow:
+ 1. Put DRAMs in CA training mode using MRW41.
+ 2. Run CS vs. CLK training.
+ 3. Map DQ pins according to the board swizzling.
+ 4. Run CA vs. CLK training.
+ 5. Select optimal CA timings for each CA bus per rank
+
+ @param[in] MrcData - The MRC global data.
+
+ @retval mrcSuccess if succeeded
+**/
+MrcStatus
+EarlyCommandTrainingLpddr (
+ IN MrcParameters * const MrcData
+)
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcStatus Status;
+ U8 ChannelBitMask;
+ U8 ValidRankMask;
+// DDRCMD_CR_DDRCRCMDPICODING_STRUCT DdrCrCmdPiCoding;
+
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+
+ ValidRankMask = Outputs->ValidRankMask;
+ ChannelBitMask = Outputs->ValidChBitMask;
+ Status = mrcSuccess;
+
+// //
+// // Set initial Pi settings for CLK / CMD / CTL - done in MrcMcConfiguration
+// //
+// DdrCrCmdPiCoding.Data = 0;
+// DdrCrCmdPiCoding.Bits.CmdPi0Code = 96;
+// DdrCrCmdPiCoding.Bits.CmdPi1Code = 96;
+// MrcWriteCR (MrcData, DDRCMD_CR_DDRCRCMDPICODING_REG, DdrCrCmdPiCoding.Data);
+
+
+ //
+ // Run CPU-to-DRAM DQ Mapping - Run after 2D CS/CA and/or before CS training
+ //
+ Status = MapCA2DQPins (MrcData);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+
+ //
+ // Run CS vs. CLK training
+ //
+ Status = EarlyChipSelectTraining (MrcData);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+
+ //
+ // Run CA vs. CLK training
+ //
+ Status = EarlyCaTraining (MrcData);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+
+ //
+ // Program DESWIZZLE_HIGH/LOW registers
+ //
+ ProgramDeswizzleRegisters (MrcData);
+
+ //
+ // Set this flag so that MrcResetSequence() will include MrcJedecInitLpddr3() as well.
+ //
+ Outputs->LpddrEctDone = TRUE;
+
+ return Status;
+}
+
+/**
+ Sweep CS Pi up or down and find edges for all bytes.
+ Main flow:
+ 1.
+ 2.
+ 3.
+
+ @param[in] MrcData - The MRC global data.
+ @param[out] Limit - array of edge PI values per channel, rank and CPU byte
+
+ @retval none
+**/
+void
+ChipSelectFindEdge (
+ IN MrcParameters * const MrcData,
+ U8 Rank,
+ U8 Start,
+ U8 Stop,
+ S8 Step,
+ OUT U8 Limit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM]
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerIn *ControllerIn;
+ MrcControllerOut *ControllerOut;
+ MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ U8 Channel;
+ U8 ChannelMask;
+ U8 Byte;
+ U8 ByteMask;
+ U8 DramByte;
+ U8 ByteDoneMask[MAX_CHANNEL];
+ U8 PiCode;
+ U8 Feedback[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U32 Pattern;
+ U32 DelayChipSelectCadb;
+ U32 Offset;
+ BOOL Done;
+ BOOL Failed;
+ char *BytesHeader;
+ char PassFail;
+ DDRDATA0CH0_CR_DATATRAINFEEDBACK_STRUCT DataTrainFeedback;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerIn = &Inputs->Controller[0];
+ ControllerOut = &Outputs->Controller[0];
+ ChannelMask = 0;
+
+ DelayChipSelectCadb = 1 * HPET_1US;
+
+ MrcOemMemorySet (ByteDoneMask, 0, sizeof (ByteDoneMask));
+
+ PiCode = Start;
+ Done = FALSE;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t Ch0 pattern 1\t\t\t Ch1 pattern 1\t\t Ch 0 pattern 2\t\t Ch 1 pattern 2\n");
+ BytesHeader = "0 1 2 3 4 5 6 7 ";
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CTL PI\t %s%s%s%s\n", BytesHeader, BytesHeader, BytesHeader, BytesHeader);
+
+ while (!Done) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d:\t", PiCode);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCtl, 1 << Rank, 1, PiCode, 0);
+ }
+
+ //
+ // Try two different paterns (0x2AA or 0x155), to see if the command is still decoded correctly
+ //
+ for (Pattern = 0; Pattern <= 1; Pattern++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ ChannelMask |= (1 << Channel);
+ SetCadbPatternBuffers (MrcData, Channel, (Pattern == 0) ? 0x3000 : 0x7000, 0x4000, 0x0000);
+ }
+
+ //
+ // Run CADB pattern on selected channels at the same time
+ //
+ ShortRunCADB (MrcData, ChannelMask);
+ MrcWait (MrcData, DelayChipSelectCadb);
+
+ //
+ // Read and process the results
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ByteDoneMask[Channel] = 0xFF;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ continue;
+ }
+ if (ByteDoneMask[Channel] == 0xFF) { // All bytes failed on this channel, no need to sweep anymore
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ continue;
+ }
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ ByteMask = (1 << Byte);
+ DramByte = ChannelIn->DqsMapCpu2Dram[Byte];
+ if (ChannelOut->Dimm[dDIMM0].SdramWidth == 32) {
+ if ((DramByte & 0x02) != 0) {
+ //
+ // Ignore upper 16 bits on x32 devices in MRW41 feedback - DRAM bytes 2, 3, 6 and 7
+ //
+ ByteDoneMask[Channel] |= ByteMask;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ continue;
+ }
+ }
+ 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) & 0xFF); // Get only DQ bits, not DQS
+ PassFail = '#';
+ if ((ByteDoneMask[Channel] & ByteMask) == 0) {
+ if (Pattern == 0) {
+ //
+ // First pattern
+ //
+ Feedback[Channel][Byte] = (U8) DataTrainFeedback.Data;
+ PassFail = ' ';
+ } else {
+ //
+ // Second Pattern
+ // If still read the same data, then DRAM was not able to decode the new command
+ //
+ Failed = FALSE;
+ if (Feedback[Channel][Byte] == (U8) DataTrainFeedback.Data) {
+ Failed = TRUE;
+ }
+ if (MrcCountBitsEqOne (DataTrainFeedback.Data) != 4) {
+ Failed = TRUE;
+ }
+ if (Failed) {
+ Limit[Channel][Rank][Byte] = PiCode;
+ ByteDoneMask[Channel] |= ByteMask;
+ } else {
+ PassFail = '.';
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%c%02X ", PassFail, DataTrainFeedback.Data);
+ } // for Byte
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ } // for Channel
+ } // for Pattern
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ if ((ByteDoneMask[0] == 0xFF) && (ByteDoneMask[1] == 0xFF)) {
+ // Found the limit on all bytes on both channels - no need to sweep Pi any longer
+ break;
+ }
+
+ PiCode += Step;
+ if (Step > 0) {
+ // Sweep up
+ Done = (PiCode > Stop);
+ } else {
+ // Sweep down
+ Done = (((S8) PiCode) < Stop);
+ }
+ } // while not done
+}
+
+/**
+ Process the results of the early LPDDR3 CS training and find the best PI settings for CS and CLK.
+ Flow:
+ 1. Find the worst case Right and Left limits for each group
+ 2. Find the Center for each group
+
+ @param[in] MrcData - The MRC global data.
+ @param[in] LeftLimit - array of left edge values per channel, rank and CPU byte
+ @param[in] RightLimit - array of right edge values per channel, rank and CPU byte
+ @param[out] BestCs - array of best CS PI settings, per channel and group
+ @param[out] BestClk - array of best CLK PI settings, per channel and group
+
+ @retval mrcSuccess if succeeded
+**/
+MrcStatus
+FindBestCsClkPi (
+ IN MrcParameters * const MrcData,
+ IN U8 LeftLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM],
+ IN U8 RightLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM],
+ OUT U8 BestCs[MAX_CHANNEL][2],
+ OUT U8 BestClk[MAX_CHANNEL][2]
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelIn *ChannelIn;
+ MrcStatus Status;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 Group;
+ U8 Count;
+ U8 GroupLeftLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][2]; // Per ch, rank and group
+ U8 GroupRightLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][2]; // Per ch, rank and group
+ U8 GroupCenter[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][2]; // Per ch, rank and group
+ S8 ClkDelta[MAX_RANK_IN_CHANNEL];
+ S8 MeanClkDelta;
+ S8 CsDelta[2]; // Per group
+ S8 MeanCsDelta;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+
+ Status = mrcSuccess;
+
+ MrcOemMemorySet ((U8 *) GroupRightLimit, 127, sizeof (GroupRightLimit));
+ MrcOemMemorySet ((U8 *) GroupLeftLimit, 0, sizeof (GroupLeftLimit));
+ MrcOemMemorySet ((U8 *) GroupCenter, 0, sizeof (GroupCenter));
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Finding best CS/CLK PIs:\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel: %d\tLeft\tRight\tCenter\n", Channel);
+ ChannelIn = &Inputs->Controller[0].Channel[Channel];
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ for (Group = 0; Group < 2; Group++) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // Find the worst case Right and Left limits for each group
+ //
+ if (ChannelIn->DQByteMap[MrcIterationClock][Group] & (1 << Byte)) {
+ if (GroupRightLimit[Channel][Rank][Group] > RightLimit[Channel][Rank][Byte]) {
+ GroupRightLimit[Channel][Rank][Group] = RightLimit[Channel][Rank][Byte];
+ }
+
+ if (GroupLeftLimit[Channel][Rank][Group] < LeftLimit[Channel][Rank][Byte]) {
+ GroupLeftLimit[Channel][Rank][Group] = LeftLimit[Channel][Rank][Byte];
+ }
+ }
+ } // for Byte
+
+ //
+ // Find the Center for each group
+ //
+ GroupCenter[Channel][Rank][Group] =
+ (GroupRightLimit[Channel][Rank][Group] + GroupLeftLimit[Channel][Rank][Group]) / 2;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Rank%d CLK%d\t%d\t%d\t%d\n",
+ Rank,
+ Group,
+ GroupLeftLimit[Channel][Rank][Group],
+ GroupRightLimit[Channel][Rank][Group],
+ GroupCenter[Channel][Rank][Group]
+ );
+ } // for Group
+ } // for Rank
+
+ //
+ // Find the CS delta between ranks for each clock group, and then group average
+ //
+ for (Count = 0, Group = 0; Group < 2; Group++) {
+ if (MrcRankInChannelExist (MrcData, 1, Channel)) {
+ CsDelta[Group] = (GroupCenter[Channel][1][Group] - GroupCenter[Channel][0][Group]);
+ Count++;
+ } else {
+ CsDelta[Group] = 0; // Single rank 0 case
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CLK%d delta (Rank0-Rank1) = %d \n", Group, CsDelta[Group]);
+ }
+
+ MeanCsDelta = (Count != 0) ? (CsDelta[0] + CsDelta[1]) / Count : 0;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Mean CS delta = %d\n", MeanCsDelta);
+
+ //
+ // Find the Clock delta for each rank, and then average between ranks
+ // @todo Add case of single CLK group
+ //
+ for (Count = 0, Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ClkDelta[Rank] = (GroupCenter[Channel][Rank][1] - GroupCenter[Channel][Rank][0]);
+ Count++;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rank%d delta (CLK1-CLK0) = %d \n", Rank, ClkDelta[Rank]);
+ } else {
+ ClkDelta[Rank] = 0; // No such rank
+ }
+ }
+ MeanClkDelta = (Count != 0) ? (ClkDelta[0] + ClkDelta[1]) / Count : 0;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Mean Clock delta = %d\n", MeanClkDelta);
+
+ BestClk[Channel][0] = (U8) (64 - MeanClkDelta / 2); // CLK0
+ BestClk[Channel][1] = (U8) (BestClk[Channel][0] + MeanClkDelta); // CLK1
+
+ BestCs[Channel][0] = (GroupCenter[Channel][0][0] + GroupCenter[Channel][0][1]) / 2; // CS0
+ BestCs[Channel][1] = (GroupCenter[Channel][1][0] + GroupCenter[Channel][1][1]) / 2; // CS1
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Best PI CLK0=%d, CLK1=%d, CS0=%d, CS1=%d\n",
+ BestClk[Channel][0],
+ BestClk[Channel][1],
+ BestCs[Channel][0],
+ BestCs[Channel][1]
+ );
+ } // for Channel
+
+ return Status;
+}
+
+/**
+ Early CS / CLK training for LPDDR.
+ Main flow:
+ 1. Setup CADB pattern for CS Training.
+ 2. Run CS vs. CLK training.
+ 3. Select optimal CS and CLK timings
+
+ @param[in] MrcData - The MRC global data.
+
+ @retval mrcSuccess if succeeded
+**/
+MrcStatus
+EarlyChipSelectTraining (
+ IN MrcParameters * const MrcData
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerIn *ControllerIn;
+ MrcControllerOut *ControllerOut;
+ MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ BOOL ClockPiChanged;
+ U8 Channel;
+ U8 Rank;
+ U8 RankBit;
+ U8 Byte;
+ U8 DramByte;
+ U8 Group;
+ U8 RankMask;
+ U32 Offset;
+ S32 ClkDelta;
+ U8 PiLow;
+ U8 PiHigh;
+ U8 PiMiddle;
+ U8 PiStep;
+ U8 RightLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U8 LeftLimit[MAX_CHANNEL][MAX_RANK_IN_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U8 BestCs[MAX_CHANNEL][2];
+ U8 BestClk[MAX_CHANNEL][2];
+ DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerIn = &Inputs->Controller[0];
+ ControllerOut = &Outputs->Controller[0];
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "EarlyChipSelectTraining started\n");
+
+ Status = mrcSuccess;
+ RankMask = Outputs->ValidRankMask;
+
+ PiLow = 0;
+ PiHigh = 127;
+ PiMiddle = 64;
+ PiStep = 2;
+
+ MrcOemMemorySet ((U8 *) RightLimit, PiHigh, sizeof (RightLimit));
+ MrcOemMemorySet ((U8 *) LeftLimit, PiLow, sizeof (LeftLimit));
+
+ //
+ // Enable the following bits because we will use DATATRAINFEEDBACK to read back CA values on DQ pins:
+ // DataControl0.SenseampTrainingMode and ForceOdtOn
+ // DataControl2.ForceRxOn and ForceBiasOn
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DdrCrDataControl2.Data = ChannelOut->DqControl2[Byte].Data;
+ DdrCrDataControl2.Bits.ForceBiasOn = 1;
+ DdrCrDataControl2.Bits.ForceRxOn = 1;
+ 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);
+ }
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ DdrCrDataControl0.Bits.ForceOdtOn = 1;
+ DdrCrDataControl0.Bits.SenseampTrainingMode = 1;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ }
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankBit = 1 << Rank;
+ if ((RankBit & RankMask) == 0) {
+ continue;
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+
+ //
+ // Put the current Rank in CA training mode using MRW41.
+ //
+ Status = LpddrCommandTrainingMode (MrcData, 1 << Channel, RankBit, CaTrainingMode41);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+
+ SetupCaTrainingCadb (
+ MrcData,
+ Channel,
+ Rank,
+ CadbLinesCs,
+ sizeof (CadbLinesCs) / sizeof (CadbLinesCs[0]),
+ 0x3000,
+ 0x4000,
+ 0x0000
+ );
+ } // for Channel
+
+ //
+ // Sweep CS Pi up and down from the middle, on both channels at the same time
+ //
+ ChipSelectFindEdge (MrcData, Rank, PiMiddle, PiHigh, PiStep, RightLimit);
+ ChipSelectFindEdge (MrcData, Rank, PiMiddle, PiLow, -PiStep, LeftLimit);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CS training data Ch%d Rank%d\nCPU Byte\tLeft\tRight\n", Channel, Rank);
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DramByte = ChannelIn->DqsMapCpu2Dram[Byte];
+ if (ChannelOut->Dimm[dDIMM0].SdramWidth == 32) {
+ if ((DramByte & 0x02) != 0) {
+ //
+ // Ignore upper 16 bits on x32 devices in MRW41 feedback - DRAM bytes 2, 3, 6 and 7
+ //
+ continue;
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t\t%d\t%d\n",
+ Byte, LeftLimit[Channel][Rank][Byte], RightLimit[Channel][Rank][Byte]);
+ }
+
+ //
+ // Put the CTL PI back to middle for MRW42 command
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCtl, 3, 1, PiMiddle, 1);
+
+ //
+ // Exit CA training mode on the current rank
+ //
+ Status = LpddrCommandTrainingMode (MrcData, 1 << Channel, RankBit, CaTrainingMode42);
+
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ } // for Channel
+ } // for Rank
+
+ //
+ // Restore original DataControl0 and DataControl2 values
+ //
+ 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);
+ 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);
+ }
+ }
+
+ //
+ // Select optimal CS and CLK timings for both channels
+ //
+ FindBestCsClkPi (MrcData, LeftLimit, RightLimit, BestCs, BestClk);
+
+ //
+ // Apply the new CTL and CLK PI settings
+ //
+ ClockPiChanged = FALSE;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+ //
+ // Shift CS per rank
+ //
+ ShiftPIforCmdTraining (
+ MrcData,
+ Channel,
+ MrcIterationCtl,
+ 1 << Rank,
+ 1,
+ BestCs[Channel][Rank],
+ 1 // UpdateHost
+ );
+ }
+ for (Group = 0; Group < 2; Group++) {
+ //
+ // Shift CLK per group, if needed, and update host struct
+ //
+ ClkDelta = (S32) BestClk[Channel][Group] - (S32) ChannelOut->ClkPiCode[Group];
+ if (ClkDelta != 0) {
+ ClockPiChanged = TRUE;
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationClock, RankMask, 1 << Group, ClkDelta, 1);
+
+ //
+ // Shift the corresponding CMD PI by the same amount as the CLK
+ //
+ if (ChannelIn->DQByteMap[MrcIterationClock][Group] == ChannelIn->DQByteMap[MrcIterationCmdS][0]) {
+ //
+ // CAA is controlled by CMDS.CmdPi0Code and CKE.CmdPi0Code
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, RankMask, (1 << 0),
+ ChannelOut->CmdsCmdPiCode[0] + ClkDelta, 1);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCke, RankMask, (1 << 0),
+ ChannelOut->CkeCmdPiCode[0] + ClkDelta, 1);
+ }
+ if (ChannelIn->DQByteMap[MrcIterationClock][Group] == ChannelIn->DQByteMap[MrcIterationCmdS][1]) {
+ //
+ // CAB is controlled by CMDS.CmdPi1Code and CMDN.CmdPi1Code
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdS, RankMask, (1 << 1),
+ ChannelOut->CmdsCmdPiCode[1] + ClkDelta, 1);
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCmdN, RankMask, (1 << 1),
+ ChannelOut->CmdnCmdPiCode[1] + ClkDelta, 1);
+ }
+ }
+ }
+ } // for Channel
+
+ //
+ // Perform IO reset and JEDEC reset if clock PI was changed.
+ //
+ if (ClockPiChanged) {
+ MrcResetSequence (MrcData);
+ }
+
+ return Status;
+}
+
+////
+//// CA to DQ mapping during MRW41
+//// First index is rising edge (0) / falling edge (1)
+//// Second index is CA pin
+////
+//U8 CA2DQMapping[2][9] = {
+// { 0x00,0x02,0x04,0x06,0x10,0x08,0x0A,0x0C,0x0E },
+// { 0x01,0x03,0x05,0x07,0x11,0x09,0x0B,0x0D,0x0F }
+//};
+//
+///**
+//
+// Map byte to group for the given Iteration
+//
+// @param[in] MrcData - The MRC global data.
+// @param[in] Iteration
+// @param[in] Byte
+//
+// @retval Group
+//
+//**/
+//
+//U8
+//MapByte2Group (
+// MrcParameters * const MrcData,
+// U8 Iteration,
+// U8 Channel,
+// U8 Byte
+// )
+//{
+// MrcInput *Inputs;
+// MrcControllerIn *ControllerIn;
+// MrcChannelIn *ChannelIn;
+// U32 ByteMask;
+// U32 SafeIteration;
+// U8 Group;
+// U8 SafeGroup;
+// U8 TargetGroup;
+//
+// Inputs = &MrcData->SysIn.Inputs;
+// ControllerIn = &Inputs->Controller[0];
+// ChannelIn = &ControllerIn->Channel[Channel];
+//
+// SafeIteration = 2; // CmdS has a mapping for all bytes (uses CAA & CAB)
+// SafeGroup = 0; // This is not true for all iterations
+// TargetGroup = 0; // ex: CmdN and CKE only use one of the CAA/CAB buses
+// ByteMask = 0; // ex: Clk may or may not go to all bytes
+//
+// for (Group = 0; Group < 2; Group++) {
+// ByteMask |= ChannelIn->DQByteMap[Iteration][Group];
+// if (ChannelIn->DQByteMap[Iteration][Group] & (1 << Byte)) {
+// TargetGroup = Group;
+// }
+// if (ChannelIn->DQByteMap[SafeIteration][Group] & (1 << Byte)) {
+// SafeGroup = Group;
+// }
+// }
+//
+// if (ByteMask == 0xFF) {
+// return TargetGroup;
+// } else {
+// return SafeGroup;
+// }
+//}
+//
+///**
+//
+// Update the DRAM to CPU DQ mapping for a given byte based on CA training results.
+// DQMapping is from CPU to DRAM
+// Victim is the CA victim bit
+// ByteFB is an 8 bit value that we received back for a Walking 0 VA pattern
+// Diff is an 8 bit value with ones in the victim bit positions
+// Return 0 if everything made sense, otherwise returns (1<<Group) to indicate an error
+// In the case of an error, DQMapping is not updated
+//
+// @param[in] MrcData - The MRC global data.
+//
+// @retval 0 - if a valid mapping was found
+// @retval (1 << Group) - in case of error
+//
+//**/
+//U8
+//UpdateDQMapping (
+// IN OUT U8 DQMapping[64],
+// IN U8 Byte,
+// IN U8 ByteFeedback,
+// IN U8 Diff,
+// IN U8 Victim,
+// IN U8 Group
+// )
+//{
+// U32 BitsHigh;
+// U8 DramDQHigh;
+// U8 DramDQLow;
+// U8 CpuDQHigh;
+// U8 CpuDQLow;
+// U8 Bit;
+// U8 BitValue;
+//
+// if (Diff == 0) {
+// return 0; // No error to look at
+// }
+//
+// //
+// // Should never see an error with these victim lanes since their data returns with MRW 48
+// // This function is only used with data from MRW 41
+// if ((Victim == 4) || (Victim == 9)) {
+// return (1 << Group);
+// }
+//
+// //
+// // Walk through the bits with errors and figure out the mapping
+// //
+// BitsHigh = 0;
+// DramDQHigh = 0xFF;
+// DramDQLow = 0xFF;
+// CpuDQHigh = 0xFF;
+// CpuDQLow = 0xFF;
+//
+// for (Bit = 0; Bit < 8; Bit++) {
+// BitValue = 1 << Bit;
+// if ((Diff & BitValue) != 0) {
+// BitsHigh++; // Count number of mismatches. Should be exactly 2
+// if ((ByteFeedback & BitValue) != 0) { // 1 on Victim bit during WalkZero indicates low phase
+// CpuDQLow = Bit;
+// DramDQLow = CA2DQMapping[1][Victim] & 0xF;
+// } else { // 0 on Victim bit during WalkZero indicates high phase
+// CpuDQHigh = Bit;
+// DramDQHigh = CA2DQMapping[0][Victim] & 0xF;
+// }
+// }
+// }
+//
+// if ((BitsHigh == 2) && (CpuDQLow != 0xFF) && (CpuDQHigh != 0xFF)) {
+// //
+// // This is a valid feedback that makes sense
+// //
+// DQMapping[8 * Byte + CpuDQLow] = (Group << 4) + DramDQLow;
+// DQMapping[8 * Byte + CpuDQHigh] = (Group << 4) + DramDQHigh;
+// return 0;
+// }
+//
+// //
+// // This is not a valid feedback
+// //
+// return (1 << Group);
+//}
+//
+///**
+//
+// Find the DRAM to CPU DQ mapping based on CA training results.
+// DQMapping is from CPU to DRAM
+// ByteFB is an 8 bit value that we received back for a Walking 0 VA pattern
+//
+// @param[in] MrcData - The MRC global data.
+// @param[in] ByteFB - Array of Ch X Byte for WalkZero results (WalkOne will just be the inverse)
+// @param[in] DQMapping - Array of Ch x 64 bits for mapping X64 CPU to X16 DRAM
+// @param[in] X16Count - Array of Ch x Group that indicates how many X16 words are in a given ch/group
+// Usually this is either 0 or 2 (ie: 0 bits or 32 bits)
+// @param[in] ChError - Arrray of Ch with a bit mask indicating if this Ch/Group had an error
+//
+// @retval none
+//
+//**/
+//
+//void
+//FindECTCpu2DramMapping (
+// MrcParameters * const MrcData,
+// U8 ChBitMask,
+// U8 Iteration,
+// U8 ByteFB[MAX_CHANNEL][8][11],
+// IN OUT U8 DQMapping[MAX_CHANNEL][64],
+// IN U8 X16Count[MAX_CHANNEL][2],
+// OUT U8 ChError[MAX_CHANNEL],
+// BOOL UpdateDqMapping
+// )
+//{
+// MrcOutput *Outputs;
+// MrcChannelOut *ChannelOut;
+// MrcControllerOut *ControllerOut;
+// U8 Channel;
+// U8 Byte;
+// U8 Bit;
+// U8 Group;
+// U8 Victim;
+// U8 RefFB;
+// U8 Diff;
+// U8 DramDQ;
+// U8 CountMappings[2][16];
+//
+// Outputs = &MrcData->SysOut.Outputs;
+// ControllerOut = &Outputs->Controller[0];
+//
+// MrcOemMemorySet ((U8 *) CountMappings, 0, sizeof (CountMappings));
+//
+// for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+// if (!MrcChannelExist (Outputs, Channel)) {
+// continue;
+// }
+// if (((1 << Channel) & ChBitMask) == 0) {
+// continue;
+// }
+// ChannelOut = &ControllerOut->Channel[Channel];
+//
+// //
+// // Figure out mappings for all lanes by comparing different walking zero results.
+// // XOR the Victim=10 vs Victim=N results
+// // During Victim=10, all CA lanes drive 0101
+// // During Victim<10, all CA lanes drive 0101 except the victim, which drives 1010
+// // Use this and the mapping of CA2DQ lanes (JEDEC spec) to figure out the mapping.
+// //
+// for (Byte = 0; Byte < 8; ++Byte) {
+// RefFB = ByteFB[Channel][Byte][10]; // All lanes drive WalkZero Agg = 0101
+// Group = MapByte2Group (MrcData, Iteration, Channel, Byte); // Find Byte-to-Group mapping
+//
+// for (Victim = 0; Victim < 10; ++Victim) {
+// Diff = RefFB ^ ByteFB[Channel][Byte][Victim]; // XOR should produce either (0,2) diffs
+// ChError[Channel] |= UpdateDQMapping (DQMapping[Channel], Byte, ByteFB[Channel][Byte][Victim], Diff, Victim, Group);
+// }
+// }
+//
+// //
+// // Check results by making sure everybody is mapped exactly the right number of times.
+// // DQMapping defines the mapping from 64 CPU DQ lanes to 16 DRAM DQ lanes.
+// // As a result, we should see each DRAM DQ lane is listed exactly X16Count times.
+// //
+// for (Byte = 0; Byte < 8; ++Byte) {
+// for (Bit = 0; Bit < 8; ++Bit) {
+// DramDQ = DQMapping[Channel][8 * Byte + Bit] & 0xF;
+// Group = (DQMapping[Channel][8 * Byte + Bit] >> 4) & 1;
+// CountMappings[Group][DramDQ]++;
+// }
+// }
+//
+// for (Group = 0; Group < 2; ++Group) {
+// for (Bit = 0; Bit < 16; ++Bit) {
+// if (CountMappings[Group][Bit] != X16Count[Channel][Group]) {
+// ChError[Channel] |= (1 << Group);
+// }
+// }
+// }
+//
+// //
+// // Update the DQ Mapping in the host structure if needed
+// //
+// if (UpdateDqMapping && (ChError[Channel] == 0)) {
+// for (Byte = 0; Byte < 8; ++Byte) {
+// for (Bit = 0; Bit < 8; ++Bit) {
+// ChannelOut->DqMapCpu2Dram[Byte][Bit] = DQMapping[Channel][8 * Byte + Bit] & 0xF;
+// }
+// }
+// }
+// } // for Channel
+//}
+//
+///**
+//
+// Check if the CA training test is done (everybody has already failed) or we need to keep running
+//
+// @param[in] MrcData - The MRC global data.
+// @param[in] ChBitMask - channels to work on
+// @param[in] Iteration -
+// @param[in] ChError - Array of error values per channel, each element is a bitmask per group, '1' means error.
+//
+// @retval TRUE if the test is done, FALSE otherwise
+//
+//**/
+//BOOL
+//CADBLPDDR3TestIsDone (
+// IN MrcParameters * const MrcData,
+// IN U8 ChBitMask,
+// IN U8 Iteration,
+// IN U8 ChError[MAX_CHANNEL]
+// )
+//{
+// MrcInput *Inputs;
+// MrcControllerIn *ControllerIn;
+// MrcChannelIn *ChannelIn;
+// U8 Channel;
+// U8 Group;
+//
+// Inputs = &MrcData->SysIn.Inputs;
+// ControllerIn = &Inputs->Controller[0];
+//
+// for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+// if (((1 << Channel) & ChBitMask) == 0) {
+// continue;
+// }
+// ChannelIn = &ControllerIn->Channel[Channel];
+// for (Group = 0; Group < 2; ++Group) {
+// if ((ChannelIn->DQByteMap[Iteration][Group] > 0) && ((ChError[Channel] & (1 << Group)) == 0)) {
+// //
+// // Not done if at least 1 valid ch/group is still passing
+// //
+// return FALSE;
+// }
+// }
+// }
+//
+// //
+// // If we reach this point, we are done
+// //
+// return TRUE;
+//}
+//
+///**
+//
+// Programs the CADB to output either a WalkingOne or WalkingZero pattern on VictimBit.
+// Each CA[9:0] lane will toggle with a 1010 pattern but the Victim lane will be inverted.
+// For a WalkingOne pattern, CAHi[Vic] = 1 and CALo[Vic] = 0.
+// For a WalkingZero pattern, CAHi[Vic] = 0 and CALo[Vic] = 1.
+// (VictimBit == 10) is a special case where all CAHi/CALo lanes drive ~WalkOne/WalkOne
+//
+// Sets up 3 CADB lines that will be used to send out a CA pattern.
+//
+// Example for VictimBit = 0, WalkOne = 0:
+//
+// -----------------------
+// CADB Phase Phase CS
+// Line High Low
+// -----------------------
+// 0 0x000 0x000 Off // For delay between CA patterns
+// 1 0x3FE 0x001 On // CA pattern for rank 0
+// 2 0x3FE 0x001 On // CA pattern for rank 1
+//
+// The CS pattern uses Pattern Buffer and hence has 16 lines, with CS active for one line only.
+// This will send command every 16 DCLKs.
+//
+// Pattern Buffer details:
+// The line order is:
+// for rank 0: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+// for rank 1: 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+//
+// 001 or 010
+// 000
+// 000
+// 000
+//
+// 000
+// 000
+// 000
+// 000
+//
+// 000
+// 000
+// 000
+// 000
+//
+// 000
+// 000
+// 000
+// 000
+// ----
+// 001 or 010 --> PB[0] = 0x0001 or 0x0000
+// 000 PB[1] = 0x0000 or 0x0001
+// 000 PB[2] = 0x0000
+// 000
+//
+// @param[in] MrcData - The MRC global data.
+// @param[in] ChBitMask - channels to work on
+// @param[in] VictimBit - The Victim bit
+// @param[in] WalkOne - '1': Use WalkinOne pattern, '0': use WalkingZero pattern.
+//
+// @retval TRUE if the test is done, FALSE otherwise
+//
+//**/
+//void
+//SetupCADBLPDDR3VaPattern (
+// IN MrcParameters * const MrcData,
+// IN U8 ChBitMask,
+// IN U8 VictimBit,
+// IN U8 WalkOne
+// )
+//{
+// /*
+// CADB_LINE CadbLinesCs[] = {
+// { 0x3FF, 0x3FF, 0 },
+// { 0x000, 0x000, 0 },
+// { 0x2AA, 0x2AA, 1 },
+// { 0x155, 0x155, 1 }
+// };
+//
+// SetupCaTrainingCadb (
+// MrcData,
+// Channel,
+// Rank,
+// CadbLinesCs,
+// sizeof (CadbLinesCs) / sizeof (CadbLinesCs[0]),
+// 0x9FFE,
+// 0x4000,
+// 0x0000
+// );
+// */
+//}
+//
+///**
+//
+// Runs through a VA test on all CH/Ranks for the current CMD PI timing.
+// Setups the test, walks though a walking one/zero pattern with each lane as a victim .
+// If UpdateDqMapping, it will also update host.ch[ch].DQMappingCpu2Dram
+// Returns a pass/fail based on Ch/Group in ChError
+//
+// Main flow:
+// 1.
+// 2.
+// 3.
+//
+// @param[in] MrcData - The MRC global data.
+// @param[in, out] ChError - Array of error values per channel, each element is a bitmask per group, '1' means error.
+//
+// @retval mrcSuccess if succeeded
+//
+//**/
+//MrcStatus
+//RunCADBLPDDR3VATest (
+// IN MrcParameters * const MrcData,
+// IN U8 ChBitMask,
+// IN U8 Ranks,
+// IN U8 Iteration,
+// IN OUT U8 ChError[MAX_CHANNEL],
+// IN BOOL UpdateDqMapping
+// )
+//{
+// MrcInput *Inputs;
+// MrcDebug *Debug;
+// MrcStatus Status;
+// MrcOutput *Outputs;
+// MrcControllerIn *ControllerIn;
+// MrcControllerOut *ControllerOut;
+// MrcChannelIn *ChannelIn;
+// MrcChannelOut *ChannelOut;
+// U32 Offset;
+// U8 Channel;
+// U8 Rank;
+// U8 Group;
+// U8 BytesPerGroup;
+// U8 WalkOne;
+// U8 VictimBit;
+// U8 ByteFB[MAX_CHANNEL][8][11];
+// U8 X16Count[MAX_CHANNEL][2]; // How many X16 words for each ch/group
+// U8 DQMapping[MAX_CHANNEL][64]; // Lower nibble is X64 CPU lane to X16 DRAM lanes
+// // (this does not unswizzle byte/words)
+// // Upper nibble is which group this X64 CPU lane belongs to (0 or 1)
+// DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+//
+// Inputs = &MrcData->SysIn.Inputs;
+// Debug = &Inputs->Debug;
+// Outputs = &MrcData->SysOut.Outputs;
+// ControllerOut = &Outputs->Controller[0];
+// ControllerIn = &Inputs->Controller[0];
+//
+// MrcOemMemorySet ((U8 *) ChError, 0, sizeof (ChError));
+// MrcOemMemorySet ((U8 *) ByteFB, 0, sizeof (ByteFB));
+// MrcOemMemorySet ((U8 *) X16Count, 0, sizeof (X16Count));
+// MrcOemMemorySet ((U8 *) DQMapping, 0, sizeof (DQMapping));
+//
+// //
+// // Count number of ranks that are being tested
+// //
+// if (MrcCountBitsEqOne (Ranks) == 0) {
+// return mrcSuccess;
+// }
+//
+// //
+// // Count how many X16 devices are associated with each group
+// // This will be used to determine how many Ones/Zeros are set in each results
+// //
+// for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+// if (((1 << Channel) & ChBitMask) == 0) {
+// continue;
+// }
+// ChannelIn = &ControllerIn->Channel[Channel];
+// ChannelOut = &ControllerOut->Channel[Channel];
+// for (Group = 0; Group < 2; Group++) {
+// if (ChannelIn->DQByteMap[Iteration][Group] > 0) {
+// BytesPerGroup = MrcCountBitsEqOne(ChannelIn->DQByteMap[Iteration][Group]);
+// if ((BytesPerGroup & 1) != 0) {
+// MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "ERROR - Cannot have odd number of bytes per group\n");
+// return mrcFail;
+// }
+// X16Count[Channel][Group] = BytesPerGroup / 2;
+// }
+// }
+//
+// //
+// // Enable DataControl0.SenseampTrainingMode because we will use DATATRAINFEEDBACK to read back CA values on DQ pins.
+// // Also enable DataControl0.ForceOdtOn
+// //
+// DdrCrDataControl0.Data = ChannelOut->DqControl0;
+// DdrCrDataControl0.Bits.SenseampTrainingMode = 1;
+// DdrCrDataControl0.Bits.ForceOdtOn = 1;
+// Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+// ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+// MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+// }
+//
+// //
+// // Put DRAM into MRW 41 CA Training mode
+// //
+// Status = LpddrCommandTrainingMode (MrcData, ChBitMask, Ranks, CaTrainingMode41);
+// if (Status != mrcSuccess) {
+// return Status;
+// }
+//
+// //
+// // Run associated VA tests for MRW 41 (16 out of 20 CA bits)
+// //
+// for (WalkOne = 0; WalkOne < 2; WalkOne++) {
+// for (VictimBit = 0; VictimBit < 11; VictimBit++) {
+// //
+// // Are we done ?
+// //
+// if (CADBLPDDR3TestIsDone (MrcData, ChBitMask, Iteration, ChError)) {
+// break;
+// }
+//
+// //
+// // Program VA pattern in the CADB
+// //
+// SetupCADBLPDDR3VaPattern (MrcData, ChBitMask, VictimBit, WalkOne);
+//
+// //
+// // Run test on 1 rank at a time and read out the results
+// //
+// for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+// if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+// continue;
+// }
+// if ((Ranks & (1 << Rank)) == 0) {
+// continue;
+// }
+////@todo: IssueCADBLPDDR3CmdTest (MrcData, ChBitMask, 1 << Rank, 3); // Operation = 3
+//
+///* @todo: ReadECTLPDDR3Results (
+// MrcData,
+// ChBitMask,
+// Iteration,
+// WalkOne,
+// ByteFB,
+// VictimBit,
+// DQMapping,
+// TRUE, // Using MRW41
+// X16Count,
+// ChError
+// ); */
+// }
+// }
+// }
+//
+// //
+// // Figure out DQ Lane Mapping for later steps and to ensure this sampled good data
+// // If results do not match expectations, updates ChError appropriately
+// //
+// FindECTCpu2DramMapping (MrcData, ChBitMask, Iteration, ByteFB, DQMapping, X16Count, ChError, UpdateDqMapping);
+//
+// //
+// // Put DRAM into MRW 48 CA Training Mode
+// //
+// Status = LpddrCommandTrainingMode (MrcData, ChBitMask, Ranks, CaTrainingMode48);
+// if (Status != mrcSuccess) {
+// return Status;
+// }
+//
+// //
+// // Run associated VA tests for MRW 48 (4 out of 20 CA bits)
+// //
+// for (WalkOne = 0; WalkOne < 2; WalkOne++) {
+// for (VictimBit = 0; VictimBit < 11; VictimBit++) {
+// //
+// // Are we done ?
+// //
+// if (CADBLPDDR3TestIsDone (MrcData, ChBitMask, Iteration, ChError)) {
+// break;
+// }
+//
+// //
+// // Program VA pattern in the CADB
+// //
+// SetupCADBLPDDR3VaPattern (MrcData, ChBitMask, VictimBit, WalkOne);
+//
+// //
+// // Run test on 1 rank at a time and read out the results
+// //
+// for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+// if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+// continue;
+// }
+// if ((Ranks & (1 << Rank)) == 0) {
+// continue;
+// }
+////@todo: IssueCADBLPDDR3CmdTest (MrcData, ChBitMask, 1 << Rank, 3); // Operation = 3
+//
+///* @todo: ReadECTLPDDR3Results (
+// MrcData,
+// ChBitMask,
+// Iteration,
+// WalkOne,
+// ByteFB,
+// VictimBit,
+// DQMapping,
+// FALSE, // Using MRW48
+// X16Count,
+// ChError
+// ); */
+// }
+// }
+// }
+//
+// //
+// // Exit CA training mode using MRW42
+// //
+// Status = LpddrCommandTrainingMode (MrcData, ChBitMask, Ranks, CaTrainingMode42);
+// if (Status != mrcSuccess) {
+// return Status;
+// }
+//
+// //
+// // Restore DataControl0
+// //
+// for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+// if (((1 << Channel) & ChBitMask) == 0) {
+// continue;
+// }
+// ChannelOut = &ControllerOut->Channel[Channel];
+// Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+// ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+// MrcWriteCrMulticast (MrcData, Offset, ChannelOut->DqControl0);
+// }
+// return Status;
+//}
+
+#endif // ULT_FLAG
+
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCommon.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCommon.c
new file mode 100644
index 0000000..9380989
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCommon.c
@@ -0,0 +1,8010 @@
+/** @file
+ This file include all the common tolls for the mrc algo
+
+@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 "MrcCommon.h"
+
+#ifdef MRC_DEBUG_PRINT
+const char CcdString[] = "Controller, Channel, Dimm";
+const char RcvEnDelayString[] = "RcvEnDelay";
+const char DqsDelayString[] = "DqsDelay";
+
+#endif
+
+/**
+ Return the rank mask in channel if rank exist exist.
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] Channel - Channel to work on.
+ @param[in] Rank - Rank to check.
+
+ @retval Bit mask of Rank requested if the Rank exists in the system.
+**/
+U8
+MrcRankInChannelExist (
+ IN MrcParameters *const MrcData,
+ IN const U8 Rank,
+ IN const U8 Channel
+ )
+{
+ return (MRC_BIT0 << Rank) & MrcData->SysOut.Outputs.Controller[0].Channel[Channel].ValidRankBitMask;
+}
+
+/**
+ Return the number of ranks in specific dimm.
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] Channel - Channel to work on.
+ @param[in] Dimm - Dimm in channel to return.
+
+ @retval The number of ranks in the dimm.
+**/
+U8
+MrcGetRankInDimm (
+ IN MrcParameters *const MrcData,
+ IN const U8 Dimm,
+ IN const U8 Channel
+ )
+{
+ return MrcData->SysOut.Outputs.Controller[0].Channel[Channel].Dimm[Dimm].RankInDIMM;
+}
+
+/**
+ Returns whether Channel is or is not present.
+
+ @param[in] Outputs - Pointer to MRC global Output data.
+ @param[in] Channel - Channel to test.
+
+ @retval TRUE - if there is at least one enabled DIMM in the channel.
+ @retval FALSE - if there are no enabled DIMMs in the channel.
+**/
+BOOL
+MrcChannelExist (
+ IN const MrcOutput *const Outputs,
+ IN const U8 Channel
+ )
+{
+
+ return (Outputs->Controller[0].Channel[Channel].Status == CHANNEL_PRESENT) ? TRUE : FALSE;
+}
+
+/**
+ This function disable channel parameters.
+ After this function the MRC don't use with the channel.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] ChannelToDisable - Channel to disable.
+ @param[in] SkipDimmCapacity - Switch to skip setting the DimmCapacity to 0 for the dimms in the channel disabled.
+
+ @retval Nothing
+**/
+void
+MrcChannelDisable (
+ IN MrcParameters *const MrcData,
+ IN const U8 ChannelToDisable,
+ IN const U8 SkipDimmCapacity
+ )
+{
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ U32 Dimm;
+
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[ChannelToDisable];
+ if (ChannelOut->Status == CHANNEL_PRESENT) {
+ ChannelOut->Status = CHANNEL_DISABLED;
+ ChannelOut->RankInChannel = 0;
+ ChannelOut->ValidRankBitMask = 0;
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DimmOut->Status == DIMM_PRESENT) {
+ DimmOut->Status = DIMM_DISABLED;
+ DimmOut->RankInDIMM = 0;
+ if (!SkipDimmCapacity) {
+ DimmOut->DimmCapacity = 0;
+ }
+ }
+ }
+ }
+}
+
+/**
+ Convert the given frequency and reference clock to a clock ratio.
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] Frequency - The memory frequency.
+ @param[in] RefClk - The memory reference clock.
+ @param[in] BClk - The base system reference clock.
+
+ @retval Returns the memory clock ratio.
+**/
+MrcClockRatio
+MrcFrequencyToRatio (
+ IN MrcParameters *const MrcData,
+ IN const MrcFrequency Frequency,
+ IN const MrcRefClkSelect RefClk,
+ IN const MrcBClkRef BClk
+ )
+{
+ U64 Value;
+ U64 FreqValue;
+ U32 RefClkValue;
+ U32 BClkValue;
+
+ BClkValue = (BClk == 0) ? (BCLK_DEFAULT / 100000) : (BClk / 100000);
+ RefClkValue = (RefClk == MRC_REF_CLOCK_100) ? 200000 : 266667;
+ FreqValue = MrcOemMemoryMultiplyU64ByU32 (Frequency, 1000000000ULL);
+ Value = MrcOemMemoryDivideU64ByU64 (FreqValue, (RefClkValue * BClkValue));
+ Value = ((U32) Value + 500) / 1000;
+ return ((MrcClockRatio) Value);
+}
+
+/**
+ @brief
+ Convert the given ratio and reference clocks to a memory frequency.
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] Ratio - The memory ratio.
+ @param[in] RefClk - The memory reference clock.
+ @param[in] BClk - The base system reference clock.
+
+ @retval Returns the memory frequency.
+**/
+MrcFrequency
+MrcRatioToFrequency (
+ IN MrcParameters *const MrcData,
+ IN const MrcClockRatio Ratio,
+ IN const MrcRefClkSelect RefClk,
+ IN const MrcBClkRef BClk
+ )
+{
+ U64 Value;
+ U32 BClkValue;
+ U32 RefClkValue;
+
+ BClkValue = (BClk == 0) ? BCLK_DEFAULT : BClk;
+ RefClkValue = (RefClk == MRC_REF_CLOCK_100) ? 200000000 : 266666667;
+ Value = MrcOemMemoryMultiplyU64ByU32 (RefClkValue, Ratio * BClkValue);
+ Value += 50000000000000ULL;
+ Value = MrcOemMemoryDivideU64ByU64 (Value, 100000000000000ULL);
+ return ((MrcFrequency) Value);
+}
+
+/**
+ Convert the given ratio and reference clocks to a memory clock.
+
+ @param[in] Ratio - The memory ratio.
+ @param[in] RefClk - The memory reference clock.
+ @param[in] BClk - The base system reference clock.
+
+ @retval Returns the memory clock in femtoseconds.
+**/
+U32
+MrcRatioToClock (
+ IN const MrcClockRatio Ratio,
+ IN const MrcRefClkSelect RefClk,
+ IN const MrcBClkRef BClk
+ )
+{
+ U32 BClkValue;
+ U32 RefClkValue;
+ U32 Factor;
+ U64 Value;
+
+ BClkValue = (BClk == 0) ? 100000000UL : BClk;
+ Factor = BClkValue / 100000UL;
+ RefClkValue = (RefClk == MRC_REF_CLOCK_100) ? 1000000000UL : 1333333333UL;
+ Value = MrcOemMemoryMultiplyU64ByU32 (Factor, RefClkValue);
+ Value = MrcOemMemoryMultiplyU64ByU32 (Value, Ratio);
+ return ((Value == 0) ? 0 : (U32) MrcOemMemoryDivideU64ByU64 (10000000000000000000ULL, Value));
+}
+
+/**
+ This function return the DIMM number according to the rank number.
+
+ @param[in] Rank - The requested rank.
+
+ @retval The DIMM number.
+**/
+U8
+MrcGetDimmFromRank (
+ IN const U8 Rank
+ )
+{
+ U8 Dimm;
+
+ if (Rank == rRank0 || Rank == rRank1) {
+ Dimm = dDIMM0;
+ } else {
+ Dimm = dDIMM1;
+ }
+
+ return Dimm;
+}
+
+/**
+ This function sets the memory frequency.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval mrcSuccess on success, mrcFrequencyError on error.
+**/
+MrcStatus
+McFrequencySet (
+ IN MrcParameters *const MrcData
+ )
+{
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcFrequency NewFrequency;
+ MrcClockRatio Ratio;
+ MrcRefClkSelect RefClk;
+ PCU_CR_MC_BIOS_REQ_PCU_STRUCT McBiosReq;
+ U32 MemoryClock;
+#ifdef MRC_DEBUG_PRINT
+ U8 Channel;
+#endif // MRC_DEBUG_PRINT
+ U32 Time;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ NewFrequency = MrcGetCurrentMemoryFrequency (MrcData, &MemoryClock, &Ratio, &RefClk);
+ if (NewFrequency != fNoInit) {
+ Outputs->Frequency = NewFrequency;
+ Outputs->MemoryClock = MemoryClock;
+ Outputs->RefClk = RefClk;
+ Outputs->Ratio = Ratio;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "WARNING: Memory frequency is already initialized to %u\n", Outputs->Frequency);
+ return mrcSuccess;
+ }
+ //
+ // Set the reference clock, ratio and run_busy bit.
+ if (Outputs->BootMode == bmCold) {
+ if ((Inputs->MemoryProfile == USER_PROFILE) && (Inputs->Ratio > 0)) {
+ Outputs->Ratio = Inputs->Ratio;
+ } else {
+ Outputs->Ratio = MrcFrequencyToRatio (MrcData, Outputs->Frequency, Outputs->RefClk, Inputs->BClkFrequency);
+ }
+ }
+ if ((MEMORY_RATIO_MIN > Outputs->Ratio) || (MEMORY_RATIO_MAX < Outputs->Ratio)) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "Invalid DDR ratio of %u specified, range %u - %u\n",
+ Outputs->Ratio,
+ MEMORY_RATIO_MIN,
+ MEMORY_RATIO_MAX
+ );
+ } else {
+ McBiosReq.Data = 0;
+ McBiosReq.Bits.REQ_DATA = Outputs->Ratio;
+ McBiosReq.Bits.REQ_TYPE = (Outputs->RefClk == MRC_REF_CLOCK_133) ? 0 : 1;
+ McBiosReq.Bits.RUN_BUSY = 1;
+ MrcWriteCR (MrcData, PCU_CR_MC_BIOS_REQ_PCU_REG, McBiosReq.Data);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Attempting value = 0x%x - Pll busy wait ", McBiosReq.Data);
+ Time = 1000 * (U32) MrcGetCpuTime ();
+ while (McBiosReq.Bits.RUN_BUSY && (MrcGetCpuTime () < Time))
+ {
+ McBiosReq.Data = MrcReadCR (MrcData, PCU_CR_MC_BIOS_REQ_PCU_REG);
+ }
+
+ if (McBiosReq.Bits.RUN_BUSY) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "- NOT DONE. DDR frequency Update FAILED!\n");
+ return mrcFrequencyError;
+ } else
+ {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "- done\n");
+ }
+ //
+ // Wait on RCOMP Done. Needed to ensure Rcomp completes on warm reset/S3 before restoring dclk_enable.
+ //
+ if (CheckFirstRcompDone (MrcData) != mrcSuccess) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "RComp did not complete before the timeout in McFrequencySet\n");
+ return mrcDeviceBusy;
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Channel %u post PLL RCOMP REG = %Xh\n",
+ Channel,
+ MrcReadCR (
+ MrcData,
+ DDRCKECH0_CR_DDRCRCMDCOMP_REG + ((DDRCKECH1_CR_DDRCRCMDCOMP_REG - DDRCKECH0_CR_DDRCRCMDCOMP_REG) * Channel))
+ );
+ }
+#endif
+ Outputs->Frequency = MrcGetCurrentMemoryFrequency (MrcData, &MemoryClock, &Ratio, &RefClk);
+ MRC_DEBUG_MSG (
+ Debug,
+ (Ratio == Outputs->Ratio) ? MSG_LEVEL_NOTE : MSG_LEVEL_ERROR,
+ "Requested/actual ratio %u/%u, frequency is %u, BClk %uHz RefClk %uMHz, memory clock %u\n",
+ Outputs->Ratio,
+ Ratio,
+ Outputs->Frequency,
+ Inputs->BClkFrequency,
+ (RefClk == MRC_REF_CLOCK_133) ? 133 : 100,
+ MemoryClock);
+ if (Ratio == Outputs->Ratio) {
+ return mrcSuccess;
+ }
+ }
+ return mrcFrequencyError;
+}
+
+/**
+ Returns the extrapolated margin to a fixed # of errors (logT)
+ vrefpass is 10x the first passing margin (with no errors) (10x used for int math)
+ Errors at vrefpass/10+1 = log1
+ Errors at vrefpass/10+2 = logT
+
+ @param[in] vrefpass - 10x the first pass margin (w/no errors) (10x used for int match)
+ @param[in] errLog_1 - Errors at vrefpass/10+1
+ @param[in] errLog_2 - Errors at vrefpass/10+2
+ @param[in] errLog_Target - Error target determines extrapolation vs interpolation
+ @param[in, out] *berStats - Used to track interpolation vs extrapolation or if the slope is non-monotonic.
+ NOTE: target would be Interpolation only
+
+ @retval Interpolated/Extrapolated vref with the scale increased by 10.
+**/
+U32
+interpolateVref (
+ IN U32 vrefpass,
+ IN U32 errLog_1,
+ IN U32 errLog_2,
+ IN U32 errLog_Target,
+ IN OUT U32 *berStats
+ )
+{
+ U32 vref;
+ U32 slope;
+ U32 ErrLogDiff;
+
+ ErrLogDiff = errLog_2 - errLog_1;
+ if (errLog_2 <= errLog_1) {
+ berStats[3] += 1; // non-monotonic case
+ return (vrefpass * 10 + 10);
+ } else if (errLog_2 < errLog_Target) {
+ berStats[2] += 1; // error target above errLog_2 -> extrapolation
+ } else if (errLog_1 <= errLog_Target) {
+ berStats[1] += 1; // error target between errLog_1 and errLog_2 -> interpolation
+ } else {
+ berStats[0] += 1; // error target below errLog_1 -> extrapolation
+ }
+
+ //
+ //extrapolate above errLog_2, max extrapolation is 1 tick (10)
+ //
+ if (errLog_2 < errLog_Target) {
+ vref = vrefpass * 10 + 20 + MIN (10, (10 * (errLog_Target - errLog_2)) / (ErrLogDiff));
+ } else if ( (errLog_1 <= errLog_Target) && (errLog_Target <= errLog_2) && (ErrLogDiff != 0)) {
+ vref = vrefpass * 10 + 10 + (10 * (errLog_Target - errLog_1)) / (ErrLogDiff); //interpolate
+ } else {
+ //
+ //extrapolate below errLog_1
+ //
+ slope = (ErrLogDiff) > errLog_1 ? (ErrLogDiff) : errLog_1;
+ if (slope != 0) {
+ vref = vrefpass * 10 + (10 * errLog_Target) / slope;
+ } else {
+ vref = 0;
+ }
+ }
+
+ return vref; //returns a (vref * 10) interpolation/extrapolation
+};
+
+/**
+ This function swaps a subfield, within a 32 bit integer value with the specified value.
+
+ @param[in] CurrentValue - 32 bit input value.
+ @param[in] NewValue - 32 bit New value.
+ @param[in] Start - Subfield start bit.
+ @param[in] Length - Subfield length in bits/
+
+ @retval The updated 32 bit value.
+**/
+U32
+MrcBitSwap (
+ IN U32 CurrentValue,
+ IN const U32 NewValue,
+ IN const U8 Start,
+ IN const U8 Length
+ )
+{
+ U32 mask;
+
+ //
+ // Do bitwise replacement:
+ //
+ mask = (MRC_BIT0 << Length) - 1;
+ CurrentValue &= ~(mask << Start);
+ CurrentValue |= ((NewValue & mask) << Start);
+
+ return CurrentValue;
+}
+
+/**
+ This function returns the maximim Rx margin for a given Channel, Rank(s), and byte.
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] Channel - Channel to calculate max Rx margin.
+ @param[in] RankRx - Rank index. 0xFF causes all ranks to be considered.
+ @param[in] byte - Byte to check.
+ @param[in] sign - Sign of the margins (0 - negative/min, 1 - positive/max).
+ @param[in] MaxMargin - Current max margin value.
+
+ @retval The max Rx margin, either MaxMargin or value from stored margins.
+**/
+U8
+MrcCalcMaxRxMargin (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 RankRx,
+ IN const U8 byte,
+ IN const U8 sign,
+ IN U8 MaxMargin
+ )
+{
+ MrcChannelOut *ChannelOut;
+ U8 RxDqsP;
+ U8 RxDqsN;
+ U8 Start;
+ U8 Stop;
+ U8 rank;
+
+ //
+ // Check for saturation on Rx Timing
+ //
+ if (RankRx == 0xFF) {
+ //
+ // If desired for all ranks
+ //
+ Start = 0; // Start in rank 0
+ Stop = 4; // Up to 4 ranks
+ } else {
+ Start = RankRx;
+ Stop = RankRx + 1;
+ }
+
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ for (rank = Start; rank < Stop; rank++) {
+ if (MrcRankInChannelExist (MrcData, rank, Channel)) {
+ RxDqsP = ChannelOut->RxDqsP[rank][byte];
+ RxDqsN = ChannelOut->RxDqsN[rank][byte];
+
+ if (sign == 0) {
+ if (MaxMargin > RxDqsP) {
+ MaxMargin = RxDqsP;
+ }
+
+ if (MaxMargin > RxDqsN) {
+ MaxMargin = RxDqsN;
+ }
+ } else {
+ if (MaxMargin > 63 - RxDqsP) {
+ MaxMargin = 63 - RxDqsP;
+ }
+
+ if (MaxMargin > 63 - RxDqsN) {
+ MaxMargin = 63 - RxDqsN;
+ }
+ }
+ }
+ }
+
+ return MaxMargin;
+}
+
+/**
+ This function determines if a bit lane[0-7] has seen a pass and a fail in each byte for all channels populated.
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] chBitmask - Bit mask of channels to consider.
+ @param[in] OnePass - Array of Bit masks marking DQ lanes has had a passing value.
+ @param[in] OneFail - Array of Bit masks marking DQ lanes has had a failing value.
+
+ @retval The AND result of each Channel/byte for OnePass and OneFail.
+**/
+U8
+MrcAndBytes (
+ IN MrcParameters *const MrcData,
+ IN const U8 chBitmask,
+ IN U8 OnePass[MAX_CHANNEL][MAX_SDRAM_IN_DIMM],
+ IN U8 OneFail[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]
+ )
+{
+ U8 Res;
+ U8 Channel;
+ U8 byte;
+
+ Res = 0xFF;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((MRC_BIT0 << Channel) & chBitmask) == 0) {
+ continue;
+ }
+
+ for (byte = 0; byte < MrcData->SysOut.Outputs.SdramCount; byte++) {
+ Res &= OnePass[Channel][byte];
+ Res &= OneFail[Channel][byte];
+ }
+ }
+
+ return Res;
+}
+
+/**
+ This function Finds the margin for all channels/all bits. The margin sweep is a parameterized
+ Assume REUT test has already been fully setup to run
+ This will unscale the results such that future tests start at the correct point
+ Uses ChangeMargin function to handle a variety cases (Timing, Voltage, Fan, etc.)
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] chBitMask - Channel BIT mask for Channel(s) to work on
+ @param[in] Rank - Rank to work on
+ @param[in,out] marginbit - used as the return data ( real margin measurement, no 10x)
+ marginbit[ch,byte,bit,sign] = abs(Margin)
+ Note: If param == RdTBit/RdVBit/WrVBit, marginbit is also the starting point
+ @param[in,out] marginbyte - provides the starting point on a per byte basis (still 10x)
+ @param[in] param - defines the margin type
+ @param[in] mode - allows for different types of modes for margining
+ {Bit0: PhLock (keep all bytes within in ch in phase),
+ Bit1: Ch2Ch Data out of phase (LFSR seed)
+ Bits 15:2: Reserved}
+ @param[in] MaxMargin - Default Maximum margin
+
+ @retval mrcSuccess if successful, otherwise it returns an error status.
+**/
+MrcStatus
+MrcGetMarginBit (
+ IN MrcParameters *const MrcData,
+ IN U8 chBitMask,
+ IN U8 Rank,
+ IN OUT U32 marginbit[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS][MAX_EDGES],
+ IN OUT U32 marginbyte[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES],
+ IN U8 param,
+ IN U16 mode,
+ IN U8 MaxMargin
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcStatus Status;
+ U8 Channel;
+ U8 Byte;
+ U8 bit;
+ U8 sign;
+ S8 realSign;
+ U8 pbyte;
+ BOOL PerCh;
+ U8 PerBit;
+ U8 SeqLC[4];
+ U8 Points2D;
+ U8 DoneMask;
+ U8 ByteMax;
+ U8 SkipWait;
+ U8 chPass;
+ U8 chFail;
+ U8 NoECC;
+ U8 AllFail;
+ // Set to 1 after ch/byte/bit has a passing point
+ U8 OnePass[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ // Set to 1 after ch/byte/bit has a failing point
+ U8 OneFail[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U8 ErrByte;
+ U8 ErrECC;
+ U32 ErrLower;
+ U32 ErrUpper;
+ U8 MinMargin;
+ U32 value0;
+ U32 value1;
+ U32 v0;
+ U32 CMargin[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS]; // Current Margin Point Testing
+ U32 ABMargin[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Average Byte Margin
+ U32 MinTested[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS]; // Min Margin Point Tested
+ U8 PrintPetByte;
+ S8 RdTAdjust;
+ U32 Offset;
+ U32 BitTimePerBit;
+ U8 BitMask;
+ MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_STRUCT ReutGlobalCtl;
+
+ Status = mrcSuccess;
+ SkipWait = 0;
+ NoECC = 0;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+
+ //
+ // Define Constants
+ //
+ ByteMax = MaxMargin;
+
+ //
+ // Define the correct loopcount for test
+ //
+ if (Outputs->DQPat == SegmentWDB) {
+ SeqLC[0] = Outputs->DQPatLC;
+ SeqLC[1] = Outputs->DQPatLC;
+ SeqLC[2] = Outputs->DQPatLC + 4;
+ SeqLC[3] = Outputs->DQPatLC + 2;
+ } else {
+ SeqLC[0] = 1;
+ SeqLC[1] = 1;
+ SeqLC[2] = 1;
+ SeqLC[3] = 1;
+ }
+ //
+ // How many points to test
+ //
+ Points2D = 1 + (param / 16);
+
+ //
+ // Define PerByte param for PerBit cases
+ //
+ if (param == RdTBit) {
+ pbyte = RdT;
+ PerBit = 1;
+ } else if (param == WrTBit) {
+ pbyte = WrT;
+ PerBit = 1;
+ } else if (param == RdVBit) {
+ pbyte = RdV;
+ PerBit = 1;
+ } else {
+ pbyte = 0;
+ PerBit = 0;
+ }
+ //
+ // Print results PerBit or PerByte
+ //
+ PrintPetByte = (param == RdT || param == WrT || param == RdV);
+ //
+ // Created for debug purpose
+ // Are we using DIMM Vref? If so, need to use the same Vref across all bytes
+ //
+ PerCh = ((param == WrFan2) || (param == WrFan3) || (param == WrV) || (mode & 0x1)) && (PerBit == 0);
+
+ //
+ // Get Average Byte back to real margin numbers
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (chBitMask & (MRC_BIT0 << Channel)) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ ABMargin[Channel][Byte] = (marginbyte[Channel][Byte][0] + marginbyte[Channel][Byte][1]) / 20;
+ }
+ }
+ }
+ //
+ // Find Left and Right Edges
+ //
+ for (sign = 0; sign < 2; sign++) {
+ realSign = (S8) ((2 * sign) - 1);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n+--MrcGetMarginBit, rsign = %d\n", realSign);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (PrintPetByte) ? "\nMargin\tBits\n" : "");
+
+ //
+ // Initialize variables
+ //
+ DoneMask = 0xFF;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(chBitMask & (MRC_BIT0 << Channel))) {
+ continue; // This channel is not populated
+ }
+
+ MinMargin = 0x7F; // Start with a huge unsigned number
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // Init arrays to 0
+ //
+ OnePass[Channel][Byte] = OneFail[Channel][Byte] = 0;
+
+ //
+ // Find MaxMargin for this byte
+ //
+ ByteMax = MaxMargin;
+ if (param == RdT) {
+ ByteMax = MrcCalcMaxRxMargin (MrcData, Channel, Rank, Byte, sign, MaxMargin);
+ }
+
+ CMargin[Channel][Byte][0] = ABMargin[Channel][Byte] - 2; //start from a definite pass for all bytes/bits
+
+ if ((param == RdTBit) || (param == WrTBit)) {
+ // Special case for PerBit Timing
+ v0 = realSign * (CMargin[Channel][Byte][0] + 0); // Push into failing region
+ Status = ChangeMargin (MrcData, pbyte, v0, 0, 0, Channel, Rank, Byte, 0, 0, 0, MrcRegFileStart);
+ } else if (param == RdVBit) {
+ // Special case for PerBit Voltage
+ v0 = realSign * (CMargin[Channel][Byte][0] + 4); // Push into failing region
+ Status = ChangeMargin (MrcData, pbyte, v0, 0, 0, Channel, Rank, Byte, 0, 0, 0, MrcRegFileStart);
+ }
+ //
+ // Update the variables for PerBit tracking
+ //
+ if (PerBit) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ CMargin[Channel][Byte][bit] = marginbit[Channel][Byte][bit][sign];
+ //
+ // Double check saturation limits
+ //
+ if (CMargin[Channel][Byte][bit] > MaxMargin) {
+ CMargin[Channel][Byte][bit] = MaxMargin;
+ }
+ }
+ }
+ //
+ // Find MinMargin to start and set marginbyte for the PerCh case
+ //
+ if (PerCh) {
+ if (CMargin[Channel][Byte][0] < MinMargin) {
+ MinMargin = (U8) CMargin[Channel][Byte][0];
+ }
+
+ CMargin[Channel][Byte][0] = MinMargin;
+ }
+
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ MinTested[Channel][Byte][bit] = CMargin[Channel][Byte][bit * PerBit];
+ marginbit[Channel][Byte][bit][sign] = CMargin[Channel][Byte][bit * PerBit];
+ }
+ }
+ } // END OF CHANNEL LOOP
+
+ //##########################################################
+ // Search algorithm:
+ // Walk up until everybody fails. Then Walk down until everybody passes.
+ //##########################################################
+ while (MrcAndBytes (MrcData, chBitMask, OnePass, OneFail) != DoneMask) {
+ //
+ // Walk through all 2D points
+ ReutGlobalCtl.Data = 0;
+ ReutGlobalCtl.Bits.Global_Clear_Errors = 1;
+ MrcWriteCR8 (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG, (U8) ReutGlobalCtl.Data); // Clear errors
+ for (value1 = 0; value1 < Points2D; value1++) {
+ //
+ // Set Margin level
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(chBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ SkipWait = (chBitMask >> (Channel + 1)); // Skip if there are more channels
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if (PerBit) {
+ value0 = 0;
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ //
+ // Per Bit Deskew. Note: When (sign==1), then CMargin is off by 1.
+ // Suppose RdTBit and Right/Left Edge Last Pass @ CMargin = 12, 9
+ // Real Right Edge = (15-12)=3, Right Edge Moved By (8-3)=5
+ // Real Left Edge = 9, Left Edge Moved By (9-8) =1
+ // Center Movement = (5-1)/2 = +2
+ // To get correct answer, need to add +1 to CMargin for Right Edge
+ // ie: Center Moverment = (12+1-9)/2 = +2
+ // This error will be corrected at the edge of the function
+ // For RdTBit we shift data not strobe.Since we shift the opposite signal, sign is inverted
+ //
+ if ((param == RdTBit && sign) || ((param != RdTBit) && (sign == 0))) {
+ v0 = (MaxMargin - CMargin[Channel][Byte][bit]);
+ } else {
+ v0 = CMargin[Channel][Byte][bit];
+ }
+
+ if (v0 > MaxMargin) {
+ v0 = MaxMargin;
+ }
+ value0 |= (v0 << (4 * bit));
+ }
+ } else {
+ value0 = realSign * CMargin[Channel][Byte][0];
+ }
+ //
+ // EnMultiCast=0, ch,rank,byte,0, UpdateHost=0, SkipWait
+ //
+ Status = ChangeMargin (
+ MrcData,
+ param,
+ value0,
+ value1,
+ 0,
+ Channel,
+ Rank,
+ Byte,
+ 0,
+ 0,
+ SkipWait,
+ MrcRegFileStart
+ );
+ }
+ }
+ //
+ // Run Test
+ //
+ RunIOTest (MrcData, chBitMask, Outputs->DQPat, SeqLC, (value1 == 0), mode);
+
+ //
+ // Check if we have already failed and can stop running
+ //
+ if (value1 < (U32) (Points2D - 1)) {
+ AllFail = 1;
+ NoECC = (Outputs->EccSupport == FALSE);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(chBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_DATA_STATUS_REG +
+ (Channel * (MCHBAR_CH1_CR_REUT_CH_ERR_DATA_STATUS_REG - MCHBAR_CH0_CR_REUT_CH_ERR_DATA_STATUS_REG));
+ AllFail &= (MrcReadCR (MrcData, Offset) == 0xFFFFFFFF);
+ AllFail &= (MrcReadCR (MrcData, Offset + 4) == 0xFFFFFFFF);
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_ECC_CHUNK_RANK_BYTE_NTH_STATUS_REG +
+ (
+ Channel *
+ (
+ 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
+ )
+ );
+ AllFail &= (NoECC || ((U8) MrcReadCR (MrcData, Offset) == 0xFF));
+ }
+
+ if (AllFail) {
+ break; // break if any error
+ }
+ }
+ }
+ //
+ // Collect results and Update variables for next point to test
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(chBitMask & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ // Read Error Results (Assume all reads are 32 bit access
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_DATA_STATUS_REG +
+ (Channel * (MCHBAR_CH1_CR_REUT_CH_ERR_DATA_STATUS_REG - MCHBAR_CH0_CR_REUT_CH_ERR_DATA_STATUS_REG));
+ ErrLower = MrcReadCR (MrcData, Offset); // Lower 32 bits
+ ErrUpper = MrcReadCR (MrcData, Offset + 4); // Upper 32 bits
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_ECC_CHUNK_RANK_BYTE_NTH_STATUS_REG +
+ (
+ Channel *
+ (
+ 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
+ )
+ );
+ ErrECC = (U8) MrcReadCR (MrcData, Offset);
+
+ chPass = 0xFF;
+ chFail = 0xFF;
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // Extract Errors
+ //
+ if (Byte < 4) {
+ ErrByte = (U8) (ErrLower >> (8 * Byte));
+ } else if (Byte < 8) {
+ ErrByte = (U8) (ErrUpper >> (8 * (Byte - 4)));
+ } else {
+ ErrByte = ErrECC;
+ }
+
+ ErrByte &= DoneMask;
+#ifdef MRC_DEBUG_PRINT
+ if (param == WrTBit) {
+ Offset = DDRDATA0CH0_CR_TXPERBITRANK0_REG +
+ ((DDRDATA0CH1_CR_TXPERBITRANK0_REG - DDRDATA0CH0_CR_TXPERBITRANK0_REG) * Channel) +
+ ((DDRDATA0CH0_CR_TXPERBITRANK1_REG - DDRDATA0CH0_CR_TXPERBITRANK0_REG) * Rank)+
+ ((DDRDATA1CH0_CR_TXPERBITRANK0_REG - DDRDATA0CH0_CR_TXPERBITRANK0_REG) * Byte);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "0x%08X", MrcReadCR (MrcData, Offset));
+ } else if (param == RdTBit) {
+ Offset = DDRDATA0CH0_CR_RXPERBITRANK0_REG +
+ ((DDRDATA0CH1_CR_RXPERBITRANK0_REG - DDRDATA0CH0_CR_RXPERBITRANK0_REG) * Channel) +
+ ((DDRDATA0CH0_CR_RXPERBITRANK1_REG - DDRDATA0CH0_CR_RXPERBITRANK0_REG) * Rank)+
+ ((DDRDATA1CH0_CR_RXPERBITRANK0_REG - DDRDATA0CH0_CR_RXPERBITRANK0_REG) * Byte);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "0x%08X", MrcReadCR (MrcData, Offset));
+ } else if (param == RdVBit) {
+ 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, "0x%08X", MrcReadCR (MrcData, Offset));
+ } else if (param == WrT || param == RdT || param == RdV) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "% 2d", CMargin[Channel][Byte][0]);
+ }
+#endif // MRC_DEBUG_PRINT
+
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ BitMask = MRC_BIT0 << bit;
+ BitTimePerBit = bit * PerBit;
+ //
+ // Skip if this Bit Group is done
+ //
+ if (OnePass[Channel][Byte] & OneFail[Channel][Byte] & (BitMask)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " $");
+ continue;
+ }
+ //
+ // Update variables for fail
+ //
+ if (ErrByte & (BitMask)) {
+ OneFail[Channel][Byte] |= (BitMask);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " #");
+
+ //
+ // Handle Speckles
+ //
+ if (marginbit[Channel][Byte][bit][sign] >= CMargin[Channel][Byte][BitTimePerBit]) {
+ marginbit[Channel][Byte][bit][sign] = CMargin[Channel][Byte][BitTimePerBit] - 1;
+ OnePass[Channel][Byte] &= ~(BitMask);
+ }
+ //
+ // Update variables for pass
+ //
+ } else {
+ OnePass[Channel][Byte] |= (BitMask);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " .");
+
+ if (marginbit[Channel][Byte][bit][sign] < CMargin[Channel][Byte][BitTimePerBit]) {
+ marginbit[Channel][Byte][bit][sign] = CMargin[Channel][Byte][BitTimePerBit];
+ }
+ }
+ }
+ //
+ // FIND MAX Saturation limit
+ //
+ ByteMax = MaxMargin;
+ if (param == RdT) {
+ ByteMax = MrcCalcMaxRxMargin (MrcData, Channel, Rank, Byte, sign, MaxMargin);
+
+ }
+ //
+ // HANDLE Saturation
+ //
+ if (PerBit) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ BitMask = MRC_BIT0 << bit;
+ if (CMargin[Channel][Byte][bit] >= ByteMax) {
+ OneFail[Channel][Byte] |= (BitMask);
+ }
+
+ if (CMargin[Channel][Byte][bit] == 0) {
+ OnePass[Channel][Byte] |= (BitMask);
+ }
+ }
+ } else {
+ if (CMargin[Channel][Byte][0] >= ByteMax) {
+ OneFail[Channel][Byte] = DoneMask;
+ }
+
+ if (CMargin[Channel][Byte][0] == 0) {
+ OnePass[Channel][Byte] = DoneMask;
+ }
+ }
+ //
+ // DECIDE WHAT TO TEST NEXT
+ // If PerByte, Do this within the for byte loop
+ //
+ chPass &= OnePass[Channel][Byte];
+ chFail &= OneFail[Channel][Byte];
+
+ if (PerCh == FALSE) {
+ if (PerBit) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ BitMask = MRC_BIT0 << bit;
+ //
+ // Skip if this Bit Group is done
+ //
+ if (OnePass[Channel][Byte] & OneFail[Channel][Byte] & (BitMask)) {
+ continue;
+ }
+
+ if ((OneFail[Channel][Byte] & BitMask) == 0) {
+ CMargin[Channel][Byte][bit] += 1;
+ } else if ((OnePass[Channel][Byte] & BitMask) == 0) {
+ MinTested[Channel][Byte][bit] -= 1;
+ CMargin[Channel][Byte][bit] = MinTested[Channel][Byte][bit];
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "WARNING! Can't have both: OnePass and OneFail Not Done\n");
+ }
+ }
+ } else {
+ //
+ // PerCh
+ // Skip if this Byte Group is done
+ //
+ if ((OnePass[Channel][Byte] & OneFail[Channel][Byte]) == DoneMask) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ continue;
+ }
+
+ if (OneFail[Channel][Byte] != DoneMask) {
+ CMargin[Channel][Byte][0] += 1;
+ } else if (OnePass[Channel][Byte] != DoneMask) {
+ MinTested[Channel][Byte][0] -= 1;
+ CMargin[Channel][Byte][0] = MinTested[Channel][Byte][0];
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ }
+ //
+ // END OF BYTE LOOP
+ // DECIDE WHAT TO TEST NEXT
+ // If PerCh, Do this within the for ch loop
+ //
+ if (PerCh == TRUE) {
+ if ((chPass & chFail) == DoneMask) {
+ continue;
+ }
+
+ if (chFail != DoneMask) {
+ CMargin[Channel][0][0] += 1;
+ } else {
+ MinTested[Channel][0][0] -= 1;
+ CMargin[Channel][0][0] = MinTested[Channel][0][0];
+ }
+ //
+ // All bytes must use the same margin point
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ CMargin[Channel][Byte][0] = CMargin[Channel][0][0];
+ }
+ }
+ }
+ //
+ // END OF CHANNEL LOOP
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+ //
+ // END OF WHILE LOOP
+ // Update MarginByte to have the correct result
+ //
+ if (PerBit == 0) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (chBitMask & (MRC_BIT0 << Channel)) {
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MinMargin = 0x7F; // Start with a huge unsigned number
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ if (MinMargin > marginbit[Channel][Byte][bit][sign]) {
+ MinMargin = (U8) marginbit[Channel][Byte][bit][sign];
+ }
+ }
+
+ marginbyte[Channel][Byte][sign] = MinMargin * 10;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+--marginbyte = MinMargin*10 = %d\n", MinMargin*10);
+ //
+ }
+ }
+ }
+ } else {
+ //
+ // Bit Margins
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(chBitMask & (1 << Channel))) {
+ continue;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ if ((param == RdTBit && sign) || ((param != RdTBit) && (sign == 0))) {
+ marginbit[Channel][Byte][bit][sign] = MaxMargin - marginbit[Channel][Byte][bit][sign];
+ }
+ }
+ }
+ }
+ //
+ // Cleanup after test
+ //
+ Status = ChangeMargin (MrcData, pbyte, 0, 0, 1, 0, Rank, 0, 0, 0, 0, MrcRegFileCurrent);
+ }
+ }
+ //
+ // END OF SIGN LOOP
+ // Clean up after step
+ // @todo Restore perBit to last saved value
+ //
+ value0 = (PerBit == 1 ? 0x88888888 : 0);
+ Status = ChangeMargin (MrcData, param, value0, 0, 1, 0, Rank, 0, 0, 0, 0, MrcRegFileCurrent);
+
+ //
+ // Correct for 1 tick error in Per Bit Deskew right edge
+ //
+ RdTAdjust = 1;
+#ifdef MRC_DEBUG_PRINT
+ if (PerBit == 1) {
+ if (param == RdTBit) {
+ RdTAdjust = -1;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nGains ");
+ 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++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " %d %d",
+ ((RdTAdjust) * (8 - marginbit[Channel][Byte][bit][0])),
+ ((RdTAdjust) * (marginbit[Channel][Byte][bit][1] - 8))
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ }
+ }
+ }
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nCt");
+
+ 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++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%4d",
+ (S8) (marginbit[Channel][Byte][bit][1] - marginbit[Channel][Byte][bit][0]) / 2
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ }
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+#endif // MRC_DEBUG_PRINT
+
+ return Status;
+}
+
+/**
+ Assume REUT test has already been fully setup to run
+ Finds the margin for all channels/all bytes
+ The margin sweep is parameterized
+ Uses ChangeMargin function to handle a variety of cases (Timing, Voltage, Fan, etc.)
+ mode allows for different types of modes for margining:
+ mode is {Bit0: PhLock (keep all bytes within in ch in phase),
+ Bit1: Ch2Ch Data out of phase (LFSR seed), Bit 15:2: Reserved}
+ marginByte is used as the starting point for the search (10x the actual margin)
+ marginch returns the results (10x the actual margin)
+ Interior: Search inside marginch limits, enabling multiple calls with different setups
+ To reduce repeatibility noise, the returned margins is actually a BER extrapolation
+
+ @param[in] MrcData - The global MrcData
+ @param[in,out] marginByte - Data structure with the latest margin results
+ @param[in] chBitmask - Bit mask of present channels
+ @param[in] Rank - Rank to change margins for
+ @param[in] RankRx - Ranks for Rx margin
+ @param[in] param - parameter to get margins for
+ @param[in] mode - allows for different types of modes for margining:
+ @param[in] BMap - Byte mapping to configure error counter control register
+ @param[in] EnBER - Enable BER extrapolation calculations
+ @param[in] MaxMargin - Max Margin allowed for the parameter
+ @param[in] Interior - Search inside marginch limits, enabling multiple calls with different setups
+ @param[in,out] BERStats - Bit Error Rate Statistics.
+
+ @retval mrcSuccess if successful, otherwise returns an error status.
+**/
+MrcStatus
+MrcGetBERMarginByte (
+ IN 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 Rank,
+ IN U8 RankRx,
+ IN U8 param,
+ IN U16 mode,
+ IN U8 *BMap,
+ IN U8 EnBER,
+ IN U8 MaxMargin,
+ IN U8 Interior,
+ IN OUT U32 *BERStats
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ MrcControllerOut *ControllerOut;
+ U32 *MarginByteTemp;
+ MrcStatus Status;
+ U8 ResultType;
+ U8 sign;
+ S8 rSign;
+ U8 SeqLC[4];
+ U8 Points2D;
+ U8 Channel;
+ U8 byte;
+ U8 chByte;
+ U8 SkipWait;
+ U8 byteMax;
+ U8 Margin;
+ U16 DoneMask;
+ // Set to 1 after ch has 2 passing points
+ U16 TwoPass[MAX_CHANNEL];
+ // Set to 1 after ch has 2 failing points
+ U16 TwoFail[MAX_CHANNEL];
+ U16 res;
+ U16 BitMask;
+ S8 Delta;
+ BOOL Done;
+ BOOL allFail;
+ BOOL PerCh;
+ U32 value0;
+ U32 value1;
+ U32 tmp;
+ U32 ErrCount;
+ U8 LastPassVref[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Last passing Vref
+ U32 InitValue[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Initial value from margin byte
+ U8 MaxTested[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Highest Vref Point Tested
+ U8 MinTested[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Lowest Vref Point Tested
+ // Log8(Error count) at different Vref Points. 32 bit number that is broken in 4 bytes
+ // [LastPass+2, LastPass+1, LastPass, LastPass-1]
+ U32 Errors[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U32 Offset;
+
+ MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_CTL_0_STRUCT ReutChErrCounterCtl;
+ MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_STRUCT ReutGlobalCtl;
+ MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_0_STRUCT ReutChErrCounterStatus;
+ MCHBAR_CH0_CR_SC_IO_LATENCY_STRUCT ScIoLatency;
+
+
+ Status = mrcSuccess;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ chByte = 0;
+ Points2D = (param / RdFan2) + 1;
+ ResultType = GetMarginResultType (param);
+
+ //
+ // Are we using DIMM Vref?
+ //
+ PerCh = (param == WrFan2 || param == WrFan3 || param == WrV || ((mode & 1) == 1)); // WrFan not defined
+
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+--->MrcGetBERMarginByte, Points2D: %d, PerCh: %d --\n", Points2D,PerCh);
+
+ DoneMask = (MRC_BIT0 << Outputs->SdramCount) - 1; // 0x1FF or 0xFF
+
+ if (Outputs->DQPat == SegmentWDB) {
+ SeqLC[0] = Outputs->DQPatLC;
+ SeqLC[1] = Outputs->DQPatLC;
+ SeqLC[2] = Outputs->DQPatLC + 4;
+ SeqLC[3] = Outputs->DQPatLC + 2;
+ } else {
+ SeqLC[0] = 1;
+ SeqLC[1] = 1;
+ SeqLC[2] = 1;
+ SeqLC[3] = 1;
+ }
+ //
+ // Run through margin test
+ //
+ for (sign = 0; sign < 2; sign++) {
+ rSign = (S8) ((2 * sign) - 1);
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+--MrcGetBERMarginByte, rsign = %d\n", rSign);
+ //
+ MrcOemMemorySet ((U8 *) TwoPass, 0, sizeof (TwoPass));
+ MrcOemMemorySet ((U8 *) TwoFail, 0, sizeof (TwoFail));
+
+ //
+ // Initialize variables
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & chBitmask)) {
+ TwoPass[Channel] = DoneMask;
+ TwoFail[Channel] = DoneMask;
+ continue;
+ }
+
+ MinTested[Channel][0] = 0x7F;
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ LastPassVref[Channel][byte] = 0x7F; // Start with a huge unsigned numer - 128
+ Errors[Channel][byte] = 0;
+
+ //
+ // Find MaxMargin for this byte
+ //
+ byteMax = MaxMargin;
+ if (param == RdT) {
+ byteMax = MrcCalcMaxRxMargin (MrcData, Channel, RankRx, byte, sign, MaxMargin);
+ }
+ //
+ // Scale MarginResult back to real margin numbers. Set Max/MinTested
+ //
+ MarginByteTemp = &marginByte[ResultType][Rank][Channel][byte][sign];
+ *MarginByteTemp = *MarginByteTemp / 10;
+ if (*MarginByteTemp > byteMax) {
+ *MarginByteTemp = byteMax;
+ }
+
+ InitValue[Channel][byte] = *MarginByteTemp;
+
+ //
+ // if Per Ch, find MinMargin to start. Else set margin for that Byte
+ //
+ if (PerCh == TRUE) {
+ if (*MarginByteTemp < MinTested[Channel][0]) {
+ MaxTested[Channel][0] = (U8) *MarginByteTemp;
+ MinTested[Channel][0] = (U8) *MarginByteTemp;
+ }
+ } else {
+ MaxTested[Channel][byte] = (U8) *MarginByteTemp;
+ MinTested[Channel][byte] = (U8) *MarginByteTemp;
+ }
+ //
+ // Setup REUT Error Counters to count errors per byte lane
+ // Count Errors on a particular Byte Group BITS 8:7 = 10b
+ //
+ ReutChErrCounterCtl.Data = 0;
+ ReutChErrCounterCtl.Bits.Counter_Pointer = BMap[byte];
+ ReutChErrCounterCtl.Bits.Counter_Control = 2;
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_CTL_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_ERR_COUNTER_CTL_0_REG - MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_CTL_0_REG) * Channel) +
+ ((MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_CTL_1_REG - MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_CTL_0_REG) * byte);
+ MrcWriteCR (MrcData, Offset, ReutChErrCounterCtl.Data);
+ }
+ //
+ // Set MarginResult for the PerCh case
+ //
+ if (PerCh == TRUE) {
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ marginByte[ResultType][Rank][Channel][byte][sign] = MinTested[Channel][0];
+ }
+ }
+ }
+ //
+ // Search algorithm:
+ // If start with a passing point, walk to hit 2 failing points
+ // Return as needed to hit a second passing point
+ // If start with a failing point, walk to hit 2 passing points
+ // Return as needed to hit a second failing point
+ // Keep testing until all ch/bytes find 2 passes and 2 fails
+ //
+ Done = FALSE;
+ do {
+ //
+ // Walk through all 2D points
+ //
+ ReutGlobalCtl.Data = 0;
+ ReutGlobalCtl.Bits.Global_Clear_Errors = 1;
+ MrcWriteCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG, ReutGlobalCtl.Data); // Clear errors
+ for (value1 = 0; value1 < Points2D; value1++) {
+ //
+ // Set Vref level
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & chBitmask)) {
+ continue;
+ }
+
+ SkipWait = (chBitmask >> (Channel + 1)); // Skip if there are more channels
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ value0 = rSign * marginByte[ResultType][Rank][Channel][byte][sign];
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+----->Value0 is %d, Value1 is %d\n", (S32)value0, value1);
+ //
+ Status = ChangeMargin (
+ MrcData,
+ param,
+ value0,
+ value1,
+ 0,
+ Channel,
+ Rank,
+ byte,
+ 0,
+ 0,
+ SkipWait,
+ MrcRegFileStart
+ );
+ if ((PerCh) && ((mode & 1) == 0)) {
+ //
+ // Only Byte 7 on Channel 1 is needed to update Wr DIMM Vref - Taken care of inside ChangeMargin routine
+ //
+ break;
+ }
+ }
+ }
+ //
+ // Run Test
+ //
+ RunIOTest (MrcData, chBitmask, Outputs->DQPat, SeqLC, (value1 == 0), mode);
+
+ //
+ // What is the idea behind this? What if all byte groups failed?
+ //
+ if (EnBER == 0 && value1 < (U32) (Points2D - 1)) {
+ allFail = TRUE;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & chBitmask)) {
+ continue;
+ }
+ //
+ // Read out per byte error results
+ //
+ 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);
+ res = (U16) MrcReadCR (MrcData, Offset);
+ if ((res & DoneMask) != DoneMask) {
+ allFail = FALSE;
+ }
+ }
+
+ if (allFail == TRUE) {
+ break;
+ }
+ }
+ }
+ //
+ // Collect results and Update variables for next point to test
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & chBitmask)) {
+ continue;
+ }
+
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ BitMask = MRC_BIT0 << byte;
+ //
+ // Skip if this Byte Group is done
+ //
+ if ((TwoPass[Channel] & TwoFail[Channel] & (BitMask)) != 0) {
+ continue;
+ }
+ //
+ // Handle PerCh vs. PerByte variable differences
+ //
+ chByte = (PerCh == TRUE ? 0 : byte);
+
+ //
+ // Read Error Count
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG - MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG) * Channel) +
+ ((MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_1_REG - MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG) * byte);
+ ReutChErrCounterStatus.Data = MrcReadCR (MrcData, Offset);
+ ErrCount = ReutChErrCounterStatus.Bits.Counter_Status;
+ Margin = (U8) marginByte[ResultType][Rank][Channel][byte][sign];
+ Delta = (Margin - LastPassVref[Channel][byte]);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+----->channel: %d, Error count:%x.\n", Channel, ErrCount);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+----->Margin:%d, LastPassVref:%d, delta:%d. sign:%d\n", Margin, LastPassVref[Channel][byte], (S8) Delta, sign);
+
+ // Update Pass/Fail Variables:
+ //
+ if (ErrCount == 0 && Margin == MaxTested[Channel][chByte]) {
+ //
+ // Passing while walking up
+ //
+ if (Delta < 0) {
+ //
+ // First passing point
+ // @todo: Next line should be changed to return failure. Code should never hang.
+ //
+ MRC_ASSERT (
+ MinTested[Channel][chByte] == MaxTested[Channel][chByte],
+ Debug,
+ "Error: MaxTested < LastPass after first point"
+ );
+ LastPassVref[Channel][byte] = Margin;
+ } else if (Delta == 1) {
+ //
+ // Normal, walk to fail
+ //
+ Errors[Channel][byte] = MrcBitShift (Errors[Channel][byte], -8 * Delta) & BER_ERROR_MASK;
+ LastPassVref[Channel][byte] = Margin;
+ TwoPass[Channel] |= (BitMask);
+ } else if (Delta == 2) {
+ //
+ // Speckling in response, Consider point as error
+ //
+ Errors[Channel][byte] = MrcBitSwap (Errors[Channel][byte], MrcLog8 (ErrCount), 24, 8);
+ TwoFail[Channel] |= (BitMask);
+ } else {
+ //
+ // @todo: Next line should be changed to return failure. Code should never hang.
+ //
+ MRC_ASSERT (
+ FALSE,
+ Debug,
+ "Error: Tested point twice or Tested >2 above LastPass (Passing while walking up)"
+ );
+ }
+ } else if (ErrCount == 0 && Margin == MinTested[Channel][chByte]) {
+ //
+ // Skip if this byte is already done
+ //
+ if ((TwoPass[Channel] & (BitMask)) != 0) {
+ continue;
+ }
+
+ if (Delta == -1) {
+ //
+ // Finding 2nd pass
+ //
+ Errors[Channel][byte] = MrcBitSwap (Errors[Channel][byte], 0, 0, 8);
+ TwoPass[Channel] |= (BitMask);
+ } else {
+ //
+ // 1st passing point
+ // Don't shift Errors. Fail points already assumed correct LastPass
+ //
+ LastPassVref[Channel][byte] = Margin;
+ TwoPass[Channel] &= ~(BitMask);
+ }
+ } else if (ErrCount > 0 && Margin == MaxTested[Channel][chByte]) {
+ //
+ // Failing while walking up
+ // @todo: Next line should be changed to return failure. Code should never hang.
+ //
+ MRC_ASSERT (Delta <= 2, Debug, "Error: Tested >2 above LastPass (Failing while walking up)");
+ if (Delta < 2) {
+ //
+ // first failing point
+ //
+ Errors[Channel][byte] = MrcBitSwap (Errors[Channel][byte], MrcLog8 (ErrCount), 16, 8);
+ TwoFail[Channel] &= ~(BitMask);
+ } else if (Delta == 2) {
+ //
+ // 2nd failing point
+ //
+ Errors[Channel][byte] = MrcBitSwap (Errors[Channel][byte], MrcLog8 (ErrCount), 24, 8);
+ TwoFail[Channel] |= (BitMask);
+ }
+ } else if (ErrCount > 0 && Margin == MinTested[Channel][chByte]) {
+ //
+ // Failing while walking down
+ //
+ if (LastPassVref[Channel][byte] < 0xFF && Delta <= 0) {
+ //
+ // Adjust LastPassVref and Error count to be below this failure point.
+ //
+ Errors[Channel][byte] = MrcBitSwap (Errors[Channel][byte], MrcLog8 (ErrCount), 8 * (Delta + 1), 8);
+ Errors[Channel][byte] = MrcBitShift (Errors[Channel][byte], 8 * (1 - Delta));
+ LastPassVref[Channel][byte] = Margin - 1;
+ } else {
+ tmp = ((Errors[Channel][byte] & 0xFF0000) << 8) + MrcLog8 (ErrCount);
+ Errors[Channel][byte] = MrcBitSwap (Errors[Channel][byte], tmp, 16, 16);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"Unexpected case for channel: %d, delta: %d.\n", Channel, Delta);
+ }
+
+ if (MinTested[Channel][chByte] < MaxTested[Channel][chByte]) {
+ TwoFail[Channel] |= (BitMask);
+ }
+
+ if (Delta <= 0) {
+ TwoPass[Channel] &= ~(BitMask);
+ }
+ } else {
+ //
+ // @todo: Next line should be changed to return failure. Code should never hang.
+ //
+ MRC_ASSERT (FALSE, Debug, "Error: Testing points other than Max/MinTested");
+ }
+ //
+ // FIND MAX Saturation limit
+ //
+ byteMax = MaxMargin;
+ if (param == RdT) {
+ byteMax = MrcCalcMaxRxMargin (MrcData, Channel, RankRx, byte, sign, MaxMargin);
+ }
+
+ if (Interior && InitValue[Channel][byte] == Margin) {
+ byteMax = Margin;
+ }
+ //
+ // HANDLE MAX Saturation
+ //
+ if (Margin == byteMax) {
+ TwoFail[Channel] |= (BitMask);
+ }
+
+ if (ErrCount == 0 && byteMax == LastPassVref[Channel][byte] && (TwoPass[Channel] & (BitMask)) != 0) {
+ Errors[Channel][byte] = MrcBitSwap (Errors[Channel][byte], 0xFFFE, 16, 16);
+ }
+ //
+ // HANDLE MIN Saturation
+ //
+ if (Margin == 0) {
+ TwoPass[Channel] |= (BitMask);
+ if (ErrCount > 0) {
+ TwoFail[Channel] |= (BitMask);
+ LastPassVref[Channel][byte] = 0;
+ Errors[Channel][byte] = MrcBitSwap (
+ Errors[Channel][byte],
+ (BER_LOG_TARGET << 8) + BER_LOG_TARGET,
+ 16,
+ 16
+ );
+ }
+ }
+ //
+ // DECIDE WHAT TO TEST NEXT
+ // If In PerByte, Do this within the for byte loop
+ //
+ if (PerCh == FALSE) {
+ //
+ // Skip if this Byte Group is done
+ //
+ if ((TwoPass[Channel] & TwoFail[Channel] & (BitMask)) != 0) {
+ continue;
+ }
+
+ if (ErrCount == 0) {
+ if ((TwoFail[Channel] & (BitMask)) == 0) {
+ //
+ // Count up to find 2 fails
+ //
+ marginByte[ResultType][Rank][Channel][byte][sign] = ++MaxTested[Channel][chByte];
+ } else {
+ //
+ // Count down to find 2 passes
+ //
+ marginByte[ResultType][Rank][Channel][byte][sign] = --MinTested[Channel][chByte];
+ }
+ } else {
+ if ((TwoPass[Channel] & (BitMask)) == 0) {
+ marginByte[ResultType][Rank][Channel][byte][sign] = --MinTested[Channel][chByte];
+ } else {
+ marginByte[ResultType][Rank][Channel][byte][sign] = ++MaxTested[Channel][chByte];
+ }
+ }
+ }
+ }
+ //
+ // DECIDE WHAT TO TEST NEXT
+ // If In PerCh, Do this within the for ch loop
+ //
+ if (PerCh == TRUE) {
+ if ((TwoPass[Channel] & TwoFail[Channel]) == DoneMask) {
+ continue;
+ }
+
+ if (TwoPass[Channel] != DoneMask) {
+ marginByte[ResultType][Rank][Channel][0][sign] = --MinTested[Channel][chByte];
+ } else {
+ marginByte[ResultType][Rank][Channel][0][sign] = ++MaxTested[Channel][chByte];
+ }
+ //
+ // All bytes must use the same margin point
+ //
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ marginByte[ResultType][Rank][Channel][byte][sign] = marginByte[ResultType][Rank][Channel][0][sign];
+ }
+ }
+ }
+ //
+ // check if we are done
+ //
+ Done = TRUE;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((TwoPass[Channel] & DoneMask) != DoneMask || (TwoFail[Channel] & DoneMask) != DoneMask) {
+ Done = FALSE;
+ break;
+ }
+ }
+ } while (Done == FALSE);
+
+ //
+ // Calculate the effective margin
+ // Update MarginResult with extroploated BER Margin
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((MRC_BIT0 << Channel) & chBitmask) == 0) {
+ continue;
+ }
+
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+----->marginByte[Ch %d, Byte%d, Sign %d] is: %d\n", Channel, byte, sign, marginByte[ResultType][Rank][Channel][byte][sign]);
+ if (EnBER) {
+ marginByte[ResultType][Rank][Channel][byte][sign] = interpolateVref (
+ LastPassVref[Channel][byte],
+ (Errors[Channel][byte] >> 16) & 0xFF,
+ (Errors[Channel][byte] >> 24) & 0xFF,
+ BER_LOG_TARGET,
+ BERStats
+ );
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+----->BERmarginByte[Ch %d, Byte%d, Sign %d] is: %d\n", Channel, byte, sign, marginByte[ResultType][Rank][Channel][byte][sign]);
+ } else {
+ marginByte[ResultType][Rank][Channel][byte][sign] = 10 * LastPassVref[Channel][byte];
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+----->marginByte[Ch %d, Byte%d, Sign %d] is: %d\n", Channel, byte, sign, marginByte[ResultType][Rank][Channel][byte][sign]);
+ }
+ }
+ }
+ }
+ //
+ // Clean up after step
+ //
+ if (param == RcvEnaX) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((MRC_BIT0 << Channel) & chBitmask) == 0) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ UpdateRxT (MrcData, Channel, Rank, byte, 0xff, 0);
+ 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);
+ ScIoLatency.Bits.RT_IOCOMP = MCHBAR_CH0_CR_SC_IO_LATENCY_RT_IOCOMP_MAX & ChannelOut->RTIoComp;
+ MrcWriteCR (MrcData, Offset, ScIoLatency.Data);
+ }
+ }
+ }
+ Status = ChangeMargin (MrcData, param, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileCurrent);
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ Offset = MCSCHEDS_CR_REUT_CH_ERR_COUNTER_CTL_0_REG +
+ ((MCSCHEDS_CR_REUT_CH_ERR_COUNTER_CTL_1_REG - MCSCHEDS_CR_REUT_CH_ERR_COUNTER_CTL_0_REG) * byte);
+ MrcWriteCrMulticast (MrcData, Offset, 0);
+ }
+
+ return Status;
+}
+
+/**
+ Assume REUT test has already been fully setup to run
+ Finds the margin for all channels/all bytes
+ The margin sweep is parameterized
+ Uses ChangeMargin function to handle a variety of cases (Timing, Voltage, Fan, etc.)
+ mode allows for different types of modes for margining:
+ mode is {Bit0: PhLock (keep all bytes within in ch in phase),
+ Bit1: Ch2Ch Data out of phase (LFSR seed), Bit 15:2: Reserved}
+ marginCh is used as the starting point for the search (10x the actual margin)
+ marginch returns the results (10x the actual margin)
+ Interior: Search inside marginch limits, enabling multiple calls with different setups
+ To reduce repeatibility noise, the returned margins is actually a BER extrapolation
+
+ @param[in] MrcData - The global MrcData
+ @param[in,out] marginCh - Data structure with the latest margin results
+ @param[in] chBitmask - Bit mask of present channels
+ @param[in] RankRx - Ranks for Rx margin
+ @param[in] Rank - Rank to change margins for
+ @param[in] param - parameter to get margins for
+ @param[in] mode - allows for different types of modes for margining:
+ @param[in] EnBER - Enable BER extrapolation calculations
+ @param[in] MaxMargin - Max Margin allowed for the parameter
+ @param[in] Interior - Search inside marginch limits, enabling multiple calls with different setups
+ @param[in,out] BERStats - Bit Error Rate Statistics.
+
+ @retval mrcSuccess if successful, otherwise returns an error status.
+**/
+MrcStatus
+MrcGetBERMarginCh (
+ IN MrcParameters *MrcData,
+ IN U32 marginCh[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES],
+ IN OUT U8 chBitmask,
+ IN U8 RankRx,
+ IN U8 Rank,
+ IN U8 param,
+ IN U16 mode,
+ IN U8 EnBER,
+ IN U8 MaxMargin,
+ IN U8 Interior,
+ IN OUT U32 *BERStats
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcStatus Status;
+ U8 ResultType;
+ U8 sign;
+ S8 rSign;
+ U8 SeqLC[4];
+ U8 Points2D;
+ U8 Channel;
+ U8 byte;
+ U8 SkipWait;
+ U8 byteMax[MAX_CHANNEL];
+ U8 Margin;
+ // Set to 1 after ch has 2 passing points
+ U16 TwoPass[MAX_CHANNEL];
+ // Set to 1 after ch has 2 failing points
+ U8 TwoFail[MAX_CHANNEL];
+ S8 Delta;
+ BOOL Done;
+ BOOL DimmVrefParam;
+ U32 value0;
+ U32 value1;
+ U32 tmp;
+ U32 chError;
+ U32 ErrCount;
+ U8 LastPassVref[MAX_CHANNEL]; // Last passing Vref
+ U8 MaxTested[MAX_CHANNEL]; // Highest Vref Point Tested
+ U8 MinTested[MAX_CHANNEL]; // Lowest Vref Point Tested
+ // Log8(Error count) at different Vref Points. 32 bit number that is broken in 4 bytes
+ // [LastPass+2, LastPass+1, LastPass, LastPass-1]
+ U32 Errors[MAX_CHANNEL];
+ U32 Offset;
+ BOOL PerMc;
+ U8 McChannel;
+
+ MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_STRUCT ReutGlobalCtl;
+ MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_0_STRUCT ReutChErrCounterStatus;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ Status = mrcSuccess;
+ ResultType = GetMarginResultType(param);
+ Points2D = (param / 16) + 1; // 2 for Fan2 and 3 for Fan3
+ McChannel = 0;
+
+ if (Outputs->DQPat == SegmentWDB) {
+ SeqLC[0] = Outputs->DQPatLC;
+ SeqLC[1] = Outputs->DQPatLC;
+ SeqLC[2] = Outputs->DQPatLC + 4;
+ SeqLC[3] = Outputs->DQPatLC + 2;
+ } else {
+ SeqLC[0] = 1;
+ SeqLC[1] = 1;
+ SeqLC[2] = 1;
+ SeqLC[3] = 1;
+ }
+ //
+ // Make sure we cover all DIMM Vref cases
+ //
+ DimmVrefParam = (param == WrFan2 || param == WrFan3 || param == WrV ); // WrFan not defined
+ PerMc = (param == CmdV) && (MrcCountBitsEqOne (chBitmask) >= 2);
+
+ //
+ // Run through margin test
+ //
+ for (sign = 0; sign < 2; sign++) {
+ rSign = (S8) ((2 * sign) - 1);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+----->rsign: %d \n", rSign);
+
+ MrcOemMemorySet ((U8 *) TwoPass, 0, sizeof (TwoPass));
+ MrcOemMemorySet ((U8 *) TwoFail, 0, sizeof (TwoFail));
+
+ //
+ // Initialize variables
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ //
+ // Set default of all variables
+ //
+ byteMax[Channel] = MaxMargin;
+ LastPassVref[Channel] = 0x7F; // Start with a huge unsigned numer - 128
+ Errors[Channel] = 0;
+ MinTested[Channel] = 0;
+ MaxTested[Channel] = 0;
+
+ if (((MRC_BIT0 << Channel) & chBitmask) == 0) {
+ TwoPass[Channel] = 1;
+ TwoFail[Channel] = 1;
+ continue;
+ }
+ //
+ // Find MaxMargin for this channel
+ //
+ if (param == RdT) {
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ byteMax[Channel] = MrcCalcMaxRxMargin (MrcData, Channel, RankRx, byte, sign, byteMax[Channel]);
+ }
+ }
+ //
+ // Scale back variables to normal margins and check saturation
+ //
+ marginCh[ResultType][Rank][Channel][0][sign] = marginCh[ResultType][Rank][Channel][0][sign] / 10;
+ if (marginCh[ResultType][Rank][Channel][0][sign] > byteMax[Channel]) {
+ marginCh[ResultType][Rank][Channel][0][sign] = byteMax[Channel];
+ }
+ //
+ // If PerMC, all channels should start with the lowest margin across all the channel
+ //
+ if (PerMc) {
+ if (marginCh[ResultType][Rank][McChannel][0][sign] > marginCh[ResultType][Rank][Channel][0][sign]) {
+ marginCh[ResultType][Rank][McChannel][0][sign] = marginCh[ResultType][Rank][Channel][0][sign];
+ }
+ }
+
+ MinTested[Channel] = (U8) marginCh[ResultType][Rank][Channel][0][sign];
+ MaxTested[Channel] = MinTested[Channel];
+
+ //
+ // Setup REUT Error Counters to count errors per channel
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_CTL_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_ERR_COUNTER_CTL_0_REG - MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_CTL_0_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0);
+ }
+ //
+ // If PerMC, set all channels to use margin associated with mcChannel = 0
+ //
+ if (PerMc) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((MRC_BIT0 << Channel) & chBitmask) == 0) {
+ continue;
+ }
+ marginCh[ResultType][Rank][Channel][0][sign] = marginCh[ResultType][Rank][McChannel][0][sign];
+ MinTested[Channel] = (U8) marginCh[ResultType][Rank][McChannel][0][sign];
+ MaxTested[Channel] = MinTested[Channel];
+ }
+ }
+ //
+ // Search algorithm:
+ // If start with a passing point, walk to hit 2 failing points
+ // Return as needed to hit a second passing point
+ // If start with a failing point, walk to hit 2 passing points
+ // Return as needed to hit a second failing point
+ // Keep testing until all ch/bytes find 2 passes and 2 fails
+ //
+ Done = FALSE;
+ do {
+ //
+ // Walk through all 2D points
+ //
+ ReutGlobalCtl.Data = 0;
+ ReutGlobalCtl.Bits.Global_Clear_Errors = 1;
+ MrcWriteCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG, ReutGlobalCtl.Data); // Clear errors
+ chError = 0;
+
+ for (value1 = 0; value1 < Points2D; value1++) {
+ //
+ // Set Vref level
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((MRC_BIT0 << Channel) & chBitmask) == 0) {
+ continue;
+ }
+
+ SkipWait = (chBitmask >> (Channel + 1)); // Skip if there are more channels
+ value0 = rSign * marginCh[ResultType][Rank][Channel][0][sign];
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "+----->Value0 is %d, Value1 is %d\n", (S32) value0, value1);
+
+ if (param == CmdV) {
+ UpdateVrefWaitTilStable (MrcData, 2, 0, value0, 0);
+ MrcResetSequence (MrcData);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "+----->Value0 is %d, Value1 is %d\n", (S32) value0, value1);
+ break; // Just update for one channel
+ } else {
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ Status = ChangeMargin (
+ MrcData,
+ param,
+ value0,
+ value1,
+ 0,
+ Channel,
+ RankRx,
+ byte,
+ 0,
+ 0,
+ SkipWait,
+ MrcRegFileStart
+ );
+ if (DimmVrefParam) {
+ //
+ // Only Byte 7 on Channel 1 is needed to update Wr DIMM Vref - Taken care of inside ChangeMargin routine
+ //
+ break;
+ }
+ }
+ }
+ }
+ //
+ // Run Test
+ //
+ chError |= RunIOTest (MrcData, chBitmask, Outputs->DQPat, SeqLC, (value1 == 0), mode);
+
+ //
+ // check if we have already failed and can stop running
+ //
+ if (EnBER == 0 && value1 < (U32) (Points2D - 1) && chError == chBitmask) {
+ break;
+ }
+ //
+ // Collect results and Update variables for next point to test
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((TwoPass[Channel] == 1 && TwoFail[Channel] == 1) || ((MRC_BIT0 << Channel) & chBitmask) == 0) {
+ continue;
+ }
+
+ McChannel = (PerMc) ? 0 : Channel;
+
+ //
+ // Read Error Count
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG - MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG) * Channel);
+ ReutChErrCounterStatus.Data = MrcReadCR (MrcData, Offset);
+ ErrCount = ReutChErrCounterStatus.Bits.Counter_Status;
+ Margin = (U8) marginCh[ResultType][Rank][Channel][0][sign];
+ Delta = (Margin - LastPassVref[Channel]);
+
+ //
+ // Update Pass/Fail Variables:
+ //
+ if (ErrCount == 0 && Margin == MaxTested[McChannel]) {
+ //
+ // Passing while walking up
+ //
+ if (Delta < 0) {
+ //
+ // First passing point
+ // @todo: Next line should be changed to return failure. Code should never hang.
+ //
+ MRC_ASSERT (
+ MinTested[McChannel] == MaxTested[McChannel],
+ Debug,
+ "Error: MaxTested < LastPass after first point"
+ );
+ LastPassVref[Channel] = Margin;
+ } else if (Delta == 1) {
+ //
+ // Normal, walk to fail
+ //
+ Errors[Channel] = MrcBitShift (Errors[Channel], -8 * Delta) & BER_ERROR_MASK;
+ LastPassVref[Channel] = Margin;
+ TwoPass[Channel] = 1;
+ } else if (Delta == 2) {
+ //
+ // Speckling in response, Consider point as error
+ //
+ Errors[Channel] = MrcBitSwap (Errors[Channel], MrcLog8 (ErrCount), 24, 8);
+ TwoFail[Channel] = 1;
+ } else {
+ //
+ // @todo: Next line should be changed to return failure. Code should never hang.
+ //
+ MRC_ASSERT (FALSE, Debug, "Error: Tested point twice or Tested >2 above LastPass");
+ }
+ } else if (ErrCount == 0 && Margin == MinTested[McChannel]) {
+ if (TwoPass[Channel] == 1) {
+ continue; // Skip if this channel is already done
+ }
+ //
+ // Passing while walking down
+ //
+ if (Delta == -1) {
+ Errors[Channel] = MrcBitSwap (Errors[Channel], 0, 0, 8);
+ TwoPass[Channel] = 1; // found second pass
+ } else {
+ //
+ // 1st passing point
+ // Don't shift errors. Fail points already assumed correct
+ //
+ LastPassVref[Channel] = Margin;
+ TwoPass[Channel] = 0;
+ }
+ } else if (ErrCount > 0 && Margin == MaxTested[McChannel]) {
+ //
+ // Failing while walking up
+ // @todo: Next line should be changed to return failure. Code should never hang.
+ //
+ MRC_ASSERT (Delta <= 2, Debug, "Error: Tested >2 above LastPass");
+ if (Delta < 2) {
+ //
+ // first failing point
+ //
+ Errors[Channel] = MrcBitSwap (Errors[Channel], MrcLog8 (ErrCount), 16, 8);
+ TwoFail[Channel] = 0;
+ } else if (Delta == 2) {
+ //
+ // 2nd failing point
+ //
+ Errors[Channel] = MrcBitSwap (Errors[Channel], MrcLog8 (ErrCount), 24, 8);
+ TwoFail[Channel] = 1;
+ }
+ } else if (ErrCount > 0 && Margin == MinTested[McChannel]) {
+ //
+ // Failing while walking down
+ //
+ if (LastPassVref[Channel] < 0xFF && Delta <= 0) {
+ //
+ // Adjust LastPassVref and Error count to be below this failure point
+ //
+ Errors[Channel] = MrcBitSwap (Errors[Channel], MrcLog8 (ErrCount), 8 * (Delta + 1), 8);
+ Errors[Channel] = MrcBitShift (Errors[Channel], 8 * (1 - Delta));
+ LastPassVref[Channel] = Margin - 1;
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Unexpected case for channel: %d, delta: %d.\n", Channel, Delta);
+ tmp = ((Errors[Channel] & 0xFF0000) >> 8) + MrcLog8 (ErrCount);
+ Errors[Channel] = MrcBitSwap (Errors[Channel], tmp, 16, 16);
+ }
+
+ if (MinTested[McChannel] < MaxTested[McChannel]) {
+ TwoFail[Channel] = 1;
+ }
+
+ if (Delta <= 0) {
+ TwoPass[Channel] = 0;
+ }
+ } else {
+ //
+ // @todo: Next line should be changed to return failure. Code should never hang.
+ //
+ MRC_ASSERT (FALSE, Debug, "Error: Testing points other than Max/MinTested");
+ }
+ //
+ // Find Max Saturation limit
+ //
+ if (Interior && MaxTested[Channel] == Margin) {
+ byteMax[Channel] = Margin;
+ }
+ //
+ // Handle Max Saturation
+ //
+ if (Margin == byteMax[Channel]) {
+ TwoFail[Channel] = 1;
+ }
+
+ if (ErrCount == 0 && byteMax[Channel] == LastPassVref[Channel] && TwoPass[Channel] == 1) {
+ Errors[Channel] = MrcBitSwap (Errors[Channel], 0xFFFE, 16, 16);
+ }
+ //
+ // Handle Min Saturation
+ //
+ if (Margin == 0) {
+ TwoPass[Channel] = 1;
+ if (ErrCount > 0) {
+ TwoFail[Channel] = 1;
+ LastPassVref[Channel] = 0;
+ Errors[Channel] = MrcBitSwap (Errors[Channel], (BER_LOG_TARGET << 8) + BER_LOG_TARGET, 16, 16);
+ }
+ }
+ //
+ // Decide what to test next for PerMC == FALSE
+ //
+ if (!PerMc) {
+ if (TwoPass[Channel] == 1) {
+ if (TwoFail[Channel] == 1) {
+ continue;
+ }
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"++---->MaxTested[Ch] before ++:%d\n", MaxTested[Channel]);////////
+ marginCh[ResultType][Rank][Channel][0][sign] = ++MaxTested[Channel];
+ } else {
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"++---->MinTested[Ch] before --:%d\n", MinTested[Channel]);////////
+ marginCh[ResultType][Rank][Channel][0][sign] = --MinTested[Channel];
+ }
+ }
+ }
+ //
+ // Decide what to test next for PerMC == TRUE
+ //
+ if (PerMc) {
+ if ((TwoPass[0] == 1) && (TwoPass[1] == 1)) {
+ //
+ // All Channels have 2 passes
+ //
+ if ((TwoFail[0] == 1) && (TwoFail[1] == 1)) {
+ //
+ // All Channels have 2 fails
+ //
+ continue;
+ }
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"++---->MaxTested[Ch] before ++:%d\n", MaxTested[Channel]);////////
+ marginCh[ResultType][Rank][McChannel][0][sign] = ++MaxTested[McChannel];
+ } else {
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"++---->MinTested[Ch] before --:%d\n", MinTested[Channel]);////////
+ marginCh[ResultType][Rank][McChannel][0][sign] = --MinTested[McChannel];
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((MRC_BIT0 << Channel) & chBitmask) == 0) {
+ continue;
+ }
+
+ marginCh[ResultType][Rank][Channel][0][sign] = marginCh[ResultType][Rank][McChannel][0][sign];
+ MinTested[Channel] = MinTested[McChannel];
+ MaxTested[Channel] = MaxTested[McChannel];
+ }
+ }
+ }
+ //
+ // check if we are done
+ //
+ Done = TRUE;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (TwoPass[Channel] == 0 || TwoFail[Channel] == 0) {
+ Done = FALSE;
+ break;
+ }
+ }
+ } while (Done == FALSE);
+
+ //
+ // Calculate the effective margin
+ // Update marginch with extroploated BER Margin
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((MRC_BIT0 << Channel) & chBitmask) == 0) {
+ continue;
+ }
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE,"+----->marginCh[%d,%d] is: %d\n", Channel, sign, marginCh[ResultType][Rank][Channel][0][sign]);
+ if (EnBER) {
+ marginCh[ResultType][Rank][Channel][0][sign] = interpolateVref (
+ LastPassVref[Channel],
+ (Errors[Channel] >> 16) & 0xFF,
+ (Errors[Channel] >> 24) & 0xFF,
+ BER_LOG_TARGET,
+ BERStats
+ );
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "+----->BERmarginCh[%d,%d] is: %d, Errors = 0x%x\n", Channel, sign, marginCh[ResultType][Rank][Channel][0][sign], Errors[Channel]);
+ } else {
+ marginCh[ResultType][Rank][Channel][0][sign] = 10 * LastPassVref[Channel];
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "+----->marginCh[%d,%d] is: %d\n", Channel, sign, marginCh[ResultType][Rank][Channel][0][sign]);
+ }
+ }
+ }
+ //
+ // Clean up after step
+ //
+ if (param == CmdV) {
+ UpdateVrefWaitTilStable (MrcData, 2, 0, 0, 0);
+ } else {
+ Status = ChangeMargin (MrcData, param, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileCurrent);
+ }
+
+ MrcWriteCrMulticast (MrcData, MCSCHEDS_CR_REUT_CH_ERR_COUNTER_CTL_0_REG, 0);
+
+ return Status;
+}
+
+/**
+ This function shifts a 32 bit int either positive or negative
+
+ @param[in] Value - Input value to be shifted
+ @param[in] ShiftAmount - Number of bits places to be shifted.
+
+ @retval 0 if ShiftAmount exceeds +/- 31. Otherwise the updated 32 bit value.
+**/
+U32
+MrcBitShift (
+ IN const U32 Value,
+ IN const S8 ShiftAmount
+ )
+{
+ if ((ShiftAmount > 31) || (ShiftAmount < -31)) {
+ return 0;
+ }
+
+ if (ShiftAmount > 0) {
+ return Value << ShiftAmount;
+ } else {
+ return Value >> (-1 * ShiftAmount);
+ }
+}
+
+/**
+ This function Sign extends OldMSB to NewMSB Bits (Eg: Bit 6 to Bit 7)
+
+ @param[in] CurrentValue - Input value to be shifted
+ @param[in] OldMSB - The original most significant Bit
+ @param[in] NewMSB - The new most significant bit.
+
+ @retval The updated 8 bit value.
+**/
+U8
+MrcSE (
+ IN U8 CurrentValue,
+ IN const U8 OldMSB,
+ IN const U8 NewMSB
+ )
+{
+ U8 Scratch;
+
+ Scratch = ((MRC_BIT0 << (NewMSB - OldMSB)) - 1) << OldMSB;
+ if (CurrentValue >> (OldMSB - 1)) {
+ CurrentValue |= Scratch;
+ } else {
+ CurrentValue &= (~Scratch);
+ }
+
+ return CurrentValue;
+}
+
+/**
+ This function calculates the Log base 2 of the value to a maximum of Bits
+
+ @param[in] Value - Input value
+
+ @retval Returns the log base 2 of input value
+**/
+U8
+MrcLog2 (
+ IN const U32 Value
+ )
+{
+ U8 Log;
+ U8 Bit;
+
+ //
+ // Return 0 if value is negative
+ //
+ Log = 0;
+ if ((Value + 1) != 0) {
+ for (Bit = 0; Bit < 32; Bit++) {
+ if (Value & (MRC_BIT0 << Bit)) {
+ Log = (Bit + 1);
+ }
+ }
+ }
+
+ return Log;
+}
+
+/**
+ ***** Has Buffer Overflow for 68-71, 544-575, 4352-4607, ... ****
+ This function calculates the Log base 8 of the Input parameter using integers
+
+ @param[in] Value - Input value
+
+ @retval Returns 10x the log base 8 of input Value
+**/
+U32
+MrcLog8 (
+ IN U32 Value
+ )
+{
+ const U8 Loglook[17] = { 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10 };
+ U32 Loga;
+ U32 Rema;
+
+ Loga = 0;
+ Rema = 2 * Value;
+ while (Value > 8) {
+ Rema = Value >> 2;
+ Value = Value >> 3;
+ Loga += 10;
+ };
+
+ return (Loga + Loglook[Rema]); //returns an integer approximation of "log8(a) * 10"
+}
+
+/**
+ This function Sorts Arr from largest to smallest
+
+ @param[in,out] Arr - Array to be sorted
+ @param[in] Channel - Channel to sort.
+ @param[in] lenArr - Length of the array
+
+ @retval Nothing
+**/
+void
+MrcBsortPerChannel (
+ IN OUT U32 Arr[MAX_CHANNEL][4],
+ IN const U8 Channel,
+ IN const U8 lenArr
+ )
+{
+ U8 i;
+ U8 j;
+ U32 temp;
+
+ for (i = 0; i < lenArr - 1; i++) {
+ for (j = 0; j < lenArr - (1 + i); j++) {
+ if (Arr[Channel][j] < Arr[Channel][j + 1]) {
+ temp = Arr[Channel][j];
+ Arr[Channel][j] = Arr[Channel][j + 1];
+ Arr[Channel][j + 1] = temp;
+ }
+ }
+ }
+
+ return;
+}
+
+/**
+ This function Sorts Arr from largest to smallest
+
+ @param[in,out] Arr - Array to be sort
+ @param[in] lenArr - Lenght of the array
+
+ @retval Nothing
+**/
+void
+MrcBsort (
+ IN OUT U32 *const Arr,
+ IN const U8 lenArr
+ )
+{
+ U8 i;
+ U8 j;
+ U32 temp;
+
+ for (i = 0; i < lenArr - 1; i++) {
+ for (j = 0; j < lenArr - (1 + i); j++) {
+ if (Arr[j] < Arr[j + 1]) {
+ temp = Arr[j];
+ Arr[j] = Arr[j + 1];
+ Arr[j + 1] = temp;
+ }
+ }
+ }
+
+ return;
+}
+
+/**
+ This function calculates the Natural Log of the Input parameter using integers
+
+ @param[in] Input - 100 times a number to get the Natural log from.
+ Max Input Number is 40,000 (without 100x)
+
+ @retval 100 times the actual result. Accurate within +/- 2
+**/
+U32
+MrcNaturalLog (
+ U32 Input
+ )
+{
+ U32 Output;
+
+ Output = 0;
+ while (Input > 271) {
+ Input = (Input * 1000) / 2718;
+ Output += 100;
+ }
+
+ Output += ((-16 * Input * Input + 11578 * Input - 978860) / 10000);
+
+ return Output;
+}
+
+/**
+ This function calculates the number of bits set to 1 in a 32-bit value.
+
+ @param[in] Input - The value to work on.
+
+ @retval The number of bits set to 1 in Input.
+**/
+U8
+MrcCountBitsEqOne (
+ IN U32 Input
+ )
+{
+ U8 NumOnes;
+
+ NumOnes = 0;
+ while (Input > 0) {
+ NumOnes++;
+ Input &= (Input - 1);
+ }
+
+ return NumOnes;
+}
+
+/**
+ This function calculates e to the power of of the Input parameter using integers.
+
+ @param[in] Input - 100 times a number to elevate e to.
+
+ @retval 100 times the actual result. Accurate within +/- 2.
+**/
+U32
+Mrceexp (
+ IN U32 Input
+ )
+{
+ U32 Extra100;
+ U32 Output;
+
+ Extra100 = 0;
+ Output = 1;
+ while (Input > 100) {
+ Input -= 100;
+ Output = (Output * 2718) / 10;
+ if (Extra100) {
+ Output /= 100;
+ }
+
+ Extra100 = 1;
+ }
+
+ Output = ((Output * (8 * Input * Input + 900 * Input + 101000)) / 1000);
+
+ if (Extra100) {
+ Output /= 100;
+ }
+
+ return Output;
+}
+
+/**
+ This function writes a 32 bit register.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Offset - Offset of register from MchBar Base Address.
+ @param[in] Value - Value to write.
+
+ @retval Nothing
+**/
+void
+MrcWriteCrMulticast (
+ IN MrcParameters *const MrcData,
+ IN const U32 Offset,
+ IN const U32 Value
+ )
+{
+ MrcOemMmioWrite (Offset, Value, MrcData->SysIn.Inputs.MchBarBaseAddress);
+#ifdef MRC_DEBUG_PRINT
+ if (MrcData->SysIn.Inputs.Debug.PostCode[0] == MrcData->SysIn.Inputs.Debug.PostCode[1]) {
+ MRC_DEBUG_MSG (
+ &MrcData->SysIn.Inputs.Debug,
+ MSG_LEVEL_NOTE,
+ "%08Xh > %08Xh\n",
+ MrcData->SysIn.Inputs.MchBarBaseAddress + Offset,
+ Value
+ );
+ }
+#endif // MRC_DEBUG_PRINT
+ return;
+}
+
+/**
+ This function writes a 64 bit register.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Offset - Offset of register from MchBar Base Address.
+ @param[in] Value - Value to write.
+
+ @retval Nothing
+**/
+void
+MrcWriteCR64 (
+ IN MrcParameters *const MrcData,
+ IN const U32 Offset,
+ IN const U64 Value
+ )
+{
+ MrcOemMmioWrite64 (Offset, Value, MrcData->SysIn.Inputs.MchBarBaseAddress);
+#ifdef MRC_DEBUG_PRINT
+ if (MrcData->SysIn.Inputs.Debug.PostCode[0] == MrcData->SysIn.Inputs.Debug.PostCode[1]) {
+ MRC_DEBUG_MSG (
+ &MrcData->SysIn.Inputs.Debug,
+ MSG_LEVEL_NOTE,
+ "%08Xh > %016Xh\n",
+ MrcData->SysIn.Inputs.MchBarBaseAddress + Offset,
+ Value
+ );
+ }
+#endif // MRC_DEBUG_PRINT
+ return;
+}
+
+/**
+ This function writes a 32 bit register.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Offset - Offset of register from MchBar Base Address.
+ @param[in] Value - Value to write.
+
+ @retval Nothing
+**/
+void
+MrcWriteCR (
+ IN MrcParameters *const MrcData,
+ IN const U32 Offset,
+ IN const U32 Value
+ )
+{
+ MrcOemMmioWrite (Offset, Value, MrcData->SysIn.Inputs.MchBarBaseAddress);
+#ifdef MRC_DEBUG_PRINT
+ if (MrcData->SysIn.Inputs.Debug.PostCode[0] == MrcData->SysIn.Inputs.Debug.PostCode[1]) {
+ MRC_DEBUG_MSG (
+ &MrcData->SysIn.Inputs.Debug,
+ MSG_LEVEL_NOTE,
+ "%08Xh > %08Xh\n",
+ MrcData->SysIn.Inputs.MchBarBaseAddress + Offset,
+ Value
+ );
+ }
+#endif // MRC_DEBUG_PRINT
+ return;
+}
+
+/**
+ This function writes a 8 bit register.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Offset - Offset of register from MchBar Base Address.
+ @param[in] Value - The value to write.
+
+ @retval Nothing
+**/
+void
+MrcWriteCR8 (
+ IN MrcParameters*const MrcData,
+ IN const U32 Offset,
+ IN const U8 Value
+ )
+{
+ MrcOemMmioWrite8 (Offset, Value, MrcData->SysIn.Inputs.MchBarBaseAddress);
+#ifdef MRC_DEBUG_PRINT
+ if (MrcData->SysIn.Inputs.Debug.PostCode[0] == MrcData->SysIn.Inputs.Debug.PostCode[1]) {
+ MRC_DEBUG_MSG (
+ &MrcData->SysIn.Inputs.Debug,
+ MSG_LEVEL_NOTE,
+ "%08Xh > %02Xh\n",
+ MrcData->SysIn.Inputs.MchBarBaseAddress + Offset,
+ Value
+ );
+ }
+#endif // MRC_DEBUG_PRINT
+ return;
+}
+
+/**
+ This function reads a 64 bit register.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Offset - Offset of register from MchBar Base Address.
+
+ @retval Value read from the register.
+**/
+U64
+MrcReadCR64 (
+ IN MrcParameters *const MrcData,
+ IN const U32 Offset
+ )
+{
+ U64 Value;
+
+ MrcOemMmioRead64 (Offset, &Value, MrcData->SysIn.Inputs.MchBarBaseAddress);
+ return Value;
+}
+
+/**
+ This function reads a 32 bit register.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Offset - Offset of register from MchBar Base Address.
+
+ @retval Value read from the register
+**/
+U32
+MrcReadCR (
+ IN MrcParameters *const MrcData,
+ IN const U32 Offset
+ )
+{
+ U32 Value;
+
+ MrcOemMmioRead (Offset, &Value, MrcData->SysIn.Inputs.MchBarBaseAddress);
+ return Value;
+}
+
+/**
+ This function blocks the CPU for the duration specified in HPET Delay time.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] DelayHPET - time to wait in 69.841279ns
+
+ @retval Nothing
+**/
+void
+MrcWait (
+ IN MrcParameters *const MrcData,
+ IN U32 DelayHPET
+ )
+{
+ const MrcInput *Inputs;
+ BOOL Done;
+ U32 Start;
+ volatile U32 Finish;
+ U32 Now;
+
+Inputs = &MrcData->SysIn.Inputs;
+Done = FALSE;
+
+
+ if (DelayHPET >= (5 * HPET_1US)) {
+ MrcOemMmioRead (0xF0, &Start, Inputs->HpetBaseAddress);
+ Finish = Start + DelayHPET;
+
+ do {
+ MrcOemMmioRead (0xF0, &Now, Inputs->HpetBaseAddress);
+ if (Finish > Start) {
+ if (Now >= Finish) {
+ Done = TRUE;
+ }
+ } else {
+ if ((Now < Start) && (Now >= Finish)) {
+ Done = TRUE;
+ }
+ }
+ } while (Done == FALSE);
+ } else {
+ for (Start = 0; Start < ((DelayHPET + HPET_MIN) / (2 * HPET_MIN)); Start++) {
+ //
+ // Just perform Dummy reads to CPU CR
+ //
+ Finish = MrcReadCR (MrcData, MCSCHEDS_CR_REUT_CH_ERR_DATA_STATUS_REG);
+ }
+ }
+ return;
+}
+
+/**
+ This function forces an RCOMP.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval Nothing
+**/
+void
+ForceRcomp (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcWriteCR8 (MrcData, PCU_CR_M_COMP_PCU_REG + 1, MRC_BIT0);
+
+ //
+ // 10 - 20 us wait.
+ //
+ MrcWait (MrcData, 10 * HPET_1US);
+ return;
+}
+
+/**
+ This function sets the self refresh idle timer and enables it.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval Nothing
+**/
+void
+EnterSR (
+ IN MrcParameters *const MrcData
+ )
+{
+ MCDECS_CR_PM_SREF_CONFIG_MCMAIN_STRUCT PmSrefConfig;
+
+ PmSrefConfig.Data = 0;
+ PmSrefConfig.Bits.SR_Enable = 1;
+ PmSrefConfig.Bits.Idle_timer = SELF_REFRESH_IDLE_COUNT;
+ MrcWriteCR (MrcData, MCDECS_CR_PM_SREF_CONFIG_MCMAIN_REG, PmSrefConfig.Data);
+ MrcWait (MrcData, HPET_1US);
+ return;
+}
+
+/**
+ This function sets the self refresh idle timer and disables it.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval Nothing
+**/
+void
+ExitSR (
+ IN MrcParameters *const MrcData
+ )
+{
+ MCDECS_CR_PM_SREF_CONFIG_MCMAIN_STRUCT PmSrefConfig;
+
+ PmSrefConfig.Data = 0;
+ PmSrefConfig.Bits.Idle_timer = SELF_REFRESH_IDLE_COUNT;
+ MrcWriteCR (MrcData, MCDECS_CR_PM_SREF_CONFIG_MCMAIN_REG, PmSrefConfig.Data);
+ MrcWait (MrcData, HPET_1US);
+ return;
+}
+
+/**
+ This function programs the WDB.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval Nothing
+**/
+void
+SetupWDB (
+ IN MrcParameters *const MrcData
+ )
+{
+ U8 a;
+ const U32 vmask = 0x41041041;
+ const U32 amask[9] = {0x86186186, 0x18618618, 0x30C30C30, 0xA28A28A2, 0x8A28A28A,
+ 0x14514514, 0x28A28A28, 0x92492492, 0x24924924};
+ const U32 seeds[MRC_WDB_NUM_MUX_SEEDS] = {0xA10CA1, 0xEF0D08, 0xAD0A1E};
+ U8 Channel;
+ U32 Offset;
+ MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_LMN_STRUCT ReutPatWdbClMuxLmn;
+
+ //
+ // Fill first 8 entries as simple 2 LFSR VA pattern
+ // VicRot=8, Start=0
+ //
+ WriteWDBVAPattern (MrcData, 0, BASIC_VA_PATTERN_SPRED_8, 8, 0);
+
+ //
+ // Fill next 54 entries as 3 LFSR VA pattern
+ //
+ for (a = 0; a < 9; a++) {
+ //
+ // VicRot=6, Start=8+a*6
+ //
+ WriteWDBVAPattern (MrcData, amask[a], vmask, 6, 8 + a * 6);
+ }
+ //
+ // Write the LFSR seeds
+ //
+ MrcProgramLFSR (MrcData, seeds);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (&MrcData->SysOut.Outputs, Channel)) {
+ ReutPatWdbClMuxLmn.Data = 0;
+ ReutPatWdbClMuxLmn.Bits.N_counter = 10;
+ ReutPatWdbClMuxLmn.Bits.M_counter = 1;
+ ReutPatWdbClMuxLmn.Bits.L_counter = 1;
+ ReutPatWdbClMuxLmn.Bits.Enable_Sweep_Frequency = 1;
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_LMN_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_WDB_CL_MUX_LMN_REG - MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_LMN_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ReutPatWdbClMuxLmn.Data);
+ }
+ }
+
+ return;
+}
+
+/**
+ This function will program all present channels with the 3 seeds passed in.
+
+ @param[in] MrcData - Global MRC data structure
+ @param[in] seeds - Array of 3 seeds programmed into PAT_WDB_CL_MUX_PB_RD/WR
+
+ @retval - Nothing
+
+**/
+void
+MrcProgramLFSR (
+ IN MrcParameters *const MrcData,
+ IN U32 const seeds[MRC_WDB_NUM_MUX_SEEDS]
+ )
+{
+ U32 CrOffset;
+ U8 Channel;
+ U8 s;
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (&MrcData->SysOut.Outputs, Channel)) {
+ for (s = 0; s < MRC_WDB_NUM_MUX_SEEDS; s++) {
+ CrOffset = MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_PB_RD_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_WDB_CL_MUX_PB_RD_0_REG - MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_PB_RD_0_REG) * Channel) +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_WDB_CL_MUX_PB_RD_1_REG - MCHBAR_CH1_CR_REUT_CH_PAT_WDB_CL_MUX_PB_RD_0_REG) * s);
+ MrcWriteCR (MrcData, CrOffset, seeds[s]);
+ CrOffset = MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_PB_WR_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_WDB_CL_MUX_PB_WR_0_REG - MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_PB_WR_0_REG) * Channel) +
+ ((MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_PB_WR_1_REG - MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_PB_WR_0_REG) * s);
+ MrcWriteCR (MrcData, CrOffset, seeds[s]);
+ }
+ }
+ }
+}
+
+/**
+ This function Write 1 cacheline worth of data to the WDB
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Patterns - Array of bytes. Each bytes represents 8 chunks of the cachelines for 1 lane.
+ Each entry in Patterns represents a different cacheline for a different lane.
+ @param[in] PMask - Array of len Spread uint8. Maps the patterns to the actual DQ lanes.
+ DQ[0] = Patterns[PMask[0]], ... DQ[Spread-1] = Patterns[PMask[Spread-1]]
+ DQ[Spread] = DQ[0], ... DQ[2*Spread-1] = DQ[Spread-1]
+ @param[in] Start - Starting entry in the WDB.
+
+ @retval Nothing
+**/
+void
+WriteWDBFixedPattern (
+ IN MrcParameters *const MrcData,
+ IN U8 *const Patterns,
+ IN U8 *const PMask,
+ IN const U8 Spread,
+ IN const U16 Start
+ )
+{
+ MrcOutput *Outputs;
+ U8 Channel;
+ U8 up32;
+ U8 chunk;
+ U8 b;
+ U8 beff;
+ U8 burst;
+ U32 data;
+ U32 pointer;
+ U32 Offset;
+ MCHBAR_CH0_CR_QCLK_LDAT_PDAT_STRUCT QclkLdatPdat;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ for (chunk = 0; chunk < 8; chunk++) {
+ //
+ // Program LDAT_DATAIN_*
+ //
+ for (up32 = 0; up32 < 2; up32++) {
+ data = 0;
+ for (b = 0; b < 32; b++) {
+ beff = (b + 32 * up32) % Spread;
+ burst = Patterns[PMask[beff]];
+ if (burst & (MRC_BIT0 << chunk)) {
+ data |= (MRC_BIT0 << b);
+ }
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Offset = MCHBAR_CH0_CR_QCLK_LDAT_DATAIN_0_REG +
+ ((MCHBAR_CH1_CR_QCLK_LDAT_DATAIN_0_REG - MCHBAR_CH0_CR_QCLK_LDAT_DATAIN_0_REG) * Channel) +
+ ((MCHBAR_CH0_CR_QCLK_LDAT_DATAIN_1_REG - MCHBAR_CH0_CR_QCLK_LDAT_DATAIN_0_REG) * up32);
+ MrcWriteCR (MrcData, Offset, data);
+ }
+ }
+ } // up32
+
+ pointer = MRC_BIT16 + chunk;
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ //
+ // Set rep = 0 don't want to replicate the data
+ // Set banksel field to the value of the chunk you want to write the 64 bits to.
+ // Set arraysel = 0 ( indicating it is the MC WDB) and mode = 'b01 in the SDAT register
+ //
+ Offset = MCHBAR_CH0_CR_QCLK_LDAT_SDAT_REG +
+ ((MCHBAR_CH1_CR_QCLK_LDAT_SDAT_REG - MCHBAR_CH0_CR_QCLK_LDAT_SDAT_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, pointer);
+
+ //
+ // Finally, write the PDAT register indicating which cacheline of the WDB you want to write to
+ // by setting fastaddr field to one of the 64 cache lines. Also set cmdb in the pdat register to 4'b1000,
+ // indicating that this is a LDAT write
+ //
+ Offset = MCHBAR_CH0_CR_QCLK_LDAT_PDAT_REG +
+ ((MCHBAR_CH1_CR_QCLK_LDAT_PDAT_REG - MCHBAR_CH0_CR_QCLK_LDAT_PDAT_REG) * Channel);
+ QclkLdatPdat.Data = 0;
+ QclkLdatPdat.Bits.CMDB = 8;
+ QclkLdatPdat.Bits.FASTADDR = MIN (Start, MCHBAR_CH0_CR_QCLK_LDAT_PDAT_FASTADDR_MAX);
+ MrcWriteCR (MrcData, Offset, QclkLdatPdat.Data);
+ }
+ }
+ } // chunk
+ //
+ // Turn off LDAT mode after writing to WDB is complete
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Offset = MCHBAR_CH0_CR_QCLK_LDAT_SDAT_REG +
+ ((MCHBAR_CH1_CR_QCLK_LDAT_SDAT_REG - MCHBAR_CH0_CR_QCLK_LDAT_SDAT_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0);
+ }
+ }
+
+ return;
+}
+
+/**
+ This rotine performs the following steps:
+ Step 0: Iterate through all VicRots
+ Step 1: Create a compressed vector for a given 32 byte cacheline
+ Each byte has a value of LFSR0=AA/LFSR1=CC/LFSR2=F0
+ Step 2: Expand compressed vector into chunks and 32 bit segments
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] vmask - 32 bit victim mask. 1 indicates this bit should use LFSR0
+ @param[in] amask - 32 bit aggressor mask. 0/1 indicates this bit should use LFSR1/2
+ @param[in] VicRot - Number of times to circular rotate vmask/amask
+ @param[in] Start - Starting entry in the WDB
+
+ @retval Nothing
+**/
+void
+WriteWDBVAPattern (
+ IN MrcParameters *const MrcData,
+ IN U32 amask,
+ IN U32 vmask,
+ IN const U8 VicRot,
+ IN const U16 Start
+ )
+{
+ const U8 VAMask2Compressed[4] = {0xAA, 0xC0, 0xCC, 0xF0};
+ MrcOutput *Outputs;
+ U8 b;
+ U8 chunk;
+ U8 Channel;
+ U8 cmask;
+ U16 v;
+ U32 Vic;
+ U32 Agg2;
+ U32 data;
+ U32 pointer;
+ U32 msb;
+ U8 Compressed[32];
+ U32 BitMask;
+ U8 Index;
+ U16 Scratch;
+ U32 Offset;
+ MCHBAR_CH0_CR_QCLK_LDAT_PDAT_STRUCT QclkLdatPdat;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ for (v = 0; v < VicRot; v++) {
+ //
+ // Iterate through all 32 bits and create a compressed version of cacheline
+ // AA = Victim (LFSR0), CC = Agg1(LFSR1), F0 = Agg2 (LFSR2)
+ //
+ for (b = 0; b < 32; b++) {
+ BitMask = MRC_BIT0 << b;
+ Vic = (vmask & BitMask);
+ Agg2 = (amask & BitMask);
+
+ //
+ // Program compressed vector
+ //
+ if (Vic && Agg2) {
+ Index = 1;
+ } else if (Vic && !Agg2) {
+ Index = 0;
+ } else if (!Vic && !Agg2) {
+ Index = 2;
+ } else {
+ Index = 3;
+ }
+
+ Compressed[b] = VAMask2Compressed[Index];
+ }
+
+ for (chunk = 0; chunk < 8; chunk++) {
+ data = 0;
+ cmask = (MRC_BIT0 << chunk);
+ for (b = 0; b < 32; b++) {
+ if (Compressed[b] & cmask) {
+ data |= (MRC_BIT0 << b);
+ }
+ }
+ //
+ // Set rep = 0 don't want to replicate the data
+ // Set banksel field to the value of the chunk you want to write the 64 bits to.
+ // Set arraysel = 0 ( indicating it is the MC WDB) and mode = 'b01 in the SDAT register
+ //
+ pointer = MRC_BIT16 + chunk;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Offset = MCHBAR_CH0_CR_QCLK_LDAT_DATAIN_0_REG +
+ ((MCHBAR_CH1_CR_QCLK_LDAT_DATAIN_0_REG - MCHBAR_CH0_CR_QCLK_LDAT_DATAIN_0_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, data);
+ Offset = MCHBAR_CH0_CR_QCLK_LDAT_DATAIN_1_REG +
+ ((MCHBAR_CH1_CR_QCLK_LDAT_DATAIN_1_REG - MCHBAR_CH0_CR_QCLK_LDAT_DATAIN_1_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, data);
+ Offset = MCHBAR_CH0_CR_QCLK_LDAT_SDAT_REG +
+ ((MCHBAR_CH1_CR_QCLK_LDAT_SDAT_REG - MCHBAR_CH0_CR_QCLK_LDAT_SDAT_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, pointer);
+
+ //
+ // Finally, write the PDAT register indicating which cacheline of the WDB you want to write to
+ // by setting fastaddr field to one of the 64 cache lines. Also set cmdb in the pdat register to 4'b1000,
+ // indicating that this is a LDAT write
+ //
+ Scratch = Start + v;
+ Offset = MCHBAR_CH0_CR_QCLK_LDAT_PDAT_REG +
+ ((MCHBAR_CH1_CR_QCLK_LDAT_PDAT_REG - MCHBAR_CH0_CR_QCLK_LDAT_PDAT_REG) * Channel);
+ QclkLdatPdat.Data = 0;
+ QclkLdatPdat.Bits.CMDB = 8;
+ QclkLdatPdat.Bits.FASTADDR = MIN (Scratch, MCHBAR_CH0_CR_QCLK_LDAT_PDAT_FASTADDR_MAX);
+ MrcWriteCR (MrcData, Offset, QclkLdatPdat.Data);
+ }
+ }
+ }
+ //
+ // Circular Rotate Vic/Agg Masks
+ //
+ msb = (vmask >> 31) & 0x1;
+ vmask = (vmask << 1) | msb;
+ msb = (amask >> 31) & 0x1;
+ amask = (amask << 1) | msb;
+ }
+ //
+ // Clear LDAT mode
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Offset = MCHBAR_CH0_CR_QCLK_LDAT_SDAT_REG +
+ ((MCHBAR_CH1_CR_QCLK_LDAT_SDAT_REG - MCHBAR_CH0_CR_QCLK_LDAT_SDAT_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0);
+ }
+ }
+
+ return;
+}
+
+/**
+ Write VA pattern in CADB
+ Use basic VA pattern for CADB with 2 LFSRs. Rotation is manual
+ Bit Order is [CKE[3:0], ODT[3:0], CMD[2:0], CS[3:0], BA[2:0], MA[15:0]]
+ [59:56] [51:48] [42:40] [35:32] [26:24] [15:0]
+
+ NOTE: CKE, ODT and CS are not used in functional mode and are ignored
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel to setup.
+ @param[in] VicSpread - Separation of the Victim Bit.
+ @param[in] VicBit - The Bit to be the Victim.
+ @param[in] LMNEn - To enable LMN counter
+
+ @retval Nothing
+**/
+void
+SetupCADB (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 VicSpread,
+ IN const U8 VicBit,
+ IN const U8 LMNEn
+ )
+{
+ const U16 seeds[3] = {0xEA1, 0xBEEF, 0xDEAD};
+ U8 Row;
+ U8 bit;
+ U8 lfsr0;
+ U8 lfsr1;
+ U8 bremap;
+ U8 Fine;
+ U32 Offset;
+ MCHBAR_CH0_CR_REUT_CH_PAT_CADB_PROG_STRUCT ReutChPatCadbProg;
+ MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_CTRL_STRUCT ReutChPatCadbMuxCtrl;
+ MCHBAR_CH0_CR_REUT_CH_PAT_CADB_CL_MUX_LMN_STRUCT ReutCadbClMuxLmn;
+
+ //
+ // Currently, always start writing at CADB row0. Could add Start point in future.
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_WRITE_POINTER_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_WRITE_POINTER_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_WRITE_POINTER_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, 0);
+
+ //
+ // Plan to use VicSpread of 7 bits
+ // Walk through CADB rows, assigning bit for 1 VA pattern
+ //
+ for (Row = 0; Row < MRC_NUM_CADB_ENTRIES; Row++) {
+
+ lfsr0 = (Row & 0x1); // 0, 1, 0, 1 0, 1, 0, 1 for r = 0,1, ..., 7
+ lfsr1 = ((Row >> 1) & 0x1); // 0, 0, 1, 1 0, 0, 1, 1 for r = 0,1, ..., 7
+ //
+ // Assign Victim/Aggressor Bits
+ //
+ ReutChPatCadbProg.Data = 0;
+ for (bit = 0; bit < 22; bit++) {
+ //
+ // b in range(22)
+ //
+ Fine = bit % VicSpread;
+ if (bit >= 19) {
+ bremap = bit + 21; // b = [40-42]
+ } else if (bit >= 16) {
+ bremap = bit + 8; // b = [24-26]
+ } else {
+ bremap = bit; // b = [0-15]
+ }
+
+ if (Fine == VicBit) {
+ ReutChPatCadbProg.Data |= MrcOemMemoryLeftShiftU64 ((U64) lfsr0, bremap);
+ } else {
+ ReutChPatCadbProg.Data |= MrcOemMemoryLeftShiftU64 ((U64) lfsr1, bremap);
+ }
+ }
+ //
+ // Write Row. CADB is auto incremented after every write
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_PROG_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_PROG_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_PROG_REG) * Channel);
+ MrcWriteCR64 (MrcData, Offset, ReutChPatCadbProg.Data);
+ }
+ //
+ // Setup CADB in terms of LFSR selects, LFSR seeds, LMN constants and overall control
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_MUX_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_CTRL_REG) * Channel);
+ ReutChPatCadbMuxCtrl.Data = 0;
+ ReutChPatCadbMuxCtrl.Bits.Mux0_Control = LMNEn ? 0 : 2;
+ ReutChPatCadbMuxCtrl.Bits.Mux1_Control = 2;
+ ReutChPatCadbMuxCtrl.Bits.Mux2_Control = 2;
+ MrcWriteCR (MrcData, Offset, ReutChPatCadbMuxCtrl.Data);
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_CL_MUX_LMN_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_CL_MUX_LMN_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_CL_MUX_LMN_REG) * Channel);
+ ReutCadbClMuxLmn.Data = 0;
+ ReutCadbClMuxLmn.Bits.Enable_Sweep_Frequency = 1;
+ ReutCadbClMuxLmn.Bits.L_counter = 1;
+ ReutCadbClMuxLmn.Bits.M_counter = 1;
+ ReutCadbClMuxLmn.Bits.N_counter = 6;
+ MrcWriteCR (MrcData, Offset, ReutCadbClMuxLmn.Data);
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_MUX_PB_0_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_0_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, seeds[0]);
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_1_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_MUX_PB_1_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_1_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, seeds[1]);
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_2_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_MUX_PB_2_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MUX_PB_2_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, seeds[2]);
+
+ return;
+}
+
+/**
+ Program the subsequence type field in a given MCDFXS_CR_REUT_CHx_SUBSEQ_CTL_MCMAIN_x_STRUCT register
+
+ @param[in] MrcData - MRC global data
+ @param[in, out] SubSeqCtl - Address of the MCDFXS_CR_REUT_CHx_SUBSEQ_CTL_MCMAIN_x_STRUCT register
+ @param[in] Type - The subsequence type to program
+
+ @retval Nothing.
+**/
+void
+SetSubsequenceType (
+ IN MrcParameters *const MrcData,
+ IN OUT U32 *SubSeqCtl,
+ IN U32 Type
+ )
+{
+ const MrcInput *Inputs;
+
+ Inputs = &MrcData->SysIn.Inputs;
+
+ if ((Inputs->CpuModel == cmHSW) && (Inputs->CpuStepping == csHswA0)) {
+ ((MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_STRUCT_HSW_A0 *) (SubSeqCtl))->Bits.Subsequence_Type = Type;
+ } else {
+ ((MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_STRUCT *) (SubSeqCtl))->Bits.Subsequence_Type = Type;
+ }
+}
+
+/**
+ This function handles writing to the REUT Addressing sequence for IO Tests.
+ To not write a certain parameter, pass a NULL pointer to the function.
+
+ @param[in] MrcData - MRC global data structure.
+ @param[in] Channel - Specifies the channel to program.
+ @param[in] StartAddr - Start value for Rank/Bank/Row/Col.
+ @param[in] StopAddr - Stop value for Rank/Bank/Row/Col.
+ @param[in] FieldOrder - Relative order for carry propagates of Rank/Bank/Row/Col.
+ @param[in] IncRate - The number of writes to Rank/Bank/Row/Col before updating the address.
+ Note: The function will handle linear vs exponential and a value of 0 specifies a rate of 1.
+ @param[in] IncValue - The amount to increase Rank/Bank/Row/Col address.
+ @param[in] WrapTriggerEn - Enables wrap trigger for Rank/Bank/Row/Col to enable stopping on subsequence and sequence.
+ @param[in] WrapCarryEn - Enables carry propagation on wrap to the next higest order field
+ @param[in] AddrInvertEn - Enables inverting the Rank/Bank/Row/Col addresses based on AddrInvertRate.
+ @param[in] AddrIvertRate - Exponential rate of address inversion. Only updated if AddrInvertEn != NULL.
+ @param[in] EnableDebug - Enables/Disables debug printing.
+
+ @retval Nothing
+**/
+void
+MrcProgramSequenceAddress (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U16 StartAddr[MrcReutFieldMax],
+ IN const U16 StopAddr[MrcReutFieldMax],
+ IN const U8 FieldOrder[MrcReutFieldMax],
+ IN const U32 IncRate[MrcReutFieldMax],
+ IN const U16 IncValue[MrcReutFieldMax],
+ IN const U8 WrapTriggerEn[MrcReutFieldMax],
+ IN const U8 WrapCarryEn[MrcReutFieldMax],
+ IN const U8 AddrInvertEn[MrcReutFieldMax],
+ IN const U8 AddrInvertRate,
+ IN const BOOL EnableDebug
+ )
+{
+ MrcInput *Inputs;
+ U64 RowMask;
+ U32 ColumnMask;
+ U32 CrOffset;
+ U32 IncRateScratch;
+ U16 ColAddrIncMax;
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_STRUCT ReutChSeqBaseAddrStart;
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_STRUCT ReutChSeqBaseAddrWrap;
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_STRUCT ReutChSeqBaseAddrIncCtl;
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_ORDER_CARRY_INVERT_CTL_MCMAIN_0_STRUCT ReutChSeqBaseAddrOrderCarryInvertCtl;
+#ifdef MRC_DEBUG_PRINT
+ MrcDebug *Debug;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+
+ //
+ // @todo: Review next stepping
+ //
+ RowMask = (U64) MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Row_Address_MSK;
+ switch (Inputs->CpuModel) {
+ case cmHSW:
+ if (Inputs->CpuStepping == csHswA0) {
+ ColumnMask = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Column_Address_MSK_A0;
+ ColAddrIncMax = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_Column_Base_Address_Increment_MAX_A0;
+ } else {
+ ColumnMask = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Column_Address_MSK;
+ ColAddrIncMax = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_Column_Base_Address_Increment_MAX;
+ }
+ break;
+
+ case cmHSW_ULT:
+ case cmCRW:
+ ColumnMask = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Column_Address_MSK;
+ ColAddrIncMax = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_Column_Base_Address_Increment_MAX;
+ break;
+
+ default:
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Invalid CPU Type in MrcProgramSequenceAddress. Defaulting to Hsw last stepping: %x.\n",
+ csHswLast
+ );
+ ColumnMask = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Column_Address_MSK;
+ ColAddrIncMax = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_Column_Base_Address_Increment_MAX;
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ if (EnableDebug) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Ch.%d Masks: Col - 0x%x\t Row - 0x%08x%08x\tColAddrIncMax: 0x%x\n",
+ Channel,
+ ColumnMask,
+ (U32) MrcOemMemoryRightShiftU64 (RowMask, 32),
+ (U32) RowMask,
+ ColAddrIncMax
+ );
+ }
+#endif
+
+ if (StartAddr != NULL) {
+ ReutChSeqBaseAddrStart.Data = MrcOemMemoryLeftShiftU64 (
+ (U64) ((StartAddr[MrcReutFieldRank] << (56 - 32)) + (StartAddr[MrcReutFieldBank] << (48 - 32))),
+ 32
+ );
+ ReutChSeqBaseAddrStart.Data |= MrcOemMemoryLeftShiftU64 ((U64) StartAddr[MrcReutFieldRow], 24) & RowMask;
+ ReutChSeqBaseAddrStart.Data |= StartAddr[MrcReutFieldCol] & ColumnMask;
+
+ CrOffset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG)
+ * Channel
+ );
+ MrcWriteCR64 (MrcData, CrOffset, ReutChSeqBaseAddrStart.Data);
+
+#ifdef MRC_DEBUG_PRINT
+ if (EnableDebug) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Start:\n\tField\tInput\t\tStruct\n");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tCol:\t0x%x\t\t0x%x\n",
+ StartAddr[MrcReutFieldCol],
+ ReutChSeqBaseAddrStart.Data & ColumnMask
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRow:\t0x%x\t\t0x%x\n",
+ StartAddr[MrcReutFieldRow],
+ MrcOemMemoryRightShiftU64 (ReutChSeqBaseAddrStart.Data, 24) & MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Row_Address_MAX
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tBank:\t0x%x\t\t0x%x\n",
+ StartAddr[MrcReutFieldBank],
+ MrcOemMemoryRightShiftU64 (ReutChSeqBaseAddrStart.Data, 48) & MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Bank_Address_MAX
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRank:\t0x%x\t\t0x%x\n",
+ StartAddr[MrcReutFieldRank],
+ MrcOemMemoryRightShiftU64 (ReutChSeqBaseAddrStart.Data, 56) & MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Rank_Address_MAX
+ );
+ }
+#endif
+ }
+
+ if (StopAddr != NULL) {
+ ReutChSeqBaseAddrWrap.Data = MrcOemMemoryLeftShiftU64 (
+ (U64) ((StopAddr[MrcReutFieldRank] << (56 - 32)) + (StopAddr[MrcReutFieldBank] << (48 - 32))),
+ 32
+ );
+ ReutChSeqBaseAddrWrap.Data |= MrcOemMemoryLeftShiftU64 ((U64) StopAddr[MrcReutFieldRow], 24) & RowMask;
+ ReutChSeqBaseAddrWrap.Data |= StopAddr[MrcReutFieldCol] & ColumnMask;
+
+ CrOffset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG)
+ * Channel
+ );
+ MrcWriteCR64 (MrcData, CrOffset, ReutChSeqBaseAddrWrap.Data);
+
+#ifdef MRC_DEBUG_PRINT
+ if (EnableDebug) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Stop:\n\tField\tInput\t\tStruct\n");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tCol:\t0x%x\t\t0x%x\n",
+ StopAddr[MrcReutFieldCol],
+ ReutChSeqBaseAddrWrap.Data & ColumnMask
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRow:\t0x%x\t\t0x%x\n",
+ StopAddr[MrcReutFieldRow],
+ MrcOemMemoryRightShiftU64 (ReutChSeqBaseAddrWrap.Data, 24) & MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Row_Address_MAX
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tBank:\t0x%x\t\t0x%x\n",
+ StopAddr[MrcReutFieldBank],
+ MrcOemMemoryRightShiftU64 (ReutChSeqBaseAddrWrap.Data, 48) & MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Bank_Address_MAX
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRank:\t0x%x\t\t0x%x\n",
+ StopAddr[MrcReutFieldRank],
+ MrcOemMemoryRightShiftU64 (ReutChSeqBaseAddrWrap.Data, 56) & MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_Rank_Address_MAX
+ );
+ }
+#endif
+ }
+
+ if (FieldOrder != NULL || WrapTriggerEn != NULL || WrapCarryEn != NULL || AddrInvertEn != NULL) {
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Data = 0;
+
+ CrOffset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_ORDER_CARRY_INVERT_CTL_MCMAIN_0_REG +
+ (
+ (
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_ORDER_CARRY_INVERT_CTL_MCMAIN_1_REG -
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_ORDER_CARRY_INVERT_CTL_MCMAIN_0_REG
+ ) * Channel
+ );
+
+ if (FieldOrder == NULL || WrapTriggerEn == NULL || WrapCarryEn == NULL || AddrInvertEn == NULL) {
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Data = MrcReadCR (MrcData, CrOffset);
+ }
+
+ if (FieldOrder != NULL) {
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Column_Address_Order = FieldOrder[MrcReutFieldCol];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Row_Address_Order = FieldOrder[MrcReutFieldRow];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Bank_Address_Order = FieldOrder[MrcReutFieldBank];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Rank_Address_Order = FieldOrder[MrcReutFieldRank];
+
+#ifdef MRC_DEBUG_PRINT
+ if (EnableDebug) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Order:\n\tField\tInput\t\tStruct\t\t\n");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tCol:\t0x%x\t\t0x%x\t\t\n",
+ FieldOrder[MrcReutFieldCol],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Column_Address_Order
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRow:\t0x%x\t\t0x%x\t\t\n",
+ FieldOrder[MrcReutFieldRow],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Row_Address_Order
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tBank:\t0x%x\t\t0x%x\t\t\n",
+ FieldOrder[MrcReutFieldBank],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Bank_Address_Order
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRank:\t0x%x\t\t0x%x\t\t\n",
+ FieldOrder[MrcReutFieldRank],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Rank_Address_Order
+ );
+ }
+#endif
+ }
+
+ if (WrapTriggerEn != NULL) {
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Column_Base_Wrap_Trigger_Enable = WrapTriggerEn[MrcReutFieldCol];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Row_Base_Wrap_Trigger_Enable = WrapTriggerEn[MrcReutFieldRow];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Bank_Base_Wrap_Trigger_Enable = WrapTriggerEn[MrcReutFieldBank];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Rank_Base_Wrap_Trigger_Enable = WrapTriggerEn[MrcReutFieldRank];
+#ifdef MRC_DEBUG_PRINT
+ if (EnableDebug) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "WrapT:\n\tField\tInput\t\tStruct\t\t\n");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tCol:\t0x%x\t\t0x%x\t\t\n",
+ WrapTriggerEn[MrcReutFieldCol],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Column_Base_Wrap_Trigger_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRow:\t0x%x\t\t0x%x\t\t\n",
+ WrapTriggerEn[MrcReutFieldRow],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Row_Base_Wrap_Trigger_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tBank:\t0x%x\t\t0x%x\t\t\n",
+ WrapTriggerEn[MrcReutFieldBank],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Bank_Base_Wrap_Trigger_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRank:\t0x%x\t\t0x%x\t\t\n",
+ WrapTriggerEn[MrcReutFieldRank],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Rank_Base_Wrap_Trigger_Enable
+ );
+ }
+#endif
+ }
+
+ if (WrapCarryEn != NULL) {
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Column_Base_Wrap_Carry_Enable = WrapCarryEn[MrcReutFieldCol];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Row_Base_Wrap_Carry_Enable = WrapCarryEn[MrcReutFieldRow];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Bank_Base_Wrap_Carry_Enable = WrapCarryEn[MrcReutFieldBank];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Rank_Base_Wrap_Carry_Enable = WrapCarryEn[MrcReutFieldRank];
+
+#ifdef MRC_DEBUG_PRINT
+ if (EnableDebug) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "WrapC:\n\tField\tInput\t\tStruct\t\t\n");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tCol:\t0x%x\t\t0x%x\t\t\n",
+ WrapCarryEn[MrcReutFieldCol],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Column_Base_Wrap_Carry_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRow:\t0x%x\t\t0x%x\t\t\n",
+ WrapCarryEn[MrcReutFieldRow],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Row_Base_Wrap_Carry_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tBank:\t0x%x\t\t0x%x\t\t\n",
+ WrapCarryEn[MrcReutFieldBank],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Bank_Base_Wrap_Carry_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRank:\t0x%x\t\t0x%x\t\t\n",
+ WrapCarryEn[MrcReutFieldRank],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Rank_Base_Wrap_Carry_Enable
+ );
+ }
+#endif
+ }
+
+ if (AddrInvertEn != NULL) {
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Address_Invert_Rate = AddrInvertRate;
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Column_Base_Address_Invert_Enable = AddrInvertEn[MrcReutFieldCol];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Row_Base_Address_Invert_Enable = AddrInvertEn[MrcReutFieldRow];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Bank_Base_Address_Invert_Enable = AddrInvertEn[MrcReutFieldBank];
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Rank_Base_Address_Invert_Enable = AddrInvertEn[MrcReutFieldRank];
+
+#ifdef MRC_DEBUG_PRINT
+ if (EnableDebug) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "InvtEn:\n\tField\tInput\t\tStruct\t\t\n");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tCol:\t0x%x\t\t0x%x\t\t\n",
+ AddrInvertEn[MrcReutFieldCol],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Column_Base_Address_Invert_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRow:\t0x%x\t\t0x%x\t\t\n",
+ AddrInvertEn[MrcReutFieldRow],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Row_Base_Address_Invert_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tBank:\t0x%x\t\t0x%x\t\t\n",
+ AddrInvertEn[MrcReutFieldBank],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Bank_Base_Address_Invert_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRank:\t0x%x\t\t0x%x\t\t\n",
+ AddrInvertEn[MrcReutFieldRank],
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Rank_Base_Address_Invert_Enable
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRate:\t0x%x\t\t0x%x\t\t\n",
+ AddrInvertRate,
+ ReutChSeqBaseAddrOrderCarryInvertCtl.Bits.Base_Address_Invert_Rate
+ );
+ }
+#endif
+ }
+
+ MrcWriteCR (MrcData, CrOffset, ReutChSeqBaseAddrOrderCarryInvertCtl.Data);
+ }
+
+ if (IncRate != 0 || IncValue != 0) {
+ ReutChSeqBaseAddrIncCtl.Data = 0;
+
+ CrOffset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_REG)
+ * Channel
+ );
+
+ if (IncRate == 0 || IncValue == 0) {
+ ReutChSeqBaseAddrIncCtl.Data = MrcReadCR64 (MrcData, CrOffset);
+ }
+
+ if (IncRate != 0) {
+ //
+ // RANK
+ //
+ IncRateScratch = (IncRate[MrcReutFieldRank] > 31) ? (MrcLog2 (IncRate[MrcReutFieldRank] - 1)) :
+ (128 + IncRate[MrcReutFieldRank]);
+ ReutChSeqBaseAddrIncCtl.Bits.Rank_Base_Address_Update_Rate = IncRateScratch;
+ ReutChSeqBaseAddrIncCtl.Bits.Rank_Base_Address_Update_Scale = IncRateScratch >> 7;
+ //
+ // BANK
+ //
+ IncRateScratch = (IncRate[MrcReutFieldBank] > 31) ? (MrcLog2 (IncRate[MrcReutFieldBank] - 1)) :
+ (128 + IncRate[MrcReutFieldBank]);
+ ReutChSeqBaseAddrIncCtl.Bits.Bank_Base_Address_Update_Rate = IncRateScratch;
+ ReutChSeqBaseAddrIncCtl.Bits.Bank_Base_Address_Update_Scale = IncRateScratch >> 7;
+ //
+ // ROW
+ //
+ IncRateScratch = (IncRate[MrcReutFieldRow] > 15) ? (MrcLog2 (IncRate[MrcReutFieldRow] - 1)) :
+ (32 + IncRate[MrcReutFieldRow]);
+ ReutChSeqBaseAddrIncCtl.Bits.Row_Base_Address_Update_Rate = IncRateScratch;
+ ReutChSeqBaseAddrIncCtl.Bits.Row_Base_Address_Update_Scale = IncRateScratch >> 5;
+ //
+ // COL
+ //
+ IncRateScratch = (IncRate[MrcReutFieldCol] > 31) ? (MrcLog2 (IncRate[MrcReutFieldCol] - 1)) :
+ (128 + IncRate[MrcReutFieldCol]);
+ ReutChSeqBaseAddrIncCtl.Bits.Column_Base_Address_Update_Rate = IncRateScratch;
+ ReutChSeqBaseAddrIncCtl.Bits.Column_Base_Address_Update_Scale = IncRateScratch >> 7;
+
+#ifdef MRC_DEBUG_PRINT
+ if (EnableDebug) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "IncRate:\n\tField\tInput\t\tStruct\t\tScale\n");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tCol:\t0x%x\t\t0x%x\t\t0x%x\n",
+ IncRate[MrcReutFieldCol],
+ ReutChSeqBaseAddrIncCtl.Bits.Column_Base_Address_Update_Rate,
+ ReutChSeqBaseAddrIncCtl.Bits.Column_Base_Address_Update_Scale
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRow:\t0x%x\t\t0x%x\t\t0x%x\n",
+ IncRate[MrcReutFieldRow],
+ ReutChSeqBaseAddrIncCtl.Bits.Row_Base_Address_Update_Rate,
+ ReutChSeqBaseAddrIncCtl.Bits.Row_Base_Address_Update_Scale
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tBank:\t0x%x\t\t0x%x\t\t0x%x\n",
+ IncRate[MrcReutFieldBank],
+ ReutChSeqBaseAddrIncCtl.Bits.Bank_Base_Address_Update_Rate,
+ ReutChSeqBaseAddrIncCtl.Bits.Bank_Base_Address_Update_Scale
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRank:\t0x%x\t\t0x%x\t\t0x%x\n",
+ IncRate[MrcReutFieldRank],
+ ReutChSeqBaseAddrIncCtl.Bits.Rank_Base_Address_Update_Rate,
+ ReutChSeqBaseAddrIncCtl.Bits.Rank_Base_Address_Update_Scale
+ );
+ }
+#endif
+ }
+
+ if (IncValue != 0) {
+ ReutChSeqBaseAddrIncCtl.Bits.Rank_Base_Address_Increment = IncValue[MrcReutFieldRank];
+ ReutChSeqBaseAddrIncCtl.Bits.Bank_Base_Address_Increment = IncValue[MrcReutFieldBank];
+ ReutChSeqBaseAddrIncCtl.Bits.Row_Base_Address_Increment = IncValue[MrcReutFieldRow];
+ ReutChSeqBaseAddrIncCtl.Bits.Column_Base_Address_Increment = IncValue[MrcReutFieldCol] & ColAddrIncMax;
+
+#ifdef MRC_DEBUG_PRINT
+ if (EnableDebug) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "IncVal:\n\tField\tInput\t\tStruct\t\t\n");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tCol:\t0x%x\t\t0x%x\t\t\n",
+ IncValue[MrcReutFieldCol],
+ ReutChSeqBaseAddrIncCtl.Bits.Column_Base_Address_Increment
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRow:\t0x%x\t\t0x%x\t\t\n",
+ IncValue[MrcReutFieldRow],
+ ReutChSeqBaseAddrIncCtl.Bits.Row_Base_Address_Increment
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tBank:\t0x%x\t\t0x%x\t\t\n",
+ IncValue[MrcReutFieldBank],
+ ReutChSeqBaseAddrIncCtl.Bits.Bank_Base_Address_Increment
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tRank:\t0x%x\t\t0x%x\t\t\n",
+ IncValue[MrcReutFieldRank],
+ ReutChSeqBaseAddrIncCtl.Bits.Rank_Base_Address_Increment
+ );
+ }
+#endif
+ }
+
+ MrcWriteCR64 (MrcData, CrOffset, ReutChSeqBaseAddrIncCtl.Data);
+ }
+}
+
+/**
+ Programs all the key registers to define a CPCG test
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] WDBPattern - Structure that stores start, Stop, IncRate and Dqpat for pattern.
+ @param[in] ChbitMask - Channel Bit mak for which test should be setup for.
+ @param[in] CmdPat - [0: PatWrRd (Standard Write/Read Loopback),
+ 1: PatWr (Write Only),
+ 2: PatRd (Read Only),
+ 3: PatRdWrTA (ReadWrite Turnarounds),
+ 4: PatWrRdTA (WriteRead Turnarounds),
+ 5: PatODTTA (ODT Turnaround]
+ @param[in] NumCL - Number of Cache lines
+ @param[in] LC - Loop Count exponent
+ @param[in] REUTAddress - Structure that stores start, stop and increment details for address
+ @param[in] SOE - [0: Never Stop, 1: Stop on Any Lane, 2: Stop on All Byte, 3: Stop on All Lane]
+ @param[in] WDBPattern - Structure that stores start, Stop, IncRate and Dqpat for pattern.
+ @param[in] EnCADB - Enable test to write random deselect packages on bus to create xtalk/isi
+ @param[in] EnCKE - Enable CKE power down by adding 64
+ @param[in] SubSeqWait - # of Dclks to stall at the end of a sub-sequence
+
+ @retval Nothing
+**/
+void
+SetupIOTest(
+ IN MrcParameters *const MrcData,
+ IN const U8 ChbitMask,
+ IN const U8 CmdPat,
+ IN const U16 NumCL,
+ IN const U8 LC,
+ IN const MRC_REUTAddress *const REUTAddress,
+ IN const U8 SOE,
+ IN MRC_WDBPattern *const WDBPattern,
+ IN const U8 EnCADB,
+ IN const U8 EnCKE,
+ IN U16 SubSeqWait
+ )
+{
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+ const U8 WrapCarryEn[MrcReutFieldMax] = {0, 0, 0, 0}; // Not used in training tests
+ const U8 WrapTriggerEn[MrcReutFieldMax] = {0, 0, 0, 0}; // Not used in training tests
+ const U8 AddrInvertEn[MrcReutFieldMax] = {0, 0, 0, 0}; // Not used in training tests
+ MrcOutput *Outputs;
+ U8 Channel;
+ S8 LCeff;
+ U32 LoopCountLinear;
+ U8 Mux0;
+ U8 Reload;
+ U8 Save;
+ U8 NumCLCR;
+ U8 NumCL2CR;
+ U16 Wait;
+ U16 NumCL2;
+ U32 LMNFreq[2];
+ U32 Offset;
+ U8 SubSeqStart;
+ U8 SubSeqEnd;
+ U8 Index;
+ MCHBAR_CH0_CR_REUT_CH_PAT_CADB_CTRL_STRUCT ReutChPatCadbCtrl;
+ MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_STRUCT ReutChSeqCfg;
+ MCSCHEDS_CR_PM_PDWN_CONFIG_STRUCT PmPdwnConfig;
+ MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_STRUCT_HSW_A0 ReutSubSeqCtl0HswA0;
+ MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_STRUCT ReutSubSeqCtl0;
+ U32 ReutSubSeqCtl0Data;
+ MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_STRUCT ReutSubSeqCtl1;
+ MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_STRUCT ReutChPatWdbCl;
+ MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_CFG_STRUCT ReutChPatWdbClMuxCfg;
+ MCHBAR_CH0_CR_REUT_CH_ERR_CTL_STRUCT ReutChErrCtrl;
+ MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_CTL_MCMAIN_0_STRUCT ReutChSeqDummyReadCtl;
+ struct LocalSubSeqCtl {
+ U8 ValidMask;
+ MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_STRUCT Ctl[8];
+ } SubSeq;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+
+ //
+ // Prepare variables needed for both channels
+ //
+ // Check for the cases where this MUST be 1: When we manually walk through SUBSEQ ODT and TAWR
+ //
+ LCeff = LC - MrcLog2 (NumCL - 1) + 1;
+ if ((LCeff < 1) || (CmdPat == PatWrRdTA) || (CmdPat == PatODTTA)) {
+ LCeff = 1;
+ }
+
+ LoopCountLinear = 1 << (LCeff - 1);
+
+ if (NumCL > 127) {
+ NumCLCR = MrcLog2 (NumCL - 1); // Assume Exponential number
+ } else {
+ NumCLCR = (U8) NumCL + (MRC_BIT0 << 7); // Set Number of Cache Lines as linear number
+ }
+
+ NumCL2 = 2 * NumCL;
+ if (NumCL2 > 127) {
+ NumCL2CR = MrcLog2 (NumCL2 - 1); // Assume Exponential number
+ } else {
+ NumCL2CR = (U8) NumCL2 + (MRC_BIT0 << 7); // Set Number of Cache Lines as linear number
+ }
+
+ Reload = MrcLog2 (WDBPattern->IncRate - 1);
+ //
+ // @todo: 'Save' is initialized but never used.
+ //
+ Save = Reload + MrcLog2 ((WDBPattern->Stop - WDBPattern->Start - 1) & 0xFF);
+
+ if (WDBPattern->IncRate > 31) {
+ WDBPattern->IncRate = Reload;
+ } else {
+ WDBPattern->IncRate += 32;
+ }
+
+ if (EnCKE) {
+ //
+ // @todo: Need to check that PDWN is programmed already.
+ //
+ PmPdwnConfig.Data = MrcReadCR (MrcData, MCSCHEDS_CR_PM_PDWN_CONFIG_REG);
+ Wait = (U16) (PmPdwnConfig.Bits.PDWN_idle_counter + 16); // Adding extra DCKs, 16, to make sure we make it to power down.
+ if (Wait > SubSeqWait) {
+ SubSeqWait = Wait;
+ }
+ }
+
+ if (SubSeqWait > 0xFF) {
+ SubSeqWait = 0xFF;
+ }
+ //
+ // Per channel settings
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChbitMask)) {
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0); // Clear global control
+ continue;
+ }
+
+ //###########################################################
+ //
+ // Program CADB
+ //
+ //###########################################################
+ 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);
+ ReutChPatCadbCtrl.Data = 0;
+ ReutChPatCadbCtrl.Bits.Enable_CADB_on_Deselect = EnCADB;
+ MrcWriteCR8 (MrcData, Offset, (U8) ReutChPatCadbCtrl.Data);
+ if (EnCADB) {
+ SetupCADB (MrcData, Channel, 7, 8, 0); // LMNEn=0
+ }
+
+ //###########################################################
+ //
+ // Program Sequence
+ //
+ //###########################################################
+ SubSeqStart = SubSeqEnd = 0;
+ switch (CmdPat) {
+ case PatWrRd:
+ SubSeqEnd = 1;
+ break;
+
+ case PatWr:
+ break;
+
+ case PatRd:
+ SubSeqStart = SubSeqEnd = 1;
+ break;
+
+ case PatRdWrTA:
+ break;
+
+ case PatWrRdTA:
+ SubSeqEnd = 7;
+ break;
+
+ case PatODTTA:
+ SubSeqEnd = 3;
+ break;
+
+ default:
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "SetupIOTest: Unknown value for Pattern\n");
+ break;
+ }
+ ReutChSeqCfg.Data = 0;
+ ReutChSeqCfg.Bits.Subsequence_Start_Pointer = SubSeqStart;
+ ReutChSeqCfg.Bits.Subsequence_End_Pointer = SubSeqEnd;
+ ReutChSeqCfg.Bits.Initialization_Mode = REUT_Testing_Mode;
+ ReutChSeqCfg.Bits.Global_Control = 1;
+ ReutChSeqCfg.Bits.Enable_Dummy_Reads = MIN (
+ Outputs->EnDumRd,
+ MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_Enable_Dummy_Reads_MAX
+ );
+ if (CmdPat == DimmTest) { // Inc address based on LC
+ ReutChSeqCfg.Bits.Address_Update_Rate_Mode = 1;
+ }
+ ReutChSeqCfg.Bits.Start_Test_Delay = 2;
+
+ if (
+ (Inputs->CpuModel == cmHSW && Inputs->CpuStepping < csHswC0) ||
+ (Inputs->CpuModel == cmCRW && Inputs->CpuStepping < csCrwC0) ||
+ (Inputs->CpuModel == cmHSW_ULT && Inputs->CpuStepping < csHswUltC0)
+ ) {
+ ReutChSeqCfg.Bits.Loopcount = MIN (LCeff, MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_Loopcount_MAX);
+ } else {
+ Offset = MCDFXS_CR_REUT_CH_SEQ_LOOPCOUNT_LIMIT_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_LOOPCOUNT_LIMIT_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_LOOPCOUNT_LIMIT_MCMAIN_0_REG) *
+ Channel
+ );
+ MrcWriteCR (MrcData, Offset, LoopCountLinear);
+ }
+
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * Channel);
+ MrcWriteCR64 (MrcData, Offset, ReutChSeqCfg.Data);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "SetupIOTest: C%d REUT_CH_SEQ_CFG_0 = 0x%X %X\n", Channel, ReutChSeqCfg.Data32[1], ReutChSeqCfg.Data32[0]);
+
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_0_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_0_Local_Clear_Errors_MSK);
+
+ //###########################################################
+ //
+ // Program Sub Sequences
+ //
+ //###########################################################
+ if ((Inputs->CpuModel == cmHSW) && (Inputs->CpuStepping == csHswA0)) {
+ ReutSubSeqCtl0HswA0.Data = 0;
+ ReutSubSeqCtl0HswA0.Bits.Number_of_Cachelines = NumCLCR;
+ ReutSubSeqCtl0HswA0.Bits.Number_of_Cachelines_Scale = NumCLCR >> 7;
+ ReutSubSeqCtl0HswA0.Bits.Reset_Current_Base_Address_To_Start = 1;
+ ReutSubSeqCtl0HswA0.Bits.Subsequence_Wait = SubSeqWait;
+ ReutSubSeqCtl0Data = ReutSubSeqCtl0HswA0.Data;
+ } else {
+ ReutSubSeqCtl0.Data = 0;
+ ReutSubSeqCtl0.Bits.Number_of_Cachelines = NumCLCR;
+ ReutSubSeqCtl0.Bits.Number_of_Cachelines_Scale = NumCLCR >> 7;
+ ReutSubSeqCtl0.Bits.Reset_Current_Base_Address_To_Start = 1;
+ ReutSubSeqCtl0.Bits.Subsequence_Wait = SubSeqWait;
+ ReutSubSeqCtl0Data = ReutSubSeqCtl0.Data;
+ }
+
+ ReutSubSeqCtl1.Data = ReutSubSeqCtl0Data;
+ ReutSubSeqCtl1.Bits.Number_of_Cachelines = NumCL2CR;
+ ReutSubSeqCtl1.Bits.Number_of_Cachelines_Scale = NumCL2CR >> 7;
+
+ switch (CmdPat) {
+ case PatWrRdTA:
+ SubSeq.Ctl[0].Data = ReutSubSeqCtl0Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[0].Data, BWr); // Write CMD
+ for (Index = 1; Index <= 6; Index++) {
+ SubSeq.Ctl[Index].Data = ReutSubSeqCtl1.Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[Index].Data, BRdWr); // Read-Write CMD
+ }
+ SubSeq.Ctl[7].Data = ReutSubSeqCtl0Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[7].Data, BRd); // Read CMD
+ SubSeq.ValidMask = 0xFF;
+ break;
+
+ case PatRdWrTA:
+ SubSeq.Ctl[0].Data = ReutSubSeqCtl1.Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[0].Data, BWrRd); // Write-Read CMD
+ SubSeq.ValidMask = 0x01;
+ break;
+
+ case PatODTTA:
+ SubSeq.Ctl[0].Data = ReutSubSeqCtl0Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[0].Data, BWr); // Write CMD
+
+ SubSeq.Ctl[1].Data = ReutSubSeqCtl1.Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[1].Data, BRdWr); // Read-Write CMD
+
+ SubSeq.Ctl[2].Data = ReutSubSeqCtl0Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[2].Data, BRd); // Read CMD
+
+ SubSeq.Ctl[3].Data = ReutSubSeqCtl1.Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[3].Data, BWrRd); // Write-Read CMD
+
+ SubSeq.ValidMask = 0x0F;
+ break;
+
+ default:
+ SubSeq.Ctl[0].Data = ReutSubSeqCtl0Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[0].Data, BWr); // Write CMD
+
+ SubSeq.Ctl[1].Data = ReutSubSeqCtl0Data;
+ SetSubsequenceType (MrcData, &SubSeq.Ctl[1].Data, BRd); // Read CMD
+
+ SubSeq.ValidMask = 0x03;
+ break;
+ }
+ Offset = MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH1_SUBSEQ_CTL_MCMAIN_0_REG - MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_REG) * Channel);
+ for (Index = 0; Index < 8; Index++) {
+ if (SubSeq.ValidMask & (MRC_BIT0 << Index)) {
+ MrcWriteCR (MrcData, Offset, SubSeq.Ctl[Index].Data);
+ Offset += MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_REG;
+ } else {
+ break;
+ }
+ }
+
+ //###########################################################
+ //
+ // Program Sequence Address
+ //
+ //###########################################################
+ MrcProgramSequenceAddress (
+ MrcData,
+ Channel,
+ REUTAddress->Start,
+ REUTAddress->Stop,
+ REUTAddress->Order,
+ REUTAddress->IncRate,
+ REUTAddress->IncVal,
+ WrapCarryEn,
+ WrapTriggerEn,
+ AddrInvertEn,
+ 0,
+ FALSE
+ );
+
+ //###########################################################
+ //
+ // Program Write Data Buffer Related Entries
+ //
+ //###########################################################
+ ReutChPatWdbCl.Data = 0;
+ ReutChPatWdbCl.Bits.WDB_End_Pointer = WDBPattern->Stop;
+ ReutChPatWdbCl.Bits.WDB_Start_Pointer = WDBPattern->Start;
+ ReutChPatWdbCl.Bits.WDB_Increment_Rate = WDBPattern->IncRate;
+ ReutChPatWdbCl.Bits.WDB_Increment_Scale = WDBPattern->IncRate >> MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_WDB_Increment_Rate_WID;
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_WDB_CL_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ReutChPatWdbCl.Data);
+
+ ReutChPatWdbClMuxCfg.Data = 0;
+
+ //
+ // Enable LMN in either LMN mode or CADB -to create lots of supply noise
+ //
+ Mux0 = ((WDBPattern->DQPat == LMNVa) || (WDBPattern->DQPat == CADB)) ? LMNMode : LFSRMode;
+
+ ReutChPatWdbClMuxCfg.Bits.ECC_Data_Source_Sel = 1;
+ ReutChPatWdbClMuxCfg.Bits.Mux2_Control = LFSRMode;
+ ReutChPatWdbClMuxCfg.Bits.Mux1_Control = LFSRMode;
+ ReutChPatWdbClMuxCfg.Bits.Mux0_Control = Mux0; // ECC, Select LFSR
+ //
+ // Program LFSR Save/Restore. Too complex unless everything is power of 2
+ //
+ if ((CmdPat == PatODTTA) || (CmdPat == PatWrRdTA)) {
+ ReutChPatWdbClMuxCfg.Bits.Reload_LFSR_Seed_Rate = MrcLog2 (NumCL - 1) + 1;
+ ReutChPatWdbClMuxCfg.Bits.Save_LFSR_Seed_Rate = 1;
+ }
+
+ 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);
+
+ //
+ // Currently, not planning to use the Inversion Mask
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_WDB_INV_REG + ((MCHBAR_CH1_CR_REUT_CH_PAT_WDB_INV_REG - MCHBAR_CH0_CR_REUT_CH_PAT_WDB_INV_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0);
+
+ //###########################################################
+ //
+ // Program Error Checking
+ //
+ //###########################################################
+
+ //
+ // Enable selective_error_enable_chunk and selective_error_enable_cacheline, mask later
+ // the bits we don't want to check.
+ //
+ 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);
+ ReutChErrCtrl.Data = 0;
+ ReutChErrCtrl.Bits.Stop_on_Nth_Error = 1;
+ ReutChErrCtrl.Bits.Stop_On_Error_Control = SOE;
+ ReutChErrCtrl.Bits.Selective_Error_Enable_Chunk = MCHBAR_CH0_CR_REUT_CH_ERR_CTL_Selective_Error_Enable_Chunk_MAX;
+ ReutChErrCtrl.Bits.Selective_Error_Enable_Cacheline = MCHBAR_CH0_CR_REUT_CH_ERR_CTL_Selective_Error_Enable_Cacheline_MAX;
+ MrcWriteCR (MrcData, Offset, ReutChErrCtrl.Data);
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_DATA_MASK_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_ERR_DATA_MASK_REG - MCHBAR_CH0_CR_REUT_CH_ERR_DATA_MASK_REG) * Channel);
+ MrcWriteCR64 (MrcData, Offset, 0);
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_ECC_MASK_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_ERR_ECC_MASK_REG - MCHBAR_CH0_CR_REUT_CH_ERR_ECC_MASK_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, 0);
+
+ //###########################################################
+ //
+ // Program Dummy Read
+ //
+ //###########################################################
+ if (Outputs->EnDumRd) {
+ //
+ // REUT traffic only uses BA[1:0] - Mask BANK that will not be used
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_MASK_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_MASK_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_MASK_MCMAIN_0_REG) *
+ Channel
+ );
+ MrcWriteCR8 (MrcData, Offset, 0xFC);
+
+ //
+ // Rotated from 40nS to 200nS
+ //
+ if (Outputs->Qclkps > 0) {
+ LMNFreq[0] = (40000 / Outputs->Qclkps);
+ LMNFreq[1] = (200000 / Outputs->Qclkps);
+ } else {
+ LMNFreq[0] = LMNFreq[1] = 0xFF;
+ }
+
+ ReutChSeqDummyReadCtl.Data = 0;
+ ReutChSeqDummyReadCtl.Bits.L_counter = LMNFreq[0];
+ ReutChSeqDummyReadCtl.Bits.M_counter = LMNFreq[0];
+ ReutChSeqDummyReadCtl.Bits.N_Counter = LMNFreq[1];
+ ReutChSeqDummyReadCtl.Bits.Enable_Sweep_Frequency = 1;
+ //
+ // Chirp Freq from 5 to 25 MHz
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_CTL_MCMAIN_0_REG + (
+ (MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_DUMMYREAD_CTL_MCMAIN_0_REG) * Channel
+ );
+ MrcWriteCR (MrcData, Offset, ReutChSeqDummyReadCtl.Data);
+ }
+ }
+ //
+ // Always do a ZQ Short before the beginning of a test
+ //
+ MrcIssueZQ (MrcData, ChbitMask, MRC_ZQ_SHORT);
+
+ return;
+}
+
+/**
+ This function sets up a test with CADB for the given channel mask.
+
+ @param[in,out] MrcData - Pointer to MRC global data.
+ @param[in] ChbitMask - Bit masks of channels to enable for the test.
+ @param[in] LC - Exponential umber of loops to run the test.
+ @param[in] SOE - Error handling switch for test.
+ @param[in] EnCADB - Switch to enable CADB
+ @param[in] EnCKE - Switch to enable CKE.
+
+ @retval Nothing
+**/
+void
+SetupIOTestCADB (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 ChbitMask,
+ IN const U8 LC,
+ IN const U8 SOE,
+ IN const U8 EnCADB,
+ IN const U8 EnCKE
+ )
+{
+ const MRC_REUTAddress REUTAddress = {
+ // Rank, Bank, Row, Col
+ { 0, 0, 0, 0 }, // Start
+ { 0, 7, 2047, 1023 }, // Stop
+ { 0, 0, 0, 0 }, // Order
+ { 32, 3, 3, 0 }, // IncRate
+ { 1, 1, 73, 53 } // IncValue
+ };
+ MRC_WDBPattern WDBPattern;
+ MrcOutput *Outputs;
+ U16 NumCL;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ WDBPattern.IncRate = 4;
+ WDBPattern.Start = 0;
+ WDBPattern.Stop = 9;
+ WDBPattern.DQPat = CADB;
+
+ NumCL = 128;
+
+ SetupIOTest (MrcData, ChbitMask, PatWrRd, NumCL, LC, &REUTAddress, SOE, &WDBPattern, EnCADB, EnCKE, 0);
+
+ Outputs->DQPatLC = LC - 2 - 3 + 1;
+ if (Outputs->DQPatLC < 1) {
+ Outputs->DQPatLC = 1;
+ }
+
+ Outputs->DQPat = CADB;
+ return;
+}
+
+/**
+ This function sets up a basic victim-aggressor test for the given channel mask.
+
+ @param[in,out] MrcData - Pointer to MRC global data.
+ @param[in] ChbitMask - Bit masks of channels to enable for the test.
+ @param[in] LC - Exponential umber of loops to run the test.
+ @param[in] SOE - Error handling switch for test.
+ @param[in] EnCADB - Switch to enable CADB
+ @param[in] EnCKE - Switch to enable CKE.
+ @param[in] Spread - Stopping point of the pattern.
+
+ @retval Nothing
+**/
+void
+SetupIOTestBasicVA (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 ChbitMask,
+ IN const U8 LC,
+ IN const U8 SOE,
+ IN const U8 EnCADB,
+ IN const U8 EnCKE,
+ IN const U32 Spread
+ )
+{
+ const MRC_REUTAddress REUTAddress = {
+ // Rank, Bank, Row, Col
+ { 0, 0, 0, 0 }, // Start
+ { 0, 0, 0, 1023 }, // Stop
+ { 0, 0, 0, 0 }, // Order
+ { 32, 0, 0, 0 }, // IncRate
+ { 1, 0, 0, 1 } // IncValue
+ };
+
+ MRC_WDBPattern WDBPattern;
+ MrcOutput *Outputs;
+ U16 NumCL;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ WDBPattern.IncRate = 4;
+ WDBPattern.Start = 0;
+ WDBPattern.Stop = Spread - 1;
+ WDBPattern.DQPat = BasicVA;
+
+ NumCL = 128;
+
+ SetupIOTest (MrcData, ChbitMask, PatWrRd, NumCL, LC, &REUTAddress, SOE, &WDBPattern, EnCADB, EnCKE, 0);
+
+ Outputs->DQPatLC = LC - 8 + 1;
+ if (Outputs->DQPatLC < 1) {
+ Outputs->DQPatLC = 1;
+ }
+
+ Outputs->DQPat = BasicVA;
+ return;
+}
+
+/**
+ This function sets up a DQ test for the given channel mask.
+
+ @param[in,out] MrcData - Pointer to MRC global data.
+ @param[in] ChbitMask - Bit masks of channels to enable for the test.
+ @param[in] LC - Exponential umber of loops to run the test.
+ @param[in] SOE - Error handling switch for test.
+ @param[in] EnCADB - Switch to enable CADB
+ @param[in] EnCKE - Switch to enable CKE.
+
+ @retval Nothing
+**/
+void
+SetupIOTestDQ (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 ChbitMask,
+ IN const U8 LC,
+ IN const U8 SOE,
+ IN const U8 EnCADB,
+ IN const U8 EnCKE
+ )
+{
+ const MRC_REUTAddress REUTAddress = {{0, 0, 0, 0}, // Start
+ {0, 1, 512, 1023}, // Stop
+ {0, 0, 0, 0}, // Order
+ {2047, 255, 255, 0}, // IncRate
+ {1, 1, 512, 1}}; // IncValue
+ MRC_WDBPattern WDBPattern;
+ MrcOutput *Outputs;
+ U16 NumCL;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ WDBPattern.IncRate = 32;
+ WDBPattern.Start = 0;
+ WDBPattern.Stop = 63;
+ WDBPattern.DQPat = SegmentWDB;
+
+ NumCL = 256;
+
+ SetupIOTest (MrcData, ChbitMask, PatWrRd, NumCL, LC, &REUTAddress, SOE, &WDBPattern, EnCADB, EnCKE, 0);
+
+ Outputs->DQPatLC = LC - 8 - 3 + 1;
+ if (Outputs->DQPatLC < 1) {
+ Outputs->DQPatLC = 1;
+ }
+
+ Outputs->DQPat = SegmentWDB;
+ return;
+}
+
+/**
+ This function sets up a test with CADB for the given channel mask.
+
+ @param[in,out] MrcData - Pointer to MRC global data.
+ @param[in] ChbitMask - Bit masks of channels to enable for the test.
+ @param[in] LC - Exponential umber of loops to run the test.
+ @param[in] SOE - Error handling switch for test.
+ @param[in] EnCADB - Switch to enable CADB
+ @param[in] EnCKE - Switch to enable CKE.
+
+ @retval Nothing
+**/
+void
+SetupIOTestC2C (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 ChbitMask,
+ IN const U8 LC,
+ IN const U8 SOE,
+ IN const U8 EnCADB,
+ IN const U8 EnCKE
+ )
+{
+ const MRC_REUTAddress REUTAddress = {{0, 0, 0, 0}, // Start
+ {0, 0, 0, 1023}, // Stop
+ {0, 0, 0, 0}, // Order
+ {2047, 0, 0, 0}, // IncRate
+ {1, 0, 0, 1}}; // IncValue
+ MRC_WDBPattern WDBPattern;
+ MrcOutput *Outputs;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ WDBPattern.IncRate = 32;
+ WDBPattern.Start = 0;
+ WDBPattern.Stop = 63;
+ WDBPattern.DQPat = SegmentWDB;
+
+ SetupIOTest (MrcData, ChbitMask, PatWrRd, 32, LC, &REUTAddress, SOE, &WDBPattern, EnCADB, EnCKE, 0);
+
+ Outputs->DQPatLC = LC - 5 + 1;
+ if (Outputs->DQPatLC < 1) {
+ Outputs->DQPatLC = 1;
+ }
+
+ Outputs->DQPat = SegmentWDB;
+ return;
+}
+
+/**
+ This function sets up a MPR test for the given channel mask.
+
+ @param[in,out] MrcData - Pointer to MRC global data.
+ @param[in] ChbitMask - Bit masks of channels to enable for the test.
+ @param[in] LC - Exponential umber of loops to run the test.
+ @param[in] SOE - Error handling switch for test.
+ @param[in] EnCADB - Switch to enable CADB
+ @param[in] EnCKE - Switch to enable CKE.
+
+ @retval Nothing
+**/
+void
+SetupIOTestMPR (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 ChbitMask,
+ IN const U8 LC,
+ IN const U8 SOE,
+ IN const U8 EnCADB,
+ IN const U8 EnCKE
+ )
+{
+ const MRC_REUTAddress REUTAddress_ddr = {
+ { 0, 0, 0, 0 }, // Start
+ { 0, 0, 0, 1023 }, // Stop
+ { 0, 0, 0, 0 }, // Order
+ { 32, 0, 0, 0 }, // IncRate
+ { 1, 0, 0, 1 } // IncValue
+ };
+ const MRC_REUTAddress REUTAddress_lpddr = {
+ { 0, 4, 0, 0 }, // Start
+ { 0, 4, 0, 0 }, // Stop
+ { 0, 0, 0, 0 }, // Order
+ { 0, 0, 0, 0 }, // IncRate
+ { 0, 0, 0, 0 } // IncValue
+ };
+ const MRC_REUTAddress *ReutAddress;
+ MRC_WDBPattern WDBPattern;
+ MrcOutput *Outputs;
+ U16 NumCL;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ WDBPattern.IncRate = 4;
+ WDBPattern.Start = 0;
+ WDBPattern.Stop = 9;
+ WDBPattern.DQPat = BasicVA;
+
+ NumCL = 128;
+
+ if (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) {
+ ReutAddress = &REUTAddress_lpddr;
+ } else {
+ ReutAddress = &REUTAddress_ddr;
+ }
+
+ SetupIOTest (MrcData, ChbitMask, PatRd, NumCL, LC, ReutAddress, SOE, &WDBPattern, EnCADB, EnCKE, 0);
+
+ Outputs->DQPatLC = 1;
+ Outputs->DQPat = BasicVA;
+ return;
+}
+
+/**
+ Runs one or more REUT tests (based on TestType)
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] ChbitMask - Channel Bit mask for which test should be setup for.
+ @param[in] DQPat - [0: BasicVA
+ 1: SegmentWDB
+ 2: CADB
+ 3: TurnAround
+ 4: LMNVa
+ 5: TurnAroundWR
+ 6: TurnAroundODT
+ 7: RdRdTA]
+ @param[in] SeqLCs - An array of one or more loopcounts.
+ @param[in] ClearErrors - Decision to clear or not errors.
+ @param[in] Mode - Allows for different types of modes for margining
+ {Bit0: PhLock (keep all bytes within in ch in phase),
+ Bit1: Ch2Ch Data out of phase (LFSR seed)
+ Bits 15:2: Reserved}
+
+ @retval Returns ch errors
+**/
+U8
+RunIOTest (
+ IN MrcParameters *const MrcData,
+ IN const U8 ChbitMask,
+ IN const U8 DQPat,
+ IN const U8 *const SeqLCs,
+ IN const U8 ClearErrors,
+ IN const U16 Mode
+ )
+{
+ const MrcDebug *Debug;
+ MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ U8 ch;
+ U8 Reload;
+ U8 NumTests;
+ U8 t;
+ U8 IncRate;
+ U8 TestSOE;
+ U8 TestDoneStatus;
+ U8 ErrorStatus;
+ U32 CRValue;
+ U32 TestRand;
+ U32 Offset;
+ U32 LoopCountLinear;
+ U8 tRDRD_dr_Min[MAX_CHANNEL];
+ U8 TurnAroundOffset;
+ // When we segment the WDB, we run a normal 2 LFSR VA pattern on the first 10 entries
+ // The last 54 entries are used for a more complex 3 LFSR pattern
+ // In this mode:
+ // SeqLC is usually [0: host.DQPatLC, 1: host.DQPatLC, 2: host.DQPatLC+4, 3: host.DQPatLC+2]
+ //
+ // Anotherwords:
+ // The first 10 entries of the LFSR are run for twice, each for 2^DQPatLC
+ // and the WDB is incremented every 25 cachelines
+ //
+ // 25 was chosen since 10 Entry * 25 cachelines = 250.
+ // This is pretty close to 256, a power of 2, which should be roughly uniform coverage across all entries
+ //
+ // The second 54 entries of the LFSR are run twice
+ // Once with 2^(DQPatLC+4) and the WDB is incremented every 19 cachelines
+ // Once with 2^(DQPatLC+2) and the WDB is incremented every 10 cachelines
+ // Again, 19*54 = 1026 and 10*54 = 540 and both of these numbers are close
+ // to power of 2 and will provide roughly uniform coverage
+ //
+ // Each entry in the first 10 entries is hit 2 ^ (DQPatLC + NumCachelines + 1) / 10
+ // or 2 ^ (DQPatLC + NumCachelines -2.32)
+ //
+ // Each entry in the second 54 entries is hit 2 ^ (DQPatLC + NumCachelines + 4.32) / 54
+ // or ~2 ^ (DQPatLC + NumCachelines -1.43)
+ // or ~2x more than the first 10 entries
+
+ U8 WDBIncRates[8];
+ U8 WDBStart[8];
+ U8 WDBStop[8];
+
+ MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_STRUCT ReutGlobalCtl;
+ MCDFXS_CR_REUT_GLOBAL_ERR_MCMAIN_STRUCT ReutGlobalErr;
+ MCHBAR_CH0_CR_REUT_CH_ERR_CTL_STRUCT ReutChErrCtl;
+ MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_STRUCT ReutChPatWdbCl;
+ MCHBAR_CH0_CR_TC_BANK_RANK_A_STRUCT TcBankRankA[MAX_CHANNEL];
+
+ TestSOE = 0;
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ MrcOemMemorySet (WDBIncRates, 1, sizeof (WDBIncRates));
+ MrcOemMemorySet (WDBStart, 0, sizeof (WDBStart));
+ MrcOemMemorySet (WDBStop, 9, sizeof (WDBStop));
+ MrcOemMemorySetDword ((U32 *) TcBankRankA, 0, sizeof (TcBankRankA) / sizeof (TcBankRankA[0]));
+ ReutGlobalErr.Data = 0;
+ ErrorStatus = 0;
+
+ TestRand = 0xBAD00451;
+ NumTests = 1;
+ if (DQPat == SegmentWDB) {
+ NumTests = 4;
+ WDBIncRates[3] = 10;
+ WDBIncRates[2] = 19;
+ WDBIncRates[1] = 25;
+ WDBIncRates[0] = 25;
+
+ WDBStart[3] = 10;
+ WDBStart[2] = 10;
+ WDBStop[3] = 63;
+ WDBStop[2] = 63;
+ } else if (DQPat == CADB) {
+ NumTests = 7;
+ } else if (DQPat == TurnAroundWR) {
+ NumTests = 8;
+ } else if (DQPat == TurnAroundODT) {
+ NumTests = 4;
+ } else if (DQPat == RdRdTA) {
+ NumTests = 2;
+ for (ch = 0; ch < MAX_CHANNEL; ch++) {
+ if (!((MRC_BIT0 << ch) & ChbitMask)) {
+ continue;
+ }
+
+ TcBankRankA[ch].Data = ControllerOut->Channel[ch].MchbarBANKRANKA;
+ }
+ } else if (DQPat == RdRdTA_All) {
+ NumTests = 8;
+ for (ch = 0; ch < MAX_CHANNEL; ch++) {
+ if (((1 << ch) & ChbitMask) == 0) {
+ continue;
+ }
+
+ TcBankRankA[ch].Data = ControllerOut->Channel[ch].MchbarBANKRANKA;
+ tRDRD_dr_Min[ch] = (U8) TcBankRankA[ch].Bits.tRDRD_dr; // save the min value allowed
+ }
+ }
+
+ for (t = 0; t < NumTests; t++) {
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "RunIOTest: t = %d\n",t);
+ Reload = MrcLog2 (WDBIncRates[t] - 1);
+ if (WDBIncRates[t] > 31) {
+ WDBIncRates[t] = Reload;
+ } else {
+ WDBIncRates[t] += 32;
+ }
+
+ for (ch = 0; ch < MAX_CHANNEL; ch++) {
+ if (!((MRC_BIT0 << ch) & ChbitMask)) {
+ continue;
+ }
+ //
+ // Check for SOE == NTHSOE, ALSOE
+ // @todo: I still feel we need to exit if we get errors on any test
+ //
+ TestSOE = 0;
+ 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) * ch);
+ ReutChErrCtl.Data = MrcReadCR (MrcData, Offset);
+ CRValue = ReutChErrCtl.Bits.Stop_On_Error_Control;
+ if ((CRValue == NTHSOE) || (CRValue == ALSOE)) {
+ TestSOE = 1; // SOE bits are set
+ }
+
+ if (DQPat == SegmentWDB) {
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_WDB_CL_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_REG) * ch);
+ ReutChPatWdbCl.Data = 0;
+ ReutChPatWdbCl.Bits.WDB_Start_Pointer = WDBStart[t];
+ ReutChPatWdbCl.Bits.WDB_End_Pointer = WDBStop[t];
+ ReutChPatWdbCl.Bits.WDB_Increment_Rate = WDBIncRates[t];
+ ReutChPatWdbCl.Bits.WDB_Increment_Scale = WDBIncRates[t] >> MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_WDB_Increment_Rate_WID;
+ MrcWriteCR (MrcData, Offset, ReutChPatWdbCl.Data);
+
+ //
+ // Skip programming LFSR Save/Restore. Too complex unless power of 2
+ //
+ if (
+ (Inputs->CpuModel == cmHSW && Inputs->CpuStepping < csHswC0) ||
+ (Inputs->CpuModel == cmCRW && Inputs->CpuStepping < csCrwC0) ||
+ (Inputs->CpuModel == cmHSW_ULT && Inputs->CpuStepping < csHswUltC0)
+ ) {
+ //
+ // Program desired loopcount
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG + 2 +
+ ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * ch);
+ MrcWriteCR8 (MrcData, Offset, (SeqLCs[t] + 1));
+ } else {
+ LoopCountLinear = 1 << SeqLCs[t];
+ Offset = MCDFXS_CR_REUT_CH_SEQ_LOOPCOUNT_LIMIT_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_LOOPCOUNT_LIMIT_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_LOOPCOUNT_LIMIT_MCMAIN_0_REG) *
+ ch
+ );
+ MrcWriteCR (MrcData, Offset, LoopCountLinear);
+ }
+
+ } else if (DQPat == CADB) {
+ SetupCADB (MrcData, ch, NumTests, t, 0); // LMNEn=0
+ } else if ( (DQPat == TurnAroundWR) || (DQPat == TurnAroundODT) ) {
+ //
+ // Program which subseq to run
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG + 3 +
+ ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * ch);
+ MrcWriteCR8 (MrcData, Offset, (t << 4) + t);
+
+ //
+ // Program RankInc Rate
+ //
+ IncRate =
+ (
+ ((DQPat == TurnAroundWR) && ((t == 0) || (t == 7))) ||
+ ((DQPat == TurnAroundODT) && ((t == 0) || (t == 2)))
+ ) ? 0 : 1;
+
+ Offset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_REG +
+ ((
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_REG
+ ) * ch
+ );
+ MrcWriteCR8 (MrcData, Offset + 7, 128 + IncRate); // 0x80+IncRate
+ CRValue = MrcReadCR (MrcData, Offset);
+ //
+ // Program bit 19, 16:12 to IncRate (assume linear mode)
+ //
+ CRValue = MrcBitSwap (CRValue, (128 + IncRate), 12, 8);
+ MrcWriteCR (MrcData, Offset, CRValue);
+ } else if (DQPat == RdRdTA) {
+ //
+ // Program tRDRD parameter
+ //
+ Offset = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * ch);
+ TcBankRankA[ch].Bits.tRDRD = (t == 0) ? 4 : 5;
+ MrcWriteCR (MrcData, Offset, TcBankRankA[ch].Data);
+ } else if (DQPat == RdRdTA_All) {
+ //
+ // Program tRDRD for SR and DR
+ // Run 8 tests, Covering tRDRD_sr = 4,5,6,7 and tRDRD_dr = Min,+1,+2,+3
+ //
+ TurnAroundOffset = (t % 4);
+ Offset = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * ch);
+ TcBankRankA[ch].Bits.tRDRD = 4 + TurnAroundOffset;
+ TcBankRankA[ch].Bits.tRDRD_dr = tRDRD_dr_Min[ch] + TurnAroundOffset;
+
+ MrcWriteCR (MrcData, Offset, TcBankRankA[ch].Data);
+ //
+ // Program RankInc Rate
+ //
+ IncRate = (t > 3)? 0 : 31; // this field + 1
+
+ Offset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_REG +
+ (
+ (
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_INC_CTL_MCMAIN_0_REG
+ ) * ch
+ );
+ MrcWriteCR8 (MrcData, Offset + 7, IncRate | MRC_BIT7); // Linear Rank Address Update Rate
+ }
+ }
+
+ //###########################################################
+ //
+ // Start Test and Poll on completion
+ //
+ //###########################################################
+ //
+ // IO Reset neded before starting test.
+ //
+ IoReset (MrcData);
+
+ ReutGlobalCtl.Data = 0;
+ ReutGlobalCtl.Bits.Global_Start_Test = 1;
+ if (ClearErrors && (t == 0)) {
+ ReutGlobalCtl.Bits.Global_Clear_Errors = 1;
+ }
+
+ MrcWriteCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG, ReutGlobalCtl.Data);
+
+ //
+ // Wait until Channel test done status matches ChbitMask
+ //
+ do {
+ ReutGlobalErr.Data = MrcReadCR (MrcData, MCDFXS_CR_REUT_GLOBAL_ERR_MCMAIN_REG);
+ TestDoneStatus = (U8) ((ReutGlobalErr.Bits.Channel_Test_Done_Status_1 << 1) | ReutGlobalErr.Bits.Channel_Test_Done_Status_0);
+ } while ((TestDoneStatus & ChbitMask) != ChbitMask);
+
+ //
+ // Exit if SOE and Channel_Test_Done_Status bits matches ChbitMask
+ //
+ ErrorStatus = (U8) ((ReutGlobalErr.Bits.Channel_Error_Status_1 << 1) | ReutGlobalErr.Bits.Channel_Error_Status_0);
+ if ((ErrorStatus & ChbitMask) && TestSOE) {
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "ERROR IN RunIOTest: REUT_GLOBAL_CTRL = %Xh, REUT_GLOBAL_ERR %Xh\n", ReutGlobalErr.Data, ErrorStatus);
+ return (ReutGlobalErr.Data & ChbitMask);
+ }
+ }
+
+ if ((DQPat == RdRdTA) || (DQPat == RdRdTA_All)) {
+ //
+ // Restore original tRDRD value
+ //
+ for (ch = 0; ch < MAX_CHANNEL; ch++) {
+ if (!((MRC_BIT0 << ch) & ChbitMask)) {
+ continue;
+ }
+
+ Offset = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * ch);
+ MrcWriteCR (MrcData, Offset, ControllerOut->Channel[ch].MchbarBANKRANKA);
+ }
+ }
+
+ return (ReutGlobalErr.Data & ChbitMask);
+}
+
+/**
+ Programs REUT to run on the selected physical ranks.
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] ch - Channel to enable.
+ @param[in] RankBitMask - Bit mask of ranks to enable.
+ @param[in] RankFeatureEnable - RankFeatureEnable is a bit mask that can enable CKE, Refresh or ZQ
+ RankFeatureEnable[0] enables Refresh on all non-selected ranks
+ RankFeatureEnable[1] enables Refresh on all ranks
+ RankFeatureEnable[2] enables ZQ on all non-selected ranks
+ RankFeatureEnable[3] enables ZQ on all ranks
+ RankFeatureEnable[4] enables CKE on all non-selected ranks
+ RankFeatureEnable[5] enables CKE on all ranks
+
+ @retval Bit mask of channel enabled if rank in the channel exists.
+**/
+U8
+SelectReutRanks (
+ IN MrcParameters *const MrcData,
+ IN const U8 ch,
+ IN U8 RankBitMask,
+ IN const U8 RankFeatureEnable
+ )
+{
+ U32 Offset;
+ U8 En;
+ U8 rank;
+ U8 RankCount;
+ MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_STRUCT ReutChSeqCfg;
+ MCHBAR_CH0_CR_REUT_CH_MISC_REFRESH_CTRL_STRUCT ReutChMiscRefreshCtrl;
+ MCHBAR_CH0_CR_REUT_CH_MISC_ZQ_CTRL_STRUCT ReutChMiscZqCtrl;
+ MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_STRUCT ReutChMiscCkeCtrl;
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_STRUCT ReutChSeqRankL2PMapping;
+
+ //
+ // Make sure valid rank bit mask for this channel
+ //
+ RankBitMask &= MrcData->SysOut.Outputs.Controller[0].Channel[ch].ValidRankBitMask;
+
+ //
+ // Check if nothing is selected
+ //
+ if ((RankBitMask & 0xF) == 0) {
+ Offset = MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG +
+ (
+ (
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_1_REG -
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG
+ ) * ch
+ );
+ MrcWriteCR (MrcData, Offset, 0);
+
+ //
+ // Disable Channel by clearing global start bit in change config
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * ch);
+ ReutChSeqCfg.Data = MrcReadCR (MrcData, Offset);
+ ReutChSeqCfg.Bits.Global_Control = 0;
+ MrcWriteCR (MrcData, Offset, (U32) ReutChSeqCfg.Data);
+
+ return 0;
+
+ } else {
+ //
+ // Normal case
+ // Setup REUT Test to iteration through appropriate ranks during test
+ //
+ ReutChSeqRankL2PMapping.Data = 0;
+ RankCount = 0;
+
+ //
+ // Prepare Rank Mapping and Max Rank
+ //
+ for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) {
+ //
+ // rank in range(4):
+ //
+ if ((MRC_BIT0 << rank) & RankBitMask) {
+ ReutChSeqRankL2PMapping.Data |= (rank << (4 * RankCount));
+ RankCount += 1;
+ }
+ }
+ //
+ // Write New Rank Mapping and Max Rank
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG +
+ (
+ (
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_1_REG -
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG
+ ) * ch
+ );
+ MrcWriteCR (MrcData, Offset, ReutChSeqRankL2PMapping.Data);
+ Offset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG + 7 +
+ ((MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG) * ch);
+ MrcWriteCR8 (MrcData, Offset, RankCount - 1);
+
+ //
+ // Make sure channel is enabled
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG + ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * ch);
+ ReutChSeqCfg.Data = MrcReadCR (MrcData, Offset);
+ ReutChSeqCfg.Bits.Global_Control = 1;
+ MrcWriteCR (MrcData, Offset, (U32) ReutChSeqCfg.Data);
+ }
+ //
+ // Need to convert RankFeatureEnable as an input parameter so we don't pass it all the time
+ //
+ if (RankFeatureEnable != 0) {
+ //
+ // Enable Refresh and ZQ - 0's to the the desired ranks
+ //
+ En = RankFeatureEnable & 0x3; // Refresh
+ ReutChMiscRefreshCtrl.Data = 0;
+ ReutChMiscRefreshCtrl.Bits.Refresh_Rank_Mask = MCHBAR_CH0_CR_REUT_CH_MISC_REFRESH_CTRL_Refresh_Rank_Mask_MAX;
+ ReutChMiscRefreshCtrl.Bits.Panic_Refresh_Only = 1;
+
+ if (En == 1) {
+ ReutChMiscRefreshCtrl.Bits.Refresh_Rank_Mask = ~RankBitMask; // Enable all non-selected ranks
+ } else if (En > 1) {
+ ReutChMiscRefreshCtrl.Bits.Refresh_Rank_Mask = 0; // Enable all ranks
+ }
+ Offset = MCHBAR_CH0_CR_REUT_CH_MISC_REFRESH_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_MISC_REFRESH_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_MISC_REFRESH_CTRL_REG) * ch);
+ MrcWriteCR (MrcData, Offset, ReutChMiscRefreshCtrl.Data);
+
+ En = (RankFeatureEnable >> 2) & 0x3; // ZQ
+ ReutChMiscZqCtrl.Data = 0;
+ ReutChMiscZqCtrl.Bits.ZQ_Rank_Mask = MCHBAR_CH0_CR_REUT_CH_MISC_ZQ_CTRL_ZQ_Rank_Mask_MAX;
+ ReutChMiscZqCtrl.Bits.Always_Do_ZQ = 1;
+ if (En == 1) {
+ ReutChMiscZqCtrl.Bits.ZQ_Rank_Mask = ~RankBitMask;
+ } else if (En > 1) {
+ ReutChMiscZqCtrl.Bits.ZQ_Rank_Mask = 0; // Enable all ranks
+ }
+ Offset = MCHBAR_CH0_CR_REUT_CH_MISC_ZQ_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_MISC_ZQ_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_MISC_ZQ_CTRL_REG) * ch);
+ MrcWriteCR (MrcData, Offset, ReutChMiscZqCtrl.Data);
+
+ //
+ // Enable CKE ranks - 1's to enable desired ranks
+ //
+ En = (RankFeatureEnable >> 4) & 0x3;
+ ReutChMiscCkeCtrl.Data = 0;
+ ReutChMiscCkeCtrl.Bits.CKE_On = MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_CKE_On_MAX;
+ if (En == 1) {
+ ReutChMiscCkeCtrl.Bits.CKE_On = ~RankBitMask;
+ ReutChMiscCkeCtrl.Bits.CKE_Override = ~RankBitMask; // Enable all non-selected ranks
+ } else if (En > 1) {
+ ReutChMiscCkeCtrl.Bits.CKE_On = MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_CKE_On_MAX;
+ ReutChMiscCkeCtrl.Bits.CKE_Override = MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_CKE_Override_MAX; // Enable all ranks.
+ }
+ Offset = MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_MISC_CKE_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG) * ch);
+ MrcWriteCR (MrcData, Offset, ReutChMiscCkeCtrl.Data);
+ }
+
+ return (U8) (MRC_BIT0 << ch);
+}
+
+/**
+ This routine updates RXTRAINRANK register's specific fields defined by the subfield
+ subfield values:
+ 0 - Update RcvEn - leave other parameter the same
+ 1 - Update RxDqsP - leave other parameter the same
+ 2 - Update RxEq - leave other parameter the same
+ 3 - Update RxDqsN - leave other parameter the same
+ 4 - Update RxVref - leave other parameter the same
+ 5 - Update RxDqsP & RxDqsN - leave other parameter the same
+ FF - leave all parameter the same
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - Defines channel to update
+ @param[in] Rank - Defines rank to update
+ @param[in] Byte - Defines byte to update
+ @param[in] Subfield - Defines the register's field or fields to update
+ @param[in] Value - value to be writen into register fields
+
+ @retval Nothing
+**/
+void
+UpdateRxT (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 Rank,
+ IN const U8 Byte,
+ IN const U8 Subfield,
+ IN const U16 Value
+ )
+{
+ MrcChannelOut *ChannelOut;
+ U32 Offset;
+ DDRDATA0CH0_CR_RXTRAINRANK0_STRUCT CrRxTrainRank;
+
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ CrRxTrainRank.Data = 0;
+ CrRxTrainRank.Bits.RxRcvEnPi = (Subfield == 0) ? Value : ChannelOut->RcvEn[Rank][Byte];
+ CrRxTrainRank.Bits.RxDqsPPi = ((Subfield == 1) || (Subfield == 5)) ? Value : ChannelOut->RxDqsP[Rank][Byte];
+ CrRxTrainRank.Bits.RxEq = (Subfield == 2) ? Value : ChannelOut->RxEq[Rank][Byte];
+ CrRxTrainRank.Bits.RxDqsNPi = ((Subfield == 3) || (Subfield == 5)) ? Value : ChannelOut->RxDqsN[Rank][Byte];
+ CrRxTrainRank.Bits.RxVref = (Subfield == 4) ? Value : ChannelOut->RxVref[Byte];
+
+ Offset = DDRDATA0CH0_CR_RXTRAINRANK0_REG +
+ ((DDRDATA0CH1_CR_RXTRAINRANK0_REG - DDRDATA0CH0_CR_RXTRAINRANK0_REG) * Channel) +
+ ((DDRDATA0CH0_CR_RXTRAINRANK1_REG - DDRDATA0CH0_CR_RXTRAINRANK0_REG) * Rank) +
+ ((DDRDATA1CH0_CR_RXTRAINRANK0_REG - DDRDATA0CH0_CR_RXTRAINRANK0_REG) * Byte);
+ MrcWriteCR (MrcData, Offset, CrRxTrainRank.Data);
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, Channel, 0, Rank, MrcRegFileRank, Byte, 1, 0);
+ return;
+}
+
+/**
+ This routine updates TXTRAINRANK register's specific fields defined by the subfield
+ subfield values:
+ 0 - Update TxDq - leave other parameter the same
+ 1 - Update TxDqs - leave other parameter the same
+ 2 - Update TxEq - leave other parameter the same
+ 3 - Update ALL from input value (non from Mrcdata structure)
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - Defines channel to update
+ @param[in] Rank - Defines rank to update
+ @param[in] Byte - Defines byte to update
+ @param[in] Subfield - Defines the register's field or fields to update
+ @param[in] Value - value to be writen into register fields
+
+ @retval Nothing
+**/
+void
+UpdateTxT (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 Rank,
+ IN const U8 Byte,
+ IN const U8 Subfield,
+ IN const U32 Value
+ )
+{
+ MrcChannelOut *ChannelOut;
+ U32 Offset;
+ DDRDATA0CH0_CR_TXTRAINRANK0_STRUCT CrTxTrainRank;
+
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ if (Subfield == 3) {
+ CrTxTrainRank.Data = Value;
+ } else {
+ CrTxTrainRank.Data = 0;
+ CrTxTrainRank.Bits.TxDqDelay = (Subfield == 0) ? Value : ChannelOut->TxDq[Rank][Byte];
+ CrTxTrainRank.Bits.TxDqsDelay = (Subfield == 1) ? Value : ChannelOut->TxDqs[Rank][Byte];
+ CrTxTrainRank.Bits.TxEqualization = (Subfield == 2) ? Value : ChannelOut->TxEq[Rank][Byte];
+ }
+
+ Offset = DDRDATA0CH0_CR_TXTRAINRANK0_REG +
+ ((DDRDATA0CH1_CR_TXTRAINRANK0_REG - DDRDATA0CH0_CR_TXTRAINRANK0_REG) * Channel) +
+ ((DDRDATA0CH0_CR_TXTRAINRANK1_REG - DDRDATA0CH0_CR_TXTRAINRANK0_REG) * Rank) +
+ ((DDRDATA1CH0_CR_TXTRAINRANK0_REG - DDRDATA0CH0_CR_TXTRAINRANK0_REG) * Byte);
+ MrcWriteCR (MrcData, Offset, CrTxTrainRank.Data);
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, Channel, 0, Rank, MrcRegFileRank, Byte, 0, 1);
+ return;
+}
+
+/**
+ Returns the index into the array MarginResult in the MrcOutput structure.
+
+ @param[in] ParamV - Margin parameter
+
+ @retval One of the following values: LastRxV(0), LastRxT (1), LastTxV(2), LastTxT (3), LastRcvEna (4),
+ LastWrLevel (5), LastCmdT (6), LastCmdV (7)
+**/
+U8
+GetMarginResultType (
+ IN const U8 ParamV
+ )
+{
+ switch (ParamV) {
+ case WrV:
+ case WrFan2:
+ case WrFan3:
+ return LastTxV;
+
+ case WrT:
+ return LastTxT;
+
+ case RdV:
+ case RdFan2:
+ case RdFan3:
+ return LastRxV;
+
+ case RdT:
+ return LastRxT;
+
+ case RcvEna:
+ case RcvEnaX:
+ return LastRcvEna;
+
+ case WrLevel:
+ return LastWrLevel;
+
+ case CmdT:
+ return LastCmdT;
+
+ case CmdV:
+ return LastCmdV;
+
+ default:
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "GetMarginByte: Unknown Margin Parameter\n");
+ break;
+ }
+
+ return 0; // Return LastRxV to point to the beginning of the array
+}
+
+/*
+1D Margin Types:
+RcvEn: Shifts just RcvEn. Only side effect is it may eat into read dq-dqs for first bit of burst
+RdT: Shifts read DQS timing, changing where DQ is sampled
+WrT: Shifts write DQ timing, margining DQ-DQS timing
+WrDqsT: Shifts write DQS timing, margining both DQ-DQS and DQS-CLK timing
+RdV: Shifts read Vref voltage for DQ only
+WrV: Shifts write Vref voltage for DQ only
+WrLevel: Shifts write DQ and DQS timing, margining only DQS-CLK timing
+WrTBit: Shifts write DQ per bit timing.
+RdTBit: Shifts read DQ per bit timing.
+RdVBit: Shifts read DQ per bit voltage.
+
+2D Margin Types (Voltage, Time)
+RdFan2: Margins both RdV and RdT at { (off, -2/3*off), (off, 2/3*off) }
+WrFan2: Margins both WrV and WrT at { (off, -2/3*off), (off, 2/3*off) }
+RdFan3: Margins both RdV and RdT at { (off, -2/3*off), (5/4*off, 0), (off, 2/3*off) }
+WrFan3: Margins both WrV and WrT at { (off, -2/3*off), (5/4*off, 0), (off, 2/3*off) }
+*/
+/**
+ This function Reads MrcData structure and finds the minimum last recorded margin for param
+ Searches across all bytes and ranks in RankMask
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in,out] MarginResult - Data structure with the latest margin results.
+ @param[in] Param - Defines the margin type
+ @param[in] Ranks - Condenses down the results from multiple ranks
+
+ @retval mrcWrongInputParameter if a bad Param is passed in, otherwise mrcSuccess.
+**/
+MrcStatus
+GetMarginCh (
+ IN MrcParameters *const MrcData,
+ IN OUT U32 MarginResult[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES],
+ IN const U8 Param,
+ IN const U8 Ranks
+ )
+{
+ MrcOutput *Outputs;
+ U32 *Margin1;
+ U32 *Margin2;
+ U8 ResultType;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 Edge;
+ U8 Scale;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ switch (Param) {
+ case WrV:
+ case WrT:
+ case RdV:
+ case RdT:
+ Scale = 10;
+ break;
+
+ case WrFan2:
+ case WrFan3:
+ case RdFan2:
+ case RdFan3:
+ Scale = 21 / 3;
+ break;
+
+ default:
+ MRC_DEBUG_MSG (&MrcData->SysIn.Inputs.Debug, MSG_LEVEL_ERROR, "GetMarginCh: Unknown Margin Parameter\n");
+ return mrcWrongInputParameter;
+ }
+
+ ResultType = GetMarginResultType (Param);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Margin2 = &MarginResult[ResultType][0][Channel][0][0];
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel) & Ranks) {
+ Margin1 = &MarginResult[ResultType][Rank][Channel][Byte][0];
+ for (Edge = 0; Edge < MAX_EDGES; Edge++, Margin1++) {
+ if (Margin2[Edge] > *Margin1) {
+ Margin2[Edge] = *Margin1;
+ }
+ }
+ }
+ }
+ }
+ //
+ // Scale results as needed
+ //
+ for (Edge = 0; Edge < MAX_EDGES; Edge++, Margin2++) {
+ *Margin2 = (*Margin2 * Scale) / 10;
+ }
+ }
+ }
+
+ return mrcSuccess;
+}
+
+/**
+ Use this function to retrieve the last margin results from MrcData
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in,out] MarginResult - Data structure with the latest margin results.
+ @param[in] Param - Defines the margin type
+ @param[in] RankIn - Which rank of the host structure you want the result returned on
+ @param[in] Ranks - Condenses down the results from multiple ranks
+
+ @retval MarginResult structure has been updated if MrcStatus returns mrcSuccess.
+ @retval Otherwise, mrcWrongInputParameter is returned if an incorrect Param is passed in.
+**/
+MrcStatus
+GetMarginByte (
+ IN MrcParameters *const MrcData,
+ IN OUT U32 MarginResult[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES],
+ IN const U8 Param,
+ IN const U8 RankIn,
+ IN const U8 Ranks
+ )
+{
+ MrcOutput *Outputs;
+ U32 *Margin1;
+ U32 *Margin2;
+ U8 ResultType;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 Edge;
+ U8 Scale;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ switch (Param) {
+ case WrV:
+ case WrT:
+ case RdV:
+ case RdT:
+ case RcvEna:
+ case RcvEnaX:
+ Scale = 10;
+ break;
+
+ case WrFan2:
+ case WrFan3:
+ case RdFan2:
+ case RdFan3:
+ Scale = 21 / 3;
+ break;
+
+ default:
+ MRC_DEBUG_MSG (&MrcData->SysIn.Inputs.Debug, MSG_LEVEL_ERROR, "GetMarginByte: Unknown Margin Parameter\n");
+ return mrcWrongInputParameter;
+ }
+
+ ResultType = GetMarginResultType (Param);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel) & Ranks) {
+ Margin1 = &MarginResult[ResultType][RankIn][Channel][Byte][0];
+ Margin2 = &MarginResult[ResultType][Rank][Channel][Byte][0];
+ for (Edge = 0; Edge < MAX_EDGES; Edge++, Margin1++, Margin2++) {
+ if (*Margin1 > *Margin2) {
+ *Margin1 = *Margin2;
+ }
+ }
+ }
+ }
+ //
+ // Scale results as needed
+ //
+ Margin1 = &MarginResult[ResultType][RankIn][Channel][Byte][0];
+ for (Edge = 0; Edge < MAX_EDGES; Edge++, Margin1++) {
+ *Margin1 = (*Margin1 * Scale) / 10;
+ }
+ }
+ }
+ }
+
+ return mrcSuccess;
+}
+
+/**
+ This function is use to "unscale" the MrcData last margin point
+ GetMarginByte will scale the results for FAN margin
+ This will unscale the results such that future tests start at the correct point
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in,out] MarginResult - Input array to be unscaled.
+ @param[in] Param - Defines the margin type for proper scale selection.
+ @param[in] Rank - Which rank of the host structure to work on
+
+ @retval mrcSuccess
+**/
+MrcStatus
+ScaleMarginByte (
+ IN MrcParameters *const MrcData,
+ IN OUT U32 MarginResult[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES],
+ IN const U8 Param,
+ IN const U8 Rank
+ )
+{
+ MrcOutput *Outputs;
+ U32 *Margin;
+ U8 ResultType;
+ U8 Channel;
+ U8 Byte;
+ U8 Edge;
+
+ //
+ // Calculate scale parameter based on param
+ // Leave room for expansion in case other params needed to be scaled
+ //
+ Outputs = &MrcData->SysOut.Outputs;
+ if ((Param == RdFan2) || (Param == RdFan3) || (Param == WrFan2) || (Param == WrFan3)) {
+ ResultType = GetMarginResultType (Param);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ Margin = &MarginResult[ResultType][Rank][Channel][Byte][0];
+ for (Edge = 0; Edge < MAX_EDGES; Edge++, Margin++) {
+ *Margin = (*Margin * 15) / 10;
+ }
+ }
+ }
+ }
+ }
+
+ return mrcSuccess;
+}
+
+/**
+ This function is used by most margin search functions to change te underlying margin parameter.
+ This function allows single search function to be used for different types of margins with minimal impact.
+ It provides multiple different parameters, including 2D parameters like Read or Write FAN.
+ It can work in either MultiCast or single register mode.
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] param - Includes parameter(s) to change including two dimentional.
+ @param[in] value0 - Selected value to program margin param to
+ @param[in] value1 - Selected value to program margin param to in 2D mode (FAN mode)
+ @param[in] EnMultiCast - To enable Multicast (broadcast) or single register mode
+ @param[in] channel - Desired Channel
+ @param[in] rankIn - Desired Rank - only used for the RxTBit and TxTBit settings and to propagate RdVref
+ @param[in] byte - Desired byte offset register
+ @param[in] bitIn - Desired bit offset Mrc data strucure if UpdateMrcData is 1
+ @param[in] UpdateMrcData - Used to decide if Mrc host must be updated
+ @param[in] SkipWait - Used to skip wait until all channel are done
+ @param[in] RegFileParam - Used to determine which Rank to download. Passed to MrcDownloadRegFile.
+
+ @retval MrcStatus - if succeeded, return mrcSuccess
+**/
+MrcStatus
+ChangeMargin (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 param,
+ IN const S32 value0,
+ IN const S32 value1,
+ IN const U8 EnMultiCast,
+ IN const U8 channel,
+ IN const U8 rankIn,
+ IN const U8 byte,
+ IN const U8 bitIn,
+ IN const U8 UpdateMrcData,
+ IN const U8 SkipWait,
+ IN const MrcRegFile RegFileParam
+ )
+{
+ //
+ // Programs margin param to the selected value0
+ // If param is a 2D margin parameter (ex: FAN), then it uses both value0 and value1
+ // For an N point 2D parameter, value1 can be an interger from 0 to (N-1)
+ // For per bit timing parameter, value1 is the sign of the shift
+ // param = {0:RcvEna, 1:RdT, 2:WrT, 3: WrDqsT, 4:RdV, 5:WrV, 6:WrLevel,
+ // 7:WrTBox, 8:WrTBit, 9:RdTBit, 10:RdVBit,
+ // 16:RdFan2, 17:WrFan2, 32:RdFan3, 33:WrFan3}
+ // Note: For Write Vref, the trained value and margin register are the same
+ // Note: rank is only used for the RxTBit and TxTBit settings and to propagate RdVref
+ // Note: PerBit Settings (WrTBit, RdTBit, RdVBit) provide all 8 offsets in value0
+
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcChannelOut *CurrentChannelOut;
+ MrcStatus Status;
+ U8 CurrentCh;
+ U8 CurrentByte;
+ U8 Max0;
+ U8 MaxT;
+ U8 MaxV;
+ U8 maskT;
+ U8 rank;
+ U8 bit;
+ U8 ReadRFRd;
+ U8 ReadRFWr;
+ S32 sign;
+ S32 v0;
+ S32 v1;
+ U32 Offset;
+ BOOL UpdateDataOffset;
+ DDRDATA0CH0_CR_DDRCRDATAOFFSETTRAIN_STRUCT CRValue;
+ MCHBAR_CH0_CR_SC_IO_LATENCY_STRUCT ScIoLatency;
+
+ Status = mrcSuccess;
+ UpdateDataOffset = FALSE;
+ ReadRFRd = 0;
+ ReadRFWr = 0;
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+
+ //
+ // Pre-Process the margin numbers
+ //
+ MaxT = MAX_POSSIBLE_TIME; // Maximum value for Time
+ MaxV = MAX_POSSIBLE_VREF; // Maximum value for Vref
+ maskT = 0x3F; // 6 bits (2's complement)
+
+ if ((param < RdV) || (param == WrLevel)) {
+ Max0 = MaxT;
+ } else if ((param == WrTBit) || (param == RdTBit) || (param == RdVBit)) {
+ Max0 = 0xFF;
+ } else {
+ Max0 = MaxV; // Vref for RdV, WrV, and FAN modes
+ }
+ //
+ // Pre-Process the margin numbers. Calculate 2D points based on FAN slopes
+ //
+ v0 = value0;
+ sign = (2 * value1 - 1);
+
+ //
+ // For Fan3, optimize point orders to minimize Vref changes and # of tests required
+ //
+ if (param >= RdFan3) {
+ sign = ((3 * value1 - 5) * value1) / 2; // Translates to {0:0, 1:-1, 2:+1}
+ if (value1 == 0) {
+ v0 = (5 * value0) / 4;
+ }
+ }
+
+ v1 = (sign * value0) / 3;
+ if (v0 > Max0) {
+ v0 = Max0;
+ } else if (v0 < (-1 * Max0)) {
+ v0 = (-1 * Max0);
+ }
+
+ if (v1 > MaxT) {
+ v1 = MaxT;
+ } else if (v1 < (0 - MaxT)) {
+ v1 = (0 - MaxT);
+ }
+ //
+ // Rank = -1 sometimes if used to indicate all ranks
+ // Does not make sense here, hence set to 0)
+ //
+ rank = (rankIn == 0xFF) ? 0 : rankIn;
+
+ ChannelOut = &ControllerOut->Channel[channel];
+ CRValue.Data = ChannelOut->DataOffsetTrain[byte];
+ switch (param) {
+ case RcvEna:
+ CRValue.Bits.RcvEnOffset = (U32) v0;
+ UpdateDataOffset = TRUE;
+ break;
+
+ case RdT:
+ CRValue.Bits.RxDqsOffset = (U32) v0;
+ UpdateDataOffset = TRUE;
+ break;
+
+ case WrT:
+ CRValue.Bits.TxDqOffset = (U32) v0;
+ UpdateDataOffset = TRUE;
+ break;
+
+ case WrDqsT:
+ CRValue.Bits.TxDqsOffset = (U32) v0;
+ UpdateDataOffset = TRUE;
+ break;
+
+ case RdV:
+ CRValue.Bits.VrefOffset = (U32) v0;
+ UpdateDataOffset = TRUE;
+ break;
+
+ case RcvEnaX:
+ //
+ // Calculate new IOComp Latency to include over/underflow
+ //
+ 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);
+ if (v0 > 0) {
+ v0 = v0 * 2 - 16;
+ ScIoLatency.Bits.RT_IOCOMP = (MCHBAR_CH0_CR_SC_IO_LATENCY_RT_IOCOMP_MAX & (ChannelOut->RTIoComp - 1));
+ } else if (v0 < 0) {
+ v0 = v0 * 2 + 16;
+ ScIoLatency.Bits.RT_IOCOMP = (MCHBAR_CH0_CR_SC_IO_LATENCY_RT_IOCOMP_MAX & (ChannelOut->RTIoComp + 1));
+ } else {
+ ScIoLatency.Bits.RT_IOCOMP = (MCHBAR_CH0_CR_SC_IO_LATENCY_RT_IOCOMP_MAX & ChannelOut->RTIoComp);
+ }
+
+ v0 += ChannelOut->RcvEn[rank][byte];// the assumption is that we are @ 1 Qclk before edge
+ //
+ // Limit RcvEna 0-511 to prevent under/overflow.
+ //
+ if (v0 < 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "WARNING: RcvEn PI wrapped below zero!\n");
+ v0 = 0;
+ } else if (v0 > DDRDATA_CR_RXTRAINRANK0_RxRcvEnPi_MAX) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "WARNING: RcvEn PI wrapped above 9 bits!\n");
+ v0 = DDRDATA_CR_RXTRAINRANK0_RxRcvEnPi_MAX;
+ }
+ UpdateRxT (MrcData, channel, rank, byte, 0,(U16) v0);
+ MrcWriteCR (MrcData, Offset, ScIoLatency.Data);
+ break;
+
+ case WrV:
+ case WrFan2:
+ case WrFan3:
+ for (CurrentCh = 0; CurrentCh < MAX_CHANNEL; CurrentCh++) {
+ if (MrcChannelExist (Outputs, CurrentCh)) {
+ if ((EnMultiCast == 1) || (CurrentCh == channel)) {
+ UpdateVrefWaitTilStable (MrcData, CurrentCh, UpdateMrcData, v0, SkipWait);
+ }
+ }
+ }
+
+ if ((param == WrFan2) || (param == WrFan3)) {
+ CRValue.Data = ChannelOut->DataOffsetTrain[byte];
+ CRValue.Bits.TxDqOffset = v1; // Update TxDqOffset
+ UpdateDataOffset = TRUE;
+ }
+ break;
+
+ case RdFan2: // Read margin in FAN modes.
+ case RdFan3:
+ CRValue.Data = ChannelOut->DataOffsetTrain[byte];
+ CRValue.Bits.VrefOffset = v0; // Update VrefOffset
+ CRValue.Bits.RxDqsOffset = v1; // Update RxDqsOffset
+ UpdateDataOffset = TRUE;
+ break;
+
+ case WrLevel: // Write DQ and DQS timing, margining only DQS-CLK timing
+ CRValue.Data = ChannelOut->DataOffsetTrain[byte];
+ CRValue.Bits.TxDqOffset = v0; // Update TxDqOffset
+ CRValue.Bits.TxDqsOffset = v0; // Update TxDqsOffset
+ UpdateDataOffset = TRUE;
+ break;
+
+ case WrTBit: // Write DQ per BIT timing
+ ReadRFWr = 1;
+ if (EnMultiCast) {
+ Offset = DDRDATA_CR_TXPERBITRANK0_REG +
+ ((DDRDATA_CR_TXPERBITRANK1_REG - DDRDATA_CR_TXPERBITRANK0_REG) * rank);
+ MrcWriteCrMulticast (MrcData, Offset, value0);
+ for (CurrentCh = 0; CurrentCh < MAX_CHANNEL; CurrentCh++) {
+ CurrentChannelOut = &ControllerOut->Channel[CurrentCh];
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, CurrentCh, 1, rank, RegFileParam, 0, ReadRFRd, ReadRFWr);
+ if (UpdateMrcData) {
+ for (CurrentByte = 0; CurrentByte < Outputs->SdramCount; CurrentByte++) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ CurrentChannelOut->TxDqPb[rank][CurrentByte][bit].Center = (value0 >> (4 * bit)) & 0xF;
+ }
+ }
+ }
+ }
+ } else {
+ Offset = DDRDATA0CH0_CR_TXPERBITRANK0_REG +
+ ((DDRDATA0CH0_CR_TXPERBITRANK1_REG - DDRDATA0CH0_CR_TXPERBITRANK0_REG) * rank) +
+ ((DDRDATA1CH0_CR_TXPERBITRANK0_REG - DDRDATA0CH0_CR_TXPERBITRANK0_REG) * byte) +
+ ((DDRDATA0CH1_CR_TXPERBITRANK0_REG - DDRDATA0CH0_CR_TXPERBITRANK0_REG) * channel);
+ MrcWriteCR (MrcData, Offset, value0);
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, channel, 0, rank, RegFileParam, byte, ReadRFRd, ReadRFWr);
+ if (UpdateMrcData) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ ChannelOut->TxDqPb[rank][byte][bit].Center = (value0 >> (4 * bit)) & 0xF;
+ }
+ }
+ }
+ break;
+
+ case RdTBit: // Read DQ per BIT timing
+ ReadRFRd = 1;
+ if (EnMultiCast) {
+ Offset = DDRDATA_CR_RXPERBITRANK0_REG +
+ ((DDRDATA_CR_RXPERBITRANK1_REG - DDRDATA_CR_RXPERBITRANK0_REG) * rank);
+ MrcWriteCrMulticast (MrcData, Offset, value0);
+ for (CurrentCh = 0; CurrentCh < MAX_CHANNEL; CurrentCh++) {
+ CurrentChannelOut = &ControllerOut->Channel[CurrentCh];
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, CurrentCh, 1, rank, RegFileParam, 0, ReadRFRd, ReadRFWr);
+ if (UpdateMrcData) {
+ for (CurrentByte = 0; CurrentByte < Outputs->SdramCount; CurrentByte++) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ CurrentChannelOut->RxDqPb[rank][CurrentByte][bit].Center = (value0 >> (4 * bit)) & 0xF;
+ }
+ }
+ }
+ }
+ } else {
+ Offset = DDRDATA0CH0_CR_RXPERBITRANK0_REG +
+ ((DDRDATA0CH0_CR_RXPERBITRANK1_REG - DDRDATA0CH0_CR_RXPERBITRANK0_REG) * rank) +
+ ((DDRDATA1CH0_CR_RXPERBITRANK0_REG - DDRDATA0CH0_CR_RXPERBITRANK0_REG) * byte) +
+ ((DDRDATA0CH1_CR_RXPERBITRANK0_REG - DDRDATA0CH0_CR_RXPERBITRANK0_REG) * channel);
+ MrcWriteCR (MrcData, Offset, value0);
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, channel, 0, rank, RegFileParam, byte, ReadRFRd, ReadRFWr);
+ if (UpdateMrcData) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ ChannelOut->RxDqPb[rank][byte][bit].Center = (value0 >> (4 * bit)) & 0xF;
+ }
+ }
+ }
+ break;
+
+ case RdVBit: // Read DQ per BIT Voltage
+ ReadRFRd = 1;
+ if (EnMultiCast) {
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_RXOFFSETVDQ_REG, value0);
+ for (CurrentCh = 0; CurrentCh < MAX_CHANNEL; CurrentCh++) {
+ CurrentChannelOut = &ControllerOut->Channel[CurrentCh];
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, CurrentCh, 1, rank, RegFileParam, 0, ReadRFRd, ReadRFWr);
+ if (UpdateMrcData) {
+ for (CurrentByte = 0; CurrentByte < Outputs->SdramCount; CurrentByte++) {
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ CurrentChannelOut->RxDqVrefPb[rank][CurrentByte][bit].Center = (value0 >> (4 * bit)) & 0xF;
+ }
+ }
+ }
+ }
+ } else {
+ Offset = DDRDATA0CH0_CR_RXOFFSETVDQ_REG +
+ ((DDRDATA1CH0_CR_RXOFFSETVDQ_REG - DDRDATA0CH0_CR_RXOFFSETVDQ_REG) * byte) +
+ ((DDRDATA0CH1_CR_RXOFFSETVDQ_REG - DDRDATA0CH0_CR_RXOFFSETVDQ_REG) * channel);
+ MrcWriteCR (MrcData, Offset, value0);
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, channel, 0, rank, RegFileParam, byte, ReadRFRd, ReadRFWr);
+ if (UpdateMrcData) {
+ ChannelOut = &ControllerOut->Channel[channel];
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ ChannelOut->RxDqVrefPb[rank][byte][bit].Center = (value0 >> (4 * bit)) & 0xF;
+ }
+ }
+ }
+
+ break;
+
+ default:
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Function ChangeMargin, Invalid parameter %d\n", param);
+ return mrcWrongInputParameter;
+ } // end switch (param)
+
+ if (UpdateDataOffset) {
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ if ((param == RcvEnaX) ||(param == RcvEna) || (param == RdT) || (param == RdV) || (param == RdFan2) || (param == RdFan3)) {
+ ReadRFRd = 1;
+ } else if ((param == WrT) || (param == WrDqsT) || (param == WrLevel) || (param == WrFan2) || (param == WrFan3)) {
+ ReadRFWr = 1;
+ }
+ //
+ // Write CR
+ //
+ if (EnMultiCast) {
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_DDRCRDATAOFFSETTRAIN_REG, CRValue.Data);
+ for (CurrentCh = 0; CurrentCh < MAX_CHANNEL; CurrentCh++) {
+ if (MrcChannelExist (Outputs, CurrentCh)) {
+ CurrentChannelOut = &ControllerOut->Channel[CurrentCh];
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, CurrentCh, 1, rank, RegFileParam, 0, ReadRFRd, ReadRFWr);
+ for (CurrentByte = 0; CurrentByte < Outputs->SdramCount; CurrentByte++) {
+ if (UpdateMrcData) {
+ CurrentChannelOut->DataOffsetTrain[CurrentByte] = CRValue.Data;
+ }
+ }
+ }
+ }
+ } else {
+ Offset = DDRDATA0CH0_CR_DDRCRDATAOFFSETTRAIN_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATAOFFSETTRAIN_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETTRAIN_REG) * channel) +
+ ((DDRDATA1CH0_CR_DDRCRDATAOFFSETTRAIN_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETTRAIN_REG) * byte);
+ MrcWriteCR (MrcData, Offset, CRValue.Data);
+ //
+ // Download new settings from the RegFile to the Pads
+ //
+ MrcDownloadRegFile (MrcData, channel, 0, rank, RegFileParam, byte, ReadRFRd, ReadRFWr);
+ if (UpdateMrcData) {
+ ChannelOut->DataOffsetTrain[byte] = CRValue.Data;
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ This function triggers the hardware to download the specified RegFile.
+ The setting of ReadRfRd and ReadRfWr must be mutually exclusive.
+ Only 1 (start download) and 0 (do nothing) are valid values for ReadRfXx.
+
+ @param[in] MrcData - Global MRC Data
+ @param[in] Channel - The Channel to download target.
+ @param[in] ByteMulticast - Enable Multicasting all bytes on that Channel.
+ @param[in] Rank - The Rank download target.
+ @param[in] RegFileParam - Used to determine which Rank to download.
+ MrcRegFileRank - Uses the Rank Parameter.
+ MrcRegFileStart - Uses the Rank in REUT_CH_SEQ_BASE_ADDR_START after decoding logical to physical.
+ MrcRegFileCurrent - Uses the Rank in REUT_CH_SEQ_BASE_ADDR_CURRENT after decoding logical to physical.
+ @param[in] Byte - The Byte download target.
+ @param[in] ReadRfRd - Download the read RegFile. 1 enables, 0 otherwise
+ @param[in] ReadRfWr - Download the write RegFile. 1 enables, 0 otherwise
+
+ @retval MrcStatus - If both ReadRfRd and ReadRfWr are set, the functions returns mrcWrongInputParameters.
+ Otherwise, mrcSuccess.
+**/
+void
+MrcDownloadRegFile (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const BOOL ByteMulticast,
+ IN U8 Rank,
+ IN const MrcRegFile RegFileParam,
+ IN const U8 Byte,
+ IN const BOOL ReadRfRd,
+ IN const BOOL ReadRfWr
+ )
+{
+ DDRDATA0CH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_STRUCT ReutChSeqRankL2PMapping;
+ U64 ReutChSeqBaseAddr;
+ MrcChannelOut *ChannelOut;
+ U32 CrOffset;
+ U8 LogicalRank;
+
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ //
+ // Determine the rank to download the Reg File
+ //
+ switch (RegFileParam) {
+ case MrcRegFileStart:
+ CrOffset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG) *
+ Channel
+ );
+ break;
+
+ case MrcRegFileCurrent:
+ CrOffset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_CURRENT_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_CURRENT_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_CURRENT_MCMAIN_0_REG) *
+ Channel
+ );
+ break;
+
+ case MrcRegFileRank:
+ default:
+ CrOffset = 0;
+ break;
+ }
+
+ if (CrOffset != 0) {
+ ReutChSeqBaseAddr = MrcReadCR64 (MrcData, CrOffset);
+ ReutChSeqBaseAddr &= MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_CURRENT_MCMAIN_0_Rank_Address_MSK;
+ LogicalRank = (U8) MrcOemMemoryRightShiftU64 (
+ ReutChSeqBaseAddr,
+ MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_CURRENT_MCMAIN_0_Rank_Address_OFF
+ );
+
+ CrOffset = MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG +
+ (
+ (
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_1_REG -
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG
+ ) * Channel
+ );
+ ReutChSeqRankL2PMapping.Data = MrcReadCR (MrcData, CrOffset);
+ Rank = (U8)
+ (
+ (ReutChSeqRankL2PMapping.Data >> (LogicalRank * 4)) &
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_Logical_to_Physical_Rank0_Mapping_MSK
+ );
+ }
+
+ if (ByteMulticast) {
+ //
+ // Multicast settings on the channel
+ //
+ CrOffset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ } else {
+ CrOffset = DDRDATA0CH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL0_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL0_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL0_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL0_REG) * Byte);
+ }
+
+ DdrCrDataControl0.Data = MrcReadCR (MrcData, CrOffset);
+ DdrCrDataControl0.Bits.ReadRFRd = ReadRfRd;
+ DdrCrDataControl0.Bits.ReadRFWr = ReadRfWr;
+ DdrCrDataControl0.Bits.ReadRFRank = Rank;
+ MrcWriteCR (MrcData, CrOffset, DdrCrDataControl0.Data);
+}
+
+/**
+ This procedure is meant to handle basic timing centering, places strobe in the middle of the data eye,
+ for both read and write DQ/DQS using a very robust, linear search algorthim.
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] chBitMaskIn - 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 RdT and WrT are allowed in this function
+ @param[in] ResetPerBit - Option to Reset PerBit Deskew to middle value before byte training
+ @param[in] loopcount - loop count
+
+ @retval MrcStatus - If succeeded, return mrcSuccess
+**/
+MrcStatus
+DQTimeCentering1D (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 chBitMaskIn,
+ IN const U8 param,
+ IN const U8 ResetPerBit,
+ IN const U8 loopcount
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ S32 *CurrentPS;
+ S32 *CurrentPE;
+ S32 *LargestPS;
+ S32 *LargestPE;
+ U32 *Margin;
+ MrcStatus Status;
+ BOOL Pass;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 Step;
+ U8 MinWidth;
+ U8 chBitMask;
+ U8 DumArr[7];
+ U16 Result;
+ 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 DqsDelay;
+ U32 Start;
+ U32 End;
+ U32 Offset;
+ MCHBAR_CH0_CR_SC_IO_LATENCY_STRUCT ScIoLatency;
+#ifdef MRC_DEBUG_PRINT
+ U64 BitLaneFailures[MAX_CHANNEL][(MAX_POSSIBLE_TIME * 2) + 1];
+ U8 BitCount;
+ const char *DelayString;
+#endif
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ chBitMask = chBitMaskIn;
+ Status = mrcSuccess;
+ Center = 0;
+ MinWidth = 8;
+ MrcOemMemorySet (DumArr, 1, sizeof (DumArr));
+
+ if ((param != RdT) && (param != WrT) && (param != RcvEnaX)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "DataTimeCentering1D: Unknown Margin Parameter\n");
+ return mrcWrongInputParameter;
+ }
+
+ Step = 1;
+ if (param == RcvEnaX) {
+ SetupIOTestBasicVA (MrcData, chBitMask, loopcount - 3, 0, 0, 0, 8);
+ Outputs->DQPat = RdRdTA_All;
+ } else {
+ SetupIOTestBasicVA (MrcData, chBitMask, loopcount, NSOE, 0, 0, 8);
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ DelayString = (param == RcvEnaX) ? RcvEnDelayString : DqsDelayString;
+#endif
+ //
+ // Reset PerBit Deskew to middle value before byte training
+ // Write timing offset for bit[x] of the DQ byte. Linear from 0-15, each step size is tQCLK/64
+ // Read timing offset for bit[x] of the DQ byte. Linear from 0-15, each step size is tQCLK/64
+ //
+ if (ResetPerBit == 1) {
+ //
+ // EnMultiCast, UpdateMrcData
+ //
+ Status = ChangeMargin (
+ MrcData,
+ (param == RdT) ? RdTBit : WrTBit,
+ 0x88888888,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ MrcRegFileStart
+ );
+ }
+ //
+ // Center all Ranks
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+
+#ifdef MRC_DEBUG_PRINT
+ if (Outputs->ValidRankMask & (MRC_BIT0 << Rank)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rank = %d\n", Rank);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel 0 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 Error Count" : "0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 Error Count"
+ );
+ }
+#endif // MRC_DEBUG_PRINT
+
+ chBitMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ chBitMask |= SelectReutRanks (MrcData, Channel, (MRC_BIT0 << Rank), 0);
+ ChannelOut = &ControllerOut->Channel[Channel];
+ if ((MRC_BIT0 << Channel) & chBitMask) {
+ //
+ // Clear out anything left over in DataOffsetTrain
+ // Update rank timing to middle value
+ //
+ for (Byte = 0; (Byte < Outputs->SdramCount) && (param != RcvEnaX); Byte++) {
+ if (param == RdT) {
+ //
+ // Read Dq/Dqs
+ //
+ ChannelOut->RxDqsP[Rank][Byte] = 32;
+ ChannelOut->RxDqsN[Rank][Byte] = 32;
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ } else if (param == WrT) {
+ //
+ // Write Dq/Dqs
+ //
+ ChannelOut->TxDq[Rank][Byte] = ChannelOut->TxDqs[Rank][Byte] + 32;
+ UpdateTxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ }
+ //
+ // Clear any old state in DataTrain Offset
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+
+ //
+ // Setup REUT Error Counters to count errors per channel
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_CTL_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_ERR_COUNTER_CTL_0_REG - MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_CTL_0_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0);
+ }
+ }
+ //
+ // Continue if not valid rank on any channel
+ //
+ if (chBitMask == 0) {
+ continue; // This rank does not exist on any of the channels
+ }
+ //
+ // Sweep through values
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n%s", DelayString);
+ for (DqsDelay = -MAX_POSSIBLE_TIME; DqsDelay <= MAX_POSSIBLE_TIME; DqsDelay += Step) {
+ //
+ // Program DQS Delays
+ //
+ if (param == RcvEnaX){
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((MRC_BIT0 << Channel) & chBitMask) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ Status = ChangeMargin (MrcData, param, DqsDelay, 0, 0, Channel, Rank, Byte, 0, 0, 0, MrcRegFileStart);
+ }
+ }
+ }
+ } else {
+ Status = ChangeMargin (MrcData, param, DqsDelay, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileStart);
+ }
+
+ //
+ // Clear Errors and Run Test
+ //
+ RunIOTest (MrcData, chBitMask, Outputs->DQPat, DumArr, 1, 0);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n% 5d \t", DqsDelay);
+
+ //
+ // Update results for all Channel/bytes
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(chBitMask & (MRC_BIT0 << Channel))) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ (Channel != 0) ? "" : ((Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? " " : " ")
+ );
+ continue;
+ }
+
+ //
+ // Read out per byte error results and update limit
+ //
+ 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);
+
+#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);
+
+ BitLaneFailures[Channel][DqsDelay + MAX_POSSIBLE_TIME] = MrcReadCR64 (MrcData, Offset);
+#endif
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // Check for Byte group error status
+ //
+ Pass = ((Result & (MRC_BIT0 << Byte)) == 0);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, Pass ? ". " : "# ");
+
+ CurrentPS = &CurrentPassingStart[Channel][Byte];
+ CurrentPE = &CurrentPassingEnd[Channel][Byte];
+ LargestPS = &LargestPassingStart[Channel][Byte];
+ LargestPE = &LargestPassingEnd[Channel][Byte];
+ if (DqsDelay == -31) {
+ if (Pass) {
+ //
+ // No error on this Byte group
+ //
+ *CurrentPS = *CurrentPE = *LargestPS = *LargestPE = DqsDelay;
+ } else {
+ //
+ // Selected Byte group has accumulated an error during loop back pattern
+ //
+ *CurrentPS = *CurrentPE = *LargestPS = *LargestPE = -33;
+ }
+ } else {
+ if (Pass) {
+ //
+ // No error on this Byte group
+ //
+ if (*CurrentPE != (DqsDelay - Step)) {
+ *CurrentPS = DqsDelay;
+ }
+ *CurrentPE = DqsDelay;
+
+ //
+ // Update Largest variables
+ //
+ cWidth = *CurrentPE - *CurrentPS;
+ lWidth = *LargestPE - *LargestPS;
+ if (cWidth > lWidth) {
+ *LargestPS = *CurrentPS;
+ *LargestPE = *CurrentPE;
+ }
+ }
+ }
+ } // for Byte
+ } // for Channel
+#ifdef MRC_DEBUG_PRINT
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (chBitMask & (MRC_BIT0 << Channel)) {
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG - MCHBAR_CH0_CR_REUT_CH_ERR_COUNTER_STATUS_0_REG) * Channel);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " 0x%x\t", MrcReadCR (MrcData, Offset));
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+ } // for DqsDelay
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "\n"); // End last line of Byte table.
+
+#ifdef MRC_DEBUG_PRINT
+ //
+ // Print out the bit lane failure information
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Bit Lane Information\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %d\nBitLane ", Channel);
+ for (BitCount = 0; BitCount < 7; BitCount++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%u ", BitCount);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n "); // End tens number and align ones number
+ for (BitCount = 0; BitCount < 64; BitCount++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%u", BitCount % 10);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n%s", DelayString);
+
+ for (DqsDelay = -MAX_POSSIBLE_TIME; DqsDelay <= MAX_POSSIBLE_TIME; DqsDelay += Step) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n% 5d\t", DqsDelay); // Begin with a new line and print the DqsDelay value
+ for (BitCount = 0; BitCount < 64; BitCount++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ (BitLaneFailures[Channel][DqsDelay + MAX_POSSIBLE_TIME] & MrcOemMemoryLeftShiftU64 (1, BitCount)) ? "#" : "."
+ );
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // Gap after Channel
+ }
+ }
+#endif
+
+ //
+ // Clean Up for next Rank
+ //
+ Status = ChangeMargin (MrcData, param, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileCurrent);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (chBitMask & (MRC_BIT0 << Channel)) {
+ 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++) {
+ LargestPS = &LargestPassingStart[Channel][Byte];
+ LargestPE = &LargestPassingEnd[Channel][Byte];
+ lWidth = *LargestPE - *LargestPS;
+ if (lWidth < MinWidth) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "ERROR!! DataTimeCentering1D Eye Too Small Channel: %u, Rank: %u, Byte: %u\n",
+ Channel,
+ Rank,
+ Byte
+ );
+ Status = mrcDataTimeCentering1DErr;
+ } else {
+ Center = *LargestPS + (lWidth / 2);
+ }
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " B%d: %d\t%d\t%d\t%d\n",
+ Byte,
+ *LargestPS,
+ *LargestPE,
+ lWidth,
+ Center
+ );
+
+ Start = ABS (10 **LargestPS);
+ End = ABS (10 **LargestPE);
+ if (param == RdT) {
+ //
+ // read Dq./Dqs
+ //
+ Margin = &Outputs->MarginResult[LastRxT][Rank][Channel][Byte][0];
+ *Margin = Start;
+ Margin[1] = End;
+ ChannelOut->RxDqsP[Rank][Byte] = (U8) ((S32) ChannelOut->RxDqsP[Rank][Byte] + Center);
+ ChannelOut->RxDqsN[Rank][Byte] = (U8) ((S32) ChannelOut->RxDqsN[Rank][Byte] + Center);
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ } else if (param == WrT){
+ //
+ // Write Dq/Dqs
+ //
+ Margin = &Outputs->MarginResult[LastTxT][Rank][Channel][Byte][0];
+ *Margin = Start;
+ Margin[1] = End;
+ ChannelOut->TxDq[Rank][Byte] = (U16) ((S32) ChannelOut->TxDq[Rank][Byte] + Center);
+ UpdateTxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ } else if (param == RcvEnaX){
+ //
+ // Receive Enable
+ //
+ Margin = &Outputs->MarginResult[LastRcvEna][Rank][Channel][Byte][0];
+ *Margin = Start;
+ Margin[1] = End;
+ ChannelOut->RcvEn[Rank][Byte] = (ChannelOut->RcvEn[Rank][Byte] + (U16) (2 * Center));
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ }
+ if (param == RcvEnaX){
+ //
+ // clean up
+ //
+ 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);
+ ScIoLatency.Bits.RT_IOCOMP = MCHBAR_CH0_CR_SC_IO_LATENCY_RT_IOCOMP_MAX & ChannelOut->RTIoComp;
+ MrcWriteCR (MrcData, Offset, ScIoLatency.Data);
+ }
+ }
+ }
+ }
+
+ if (param == RcvEnaX) {
+ IoReset (MrcData);
+ }
+
+ return Status;
+}
+
+/**
+ This procedure is meant to handle much more complex centering that will use a 2D algorithm to optimize asymetical
+ eyes for both timing and voltage margin.
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in,out] MarginResult - Margin data from centering
+ @param[in] ChBitMaskIn - 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 RdT and WrT are allowed in this function
+ @param[in] EnPerBit - Option to enable per bit margining
+ @param[in] EnRxDutyCycleIn - Phase to center.
+ @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 succeeded, return mrcSuccess
+**/
+MrcStatus
+DataTimeCentering2D (
+ IN OUT MrcParameters *const MrcData,
+ IN OUT U32 MarginResult[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES],
+ IN const U8 ChBitMaskIn,
+ IN const U8 Param,
+ IN const U8 EnPerBit,
+ IN const U8 EnRxDutyCycleIn,
+ IN const U8 ResetPerBit,
+ IN const U8 LoopCount,
+ IN const U8 En2D
+ )
+{
+ const U32 EHWeights[] = {6, 2, 1, 0, 2, 1, 0};
+ const U32 EWWeights[] = {0, 1, 2, 3, 1, 2, 3};
+ const S32 VrefPointsConst[] = {0, -6, -12, -18, 6, 12, 18};
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ U8 *RxDqPbCenter;
+ U8 *TxDqPbCenter;
+ U16 centerTiming;
+ U32 *Margin;
+ U32 *Eye;
+ S32 *CenterBit;
+ S32 *CSum;
+ MrcStatus Status;
+ U8 ResultType;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 Bit;
+ U8 ParamV;
+ U8 ParamB;
+ U8 MaxVScale;
+ U8 EnPerBitEH;
+ U8 Strobe;
+ U8 Strobes;
+ U8 Vref;
+ U8 SaveLC;
+ U8 LCloop;
+ U8 i;
+ U8 SkipWait;
+ U8 ChBitMask;
+ U8 EnRxDutyCycle;
+ U8 Edge;
+ U8 BMap[9];
+ U8 LoopEnd;
+ U16 Mode;
+ U32 MarginBit[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS][MAX_EDGES];
+ S32 Center;
+ U32 Weight;
+ S32 VrefPoints[sizeof (VrefPointsConst) / sizeof (VrefPointsConst[0])];
+ U32 SumEH;
+ U32 SumEW;
+ U32 BERStats[4];
+ U32 VrefScale[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U32 EH[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U32 EW[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U32 EyeShape[7][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES]; // Store all eye edges for Per Bit
+ U32 StrobeMargin[7][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][2][MAX_EDGES];//Save Edges per Strobe to pass Min (Stobe1, Strobe2)
+ S32 CenterSum[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S32 DivBy;
+ S8 DivBySign;
+ S32 Value0;
+ U32 Offset;
+ S32 CenterSumBit[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS];
+ S32 Calc;
+ MCHBAR_CH0_CR_REUT_CH_ERR_CTL_STRUCT ReutChErrCtl;
+ DDRDATA0CH0_CR_RXPERBITRANK0_STRUCT CrPerBitRank;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+
+ //
+ // 2D Margin Types (Voltage, Time)
+ // RdFan2: Margins both RdV and RdT at { (off, -2/3*off), (off, 2/3*off) }
+ // WrFan2: Margins both WrV and WrT at { (off, -2/3*off), (off, 2/3*off) }
+ // RdFan3: Margins both RdV and RdT at { (off, -2/3*off), (5/4*off, 0), (off, 2/3*off) }
+ // WrFan3: Margins both WrV and WrT at { (off, -2/3*off), (5/4*off, 0), (off, 2/3*off) }
+ //
+ if ((Param != RdT) && (Param != WrT)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "DataTimeCentering2D: Incorrect Margin Parameter %d\n", Param);
+ return mrcWrongInputParameter;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Parameter = %d (%sT)\n", Param, (Param == RdT) ? "Rd" : "Wr");
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ ChBitMask = ChBitMaskIn;
+ EnRxDutyCycle = EnRxDutyCycleIn;
+ Status = mrcSuccess;
+ MaxVScale = 24;
+ Strobes = 2;
+ Center = 0;
+ Value0 = 0;
+ MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats));
+ for (i = 0; i < (sizeof (BMap) / sizeof (BMap[0])); i++) {
+ BMap[i] = i;
+ }
+
+ ResultType = GetMarginResultType (Param);
+
+ EnPerBitEH = 1; // Repeat EH Measurement after byte training, before bit training
+ //
+ // SOE = 10b ( Stop on All Byte Groups Error )
+ //
+ SetupIOTestBasicVA (MrcData, ChBitMask, LoopCount - 1, NSOE, 0, 0, 8);
+ Outputs->DQPat = RdRdTA;
+ //
+ // Duty cycle should be ONLY for Rx
+ //
+ if (Param != RdT) {
+ EnRxDutyCycle = 0;
+ }
+
+ Strobes = 1 + EnRxDutyCycle;
+
+ //
+ // Option to only run center at nominal Vref point
+ //
+ if (En2D == 0) {
+ MrcOemMemorySet ((U8 *) &VrefPoints[0], 0, sizeof (VrefPoints));
+ } else {
+ MrcOemMemoryCpy ((U8 *) &VrefPoints[0], (U8 *) &VrefPointsConst[0], sizeof (VrefPoints));
+ }
+ //
+ // Calculate SumEH / SumEW for use in weighting equations
+ //
+ SumEH = SumEW = 0;
+ for (Vref = 0; Vref < sizeof (VrefPoints) / sizeof (VrefPoints[0]); Vref++) {
+ SumEH += EHWeights[Vref];
+ SumEW += EWWeights[Vref];
+
+ //
+ // Loop once at nominal Vref point
+ //
+ if (En2D == 0) {
+ Vref = 6;
+ }
+ }
+
+ if (Param == RdT) {
+ ParamV = RdV;
+ ParamB = RdTBit;
+ } else {
+ ParamV = WrV;
+ ParamB = WrTBit;
+ }
+ //
+ // Optimize timing per rank
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Optimization is per rank\n");
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ ChBitMask = 0;
+ //
+ // Select rank for REUT test
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((MRC_BIT0 << Channel) & ChBitMaskIn) {
+ ChBitMask |= SelectReutRanks (MrcData, Channel, (MRC_BIT0 << Rank), 0);
+ if ((MRC_BIT0 << Channel) & ChBitMask) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ //
+ // Clear any old state in DataTrain Offset
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ }
+ }
+ //
+ // Continue if not valid rank on any channel
+ //
+ if (ChBitMask == 0) {
+ continue;
+ //
+ // This rank does not exist on any of the channels
+ //
+ }
+ //
+ // Reset PerBit Deskew to middle value before byte training
+ // Write timing offset for bit[x] of the DQ byte. Linear from 0-15, each step size is tQCLK/64
+ // Read timing offset for bit[x] of the DQ byte. Linear from 0-15, each step size is tQCLK/64
+ //
+ if (ResetPerBit == 1) {
+ Status = ChangeMargin (MrcData, ParamB, 0x88888888, 0, 1, 0, Rank, 0, 0, 1, 0, MrcRegFileRank);
+ }
+
+ //####################################################
+ //###### Get EH to scale vref sample point by #####
+ //####################################################
+ //
+ // Pass the host last edges by reference
+ // Get EH/VrefScale for the use in timing centering
+ //
+ if (En2D > 0) {
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nCalling DQTimeCenterEH\n");
+ Status = DQTimeCenterEH (
+ MrcData,
+ ChBitMask,
+ Rank,
+ ParamV,
+ MaxVScale,
+ BMap,
+ EH,
+ VrefScale,
+ BERStats
+ );
+ if (Status != mrcSuccess) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "\nDQTimeCenterEH FAILED - Using VrefScale = %d\n", MaxVScale);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ MrcOemMemorySetDword (&VrefScale[Channel][0], MaxVScale, Outputs->SdramCount);
+ }
+ }
+ } else {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ MrcOemMemorySetDword (&EH[Channel][0], 1, Outputs->SdramCount);
+ MrcOemMemorySetDword (&VrefScale[Channel][0], 1, Outputs->SdramCount);
+ }
+ }
+
+ Status = GetMarginByte (MrcData, MarginResult, Param, Rank, (MRC_BIT0 << Rank));
+
+#if 0
+#ifdef MRC_DEBUG_PRINT
+ //
+ // Read the margins
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nLstSavd Margins ");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((1 << Channel) & ChBitMask)) {
+ continue;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d %d ",
+ MarginResult[ResultType][Rank][Channel][Byte][0],
+ MarginResult[ResultType][Rank][Channel][Byte][1]
+ );
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+#endif
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "### Rank = %d ###\n", Rank);
+ for (Strobe = 0; Strobe < Strobes; Strobe++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n### Strobe = %d ###\n", Strobe);
+ if (Outputs->ValidRankMask & (MRC_BIT0 << Rank)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nChannel\t\t0\t\t\t\t\t\t\t\t\t 1\nByte\t\t");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE, (
+ Outputs->SdramCount == MAX_SDRAM_IN_DIMM
+ ) ? "0\t1\t2\t3\t4\t\t5\t6\t7\t8\t0\t1\t2\t3\t4\t5\t6\t7\t8\nEdges L/R" :
+ "0\t1\t2\t3\t4\t5\t6\t7\t0\t1\t2\t3\t4\t5\t6\t7\nEdges L/R"
+ );
+ }
+ //####################################################
+ //###### Measure Eye Width at all Vref Points #####
+ //####################################################
+ //
+ // Program Selective error checking for RX. if strobe = 0 then Check even else Check odd
+ //
+ if (EnRxDutyCycle) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ Offset = 2 + 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);
+ MrcWriteCR8 (MrcData, Offset, (0x55 << Strobe));
+ }
+ }
+ }
+ //
+ // Loop through all the Vref Points to Test
+ //
+ for (Vref = 0; Vref < sizeof (VrefPoints) / sizeof (VrefPoints[0]); Vref++) {
+ //
+ // Setup Vref Voltage for this point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ SkipWait = (ChBitMask >> (Channel + 1)); // Skip if there are more channels
+
+ LoopEnd = (U8) ((ParamV == RdV) ? Outputs->SdramCount : 1);
+ for (Byte = 0; Byte < LoopEnd; Byte++) {
+ Value0 = (S32) (VrefPoints[Vref] * VrefScale[Channel][Byte]) / MaxVScale;
+ Status = ChangeMargin (
+ MrcData,
+ ParamV,
+ Value0,
+ 0,
+ 0,
+ Channel,
+ Rank,
+ Byte,
+ 0,
+ 0,
+ SkipWait,
+ MrcRegFileRank
+ );
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nVref = %d:\t", Value0);
+
+ //
+ // Run Margin Test
+ //
+ Mode = 0;
+ Status = MrcGetBERMarginByte (
+ MrcData,
+ MarginResult,
+ ChBitMask,
+ Rank,
+ Rank,
+ Param,
+ Mode,
+ BMap,
+ 1,
+ 31,
+ 0,
+ BERStats
+ );
+ //
+ // Store Results; Setup Vref Voltage for this point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((ChBitMask & (MRC_BIT0 << Channel))) {
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ Margin = &MarginResult[ResultType][Rank][Channel][Byte][0];
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d %d ",
+ MarginResult[ResultType][Rank][Channel][Byte][0],
+ MarginResult[ResultType][Rank][Channel][Byte][1]
+ );
+
+ Center = (S32) (Margin[1] -*Margin);
+ if (Vref == 0) {
+ EW[Channel][Byte] = (Margin[1] +*Margin) / 10;
+ CenterSum[Channel][Byte] = 0;
+ }
+ //
+ // Calculate weight for this point
+ //
+ Weight = EHWeights[Vref] * EH[Channel][Byte] + EWWeights[Vref] * EW[Channel][Byte];
+ CenterSum[Channel][Byte] += Weight * Center;
+ //
+ // Store Edges for Per Bit deskew
+ //
+ Eye = &EyeShape[Vref][Channel][Byte][0];
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ Eye[Edge] = Margin[Edge];
+ }
+ //
+ // RunTime Improvement. Set margin back to Vref = 0 point when the sign of the VrefPoint changes
+ //
+ if ((VrefPoints[Vref] < 0) &&
+ (Vref < (sizeof (VrefPoints) / sizeof (VrefPoints[0]) - 1)) &&
+ (VrefPoints[Vref + 1] > 0)
+ ) {
+ Eye = &EyeShape[0][Channel][Byte][0];
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ Margin[Edge] = Eye[Edge];
+ }
+ }
+ }
+ }
+ }
+ //
+ // Loop once at nominal Vref point
+ //
+ if (En2D == 0) {
+ Vref = sizeof (VrefPoints) / sizeof (VrefPoints[0]);
+ }
+ }
+
+ //####################################################
+ //############ Center Results per Byte ###########
+ //####################################################
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\nWeighted Center\t");
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Calculate and apply CenterPoint. Round to Nearest Int
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DivBy = (SumEH * EH[Channel][Byte] + SumEW * EW[Channel][Byte]);
+ if (DivBy == 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "DataTimeCentering2D: Divide by zero\n");
+ return mrcFail;
+ }
+
+ CSum = &CenterSum[Channel][Byte];
+ DivBySign = (*CSum < 0) ? (-1) : 1;
+
+ *CSum = (*CSum + 10 * (DivBySign * DivBy)) / (20 * DivBy);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", *CSum);
+
+ //
+ // Apply new centerpoint
+ //
+ if (Param == RdT) {
+ if (Strobe == 0) {
+ ChannelOut->RxDqsP[Rank][Byte] = (U8) ((S32) ChannelOut->RxDqsP[Rank][Byte] +*CSum);
+ }
+
+ if ((!EnRxDutyCycle) || (Strobe == 1)) {
+ ChannelOut->RxDqsN[Rank][Byte] = (U8) ((S32) ChannelOut->RxDqsN[Rank][Byte] +*CSum);
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ } else {
+ ChannelOut->TxDq[Rank][Byte] = (U16) ((S32) ChannelOut->TxDq[Rank][Byte] +*CSum);
+ UpdateTxT (MrcData, Channel, Rank, Byte, 0xFF, 0);
+ }
+ //
+ // Update the Eye Edges
+ //
+ for (Vref = 0; Vref < sizeof (VrefPoints) / sizeof (VrefPoints[0]); Vref++) {
+ Calc = 10 **CSum;
+ Eye = &EyeShape[Vref][Channel][Byte][0];
+ *Eye += Calc;
+ Eye[1] -= Calc;
+
+ //
+ // Save Per Strobe Edges
+ //
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ StrobeMargin[Vref][Channel][Byte][Strobe][Edge] = EyeShape[Vref][Channel][Byte][Edge];
+ }
+ //
+ // Loop once at nominal Vref point
+ //
+ if (En2D == 0) {
+ Vref = sizeof (VrefPoints) / sizeof (VrefPoints[0]);
+ }
+ }
+ //
+ // Update MrcData for future tests (MarginResult points back to MrcData)
+ // EyeShape for Vref 0 is assumed to have the best shape for future tests.
+ //
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ MarginResult[ResultType][Rank][Channel][Byte][Edge] = EyeShape[0][Channel][Byte][Edge];
+ }
+ }
+ //
+ // Clean up after test
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ }
+
+ centerTiming = 0;
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nFinal Center\t");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Calculate final center point relative to "zero" as in the 1D case
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if (Param == RdT) {
+ if (Strobe == 0) {
+ centerTiming = (U8) (ChannelOut->RxDqsP[Rank][Byte] - 32);
+ }
+
+ if ((!EnRxDutyCycle) || (Strobe == 1)) {
+ centerTiming = (U8) (ChannelOut->RxDqsN[Rank][Byte] - 32);
+ }
+ } else {
+ centerTiming = ChannelOut->TxDq[Rank][Byte] - (ChannelOut->TxDqs[Rank][Byte] + 32);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", (S8) centerTiming);
+ }
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+ } // End of Byte Centering
+
+ //######################################################
+ //############ Measure Eye Width Per BIT ##########
+ //######################################################
+
+ if (EnPerBit) {
+#if 0
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\nEdges we pass on to GetMarginBit are\n");
+ for (Vref = 0; Vref < sizeof (VrefPoints) / sizeof (VrefPoints[0]); Vref++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t");
+ //
+ // Setup Vref Voltage for this point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d ", EyeShape[Vref][Channel][Byte][Edge]);
+ }
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ if (En2D == 0) {
+ Vref = sizeof (VrefPoints) / sizeof (VrefPoints[0]);
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+#endif
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n### Measure Eye Width Per BIT\n");
+ //
+ // Recalculate the EH after the Byte Centering
+ //
+ if (EnPerBitEH && (En2D > 0)) {
+ Status = DQTimeCenterEH (
+ MrcData,
+ ChBitMask,
+ Rank,
+ ParamV,
+ MaxVScale,
+ BMap,
+ EH,
+ VrefScale,
+ BERStats
+ );
+ if (Status != mrcSuccess) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "\nDQTimeCenterEH FAILED - Using VrefScale = %d\n", MaxVScale);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ MrcOemMemorySetDword (&VrefScale[Channel][0], MaxVScale, Outputs->SdramCount);
+ }
+ }
+ }
+ //
+ // No stop on error or selective error cheking
+ // 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_Cacheline = MCHBAR_CH0_CR_REUT_CH_ERR_CTL_Selective_Error_Enable_Cacheline_MAX;
+ ReutChErrCtl.Bits.Selective_Error_Enable_Chunk = MCHBAR_CH0_CR_REUT_CH_ERR_CTL_Selective_Error_Enable_Chunk_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 (Vref = 0; Vref < sizeof (VrefPoints) / sizeof (VrefPoints[0]); Vref++) {
+ //
+ // Setup Vref Voltage for this point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((ChBitMask & (MRC_BIT0 << Channel))) {
+
+ SkipWait = (ChBitMask >> (Channel + 1)); // Skip if there are more channels
+ //
+ // Change Vref margin
+ //
+ LoopEnd = (U8) ((ParamV == RdV) ? Outputs->SdramCount : 1);
+ for (Byte = 0; Byte < LoopEnd; Byte++) {
+ Value0 = (S32) (VrefPoints[Vref] * VrefScale[Channel][Byte]) / MaxVScale;
+ Status = ChangeMargin (
+ MrcData,
+ ParamV,
+ Value0,
+ 0,
+ 0,
+ Channel,
+ Rank,
+ Byte,
+ 0,
+ 0,
+ SkipWait,
+ MrcRegFileRank
+ );
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %d Vref = %d\t", Channel, Value0);
+ MrcOemMemorySetDword (&MarginBit[Channel][0][0][0], 8, MAX_SDRAM_IN_DIMM * MAX_BITS * MAX_EDGES);
+ }
+ //
+ // Run Margin Test; Loop through 2 times. Once at low loop count and Once at high loopcount. Improves runtime
+ // @todo: Need loop count of 2 when not using BASICVA
+ //
+ for (LCloop = 0; LCloop < 1; LCloop++) {
+ Outputs->DQPatLC = (LCloop == 0) ? 1 : SaveLC;
+
+ Mode = 0;
+ Status = MrcGetMarginBit (MrcData, ChBitMask, Rank, MarginBit, EyeShape[Vref], ParamB, Mode, 15);
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nCSum ");
+ // Store Results
+ // Setup Vref Voltage for this point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // Calculate weight for this point
+ //
+ Weight = EHWeights[Vref] * EH[Channel][Byte] + EWWeights[Vref] * EW[Channel][Byte];
+ for (Bit = 0; Bit < MAX_BITS; Bit++) {
+ Margin = &MarginBit[Channel][Byte][Bit][0];
+ CSum = &CenterSumBit[Channel][Byte][Bit];
+
+ Center = ((Margin[1] - 8) - (8 - *Margin));
+ if (Vref == 0) {
+ *CSum = 0;
+ }
+
+ *CSum += Weight * Center;
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "% 4d", *CSum);
+ }
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Weight %d ", Weight);
+ }
+ }
+ }
+ //
+ // Loop once at nominal Vref point
+ //
+ if (En2D == 0) {
+ Vref = sizeof (VrefPoints) / sizeof (VrefPoints[0]);
+ }
+ }
+
+ //######################################################
+ //############# Center Result Per BIT #############
+ //######################################################
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nWtd Ctr\t ");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Cleanup after test - go back to the per byte setup
+ //
+ ReutChErrCtl.Data = 0;
+ ReutChErrCtl.Bits.Stop_on_Nth_Error = 1;
+ ReutChErrCtl.Bits.Stop_On_Error_Control = NSOE;
+ 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;
+ 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);
+ MrcWriteCR (MrcData, Offset, ReutChErrCtl.Data);
+
+ //
+ // Calculate and apply CenterPoint. Round to Nearest Int
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DivBy = (SumEH * EH[Channel][Byte] + SumEW * EW[Channel][Byte]);
+
+ //
+ // Make sure DivBy is never 0
+ //
+ if (DivBy == 0) {
+ DivBy = 1;
+ }
+
+ CrPerBitRank.Data = 0;
+ for (Bit = 0; Bit < MAX_BITS; Bit++) {
+ CenterBit = &CenterSumBit[Channel][Byte][Bit];
+ RxDqPbCenter = &ChannelOut->RxDqPb[Rank][Byte][Bit].Center;
+ TxDqPbCenter = &ChannelOut->TxDqPb[Rank][Byte][Bit].Center;
+
+ DivBySign = (*CenterBit < 0) ? (-1) : 1;
+ *CenterBit = (*CenterBit + (DivBySign * DivBy)) / (2 * DivBy);
+
+ //
+ // Centerpoint needs to be added to starting DqPb value
+ //
+ *CenterBit += (Param == RdT) ? (S32) *RxDqPbCenter : (S32) *TxDqPbCenter;
+
+ //
+ // Check for saturation
+ //
+ if (*CenterBit > DDRDATA0CH0_CR_RXPERBITRANK0_Lane0_MAX) {
+ *CenterBit = DDRDATA0CH0_CR_RXPERBITRANK0_Lane0_MAX;
+ } else if (*CenterBit < 0) {
+ *CenterBit = 0;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "% 4x", *CenterBit);
+
+ //
+ // Update MrcData
+ //
+ if (Param == RdT) {
+ *RxDqPbCenter = (U8) *CenterBit;
+ } else {
+ *TxDqPbCenter = (U8) *CenterBit;
+ }
+
+ CrPerBitRank.Data |= (*CenterBit << (4 * Bit));
+ }
+ //
+ // Apply new centerpoint
+ // ParamB already has the proper per bit parameter based on Param
+ //
+ Status = ChangeMargin (
+ MrcData,
+ ParamB,
+ CrPerBitRank.Data,
+ 0,
+ 0,
+ Channel,
+ Rank,
+ Byte,
+ 0,
+ 0,
+ 0,
+ MrcRegFileRank
+ );
+
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " DivBy %d ", DivBy);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ }
+ //
+ // Clear any old state in DataTrain Offset
+ //
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ }
+ //
+ // No stop on error or selective error cheking
+ // Stop on all lane fail
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ //
+ // SOE = 11b ( Stop on All Lanes Error )
+ //
+ ReutChErrCtl.Data = 0;
+ ReutChErrCtl.Bits.Stop_on_Nth_Error = 1;
+ ReutChErrCtl.Bits.Stop_On_Error_Control = ALSOE;
+ 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;
+ 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);
+ MrcWriteCR (MrcData, Offset, ReutChErrCtl.Data);
+ }
+ }
+
+#if 0 // This code is for debug purposes ONLY if we want to know the perbyte margins after calling the perbit centering
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\nEdges\t");
+ for (Vref = 0; Vref < (sizeof (VrefPoints) / sizeof (VrefPoints[0])); Vref++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d ", EyeShape[Vref][Channel][Byte][Edge]);
+ }
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ if (En2D == 0) {
+ Vref = sizeof (VrefPoints) / sizeof (VrefPoints[0]);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nCalling GetMarginBit with per Byte Timing\nByte\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (1 << Channel)) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t\t\t\t\t", Byte);
+ }
+ }
+ }
+
+ for (Vref = 0; Vref < (sizeof (VrefPoints) / sizeof (VrefPoints[0])); Vref++) {
+ Mode = 0;
+ Status = MrcGetMarginBit (MrcData, ChBitMask, Rank, MarginBit, EyeShape[Vref], Param, Mode, 31);
+ //
+ // Loop once at nominal Vref point
+ //
+ if (En2D == 0) {
+ Vref = sizeof (VrefPoints) / sizeof (VrefPoints[0]);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nPerByte Margins after Bit Centering\nLeft\tRight\tCenter\n");
+ for (Vref = 0; Vref < (sizeof (VrefPoints) / sizeof (VrefPoints[0])); Vref++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %d\n", Channel);
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d\t%d\t%d\n",
+ EyeShape[Vref][Channel][Byte][0],
+ EyeShape[Vref][Channel][Byte][1],
+ (((S32) EyeShape[Vref][Channel][Byte][1] - (S32) EyeShape[Vref][Channel][Byte][0]) / (2 * 10))
+ );
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ if (En2D == 0) {
+ Vref = sizeof (VrefPoints) / sizeof (VrefPoints[0]);
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+#endif
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ //
+ // Cleanup after test
+ //
+ ReutChErrCtl.Data = 0;
+ ReutChErrCtl.Bits.Stop_on_Nth_Error = 1;
+ ReutChErrCtl.Bits.Stop_On_Error_Control = NSOE;
+ 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;
+ 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);
+ MrcWriteCR (MrcData, Offset, ReutChErrCtl.Data);
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ } //End of Rank
+ //
+ // Clean Up after test
+ //
+ Outputs->EnDumRd = 0;
+ Status = ChangeMargin (MrcData, ParamV, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileCurrent);
+ return Status;
+}
+
+/**
+ Subfunction of 2D Timing Centering
+ Measures paramV margin across ch/bytes and updates the EH/VrefScale variables
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] ChBitMask - Channel Bit mak for which test should be setup for.
+ @param[in] rank - Defines rank to used for MrcData
+ @param[in] ParamV - Margin parameter
+ @param[in] MaxVScale - Maximum Voltage Scale to use
+ @param[in] BMap - Byte mapping to configure error counter control register
+ @param[in,out] EH - Structure that stores start, stop and increment details for address
+ @param[in,out] VrefScale - Parameter to be updated
+ @param[in,out] BERStats - Bit Error Rate Statistics.
+
+ @retval mrcSuccess if successful, otherwise the function returns an error status.
+**/
+MrcStatus
+DQTimeCenterEH (
+ IN MrcParameters * const MrcData,
+ IN const U8 ChBitMask,
+ IN const U8 rank,
+ IN const U8 ParamV,
+ IN const U8 MaxVScale,
+ IN U8 * const BMap,
+ IN OUT U32 EH[MAX_CHANNEL][MAX_SDRAM_IN_DIMM],
+ IN OUT U32 VrefScale[MAX_CHANNEL][MAX_SDRAM_IN_DIMM],
+ IN OUT U32 * const BERStats
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ U32 *MarginResult;
+ U32 *VrefS;
+ MrcStatus Status;
+ U8 ResultType;
+ U8 Channel;
+ U8 Byte;
+ U32 MinVrefScale;
+ U16 Mode;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "DQTimeCenterEH:\n");
+ // Run Margin Test
+ //
+ Mode = 0;
+ Status = GetMarginByte (MrcData, Outputs->MarginResult, ParamV, rank, (MRC_BIT0 << rank));
+ if (mrcSuccess == Status) {
+ Status = MrcGetBERMarginByte (
+ MrcData,
+ Outputs->MarginResult,
+ ChBitMask,
+ rank,
+ rank,
+ ParamV,
+ Mode,
+ BMap,
+ 1,
+ MAX_POSSIBLE_VREF,
+ 0,
+ BERStats
+ );
+ if (mrcSuccess == Status) {
+ Status = ScaleMarginByte (MrcData, Outputs->MarginResult, ParamV, rank);
+ if (mrcSuccess == Status) {
+ ResultType = GetMarginResultType (ParamV);
+
+ //
+ // Update VrefScale with results
+ //
+ for (Channel = 0; (Channel < MAX_CHANNEL) && (mrcSuccess == Status); Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ //
+ // Calculate EH and VrefScale
+ //
+ MinVrefScale = MaxVScale;
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MarginResult = &Outputs->MarginResult[ResultType][rank][Channel][Byte][0];
+ VrefS = &VrefScale[Channel][Byte];
+ EH[Channel][Byte] = (*MarginResult + *(MarginResult + 1)) / 10;
+ *VrefS = EH[Channel][Byte] / 2;
+
+ if (*VrefS > MaxVScale) {
+ *VrefS = MaxVScale;
+ }
+
+ if (MinVrefScale > *VrefS) {
+ MinVrefScale = *VrefS;
+ }
+ //
+ // Scale host back to correct values
+ //
+ Status = ScaleMarginByte (MrcData, Outputs->MarginResult, ParamV, rank);
+ if (mrcSuccess != Status) {
+ break;
+ }
+ //
+ // For Tx, use the same Vref limit for all bytes. Store result in byte0
+ //
+ if (ParamV == WrV) {
+ MrcOemMemorySetDword (&VrefScale[Channel][0], MinVrefScale, Outputs->SdramCount);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ //
+ // Updates EH and VrefScale
+ //
+ return Status;
+}
+
+/**
+ Update the Vref value
+ if VrefType = 0 Updates Ch0 Vref_Dq
+ if VrefType = 1 Updates Ch1 Vref_Dq
+ if VrefType = 2 Updates Vref_CA
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] VrefType - Determines the Vref to change
+ @param[in] UpdateMrcData - Used to decide if Mrc host must be updated
+ @param[in] Offset - Vref value
+ @param[in] SkipWait - Determines if we will wait for vref to settle after writing to register
+
+ @retval Nothing
+**/
+void
+UpdateVrefWaitTilStable (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 VrefType,
+ IN const U8 UpdateMrcData,
+ IN S32 Offset,
+ IN U8 SkipWait
+ )
+{
+ const MrcDebug *Debug;
+ MrcInput *Inputs;
+ MrcOutput *Outputs;
+ U32 CheckMask;
+ U8 OffH;
+ U8 Count;
+ DDRDATA7CH1_CR_DDRCRVREFADJUST1_STRUCT DdrCrVrefAdjust;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+
+ //
+ // Calculate and write the new Vref offset value.
+ //
+ switch (VrefType) {
+ case 0:
+ OffH = (U8) (((DDRDATA7CH1_CR_DDRCRVREFADJUST1_STRUCT *) (&Outputs->DimmVref))->Bits.Ch0VrefCtl);
+ break;
+
+ case 1:
+ OffH = (U8) (((DDRDATA7CH1_CR_DDRCRVREFADJUST1_STRUCT *) (&Outputs->DimmVref))->Bits.Ch1VrefCtl);
+ break;
+
+ case 2:
+ OffH = (U8) (((DDRDATA7CH1_CR_DDRCRVREFADJUST1_STRUCT *) (&Outputs->DimmVref))->Bits.CAVrefCtl);
+ break;
+
+ default:
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "UpdateVrefWaitTilStable called with an incorrect value!\n");
+ return;
+ }
+
+ Offset = Offset + (S8) MrcSE (OffH, 7, 8); // Get offset from host. SE = Sign Extend number 7->8 bits
+ if (Offset > MAX_POSSIBLE_VREF) {
+ Offset = MAX_POSSIBLE_VREF;
+ } else if (Offset < (-1 * MAX_POSSIBLE_VREF)) {
+ Offset = -1 * MAX_POSSIBLE_VREF;
+ }
+
+ if (UpdateMrcData) {
+ switch (VrefType) {
+ case 0:
+ (((DDRDATA7CH1_CR_DDRCRVREFADJUST1_STRUCT *) (&Outputs->DimmVref))->Bits.Ch0VrefCtl) = Offset;
+ break;
+
+ case 1:
+ (((DDRDATA7CH1_CR_DDRCRVREFADJUST1_STRUCT *) (&Outputs->DimmVref))->Bits.Ch1VrefCtl) = Offset;
+ break;
+
+ case 2:
+ (((DDRDATA7CH1_CR_DDRCRVREFADJUST1_STRUCT *) (&Outputs->DimmVref))->Bits.CAVrefCtl) = Offset;
+ break;
+
+ default:
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "UpdateVrefWaitTilStable called with an incorrect value!\n");
+ return;
+ }
+ }
+
+ DdrCrVrefAdjust.Data = MrcReadCR (MrcData, DDRDATA7CH1_CR_DDRCRVREFADJUST1_REG);
+ switch (VrefType) {
+ case 0:
+ DdrCrVrefAdjust.Bits.Ch0VrefCtl = Offset;
+ CheckMask = DDRDATA7CH1_CR_DDRCRVREFADJUST1_ch0SlowBW_MSK;
+ break;
+
+ case 1:
+ DdrCrVrefAdjust.Bits.Ch1VrefCtl = Offset;
+ CheckMask = DDRDATA7CH1_CR_DDRCRVREFADJUST1_ch1SlowBW_MSK;
+ break;
+
+ case 2:
+ DdrCrVrefAdjust.Bits.CAVrefCtl = Offset;
+ CheckMask = DDRDATA7CH1_CR_DDRCRVREFADJUST1_caSlowBW_MSK;
+ break;
+
+ default:
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "UpdateVrefWaitTilStable called with an incorrect value!\n");
+ return;
+ }
+
+ MrcWriteCrMulticast (MrcData, DDRDATA7CH1_CR_DDRCRVREFADJUST1_REG, DdrCrVrefAdjust.Data);
+
+ //
+ // Wait for Vref to settle. Note VrefCA needs longer to settle.
+ //
+ if (!SkipWait) {
+ Count = 0;
+ while (Count < 50) {
+ //
+ // Don't wait more than 50uS
+ //
+ if ((MrcReadCR (MrcData, DDRDATA7CH1_CR_DDRCRVREFADJUST1_REG) & CheckMask) == CheckMask) {
+ break;
+ }
+
+ MrcWait (MrcData, 1 * HPET_1US);
+ Count += 1;
+ }
+
+ if (Count >= 50) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "CAVref circuit failed to converge, \n");
+ }
+
+ MrcWait (MrcData, 5 * HPET_1US); // Add 5us to make sure everything is done
+ }
+}
+
+/**
+ This function is used to move CMD/CTL/CLK/CKE PIs during training
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel to shift PI for
+ @param[in] Iteration - Determines which PI to shift:
+ MrcIterationClock = 0
+ MrcIterationCmdN = 1
+ MrcIterationCmdS = 2
+ MrcIterationCke = 3
+ MrcIterationCtl = 4
+ MrcIterationCmdV = 5
+ @param[in] RankMask - Ranks to work on
+ @param[in] GroupMask - which LPDDR groups to work on for CMD/CLK/CKE; not used for DDR3
+ @param[in] NewValue - value to shift in case of CLK Iteration, New value for all other cases
+ @param[in] UpdateHost - Determines if MrcData structure is updated
+
+ @retval Nothing
+**/
+void
+ShiftPIforCmdTraining (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 Iteration,
+ IN const U8 RankMask,
+ IN const U8 GroupMask,
+ IN S32 NewValue,
+ IN const U8 UpdateHost
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ const MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ MrcOutput *Outputs;
+ U32 Offset;
+ U32 ByteMask;
+ U32 BitOffset;
+ U8 Rank;
+#ifdef ULT_FLAG
+ U8 Group;
+ U32 Cke;
+ U32 CkeRankMapping;
+#endif // ULT_FLAG
+ S8 Shift;
+ BOOL Lpddr;
+ DDRCLKCH0_CR_DDRCRCLKPICODE_STRUCT ClkPiCode;
+ DDRCKECH0_CR_DDRCRCMDPICODING_STRUCT CkeCmdPiCoding;
+ DDRCMDSCH0_CR_DDRCRCMDPICODING_STRUCT CmdSPiCoding;
+ DDRCMDNCH0_CR_DDRCRCMDPICODING_STRUCT CmdNPiCoding;
+ DDRCTLCH0_CR_DDRCRCTLPICODING_STRUCT CtlPiCoding;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ ChannelIn = &Inputs->Controller[0].Channel[Channel];
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ if (Iteration != MrcIterationClock) {
+ if (NewValue < 0) {
+ NewValue = 0;
+ } else if (NewValue > 127) {
+ NewValue = 127;
+ }
+ }
+
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nShiftPIforCmdTraining: Iteration: %d, Channel: %d, RankMask: %d, GroupMask: %d, NewValue = 0x%x\n", Iteration, Channel, RankMask, GroupMask, NewValue);
+
+ switch (Iteration) {
+ case MrcIterationClock: // SHIFT CLOCK
+ ClkPiCode.Data = 0;
+ ByteMask = 0x1FF; // Shift DQ PI on all 9 bytes by default on DDR3
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ //
+ // In LPDDR clocks are per group, not per rank
+ //
+ for (Group = 0; Group < 2; Group++) {
+ BitOffset = DDRCLKCH0_CR_DDRCRCLKPICODE_PiSettingRank0_WID * Group;
+ if (GroupMask & (1 << Group)) {
+ Shift = (ChannelOut->ClkPiCode[Group] + NewValue) % 128;
+ if (Shift < 0) {
+ Shift = (128 - ABS (Shift));
+ }
+
+ Shift &= DDRCLKCH0_CR_DDRCRCLKPICODE_PiSettingRank0_MSK;
+ if (UpdateHost) {
+ ChannelOut->ClkPiCode[Group] = Shift;
+ }
+
+ ClkPiCode.Data += (Shift << BitOffset);
+ //
+ // Each clock spans all ranks, so need to shift DQ PIs on all ranks, for bytes in this group only
+ //
+ ByteMask = ChannelIn->DQByteMap[MrcIterationClock][Group];
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ ShiftDQPIs (MrcData, Channel, Rank, ByteMask, (S8) NewValue, UpdateHost, 0);
+ }
+ }
+ } else {
+ ClkPiCode.Data += (ChannelOut->ClkPiCode[Group] << BitOffset);
+ }
+ } // for Group
+ } else
+#endif // ULT_FLAG
+ {
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ BitOffset = DDRCLKCH0_CR_DDRCRCLKPICODE_PiSettingRank0_WID * Rank;
+ if (RankMask & (1 << Rank)) {
+ Shift = (ChannelOut->ClkPiCode[Rank] + NewValue) % 128;
+ if (Shift < 0) {
+ Shift = (128 - ABS (Shift));
+ }
+
+ Shift &= DDRCLKCH0_CR_DDRCRCLKPICODE_PiSettingRank0_MSK;
+ if (UpdateHost) {
+ ChannelOut->ClkPiCode[Rank] = Shift;
+ }
+
+ ClkPiCode.Data += (Shift << BitOffset);
+ ShiftDQPIs (MrcData, Channel, Rank, ByteMask, (S8) NewValue, UpdateHost, 0);
+ } else {
+ ClkPiCode.Data += (ChannelOut->ClkPiCode[Rank] << BitOffset);
+ }
+ }
+ }
+
+ Offset = DDRCLKCH0_CR_DDRCRCLKPICODE_REG + ((DDRCLKCH1_CR_DDRCRCLKPICODE_REG - DDRCLKCH0_CR_DDRCRCLKPICODE_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ClkPiCode.Data);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "**** ShiftPIforCmdTraining, Iteration = %d, CRValue = 0x%x ****\n", Iteration,CRValue);
+ break;
+
+ case MrcIterationCmdN: // SHIFT COMMAND NORTH
+ CmdNPiCoding.Data = 0;
+ NewValue = MIN (NewValue, DDRCMDNCH0_CR_DDRCRCMDPICODING_CmdPi0Code_MAX);
+ //
+ // HSW ULT LPDDR3: CMDN.CmdPi1Code controls CAB
+ // HSW ULT DDR3L: Both CmdPi0Code and CmdPi1Code should have the same value
+ // HSW TRAD DDR3L: No harm setting CmdPi1Code same as CmdPi0Code
+ //
+ CmdNPiCoding.Bits.CmdPi0Code = NewValue;
+ CmdNPiCoding.Bits.CmdPi1Code = NewValue;
+ Offset = DDRCMDNCH0_CR_DDRCRCMDPICODING_REG +
+ ((DDRCMDNCH1_CR_DDRCRCMDPICODING_REG - DDRCMDNCH0_CR_DDRCRCMDPICODING_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, CmdNPiCoding.Data);
+ if (UpdateHost) {
+ ChannelOut->CmdnCmdPiCode[0] = NewValue;
+ ChannelOut->CmdnCmdPiCode[1] = NewValue;
+ }
+ break;
+
+ case MrcIterationCmdS: // SHIFT COMMAND SOUTH
+ CmdSPiCoding.Data = 0;
+ NewValue = MIN (NewValue, DDRCMDSCH0_CR_DDRCRCMDPICODING_CmdPi0Code_MAX);
+ //
+ // HSW ULT LPDDR3: CMDS.CmdPi0Code controls CAA, CMDS.CmdPi1Code controls CAB
+ // HSW ULT DDR3L: Both CmdPi0Code and CmdPi1Code should have the same value, also program CKE fub
+ // HSW TRAD DDR3L: No harm setting CmdPi1Code same as CmdPi0Code, also program CKE fub
+ //
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ CmdSPiCoding.Bits.CmdPi0Code = (GroupMask & 1) ? (U32) NewValue : ChannelOut->CmdsCmdPiCode[0]; // CAA
+ CmdSPiCoding.Bits.CmdPi1Code = (GroupMask & 2) ? (U32) NewValue : ChannelOut->CmdsCmdPiCode[1]; // CAB
+ } else
+#endif // ULT_FLAG
+ {
+ CmdSPiCoding.Bits.CmdPi0Code = NewValue;
+ CmdSPiCoding.Bits.CmdPi1Code = NewValue;
+
+ CkeCmdPiCoding.Data = 0;
+ NewValue = MIN (NewValue, DDRCKECH0_CR_DDRCRCMDPICODING_CmdPi0Code_MAX);
+ CkeCmdPiCoding.Bits.CmdPi0Code = NewValue;
+ CkeCmdPiCoding.Bits.CmdPi1Code = NewValue;
+ Offset = DDRCKECH0_CR_DDRCRCMDPICODING_REG +
+ ((DDRCKECH1_CR_DDRCRCMDPICODING_REG - DDRCKECH0_CR_DDRCRCMDPICODING_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, CkeCmdPiCoding.Data);
+ if (UpdateHost) {
+ ChannelOut->CkeCmdPiCode[0] = NewValue;
+ ChannelOut->CkeCmdPiCode[1] = NewValue;
+ }
+ }
+
+ if (UpdateHost) {
+ ChannelOut->CmdsCmdPiCode[0] = CmdSPiCoding.Bits.CmdPi0Code;
+ ChannelOut->CmdsCmdPiCode[1] = CmdSPiCoding.Bits.CmdPi1Code;
+ }
+
+ Offset = DDRCMDSCH0_CR_DDRCRCMDPICODING_REG +
+ ((DDRCMDSCH1_CR_DDRCRCMDPICODING_REG - DDRCMDSCH0_CR_DDRCRCMDPICODING_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, CmdSPiCoding.Data);
+ break;
+
+ case MrcIterationCke: // Shift CKE command
+ CkeCmdPiCoding.Data = 0;
+ NewValue = MIN (NewValue, DDRCKECH0_CR_DDRCRCMDPICODING_CmdPi0Code_MAX);
+ //
+ // HSW ULT LPDDR3: CKE.CmdPi0Code controls CAA
+ // HSW ULT DDR3L: Both CmdPi0Code and CmdPi1Code should have the same value
+ // HSW TRAD DDR3L: No harm setting CmdPi1Code same as CmdPi0Code
+ //
+ CkeCmdPiCoding.Bits.CmdPi0Code = NewValue;
+ CkeCmdPiCoding.Bits.CmdPi1Code = NewValue;
+ Offset = DDRCKECH0_CR_DDRCRCMDPICODING_REG +
+ ((DDRCKECH1_CR_DDRCRCMDPICODING_REG - DDRCKECH0_CR_DDRCRCMDPICODING_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, CkeCmdPiCoding.Data);
+ if (UpdateHost) {
+ ChannelOut->CkeCmdPiCode[0] = NewValue;
+ ChannelOut->CkeCmdPiCode[1] = NewValue;
+ }
+ break;
+
+ case MrcIterationCtl: // Shift CS/ODT and CKE.Control
+ CtlPiCoding.Data = 0;
+ NewValue = MIN (NewValue, DDRCTLCH0_CR_DDRCRCTLPICODING_CtlPiCode0_MAX);
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (RankMask & (1 << Rank)) {
+ CtlPiCoding.Data += (NewValue << (DDRCTLCH0_CR_DDRCRCTLPICODING_CtlPiCode0_WID * Rank));
+ if (UpdateHost) {
+ ChannelOut->CtlPiCode[Rank] = (U8) NewValue;
+ ChannelOut->CkePiCode[Rank] = (U8) NewValue;
+ }
+ } else {
+ CtlPiCoding.Data += (ChannelOut->CtlPiCode[Rank] << (DDRCTLCH0_CR_DDRCRCTLPICODING_CtlPiCode0_WID * Rank));
+ }
+ }
+
+#ifdef ULT_FLAG
+ if (Lpddr && Inputs->LpddrDramOdt) {
+ //
+ // ODT[0] (equal to CS[0] PI setting) goes in to CTL.CtlPiCode2
+ //
+ CtlPiCoding.Bits.CtlPiCode2 = ChannelOut->CtlPiCode[0];
+ }
+#endif // ULT_FLAG
+ Offset = DDRCTLCH0_CR_DDRCRCTLPICODING_REG +
+ ((DDRCTLCH1_CR_DDRCRCTLPICODING_REG - DDRCTLCH0_CR_DDRCRCTLPICODING_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, CtlPiCoding.Data);
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ CtlPiCoding.Data = 0;
+ //
+ // Use CKE-to-Rank mapping: [3:0] - Channel 0, [7:4] - Channel 1
+ //
+ CkeRankMapping = (Inputs->CkeRankMapping >> (Channel * 4)) & 0x0F;
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ for (Cke = 0; Cke <= 3; Cke++) {
+ if (((CkeRankMapping >> Cke) & 1) == Rank) {
+ //
+ // This CKE pin is connected to this Rank
+ //
+ CtlPiCoding.Data += (ChannelOut->CkePiCode[Rank] << (DDRCKECH0_CR_DDRCRCTLPICODING_CtlPiCode0_WID * Cke));
+ }
+ }
+ }
+ //
+ // Put average of CKE2 and CKE3 into CKE2 PI setting.
+ //
+ CtlPiCoding.Bits.CtlPiCode2 = (CtlPiCoding.Bits.CtlPiCode2 + CtlPiCoding.Bits.CtlPiCode3) / 2;
+ }
+#endif // ULT_FLAG
+ Offset = DDRCKECH0_CR_DDRCRCTLPICODING_REG +
+ ((DDRCKECH1_CR_DDRCRCTLPICODING_REG - DDRCKECH0_CR_DDRCRCTLPICODING_REG) * Channel);
+ CtlPiCoding.Bits.CtlPiCode3 = 0; // Do not write PiCode3
+ MrcWriteCR (MrcData, Offset, CtlPiCoding.Data);
+ break;
+
+ default:
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "WARNING...Unknown parameter to shift\n");
+ break;
+ }
+
+ return;
+}
+
+/**
+ Shifts RcvEn, WriteLevel and WriteDQS timing for all bytes
+ Usually used when moving the clocks on a channel
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel to update
+ @param[in] Rank - Rank to update
+ @param[in] ByteMask - Bytes to update
+ @param[in] Offset - value to shift
+ @param[in] UpdateHost - Determines if MrcData structure is updated
+ @param[in] SkipTx - Determines if TX update should be skipped
+
+ @retval Nothing
+**/
+void
+ShiftDQPIs (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 Rank,
+ IN const U32 ByteMask,
+ IN const S8 Offset,
+ IN const U8 UpdateHost,
+ IN const U8 SkipTx
+ )
+{
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ U8 Byte;
+ S8 OffTx;
+ U16 RcvEnValue;
+ DDRDATA0CH0_CR_TXTRAINRANK0_STRUCT CrTxTrainRank;
+
+ const MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ OffTx = SkipTx ? 0 : Offset;
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if (((1 << Byte) & ByteMask) == 0) {
+ continue;
+ }
+
+ RcvEnValue = ChannelOut->RcvEn[Rank][Byte] + Offset;
+ if ((S16) RcvEnValue < 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "WARNING: RcvEn PI wrapped below zero!\n");
+ RcvEnValue = 0; // Don't go below zero
+ } else if (RcvEnValue > DDRDATA_CR_RXTRAINRANK0_RxRcvEnPi_MAX) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "WARNING: RcvEn PI wrapped above 9 bits!\n");
+ RcvEnValue = DDRDATA_CR_RXTRAINRANK0_RxRcvEnPi_MAX; // Don't go above max
+ }
+
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0, RcvEnValue);
+
+ CrTxTrainRank.Data = 0;
+ CrTxTrainRank.Bits.TxEqualization = ChannelOut->TxEq[Rank][Byte];
+ CrTxTrainRank.Bits.TxDqsDelay = ChannelOut->TxDqs[Rank][Byte] + OffTx;
+ CrTxTrainRank.Bits.TxDqDelay = ChannelOut->TxDq[Rank][Byte] + OffTx;
+ UpdateTxT (MrcData, Channel, Rank, Byte, 3, CrTxTrainRank.Data);
+
+ if (UpdateHost) {
+ ChannelOut->RcvEn[Rank][Byte] = RcvEnValue;
+ ChannelOut->TxDqs[Rank][Byte] += OffTx;
+ ChannelOut->TxDq[Rank][Byte] += OffTx;
+ }
+ }
+
+ return;
+}
+
+/**
+ Retrieve the current memory frequency and clock from the memory controller.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in, out] MemoryClock - The current memory clock.
+ @param[in, out] Ratio - The current memory ratio setting.
+ @param[in, out] RefClk - The current memory reference clock.
+
+ @retval: The current memory frequency.
+**/
+MrcFrequency
+MrcGetCurrentMemoryFrequency (
+ MrcParameters * const MrcData,
+ U32 * const MemoryClock,
+ MrcClockRatio * const Ratio,
+ MrcRefClkSelect * const RefClk
+ )
+{
+ const MrcInput *Inputs;
+ const MrcOutput *Outputs;
+ PCU_CR_MC_BIOS_DATA_PCU_STRUCT McBiosData;
+ PCU_CR_MC_BIOS_REQ_PCU_STRUCT McBiosReqPcu;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ McBiosReqPcu.Data = MrcReadCR (MrcData, PCU_CR_MC_BIOS_REQ_PCU_REG);
+ McBiosData.Data = MrcReadCR (MrcData, PCU_CR_MC_BIOS_DATA_PCU_REG);
+ if (MemoryClock != NULL) {
+ *MemoryClock = MrcRatioToClock ((MrcClockRatio) McBiosData.Bits.MC_FREQ, McBiosReqPcu.Bits.REQ_TYPE, Inputs->BClkFrequency);
+ }
+ if (Ratio != NULL) {
+ *Ratio = (MrcClockRatio) McBiosData.Bits.MC_FREQ;
+ }
+ if (RefClk != NULL) {
+ *RefClk = (MrcRefClkSelect) McBiosReqPcu.Bits.REQ_TYPE;
+ }
+ return MrcRatioToFrequency (
+ MrcData,
+ (MrcClockRatio) McBiosData.Bits.MC_FREQ,
+ McBiosReqPcu.Bits.REQ_TYPE,
+ Inputs->BClkFrequency
+ );
+}
+
+#ifdef ULT_FLAG
+
+/**
+ Translate LPDDR command from CA[9:0] high and low phase to DDR3 MA/BA/CMD.
+ This is needed to program CADB.
+
+ @param[in] CaHigh - CA[9:0] value on the rising clock
+ @param[in] CaLow - CA[9:0] value on the falling clock
+ @param[out] MA - Translated value of MA[15:0]
+ @param[out] BA - Translated value of BA[2:0]
+ @param[out] CMD - Translated value of CMD[2:0] = [RASb, CASb, WEb]
+
+ @retval none
+**/
+void
+MrcConvertLpddr2Ddr (
+ IN U32 const CaHigh,
+ IN U32 const CaLow,
+ OUT U32 *MA,
+ OUT U32 *BA,
+ OUT U32 *CMD
+ )
+{
+ *MA = MRC_BIT15; // MA[15] should be 1
+ *BA = 0;
+ *CMD = MRC_BIT2; // RASb should be 1
+
+ //
+ // Translation table
+ //
+ // Command High phase Low phase
+ //-----------------------------
+ // CA[0] CASb MA[0]
+ // CA[1] WEb MA[1]
+ // CA[2] MA[8] MA[2]
+ // CA[3] MA[9] MA[3]
+ // CA[4] MA[10] MA[4]
+ // CA[5] MA[11] MA[5]
+ // CA[6] MA[12] MA[6]
+ // CA[7] BA[0] MA[7]
+ // CA[8] BA[1] MA[13]
+ // CA[9] BA[2] MA[14]
+
+ *MA |= (CaLow & 0xFF); // MA[7:0]
+ *MA |= ((CaHigh & 0x7C) << 6); // MA[12:8]
+ *MA |= ((CaLow & 0x300) << 5); // MA[14:13]
+
+ *BA |= ((CaHigh & 0x380) >> 7); // BA[2:0]
+
+ *CMD |= ((CaHigh & 0x02) >> 1); // CMD[0] = WEb
+ *CMD |= ((CaHigh & 0x01) << 1); // CMD[1] = CASb
+}
+
+/**
+ Run a short CADB sequence on selected channels
+
+ @param[in] MrcData - The MRC global data.
+ @param[in] ChBitMask - channels to work on.
+
+ @retval none
+**/
+void
+ShortRunCADB (
+ IN MrcParameters *const MrcData,
+ IN U8 ChBitMask
+ )
+{
+ U32 Channel;
+ U32 Offset;
+ MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_STRUCT ReutGlobalCtl;
+ MCSCHEDS_CR_REUT_CH_PAT_CADB_CTRL_STRUCT ReutChPatCadbCtrl;
+ MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_STRUCT ReutChSeqCfg;
+
+ //
+ // Enable REUT mode and Global Control on selected channels
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * Channel);
+ ReutChSeqCfg.Data = MrcReadCR64 (MrcData, Offset);
+ if (((1 << Channel) & ChBitMask) != 0) {
+ ReutChSeqCfg.Bits.Initialization_Mode = REUT_Testing_Mode;
+ ReutChSeqCfg.Bits.Global_Control = 1;
+ } else {
+ ReutChSeqCfg.Bits.Global_Control = 0;
+ }
+
+ MrcWriteCR64 (MrcData, Offset, ReutChSeqCfg.Data);
+ }
+ //
+ // Enable CADB Always On mode
+ //
+ ReutChPatCadbCtrl.Data = 0;
+ ReutChPatCadbCtrl.Bits.Enable_CADB_Always_On = 1;
+ MrcWriteCR (MrcData, MCSCHEDS_CR_REUT_CH_PAT_CADB_CTRL_REG, ReutChPatCadbCtrl.Data);
+
+ //
+ // Start CADB
+ //
+ ReutGlobalCtl.Data = 0;
+ ReutGlobalCtl.Bits.Global_Start_Test = 1;
+ MrcWriteCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG, ReutGlobalCtl.Data);
+
+ //
+ // It's enough to read from this register, no need for an extra delay
+ //
+ ReutGlobalCtl.Data = MrcReadCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG);
+ //
+ // Disable CADB Always On mode
+ //
+ ReutChPatCadbCtrl.Bits.Enable_CADB_Always_On = 0;
+ MrcWriteCR (MrcData, MCSCHEDS_CR_REUT_CH_PAT_CADB_CTRL_REG, ReutChPatCadbCtrl.Data);
+
+ //
+ // Stop CADB
+ //
+ ReutGlobalCtl.Bits.Global_Start_Test = 0;
+ ReutGlobalCtl.Bits.Global_Stop_Test = 1;
+ MrcWriteCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG, ReutGlobalCtl.Data);
+
+ //
+ // Read back
+ //
+ ReutGlobalCtl.Data = MrcReadCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG);
+
+ //
+ // Disable Global Control on selected channels
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (((1 << Channel) & ChBitMask) != 0) {
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * Channel);
+ ReutChSeqCfg.Data = MrcReadCR64 (MrcData, Offset);
+ ReutChSeqCfg.Bits.Global_Control = 0;
+ MrcWriteCR64 (MrcData, Offset, ReutChSeqCfg.Data);
+ }
+ }
+}
+
+#endif // ULT_FLAG
+
+/**
+ Get the Rx Bias values
+
+ @param[in, out] MrcData - Include all MRC global data.
+ @param[in, out] RxFselect - Location to save RxFselect.
+ @param[in, out] RxCBSelect - Location to save RxCBSelect.
+
+ @retval none
+**/
+void
+GetRxFselect (
+ IN MrcParameters *const MrcData,
+ IN OUT S8 *RxFselect,
+ IN OUT U8 *RxCBSelect
+ )
+{
+ MrcOutput *Outputs;
+ DDRCLK_CR_DDRCBSTATUS_STRUCT DdrCbStatus;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ DdrCbStatus.Data = MrcReadCR (MrcData, DDRCLK_CR_DDRCBSTATUS_REG);
+ *RxCBSelect = (U8) DdrCbStatus.Bits.DllCB;
+ *RxFselect = (Outputs->Ratio - ((Outputs->RefClk == MRC_REF_CLOCK_133) ? RXF_SELECT_RC_133 : RXF_SELECT_RC_100));
+
+ //
+ // Limit ratios for 1067, 1333, 1600, 1867 & 2133 MHz
+ //
+ *RxFselect = MIN (*RxFselect, RXF_SELECT_MAX); // Maximum 2133 MHz
+ *RxFselect = MAX (*RxFselect, RXF_SELECT_MIN); // Minimum 1067 MHz
+ return;
+}
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCrosser.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCrosser.c
new file mode 100644
index 0000000..4086802
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCrosser.c
@@ -0,0 +1,9860 @@
+/** @file
+ These functions implement the crosser training algorithm.
+
+@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 "MrcCrosser.h"
+#if SUPPORT_SODIMM == SUPPORT
+#include "MrcSpdProcessing.h"
+#endif //SUPPORT_SODIMM == SUPPORT
+
+///
+/// Module Definitions
+///
+#define BIT_TX_XTALK_SWEEP_START (-3)
+#define BIT_TX_XTALK_SWEEP_STOP (3)
+#define BIT_TX_XTALK_SWEEP_LEN (BIT_TX_XTALK_SWEEP_STOP - BIT_TX_XTALK_SWEEP_START + 1)
+#define BIT_TX_XTALK_RANGE (16)
+
+#define DIMM_ODT_DIMM_MASK_SHIFT (4)
+
+///
+/// Power optimizations Global Parameters
+///
+#define OPT_PARAM_LOOP_COUNT (15)
+#define OPT_PARAM_1D_LC (15)
+
+///
+/// UPM/PWR increment value if margins are at or below the retrain limit.
+///
+#define MRC_UPM_PWR_INC_VAL (40)
+
+//
+// Module Globals
+//
+const MrcUpmPwrRetrainLimits InitialLimits[MRC_NUMBER_UPM_PWR_RETRAIN_MARGINS] = {
+ //
+ // UPM, PWR Retrain
+ //
+ {RdT, {240, 320, 90 }},
+ {WrT, {220, 280, 90 }},
+ {RdV, {400, 520, 160}},
+ // For ULT DDR3L rcF the values are increased by 20 ticks, see UpmPwrLimitValue()
+ {WrV, {400, 520, 160}},
+ {RdFan2, {240, 480, 0 }},
+ {WrFan2, {240, 480, 0 }},
+ {RdFan3, {240*4/5, 480*4/5, 0 }},
+ {WrFan3, {240*4/5, 480*4/5, 0 }},
+ // {650ps,750ps} * 64 pi ticks * 2 (for width) = 134 PI Ticks. ~1.3nsec for UPM,~1.5nsec for PWR
+ // We must subtract out the built in margin of 96 when shifting IO Lat.
+ // Margin function works in steps of 2, so we divide the margin by 2.
+ // Margin numbers are scaled by 10.
+ {RcvEnaX, {(((134 - 96) / 2) * 10), (((154 - 96) / 2) * 10), 0}}
+};
+
+const U8 ActualDimmOdt[6] = { 0, 120, 60, 40, 30, 20 };
+
+#ifdef MRC_DEBUG_PRINT
+const char *TOptParamOffsetString[] = {
+ "OptWrDS",
+ "OptRdOdt",
+ "OptSComp",
+ "OptTComp",
+ "OptTxEq",
+ "OptRxEq",
+ "OptRxBias",
+ "OptDimmOdt",
+ "OptDimmOdtWr",
+ "OptDimmRon",
+ "OptDefault"
+};
+
+const char *MarginTypesString[] = {
+ "RcvEna",
+ "RdT",
+ "WrT",
+ "WrDqsT",
+ "RdV",
+ "WrV",
+ "WrLevel",
+ "WrTBox",
+ "WrTBit",
+ "RdTBit",
+ "RdVBit",
+ "RcvEnaX",
+ "CmdT",
+ "CmdV"
+};
+
+///
+/// These strings match the OptResultPerByteDbgStr enum for indexing
+/// the switch PrintCalcResultTableCh and PrintODTResultTable.
+///
+const char *OptResultDbgStrings[] = {
+ "Best",
+ "GrdBnd",
+ "OffSel",
+ "Scale",
+ "Signal",
+ "Noise",
+ "Ratio",
+ "MaxPost",
+ "MinPost",
+ "Ticks",
+ "SNRTot."
+};
+
+#endif // MRC_DEBUG_PRINT
+
+/**
+ This function implements Sense Amp Offset training.
+ SenseAmp/ODT offset cancellation
+ Find the best "average" point for Vref Control
+ Test Vref point with SampOffset=-7 and Test Vref Point with SampOffset=+7
+ Find Vref on per ch/byte basis where -7 samples all 1 and +7 samples all 0
+
+ @param[in,out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeded return mrcSuccess
+**/
+MrcStatus
+MrcSenseAmpOffsetTraining (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U32 Offset;
+ S8 sumBits[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S8 FirstBestPoint[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ S8 LastBestPoint[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ //
+ // additional bit for DQS per each byte
+ //
+ S8 firstZero[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS_FOR_OFFSET_TRAINING];
+ //
+ // additional bit for DQS per each byte
+ //
+ S8 lastOne[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS_FOR_OFFSET_TRAINING];
+ S8 sampOffset;
+ S8 vref;
+ S8 VrefWidth;
+ U8 HighMask[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U8 LowMask[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+ U8 Channel;
+ U8 Rank;
+ U8 byte;
+ U8 bit;
+ U8 MaxBits;
+ BOOL Lpddr;
+ DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2;
+ DDRDATA0CH0_CR_DATATRAINFEEDBACK_STRUCT DataTrainFeedback;
+ DDRDATA_CR_RXOFFSETVDQ_STRUCT RxOffsetVdq;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Status = mrcSuccess;
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ //
+ // Init LastBestPoint to 0, FirstBestPoint to -8, LowMask to 0xff and HighMask to 0
+ //
+ MrcOemMemorySet ((U8 *) LastBestPoint, 0, sizeof (LastBestPoint));
+ MrcOemMemorySet ((U8 *) FirstBestPoint, (U32) (-8), sizeof (FirstBestPoint));
+ MrcOemMemorySet ((U8 *) LowMask, (U32) (-1), sizeof (LowMask));
+ MrcOemMemorySet ((U8 *) HighMask, 0, sizeof (LowMask));
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Stage 1: Vref Offset Training\nPlot Of SumOfBits across Vref settings\nChannel\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"
+ );
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ //
+ // Force RXAmp and Bias on -MUST use Per byte as preious DqControl2 values depended on byte number
+ //
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ DdrCrDataControl2.Data = ChannelOut->DqControl2[byte].Data;
+ DdrCrDataControl2.Bits.ForceBiasOn = 1;
+ DdrCrDataControl2.Bits.ForceRxOn = 1;
+ 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);
+ }
+ }
+ }
+ //
+ // Sweep through vref settings and find point SampOffset of +/- 7 passes
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n1/2 Vref");
+ for (vref = -8; vref <= 8; vref++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n% 5d\t", vref);
+
+ //
+ // Run Test and Record Results.
+ //
+ Status = ChangeMargin (MrcData, RdV, vref, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileRank);
+
+ //
+ // Program settings for Vref and SampOffset = 7
+ //
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_RXOFFSETVDQ_REG, 0xFFFFFFFF); // (8+7)
+ //
+ // To run test, enable Offset Cancel mode and Enable ODT
+ // Check Results and Update variables. Ideal result is all 0
+ // Clear Offset Cancel mode at end test to enable writing RX_OffsetV
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Propagate delay values (without a read command)
+ //
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ DdrCrDataControl0.Bits.ReadRFRd = 1;
+ DdrCrDataControl0.Bits.ReadRFWr = 0;
+ DdrCrDataControl0.Bits.ReadRFRank = 0;
+ DdrCrDataControl0.Bits.ForceOdtOn = 1;
+ DdrCrDataControl0.Bits.SenseampTrainingMode = 1;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+
+ MrcWait (MrcData, HPET_1US);
+
+ 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 = (U8) MrcReadCR (MrcData, Offset);
+ sumBits[Channel][byte] = -(MrcCountBitsEqOne (DataTrainFeedback.Bits.DataTrainFeedback));
+ LowMask[Channel][byte] &= (U8) DataTrainFeedback.Bits.DataTrainFeedback;
+ }
+
+ DdrCrDataControl0.Bits.SenseampTrainingMode = 0;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ }
+ }
+ //
+ // Program settings for SampOffset = -7
+ //
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_RXOFFSETVDQ_REG, 0x11111111); // (8-7)
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcChannelExist (Outputs, Channel))) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ (Channel != 0) ? "" : ((Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? " " : " ")
+ );
+ } else {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Propagate delay values (without a read command)
+ //
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ DdrCrDataControl0.Bits.ReadRFRd = 1;
+ DdrCrDataControl0.Bits.ReadRFWr = 0;
+ DdrCrDataControl0.Bits.ReadRFRank = 0;
+ DdrCrDataControl0.Bits.ForceOdtOn = 1;
+ DdrCrDataControl0.Bits.SenseampTrainingMode = 1;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+
+ MrcWait (MrcData, HPET_1US);
+ 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 = (U8) MrcReadCR (MrcData, Offset);
+ sumBits[Channel][byte] += MrcCountBitsEqOne (DataTrainFeedback.Bits.DataTrainFeedback);
+ HighMask[Channel][byte] |= DataTrainFeedback.Bits.DataTrainFeedback;
+
+ //
+ // Check if this point is better
+ //
+ if (sumBits[Channel][byte] > FirstBestPoint[Channel][byte]) {
+ FirstBestPoint[Channel][byte] = sumBits[Channel][byte];
+ LastBestPoint[Channel][byte] = vref;
+ ChannelOut->RxVref[byte] = vref;
+ } else if (sumBits[Channel][byte] == FirstBestPoint[Channel][byte]) {
+ LastBestPoint[Channel][byte] = vref;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d ", sumBits[Channel][byte]);
+ }
+
+ DdrCrDataControl0.Bits.SenseampTrainingMode = 0;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ }
+ }
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "\nHi-Lo\t");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "0x%x 0x%x ", HighMask[Channel][byte], LowMask[Channel][byte]);
+ //
+ // Exit with error if any bit did not change
+ //
+ if ((HighMask[Channel][byte] ^ LowMask[Channel][byte]) != 0xFF) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "\nERROR! At least one bit with unexpected results for Channel %u Byte %u\n",
+ Channel,
+ byte
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "A '0' in the following BitMask value represents the failing Bit(s) 0x%x\n",
+ (HighMask[Channel][byte] ^ LowMask[Channel][byte])
+ );
+ return mrcSenseAmpErr;
+ }
+ }
+ }
+ }
+
+ //
+ // Display the selected Read Vref per byte
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRdVref\t");
+#endif // MRC_DEBUG_PRINT
+ //
+ // Clear RdV offset
+ //
+ Status = ChangeMargin (MrcData, RdV, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileRank);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ //
+ // Upate RxVref delay center
+ //
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ VrefWidth = (S8) (LastBestPoint[Channel][byte] - ChannelOut->RxVref[byte]);
+ vref = (S8) (ChannelOut->RxVref[byte] + (VrefWidth / 2));
+
+ //
+ // Add 1 to Round Up and find the center
+ //
+ if (vref < 0) {
+ vref--;
+ } else {
+ vref++;
+ }
+ //
+ // step size for RxVref is about 7.8mv AND for RxVrefOffset is about 3.9mv
+ //
+ ChannelOut->RxVref[byte] = vref / 2;
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ UpdateRxT (MrcData, Channel, Rank, byte, 0xFF, 0);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d ", (S8) ChannelOut->RxVref[byte]);
+ }
+ }
+ }
+ //
+ // init firstZero and lastOne to 0
+ //
+ MrcOemMemorySet ((U8 *) firstZero, 0, sizeof (firstZero));
+ MrcOemMemorySet ((U8 *) lastOne, 0, sizeof (lastOne));
+
+ MaxBits = MAX_BITS_FOR_OFFSET_TRAINING - 1;
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ MaxBits++; // for ULT offset training done for 8 bits + DQS bit
+ }
+#endif //ULT_FLAG
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\nStage 2: SampOffset Training\n");
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel 0\t\t\t\t\t\t\t\t\t %s1\nByte ", Lpddr ? "\t" : "");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d %s", byte, Lpddr ? " " : "");
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nBits ");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "01234567%s ", Lpddr ? "S" : "");
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n SAmp");
+#endif // MRC_DEBUG_PRINT
+
+ for (sampOffset = 1; sampOffset <= 15; sampOffset++) {
+ //
+ // Display per Byte Feedback from REUT Registers
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n% 5d\t", sampOffset);
+
+ //
+ // Program Offset and Propagate new value from RF
+ //
+ RxOffsetVdq.Data = 0;
+ for (bit = 0; bit < MAX_BITS; bit++) {
+ RxOffsetVdq.Data += (sampOffset & DDRDATA_CR_RXOFFSETVDQ_Lane0_MSK) << (DDRDATA_CR_RXOFFSETVDQ_Lane0_WID * bit);
+ }
+
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_RXOFFSETVDQ_REG, RxOffsetVdq.Data);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t%s", Lpddr ? "\t" : "");
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ //
+ // write DQS offset to control2 reg sampOffset
+ //
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ DdrCrDataControl2.Data = ChannelOut->DqControl2[byte].Data;
+
+ DdrCrDataControl2.Bits.RxDqsAmpOffset = sampOffset;
+ DdrCrDataControl2.Bits.ForceBiasOn = 1;
+ DdrCrDataControl2.Bits.ForceRxOn = 1;
+ DdrCrDataControl2.Bits.LeakerComp = 0;
+
+ 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);
+ }
+ }
+#endif //ULT_FLAG
+ //
+ // Propagate delay values (without a read command)
+ //
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ DdrCrDataControl0.Bits.ReadRFRd = 1;
+ DdrCrDataControl0.Bits.ReadRFWr = 0;
+ DdrCrDataControl0.Bits.ReadRFRank = 0;
+ DdrCrDataControl0.Bits.ForceOdtOn = 1;
+ DdrCrDataControl0.Bits.SenseampTrainingMode = 1;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+
+ MrcWait (MrcData, HPET_1US);
+
+ 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);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "DataTrainFeedback = 0x%x, sampOffset = %d\n", DataTrainFeedback.Data, sampOffset);
+
+ for (bit = 0; bit < MaxBits; bit++) {
+ if (DataTrainFeedback.Bits.DataTrainFeedback & (MRC_BIT0 << bit)) {
+ lastOne[Channel][byte][bit] = sampOffset;
+ } else {
+ if (firstZero[Channel][byte][bit] == 0) {
+ firstZero[Channel][byte][bit] = sampOffset;
+ }
+ }
+ //
+ // Display in bits
+ //
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ ((MRC_BIT0 << bit) & DataTrainFeedback.Bits.DataTrainFeedback) ? "1" : "0"
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ }
+
+ DdrCrDataControl0.Bits.SenseampTrainingMode = 0;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ } // for Channel
+ } // for sampOffset
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nBitSAmp ");
+
+ //
+ // Calculate and Program Offsets and display per bit SenseAmp Offset
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ RxOffsetVdq.Data = 0;
+ for (bit = 0; bit < MaxBits; bit++) {
+ //
+ // Find vref center, add 1 for Round Up
+ //
+ vref = (firstZero[Channel][byte][bit] + lastOne[Channel][byte][bit]) / 2;
+
+ //
+ // Check for saturation conditions
+ // to make sure we are as close as possible to vdd/2 (750mv)
+ //
+ if (firstZero[Channel][byte][bit] == 0) {
+ vref = 15;
+ }
+
+ if (lastOne[Channel][byte][bit] == 0) {
+ vref = 0;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%X", vref);
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ if (bit == 8) {
+ //
+ // Write the DQS sense amp offset value to the host struct.
+ // It will be written to the HW at the end of this routine.
+ //
+ // Add 8 to the center value, to better suppress DQS reflections before the read preamble.
+ //
+ ChannelOut->DqControl2[byte].Bits.RxDqsAmpOffset = MIN (vref + 8, DDRDATA_CR_DDRCRDATACONTROL2_RxDqsAmpOffset_MAX);
+ break;
+ }
+ }
+#endif // ULT_FLAG
+
+ RxOffsetVdq.Data += (vref & DDRDATA0CH0_CR_RXOFFSETVDQ_Lane0_MSK) << (DDRDATA0CH0_CR_RXOFFSETVDQ_Lane0_WID * bit);
+ ChannelOut->RxDqVrefPb[0][byte][bit].Center = vref;
+ }
+
+ Offset = DDRDATA0CH0_CR_RXOFFSETVDQ_REG +
+ ((DDRDATA0CH1_CR_RXOFFSETVDQ_REG - DDRDATA0CH0_CR_RXOFFSETVDQ_REG) * Channel) +
+ ((DDRDATA1CH0_CR_RXOFFSETVDQ_REG - DDRDATA0CH0_CR_RXOFFSETVDQ_REG) * byte);
+ MrcWriteCR (MrcData, Offset, RxOffsetVdq.Data);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+
+ //
+ // Propagate delay values (without a read command)
+ //
+ MrcDownloadRegFile (MrcData, Channel, 1, 0, MrcRegFileRank, 0, 1, 0);
+ }
+ }
+#ifdef MRC_DEBUG_PRINT
+ else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t%s", Lpddr ? "\t" : ""); // Line up Channel 1
+ }
+#endif
+ }
+ //
+ // Clean up after test
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // Restore DataControl2
+ //
+ 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);
+ }
+ //
+ // Restore DataControl0
+ //
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, ChannelOut->DqControl0.Data);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ Status = IoReset (MrcData);
+
+ return Status;
+}
+
+/**
+ This function looks at the margin values stored in the global data structure and checks
+ WrT, WrV, RdT, and RdV to see if they are above the minimum margin required.
+
+ @param[in, out] MrcData - MRC global data.
+
+ @retval mrcSuccess if margins are acceptable.
+ @retval Otherwise, mrcRetrain.
+**/
+MrcStatus
+MrcRetrainMarginCheck (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ MrcDebug const *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcUpmPwrRetrainLimits *UpmPwrRetrainLimits;
+ MRC_MarginTypes MarginParam;
+ MrcMarginResult LastResultParam;
+ MrcStatus Status;
+ MRC_MARGIN_LIMIT_TYPE MarginLimitType;
+ U32 (*LastMargins)[MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES];
+ U32 BERStats[4];
+ U16 MinEdgeMargin[MAX_EDGES];
+ U16 RetrainMarginLimit;
+ U16 CurrentMargin;
+ U8 Channel;
+ U8 ChannelMask;
+ U8 Rank;
+ U8 RankMask;
+ U8 Edge;
+ U8 Loopcount;
+ U8 MaxMargin;
+ BOOL RdWrMarginFail[2];
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ LastMargins = Outputs->MarginResult;
+ UpmPwrRetrainLimits = Outputs->UpmPwrRetrainLimits.Pointer;
+ Status = mrcSuccess;
+ Loopcount = 17;
+ MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats));
+ RdWrMarginFail[0] = FALSE;
+ RdWrMarginFail[1] = FALSE;
+
+
+ //
+ // Loop is dependent on the order of MRC_MarginTypes. If this changes, pleas ensure functionality
+ // stays the same.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Loopcount: %d\n", Loopcount);
+ SetupIOTestBasicVA (MrcData, Outputs->ValidChBitMask, Loopcount, NSOE, 0, 0, 8);
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = 1 << Rank;
+ ChannelMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelMask |= SelectReutRanks (MrcData, Channel, RankMask, 0);
+ if ((1 << Channel) & ChannelMask) {
+ MrcOemMemorySetDword (&ControllerOut->Channel[Channel].DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+ }
+
+ if (ChannelMask == 0) {
+ continue;
+ }
+
+ for (MarginParam = RdT; MarginParam <= WrV; MarginParam++) {
+ if (MarginParam == WrDqsT) {
+ continue;
+ }
+
+ if (MarginParam == RdT) {
+ Outputs->DQPat = RdRdTA;
+ } else if (MarginParam == RdV) {
+ Outputs->DQPat = BasicVA;
+ }
+
+ MaxMargin = ((MarginParam == RdV) || (MarginParam == WrV)) ? MAX_POSSIBLE_VREF : MAX_POSSIBLE_TIME;
+
+ Status = MrcGetBERMarginCh (
+ MrcData,
+ LastMargins,
+ ChannelMask,
+ 0xFF,
+ Rank,
+ MarginParam,
+ 0,
+ 1,
+ MaxMargin,
+ 0,
+ BERStats
+ );
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Margins\nParams: RdT\tWrT\tRdV\tWrV\n\tLft Rgt Lft Rgt Low Hi Low Hi");
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nC%dR%d\t", Channel, Rank);
+ for (MarginParam = RdT; MarginParam <= WrV; MarginParam++) {
+ if (MarginParam == WrDqsT) {
+ continue;
+ }
+
+ LastResultParam = GetMarginResultType (MarginParam);
+ RetrainMarginLimit = UpmPwrLimitValue (MrcData, MarginParam, RetrainLimit) / 10;
+ MrcOemMemorySetWord (MinEdgeMargin, (U16) (~0), MAX_EDGES);
+
+
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ CurrentMargin = (U16) LastMargins[LastResultParam][Rank][Channel][0][Edge] / 10;
+ MinEdgeMargin[Edge] = MIN (MinEdgeMargin[Edge], CurrentMargin);
+ if ((CurrentMargin <= RetrainMarginLimit)) {
+ Status = mrcRetrain;
+ if ((MarginParam == RdT) || (MarginParam == RdV)) {
+ RdWrMarginFail[0] = TRUE;
+ } else if ((MarginParam == WrT) || (MarginParam == WrV)) {
+ RdWrMarginFail[1] = TRUE;
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%2d %2d\t", MinEdgeMargin[0], MinEdgeMargin[1]);
+ if ((RdWrMarginFail[0] == TRUE) && (RdWrMarginFail[1] == TRUE)) {
+ Rank = MAX_RANK_IN_CHANNEL;
+ Channel = MAX_CHANNEL;
+ break;
+ }
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // End of table
+
+ if (Status == mrcRetrain) {
+ //
+ // Loop is dependent on the order of MRC_MarginTypes. If this changes, please ensure functionality
+ // stays the same.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "*** Margin Limit Check Failed! ***\nNew Limits:\nParam\tUPM\tPWR");
+ for (MarginParam = RdT; MarginParam <= WrV; MarginParam++) {
+ if (((RdWrMarginFail[0] == FALSE) && ((MarginParam == RdT) || (MarginParam == RdV))) ||
+ ((RdWrMarginFail[1] == FALSE) && ((MarginParam == WrT) || (MarginParam == WrV))) ||
+ (MarginParam == WrDqsT)) {
+ continue;
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n%s", MarginTypesString[MarginParam]);
+ for (MarginLimitType = UpmLimit; MarginLimitType < RetrainLimit; MarginLimitType++) {
+ RetrainMarginLimit = MrcUpdateUpmPwrLimits (MrcData, MarginParam, MarginLimitType, MRC_UPM_PWR_INC_VAL);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t%d", RetrainMarginLimit);
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // End of table.
+ }
+
+ return Status;
+}
+
+
+/**
+ This function implements DIMM ODT training.
+ Adjust DIMM RTT_NOM/RTT_WR value to maximize read/write voltage/timing
+
+ RdOdtPriority Needs to be an input parameter
+ option to prioritize the ReadODT setting and attempt to optimize that value first,
+ reducing CPU TDP power (as opposed to system power for the DRAM).
+ For this case, the base value for ReadODT is changed at the compensation block
+ by looking at the following values:
+ RdOdt Global: (50, 64, 84, 110)
+
+ In the case of 2 dpc, the flow will first optimizing RttNom, while keeping RttWr fixed
+ at 60 Ohms (60 Ohms usually gives the best results). It will then try to reduce RttWr
+ to 120 Ohms if possible.
+
+ In the case of 1 dpc, only RttNom is used and only a single pass is required.
+ However, it is important to note that the two channels are completely independent
+ and can have different numbers of dimms populated.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeed return mrcSuccess
+**/
+MrcStatus
+MrcDimmODTTraining (
+ IN MrcParameters *const MrcData
+ )
+{
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U8 Channel;
+ U8 byte;
+ U32 NumBytes;
+ U8 NumCh;
+ U8 RankMask;
+ U8 LocalRanks[MAX_CHANNEL];
+ U8 ChMask;
+ U8 RttNomPoints;
+ U8 RdOdtPoints;
+ S8 GRdOdt;
+ U8 RttWr0;
+ U8 Dimm;
+ U8 RttWr[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL];
+ U8 RttNom[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL];
+ S8 RttNom0;
+ S8 RttNom1;
+ U8 BestRttNom[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL];
+ U8 BestRttWr[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL];
+ S8 BestRdOdt[MAX_CHANNEL];
+ U8 offset;
+ U8 test;
+ U8 *TestList;
+ U8 TestListSize;
+ S32 RdOdtCodes[2]; // Store Comp Codes associated with each RdOdt
+ S8 RttNom1Off;
+ BOOL any2DPC;
+ BOOL any1DPC;
+ BOOL Lpddr;
+ S8 BestGRdOdt;
+ U8 RttOffset;
+ U8 OffsetPoints;
+ U8 loopcount; // for centering
+ S8 GRdOdtStep;
+ BOOL IncEnds;
+ BOOL SubPwrLimits;
+ BOOL skipSubOpt;
+ BOOL skipOptPrint;
+ BOOL ReCenterPoints;
+ U8 TestListTradRd[] = { OptRxBias };
+ U8 TestListWr[] = { OptWrDS, OptTxEq };
+ U8 TestListRdWr[] = { OptRxBias, OptWrDS, OptTxEq };
+ U8 ScaleTest[] = { 1, 1, 1, 1, 1 }; // must specify scale=0 to unpopulated slots !!
+ U8 ScaleTest1DPC[] = { 1, 1, 1, 0, 0 }; // must specify scale=0 to unpopulated slots !!
+ U8 *Scale;
+ U16 PwrLimits[] = { 3000, 3000, 0, 0, 0 };
+ S16 Best;
+ DimmOptPoint DimmOptPoints[MaxOptOff];
+ U16 Points2calc[5][MaxOptOff];
+ U8 PWRTrendSlope2D;
+ U8 NumTests;
+ U8 ArrayLength;
+ U8 localChMask;
+ U8 OdtTrainingDimmMask;
+ S32 OdtOff;
+ BOOL ForceCenter;
+ S8 GRdOdtOff;
+ U32 BestGRdOdtCode;
+ S8 Average;
+ OptResultsPerByte BestOff;
+#ifdef ULT_FLAG
+ U8 TestListUltRd[] = { OptRxBias, OptRxEq };
+ BOOL UltCpu;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Status = mrcSuccess;
+ loopcount = 15;
+ GRdOdtStep = 16;
+ IncEnds = 1;
+ SubPwrLimits = 0;
+ skipSubOpt = 0;
+ skipOptPrint = 1;
+ ReCenterPoints = 0;
+ NumTests = 5;
+ OdtTrainingDimmMask = 0;
+ PWRTrendSlope2D = 65;
+ NumBytes = Outputs->SdramCount;
+ ArrayLength = sizeof (Points2calc) / sizeof (U16) / NumTests;
+#ifdef ULT_FLAG
+ UltCpu = (Inputs->CpuModel == cmHSW_ULT);
+#endif
+
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ MrcOemMemorySet ((U8 *) &BestOff, 0, sizeof (BestOff));
+ MrcOemMemorySet ((U8 *) DimmOptPoints, 0, sizeof (DimmOptPoints));
+ MrcOemMemorySet ((U8 *) Points2calc, 0, sizeof (Points2calc));
+ MrcOemMemorySet ((U8 *) LocalRanks, 0, sizeof (LocalRanks));
+ MrcOemMemorySet ((U8 *) RdOdtCodes, 0, sizeof (RdOdtCodes));
+
+ TestList = TestListRdWr;
+ TestListSize = sizeof (TestListRdWr);
+ Scale = ScaleTest;
+
+ //
+ // GOdt : [150,110, 84, 64, 50]
+ // 1 dpc: Search [Off, 120, 60]
+ // 2 dpc: Search [120, 60, 40]
+ // Dimm0/1 = [40/40, 40/30, 30/40, 30/30, 30/20, 20/30, 20/20]
+ //
+ ChMask = 0x3;
+ RankMask = 0xf;
+
+ //
+ // Possible RttNom values to pick
+ //
+#ifdef ULT_FLAG
+ if (UltCpu) {
+ RttOffset = 0; //In ULT no Rtt nom by definition
+ } else
+#endif
+ {
+ RttOffset = 1;
+ }
+ RttNomPoints = 2; //[120, 60]
+ RdOdtPoints = 2; //[150,110]
+ ChMask &= Outputs->ValidChBitMask;
+ RankMask &= Outputs->ValidRankMask;
+
+ any2DPC = FALSE;
+ any1DPC = FALSE;
+ //
+ // set channel and rank population
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChMask)) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ //
+ // Setup Dimm Masks for CalcPowerTrend so we don't access a Dimm that isn't present.
+ //
+ if (ChannelOut->DimmCount == 2) {
+ any2DPC |= 1;
+ OdtTrainingDimmMask |= 0x3 << (DIMM_ODT_DIMM_MASK_SHIFT * Channel);
+ } else {
+ any1DPC |= 1;
+ OdtTrainingDimmMask |= 0x1 << (DIMM_ODT_DIMM_MASK_SHIFT * Channel);
+ }
+
+ LocalRanks[Channel] = RankMask & ChannelOut->ValidRankBitMask;
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ //
+ // start with 60 Ohm by default
+ //
+#ifdef ULT_FLAG
+ if (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3) {
+ RttWr[Channel][Dimm] = 0;
+ } else
+#endif
+ {
+ RttWr[Channel][Dimm] = 2;
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "apply 2DPC optimization to ChMask %d\n", ChMask);
+
+ //
+ // *** Read flow ***
+ // if both ch 1DPC RttNomPoints=1 and RttNom1Off=0 i.e. only GRdOdt loop
+ //
+ if (any2DPC == 0) {
+ //
+ // if no 2DPC ch
+ //
+ RttNomPoints = 1;
+#ifdef ULT_FLAG
+ if (UltCpu && Inputs->TrainingEnables.RDEQT) {
+ TestList = TestListUltRd;
+ TestListSize = sizeof (TestListUltRd);
+ } else
+#endif
+ {
+ TestList = TestListTradRd;
+ TestListSize = sizeof (TestListTradRd);
+ }
+ Scale = ScaleTest1DPC;
+ }
+
+ OffsetPoints = 0;
+ //
+ // Walk Through RttNOM Settings - going from negative to positive
+ //
+ for (GRdOdt = 0; GRdOdt < RdOdtPoints; GRdOdt++) {
+ GRdOdtOff = -16 + GRdOdt * GRdOdtStep;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CalcRdOdt = %d, GRdOdt = %d GRdOdtOff=%d\n",CalcRdOdt(GRdOdt),GRdOdt,GRdOdtOff);
+ //
+ for (RttNom0 = RttOffset; RttNom0 < (RttNomPoints + RttOffset); RttNom0++) {
+ //
+ // Dimm0 RttNom Value
+ //
+ for (RttNom1Off = -1; RttNom1Off < 2; RttNom1Off++) {
+ //
+ // Dimm1 RttNom Value
+ // Calculate RttNom1 and check if out of range
+ //
+ RttNom1 = RttNom0 + RttNom1Off;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "RttNom0 = %d, RttNom1 = %d, RttNom1Off = %d\n",RttNom0,RttNom1,RttNom1Off);
+ //
+ if ((RttNom1 == (RttNomPoints + RttOffset)) || (RttNom1 < RttOffset)) {
+ continue;
+ }
+ //
+ // if RttNom == 120ohm run recenter timing
+ //
+ if (((RttNom0 == 1) || (RttNom1 == 1)) && (any2DPC)) {
+ ForceCenter = 1;
+ } else {
+ ForceCenter = 0;
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ //
+ // set default opt params offset
+ //
+ if (!((MRC_BIT0 << Channel) & ChMask)) {
+ continue; // check if the ch exist
+ }
+
+ RttNom[Channel][0] = RttNom0;
+ RttNom[Channel][1] = RttNom1;
+ for (byte = 0; byte < NumBytes; byte++) {
+ UpdateOptParamOffset (MrcData, Channel, 0, byte, OptWrDS, 0, 1);
+ UpdateOptParamOffset (
+ MrcData,
+ Channel,
+ LocalRanks[Channel],
+ byte,
+ OptTxEq,
+ (S16) (3 * (TXEQFULLDRV >> 4) + 3),
+ 1
+ );
+ }
+ }
+ //
+ // Apply new settings and optimize various parameters
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TrainDimmOdtSetting: GRdOdt = %d, RdOdtOff = %d, RttNom0= %d, RttNom1= %d \n",GRdOdt,RdOdtOffsets[GRdOdt], RttNom0, RttNom1);
+ //
+ if (OffsetPoints < MaxOptOff) {
+ TrainDimmOdtSetting (
+ MrcData,
+ &DimmOptPoints[OffsetPoints],
+ ChMask,
+ RankMask,
+ 0,
+ RttNom,
+ RttWr,
+ GRdOdtOff,
+ TestList,
+ TestListSize,
+ SubPwrLimits,
+ skipSubOpt,
+ skipOptPrint,
+ ReCenterPoints | ForceCenter,
+ ReCenterPoints,
+ Points2calc,
+ ArrayLength,
+ OffsetPoints
+ );
+ OffsetPoints++;
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Error: DimmOptPoints array out of bounds! %d > %d\n",
+ OffsetPoints,
+ MaxOptOff - 1
+ );
+ }
+ }
+ }
+ }
+ //
+ // for each channel apply Power Trend and find best point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ BestRdOdt[Channel] = 0;
+ if (!((MRC_BIT0 << Channel) & ChMask)) {
+ continue; // check if the ch exist in this ch
+ }
+
+ for (offset = 0; offset < OffsetPoints; offset++) {
+ //
+ // copy point for the FindOptTradeOff routing
+ //
+ for (test = 0; test < (DimmOptPoints->NumTests); test++) {
+ Points2calc[test][offset] = DimmOptPoints[offset].Points2Trade[test][Channel];
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "read channel=%d Points2calc[test=%d][offset=%d]=%d\n",Channel,test,offset,Points2calc[test][offset]);
+ //
+ }
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "FindOptimalTradeOff read\n");
+ // LenMargin,TestList,TestListSize,noPwrCalc
+ //
+ CalcPowerTrend (
+ MrcData,
+ Channel,
+ (OdtTrainingDimmMask >> (DIMM_ODT_DIMM_MASK_SHIFT * Channel)),
+ DimmOptPoints,
+ Points2calc,
+ MaxOptOff,
+ OffsetPoints,
+ DimmOptPoints->TestList,
+ Scale,
+ DimmOptPoints->NumTests,
+ 0,
+ PWRTrendSlope2D
+ );
+ //
+ // senSq=0,AveN=1,caleM=1,powerOpHigh=0
+ //
+ FindOptimalTradeOff (
+ MrcData,
+ &BestOff,
+ Points2calc,
+ ArrayLength,
+ OffsetPoints,
+ Scale,
+ 0,
+ 1,
+ IncEnds,
+ 1,
+ PwrLimits,
+ 0,
+ 0 // GuardBand
+ );
+ Best = BestOff.Best + BestOff.GuardBand;
+ UpdateOdtsValues (
+ MrcData,
+ MRC_BIT0 << Channel,
+ &DimmOptPoints[Best],
+ 0,
+ 0,
+ skipSubOpt,
+ 1
+ );
+ //
+ // MrcWait (MrcData, 10 * HPET_1MS);
+ // MrcResetSequence (MrcData);
+ //
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ BestRttNom[Channel][Dimm] = DimmOptPoints[Best].ODTSet.RttNom[Channel][Dimm];
+ }
+
+ BestRdOdt[Channel] = DimmOptPoints[Best].ODTSet.GRdOdt;
+ RdOdtCodes[Channel] = DimmOptPoints[Best].ODTSet.GRdOdtCode;
+
+#ifdef MRC_DEBUG_PRINT
+ //
+ // printing the results
+ //
+ PrintODTResultTable (
+ MrcData,
+ &BestOff,
+ DimmOptPoints,
+ OffsetPoints,
+ 0,
+ 1,
+ OptDimmOdt,
+ Channel,
+ LocalRanks[Channel],
+ 1,
+ 0,
+ 1
+ );
+ //
+ // PrintODTResultTable(*MrcData,calcResultSummary,*TestList,NumTest,NumOffsets,MidPoint,IncEnds,OptParam,Channel,Ranks,TrendLine,Nibble,perCh);
+ //
+#endif // MRC_DEBUG_PRINT
+ } // end ch loop
+ //
+ // Find Best "Average" value for Global RdOdt Offset
+ //
+ NumCh = 0;
+ Average = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChMask)) {
+ continue;
+ }
+
+ NumCh += 1;
+ Average += BestRdOdt[Channel];
+ //
+ // RdOdtChOffset[Channel] = RdOdtCodes[BestRdOdt[Channel]]; //set comp code associated comp offset per ch
+ //
+ }
+
+ BestGRdOdt = (NumCh != 0) ? (Average / NumCh) : Average;
+ //
+ // update average rdOdt offset (both ch)
+ //
+ BestGRdOdtCode = UpdateCompGlobalOffset (MrcData, RdOdt, (U8) BestGRdOdt, 1);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Calculated Average (both ch) GRdOdt is %d\n", BestGRdOdt);
+
+ if (NumCh > 1) {
+ //
+ // adjust RdOdt to per channel/byte
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChMask)) {
+ continue;
+ }
+
+ RdOdtCodes[Channel] -= BestGRdOdtCode;
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ //
+ // Apply Best RdOdt in case we didnt run Godt.
+ //
+ OdtOff = RdOdtCodes[Channel] + ((Outputs->Controller[0].Channel[Channel].DataCompOffset[byte] >> 12) & 0x1f);
+ UpdateOptParamOffset (MrcData, Channel, 0xF, byte, OptRdOdt, (S16) OdtOff, 1);
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Voltage\n");
+ Status = ReadVoltageCentering2D (
+ MrcData,
+ Outputs->MarginResult,
+ ChMask,
+ RdV,
+ 0,
+ 0,
+ loopcount,
+ 0
+ );
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Timing\n");
+ Status = DataTimeCentering2D (
+ MrcData,
+ Outputs->MarginResult, // prev. margin results
+ ChMask,
+ RdT,
+ 0, // EnPerBit,
+ 0, // EnRxDutyCycle
+ 0, // ResetPerBit
+ loopcount,
+ 0 // En2D
+ );
+
+ if (!Lpddr) {
+ //
+ // DIMM ODT is disabled by default on LPDDR, so skip this section
+ //
+
+ //
+ // *** Write flow ***//
+ //
+ RttOffset = (Inputs->MaxRttWr < 0x2) ? (Inputs->MaxRttWr) : (0x1); // Get user input for MaxRttWr, 0 = off, 1 = 120 ohms
+ if (any2DPC) {
+ //
+ // At least one 2DPC ch
+ //
+ if (!any1DPC) {
+ RttOffset = 1; // Start with 120ohm
+ }
+ }
+
+ TestList = TestListWr;
+ TestListSize = sizeof (TestListWr);
+ Scale = ScaleTest1DPC;
+
+ //
+ // option 1: Keep RttWr the same for both DIMMs (ie: train per Ch for both RttWr & WrDrv)
+ // option 2: Allow different RttWr for each DIMM and break WrDrv out of this optimization (ie: do it later).
+ //
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ //
+ // check if dimm exist in any channel
+ //
+ OdtTrainingDimmMask = (0x3 << (Dimm * 2));
+ if (!(Outputs->ValidRankMask & OdtTrainingDimmMask)) {
+ continue;
+ }
+
+ localChMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (OdtTrainingDimmMask & LocalRanks[Channel]) {
+ localChMask |= MRC_BIT0 << Channel; // can run 1 or 2 ch
+ }
+ }
+
+ localChMask &= ChMask;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "write odt train localChMask=%x\n", localChMask);
+
+ OffsetPoints = 0;
+ for (RttWr0 = RttOffset; RttWr0 < 3; RttWr0++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ RttWr[Channel][Dimm] = RttWr0; // start with 60 Ohm by default
+ if (!((MRC_BIT0 << Channel) & localChMask)) {
+ continue; // skip if the ch doesn't exist
+ }
+ }
+
+ if ((RttWr0 == 0) && (any1DPC)) {
+ ForceCenter = 1;
+ } else {
+ ForceCenter = 0;
+ }
+
+ TrainDimmOdtSetting (
+ MrcData,
+ &DimmOptPoints[OffsetPoints],
+ localChMask,
+ OdtTrainingDimmMask,
+ 1,
+ BestRttNom,
+ RttWr,
+ BestGRdOdt,
+ TestList,
+ TestListSize,
+ SubPwrLimits,
+ skipSubOpt,
+ skipOptPrint,
+ ReCenterPoints,
+ ReCenterPoints | ForceCenter,
+ Points2calc,
+ ArrayLength,
+ OffsetPoints
+ );
+ OffsetPoints++;
+ }
+ //
+ // for each channel apply Power Trend and find best point
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & localChMask)) {
+ continue; // check if the ch exist
+ }
+
+ for (offset = 0; offset < OffsetPoints; offset++) {
+ //
+ // copy point for the FindOptTradeOff routing
+ //
+ for (test = 0; test < (DimmOptPoints->NumTests); test++) {
+ Points2calc[test][offset] = DimmOptPoints[offset].Points2Trade[test][Channel];
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Write channel=%d Points2calc[test=%d][offset=%d]=%d\n",Channel,test,offset,Points2calc[test][offset]);
+ //
+ }
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "FindOptimalTradeOff write\n");
+ // LenMargin,TestList,TestListSize,noPwrCalc
+ //
+ CalcPowerTrend (
+ MrcData,
+ Channel,
+ MRC_BIT0 << Dimm,
+ DimmOptPoints,
+ Points2calc,
+ MaxOptOff,
+ OffsetPoints,
+ DimmOptPoints->TestList,
+ Scale,
+ DimmOptPoints->NumTests,
+ 0,
+ PWRTrendSlope2D
+ );
+ //
+ // senSq=0,AveN=1,caleM=1,powerOpHigh=0
+ //
+ FindOptimalTradeOff (
+ MrcData,
+ &BestOff,
+ Points2calc,
+ ArrayLength,
+ OffsetPoints,
+ Scale,
+ 0,
+ 1,
+ IncEnds,
+ 1,
+ PwrLimits,
+ 0,
+ 0 // GuardBand
+ );
+ Best = BestOff.Best + BestOff.GuardBand;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "===============> BestOff=%d\n",BestOff.Best);
+ // skipGRdOdt=1 SkipDimmOdts=0, SkipBestOffsets,updateHost
+ //
+ UpdateOdtsValues (MrcData, MRC_BIT0 << Channel, &DimmOptPoints[Best], 1, 0, skipSubOpt, 1);
+ RttWr[Channel][Dimm] = DimmOptPoints[Best].ODTSet.RttWr[Channel][Dimm];
+ BestRttWr[Channel][Dimm] = DimmOptPoints[Best].ODTSet.RttWr[Channel][Dimm]; // delete?
+ #ifdef MRC_DEBUG_PRINT // printing the results
+ PrintODTResultTable (
+ MrcData,
+ &BestOff,
+ DimmOptPoints,
+ OffsetPoints,
+ 0,
+ 1,
+ OptDimmOdtWr,
+ Channel,
+ OdtTrainingDimmMask,
+ 1,
+ 0,
+ 1
+ );
+ //
+ // PrintODTResultTable(*MrcData,calcResultSummary,*TestList,NumTest,NumOffsets,MidPoint,IncEnds,OptParam,Channel,Ranks,TrendLine,Nibble,perCh);
+ //
+ #endif // MRC_DEBUG_PRINT
+ } // end of channel loop
+ } // end dimm loop
+ //
+ //set equal TxEq for all bytes before DS
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & Outputs->ValidChBitMask)) {
+ continue; // skip if the ch doesn't exist
+ }
+
+ for (byte = 0; byte < NumBytes; byte++) {
+ UpdateOptParamOffset (
+ MrcData,
+ Channel,
+ LocalRanks[Channel],
+ byte,
+ OptTxEq,
+ (S16) (3 * (TXEQFULLDRV >> 4) + 3),
+ 1
+ );
+ }
+ }
+ } // if (!Lpddr)
+
+ //
+ // run WriteDS
+ //
+ Status = TrainWriteDriveStrength (MrcData, Outputs->ValidChBitMask, 0, OPT_PARAM_1D_LC, 0);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Vref\n");
+ Status = MrcWriteVoltageCentering2D (MrcData);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Timing\n");
+ Status = DataTimeCentering2D (
+ MrcData,
+ Outputs->MarginResult, // prev. margin results
+ Outputs->ValidChBitMask,
+ WrT,
+ 0, // EnPerBit,
+ 0, // EnRxDutyCycle
+ 0, // ResetPerBit
+ loopcount,
+ 0 // En2D
+ );
+
+ return Status;
+}
+
+/**
+ This function implements Read Equalization training.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeeds return mrcSuccess
+**/
+MrcStatus
+MrcReadEQTraining (
+ IN MrcParameters *const MrcData
+ )
+{
+#ifdef ULT_FLAG
+ U8 RankMask;
+ U8 TestList[] = { RdV, RdT };
+ U8 Scale[] = { 1, 2, 1, 0, 0 };
+ U16 PwrLimits[] = { 1280, 1280, 0, 0, 0 };
+ U16 GlobalPwrLimit;
+ OptOffsetChByte BestOff;
+
+ if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) {
+ GlobalPwrLimit = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit);
+ PwrLimits[0] = MAX (PwrLimits[0], GlobalPwrLimit);
+ GlobalPwrLimit = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit);
+ PwrLimits[1] = MAX (PwrLimits[1], GlobalPwrLimit);
+
+ //
+ // Function Call for RxEQ Training
+ //
+ for (RankMask = 1; RankMask < (MRC_BIT0 << MAX_RANK_IN_CHANNEL); RankMask <<= 1) {
+ if (RankMask & MrcData->SysOut.Outputs.ValidRankMask) {
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ 0x3,
+ RankMask,
+ OptRxEq,
+ TestList,
+ sizeof (TestList),
+ Scale,
+ PwrLimits,
+ 0, // Start
+ 7, // Stop
+ OPT_PARAM_1D_LC,
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOptUpdate
+ 0, // RdRd2Test
+ 0 // GuardBand
+ );
+ }
+ }
+ }
+#endif
+
+ return mrcSuccess;
+}
+
+/**
+ This function implements Write (Transmitter) Equalization training.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeeds return mrcSuccess
+**/
+MrcStatus
+MrcWriteEQTraining (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcStatus Status;
+ U8 Rank;
+ U8 RankMask;
+ U8 TestList[] = { WrV, WrT };
+ U8 Scale[] = { 1, 2, 1, 0, 0 }; // must specify scale=0 to unpopulate slots !!
+ U16 PwrLimits[5];
+ OptOffsetChByte BestOff;
+
+ MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0]));
+ PwrLimits[0] = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit);
+ PwrLimits[1] = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit);
+
+ //
+ // Function Call for RxEQ Training
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = MRC_BIT0 << Rank;
+ if (RankMask & MrcData->SysOut.Outputs.ValidRankMask) {
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ 0x3,
+ RankMask,
+ OptTxEq,
+ TestList,
+ sizeof (TestList),
+ Scale,
+ PwrLimits,
+ 0, // Start
+ 11, // Stop
+ OPT_PARAM_1D_LC,
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOdtUpdate
+ 0, // RdRd2Test
+ 2 // GuardBand
+ );
+ }
+ }
+
+ DataTimeCentering2D (
+ MrcData,
+ MrcData->SysOut.Outputs.MarginResult, // prev. margin results
+ 0x3,
+ WrT,
+ 0, // EnPerBit,
+ 0, // EnRxDutyCycle
+ 0, // ResetPerBit
+ OPT_PARAM_1D_LC,
+ 0 // En2D
+ );
+
+ Status = mrcSuccess;
+ return Status;
+}
+
+/**
+ This function implements Read Amplifier Power training.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - If it succeeds return mrcSuccess
+**/
+MrcStatus
+MrcReadAmplifierPower (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcStatus Status;
+ MrcOutput *Outputs;
+ const MrcDebug *Debug;
+ U8 TestList[] = { RdV, RdT };
+ U8 Scale[] = { 1, 2, 1, 0, 0 }; // must specify scale=0 to unpopulate slots !!
+ U16 PwrLimits[5];
+ OptOffsetChByte BestOff;
+ BOOL RdCenter;
+ U8 RecenterLC;
+
+ RdCenter = 1;
+ RecenterLC = 15;
+ Status = mrcSuccess;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0]));
+ PwrLimits[0] = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit);
+ PwrLimits[1] = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit);
+
+ //
+ // Function Call for RxBias
+ //
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ 0x3,
+ 0xF,
+ OptRxBias,
+ TestList,
+ sizeof (TestList),
+ Scale,
+ PwrLimits,
+ 0, // Start
+ 7, // Stop
+ OPT_PARAM_1D_LC,
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOdtUpdate
+ 0, // RdRd2Test
+ 0 // GuardBand
+ );
+
+ if (RdCenter) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Timing\n");
+ Status = DataTimeCentering2D (
+ MrcData,
+ Outputs->MarginResult, // prev. margin results
+ 0x3,
+ RdT,
+ 0, // EnPerBit,
+ 0, // EnRxDutyCycle
+ 0, // ResetPerBit
+ RecenterLC,
+ 0 // En2D
+ );
+ }
+
+ return Status;
+}
+
+/**
+ This function implements Dimm Ron training.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - if it succeeds return mrcSuccess
+**/
+MrcStatus
+MrcDimmRonTraining (
+ IN MrcParameters *const MrcData
+ )
+{
+#ifdef ULT_FLAG
+ OptOffsetChByte BestOff;
+ U8 TestList[] = { RdV, RdT };
+ U8 Scale[] = { 1, 2, 1, 0, 0 }; // must specify scale=0 to unpopulate slots !!
+ U16 PwrLimits[5];
+
+ MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0]));
+ PwrLimits[0] = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit);
+ PwrLimits[1] = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit);
+
+ //
+ // Check if LPDDR3 memory is used
+ //
+ if (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3) {
+ //
+ // Function Call for RxBias
+ //
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ 0x3, // Channels
+ 0xF, // Ranks
+ OptDimmRon,
+ TestList,
+ sizeof (TestList),
+ Scale,
+ PwrLimits,
+ 0, // Start
+ 2, // Stop
+ OPT_PARAM_1D_LC, // Loopcount
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOdtUpdate
+ 0, // RdRd2Test
+ 0 // GuardBand
+ );
+ }
+#endif // ULT_FLAG
+
+ return mrcSuccess;
+
+}
+
+/**
+ This function implements Read ODT training and Write DS.
+ Optimize Read ODT strength for performance & power.
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in,out] BestOff - Structure containg the best offest and margins for th Opt param.
+ @param[in] ChannelMask - Channels to train
+ @param[in] RankMask - Condenses down the results from multiple ranks
+ @param[in] OptParam - Defines the OptParam Offsets.
+ Supported OptParam = [0: WrDS, 1: RdODT, 2: SComp, 3: TComp, 3: TxEq, 4: RxEq,
+ 5: RxBias, 6: DimmOdt, 7: DimmOdtWr]
+ @param[in] TestList - List of margin params that will be tested (up to 4)
+ @param[in] NumTests - The length of TestList
+ @param[in] Scale - List of the relative importance between the 4 tests
+ @param[in] PwrLimitsABC - List of the values for each test margin, above which margin is "adequate"
+ @param[in] Start - Start point of sweeping the Comp values
+ @param[in] Stop - Stop point of sweeping the Comp values
+ @param[in] LoopCount - The number of loops to run in IO tests.
+ @param[in] Repeats - Number of times to repeat the test to average out any noise
+ @param[in] NoPrint - Switch to disable printing.
+ @param[in] SkipOptUpdate - Switch to train but not update Opt settings.
+ @param[in] RdRd2Test - Switch to run with different TA times: possible values are [0, RdRdTA, RdRdTA_All]
+ @param[in] GuardBand - Signed offset to apply to the Opt param best value.
+
+ @retval Nothing
+**/
+void
+TrainDDROptParam (
+ IN OUT MrcParameters *const MrcData,
+ IN OUT OptOffsetChByte *BestOff,
+ IN U8 ChannelMask,
+ IN U8 RankMask,
+ IN U8 OptParam,
+ IN U8 *TestList,
+ IN U8 NumTests,
+ IN U8 *Scale,
+ IN U16 *PwrLimitsABC,
+ IN S8 Start,
+ IN S8 Stop,
+ IN U8 LoopCount,
+ IN U8 Repeats,
+ IN BOOL NoPrint,
+ IN BOOL SkipOptUpdate,
+ IN U8 RdRd2Test,
+ IN S8 GuardBand
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ MrcControllerOut *ControllerOut;
+ MrcStatus Status;
+ U32 (*MarginByte)[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES];
+ //
+ // TestParam X 24 Points X Ch X Byte X Hi/Lo
+ //
+ U32 BERStats[4];
+ U16 SaveMargin[4][24][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES];
+ U16 Test;
+ U16 MinEdge;
+ U16 Margins[5][24]; // TestParam X 24 Comp Points
+ U16 Mode;
+ S16 Best;
+ U8 ResultType;
+ U8 AveN;
+ U8 ChBitMask;
+ U8 Channel;
+ U8 Byte;
+ U8 Rank;
+ U8 Edge;
+ U8 FirstRank;
+ U8 OdtValue;
+ U8 NumBytes;
+ U8 BMap[9]; // Need by GetBERMarginByte
+ U8 Param;
+ U8 MaxMargin;
+ U8 localR[MAX_CHANNEL];
+ U8 Rep;
+ void *NullPtr;
+ S8 CurrentComp;
+ S8 ReservedComp;
+ S8 MaxComp;
+ U16 OptPower[24];
+ U8 PWRTrendSlope1D;
+ S8 Delta;
+ S8 Index;
+ S8 Off;
+ S8 LenMargin;
+ S8 Shift;
+ U16 NoiseTicks;
+ BOOL NoSignal;
+ BOOL IncEnds;
+ BOOL IncEndsForPrint;
+ BOOL CPUComp;
+ BOOL printPerCh;
+ DDRCOMP_CR_DDRCRDATACOMP0_STRUCT DdrCrDataComp0;
+ DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1;
+ DDRSCRAM_CR_DDRMISCCONTROL0_STRUCT DdrMiscControl0;
+ DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0;
+ DDR3_MODE_REGISTER_1_STRUCT Ddr3ModeRegister1;
+ //
+ // result print summary: 5 columns per byte
+ //
+ OptResultsPerByte calcResultSummary[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+
+ MaxComp = 63;
+ PWRTrendSlope1D = 65;
+ ResultType = 0;
+ NullPtr = 0;
+ CurrentComp = 0;
+ IncEnds = 0;
+ IncEndsForPrint = 0;
+ printPerCh = 0;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ DdrCrCompCtl0.Data = Outputs->CompCtl0;
+ MarginByte = &Outputs->MarginResult;
+ ChannelMask &= Outputs->ValidChBitMask;
+ RankMask &= Outputs->ValidRankMask;
+ MrcOemMemorySet ((U8 *) calcResultSummary, 0, sizeof (calcResultSummary));
+ MrcOemMemorySet ((U8 *) BestOff, 0xffff, sizeof (OptOffsetChByte));
+ MrcOemMemorySet ((U8 *) Margins, 0, sizeof (Margins));
+ MrcOemMemorySet ((U8 *) OptPower, 0, sizeof (OptPower));
+ MrcOemMemorySet ((U8 *) localR, 0, sizeof (localR));
+ MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats));
+ for (Byte = 0; Byte < (sizeof (BMap) / sizeof (BMap[0])); Byte++) {
+ BMap[Byte] = Byte;
+ }
+ Outputs->EnDumRd = 0;
+
+ if (RdRd2Test == RdRdTA) {
+ LoopCount -= 1; // 2 TA tests, so cut the loop count in half
+ } else if (RdRd2Test == RdRdTA_All) {
+ LoopCount -= 3; // 8 TA tests, so divide the loop count by 8
+ }
+
+ SetupIOTestBasicVA (MrcData, ChannelMask, LoopCount, 0, 0, 0, 8); // set test to all channels
+
+ if (RdRd2Test != 0) {
+ Outputs->DQPat = RdRd2Test;
+ }
+ //
+ // Select All Ranks for REUT test
+ //
+ ChBitMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChannelMask)) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ localR[Channel] = ChannelOut->ValidRankBitMask & RankMask;
+ //
+ // use ChBitMask from here down - if ch is set that mean at least 1 rank for testing, also remove ch w/o active ranks
+ //
+ ChBitMask |= SelectReutRanks (MrcData, Channel, localR[Channel], 0);
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "reut ranks ChBitMask %x Local ranks=%x\n", ChBitMask,localR[Channel]);
+ // Clear any old state in DataTrain Offset
+ //
+ MrcOemMemorySet ((U8 *) &ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ }
+
+ if (ChBitMask == 0) {
+ return ;
+ }
+ //
+ // Find the first selected rank
+ //
+ FirstRank = 0;
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((MRC_BIT0 << Rank) & RankMask) {
+ FirstRank = Rank; // could be in any channel
+ break;
+ }
+ }
+ //
+ // Store margin results for
+ //
+ NumBytes = (U8) Outputs->SdramCount;
+
+ if ((OptParam == OptDimmOdt) || (OptParam == OptDimmOdtWr) || (OptParam == OptDimmRon)) {
+ NumBytes = 1;
+ }
+ //
+ // Calculate Start/Stop Point for Comp Optimization
+ //
+ CPUComp = ((OptParam == OptWrDS) || (OptParam == OptRdOdt) || (OptParam == OptTComp) || (OptParam == OptSComp));
+ if (CPUComp) {
+ DdrCrDataComp0.Data = DdrCrDataComp1.Data = 0;
+ if (OptParam == OptRdOdt) {
+ DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG);
+ } else {
+ DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG);
+ }
+
+ switch (OptParam) {
+ case OptWrDS:
+ CurrentComp = (S8) DdrCrDataComp0.Bits.RcompDrvUp;
+ break;
+
+ case OptRdOdt:
+ CurrentComp = (S8) DdrCrDataComp1.Bits.RcompOdtUp;
+ break;
+
+ case OptSComp:
+ CurrentComp = (S8) DdrCrDataComp0.Bits.SlewRateComp;
+ //
+ // For SCOMP we have a 5-bit register with the max value of 31.
+ // All other COMPs have 6-bit registers with the max value of 63
+ //
+ MaxComp = 31;
+ break;
+
+ case OptTComp:
+ CurrentComp = (S8) DdrCrDataComp0.Bits.TcoComp;
+ break;
+
+ default:
+ CurrentComp = 0;
+ break;
+ }
+
+ ReservedComp = 3; // Reserve 3 comp codes for adjustment range
+ Delta = CurrentComp - ReservedComp + Start;
+ if (Delta < 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "------------> warning offset range is clipped by %d\n", Delta);
+ Start -= Delta;
+ }
+
+ Delta = MaxComp - CurrentComp - ReservedComp - Stop;
+ if (Delta < 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "------------> warning offset range is clipped by %d\n", Delta);
+ Stop += Delta;
+ }
+
+ if (Stop < Start) {
+ Stop = Start;
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CurrentComp = %d, Start = %d, Stop = %d, Delta = %d\n", CurrentComp, Start, Stop, Delta);
+ //
+ }
+ //
+ // Loop through all Test Params and Measure Margin
+ //
+ for (Test = 0; Test < NumTests; Test++) {
+ Param = TestList[Test]; // tl[0]=4 tl[1]=1
+ ResultType = GetMarginResultType (Param); // rxv=0 rxt=1
+ //
+ // Assign to last pass margin results by reference
+ // get lowest margin from all ch/rankS/byte save in FirstRank
+ //
+ Status = GetMarginByte (MrcData, Outputs->MarginResult, Param, FirstRank, RankMask);
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n--- FirstRank = %d ResultType=%d Param=%d ranks=0x%x\n", FirstRank,ResultType,Param,RankMask);
+ // Calculate the MaxMargin for this test
+ //
+ MaxMargin = MAX_POSSIBLE_TIME;
+ if ((Param == RdV) ||
+ (Param == RdFan2) ||
+ (Param == RdFan3) ||
+ (Param == WrV) ||
+ (Param == WrFan2) ||
+ (Param == WrFan3)
+ ) {
+ MaxMargin = MAX_POSSIBLE_VREF;
+ }
+ //
+ // No need to search too far
+ //
+ if (MaxMargin > (PwrLimitsABC[Test] / 20)) {
+ MaxMargin = (U8) (PwrLimitsABC[Test] / 20);
+ }
+ //
+ // Loop Through all Comp Codes
+ //
+ for (Off = Start; Off < Stop + 1; Off++) {
+ Index = Off - Start;
+ //
+ // Apply Code
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChBitMask)) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ //
+ // For DIMM ODT 2dpc, Start sweeping with RttNom = 40 and RttWr fixed at 60
+ // For DIMM ODT 1dpc, Start sweeping with RttNom = Off and RttWr fixed at Off
+ //
+ if ((OptParam == OptDimmOdt) && (ChannelOut->DimmCount == 2)) {
+ Shift = 0x20;
+ } else {
+ Shift = 0;
+ }
+
+ for (Byte = 0; Byte < NumBytes; Byte++) {
+ if (!SkipOptUpdate) {
+ //
+ // change OpParam offset for all ch/byte/LocalR
+ //
+ UpdateOptParamOffset (MrcData, Channel, localR[Channel], Byte, OptParam, (S16) (Off + Shift), 0);
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n--Channel=%d, localR[Channel]=%x Byte=%d OffsetComp=%d Off=%d\n",Channel,localR[Channel],Byte,OffsetComp,Off);
+ //
+ } // some are limited in range inside e.g: RdOdt +15:-16
+ }
+
+ for (Rep = 0; Rep < Repeats; Rep++) {
+ //
+ // Run Margin Test - margin_1d with chosen param
+ // run on all ranks but change param only for firstRank??
+ //
+ Mode = 0;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " start MrcGetBERMarginByte \n");
+ //
+ Status = MrcGetBERMarginByte (
+ MrcData,
+ Outputs->MarginResult,
+ ChBitMask,
+ FirstRank,
+ FirstRank,
+ Param,
+ Mode,
+ BMap,
+ 1,
+ MaxMargin,
+ 0,
+ BERStats
+ );
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " finish MrcGetBERMarginByte \n");
+ // Record Results
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChBitMask)) {
+ continue;
+ }
+
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ MinEdge = 0xFFFF;
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if (Rep == 0) {
+ SaveMargin[Test][Index][Channel][Byte][Edge] = 0;
+ }
+
+ SaveMargin[Test][Index][Channel][Byte][Edge] +=
+ (U16) (*MarginByte)[ResultType][FirstRank][Channel][Byte][Edge];
+ if (MinEdge > SaveMargin[Test][Index][Channel][Byte][Edge]) {
+ MinEdge = SaveMargin[Test][Index][Channel][Byte][Edge];
+ }
+ }
+
+ if (NumBytes == 1) {
+ SaveMargin[Test][Index][Channel][0][Edge] = MinEdge; // Todo:change Byte->0
+ }
+ }
+ }
+ }
+ } // end of offset
+#ifdef MRC_DEBUG_PRINT
+ PrintResultTableByte4by24 (
+ MrcData,
+ ChBitMask,
+ SaveMargin,
+ Test,
+ Stop - Start + 1,
+ -Start,
+ 2,
+ OptParam,
+ TestList[Test],
+ PwrLimitsABC,
+ NoPrint
+ );
+#endif // MRC_DEBUG_PRINT
+ } // end of test list
+ //
+ // Calculate the best value for every byte
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n start calculate the the best margin \n");
+ //
+ LenMargin = (Stop - Start) + 1;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChBitMask)) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Byte = 0; Byte < NumBytes; Byte++) {
+ //
+ // Populate Margins array and asymetric penalty
+ //
+ for (Test = 0; Test < NumTests; Test++) {
+ for (Off = Start; Off < Stop + 1; Off++) {
+ Index = Off - Start; // 0:..
+ //
+ // for now just get EW
+ //
+ if ((TestList[Test] == RdV) ||
+ (TestList[Test] == WrV) ||
+ (TestList[Test] == WrFan3) ||
+ (TestList[Test] == RdFan3)
+ ) {
+ Margins[Test][Index] = EffectiveMargin (
+ Scale[Test] * SaveMargin[Test][Index][Channel][Byte][0],
+ Scale[Test] * SaveMargin[Test][Index][Channel][Byte][1]
+ );
+ } else {
+ Margins[Test][Index] = Scale[Test] *
+ (SaveMargin[Test][Index][Channel][Byte][0] + SaveMargin[Test][Index][Channel][Byte][1]);
+ }
+ }
+ }
+ //
+ // Special Cases for Running Average Filter
+ //
+ if ((OptParam == OptDimmOdt) || (OptParam == OptDimmOdtWr) || (OptParam == OptDimmRon)) {
+ AveN = 1;
+ } else if (OptParam == OptRxBias) {
+ AveN = 3;
+ } else if (OptParam == OptRxEq) {
+ //
+ // Use special, 2D running average for RxEq
+ //
+ AveN = 1;
+ RunningAverage2D (Margins, 0, Stop - Start + 1, 5, 2, 1);
+ RunningAverage2D (Margins, 1, Stop - Start + 1, 5, 2, 1); // try Cscale=1 first.
+ } else {
+ AveN = 7;
+ if (LenMargin < AveN) {
+ AveN = LenMargin - 1;
+ }
+ }
+ //
+ // Use one of the Margin Arrays for fine grain power tradeoffs. This is only used if Scale[NumTests] is not 0
+ //
+ for (Off = 0; Off < LenMargin; Off++) {
+ OptPower[Off] = (U16) CalcOptPower (MrcData, Channel, 0, Byte, OptParam, Off + Start, CurrentComp, 0);
+ Margins[NumTests][Off] = OptPower[Off];
+ if ((OptParam == OptDimmRon) || (OptParam == OptWrDS) || (OptParam == OptRdOdt)) {
+ //
+ // convert from Ohm to mW to pass for T-line calc : = (Vdd/2)^2/R ~ 562 / R
+ //
+ Margins[NumTests][Off] = 562 / Margins[NumTests][Off];
+ }
+
+ if ((OptParam == OptTxEq) && (ChannelOut->DimmCount == 1)) {
+ //
+ // find first rank: 0 or 2
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((MRC_BIT0 << Rank) & ChannelOut->ValidRankBitMask) {
+ FirstRank = Rank; // could be in any channel
+ break;
+ }
+ }
+
+ if (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) {
+ OdtValue = 0;
+ } else {
+ Ddr3ModeRegister1.Data = ChannelOut->Dimm[FirstRank / 2].Rank[FirstRank % 2].MR[mrMR1];
+ OdtValue = (U8)
+ (
+ (Ddr3ModeRegister1.Bits.OdtRttValueHigh << 2) |
+ (Ddr3ModeRegister1.Bits.OdtRttValueMid << 1) |
+ Ddr3ModeRegister1.Bits.OdtRttValueLow
+ );
+ }
+
+ if (OdtValue == 0) {
+ Margins[NumTests][Off] = 1;//no power consideration
+ }
+ }
+ }
+ //
+ // need to provide set of power numbers depending on the OffsetComp codes (per byte)for trend line .
+ //
+ CalcPowerTrend (
+ MrcData,
+ Channel,
+ localR[Channel],
+ NullPtr,
+ Margins,
+ 24,
+ LenMargin,
+ TestList,
+ Scale,
+ NumTests,
+ 1,
+ PWRTrendSlope1D
+ );
+ //
+ // Use that value to create Margin Results based on power.
+ // Creates a smooth, linear function that goes from MaxSum to N/(N-1)*MaxSum
+ // RatioNum = FinePwrRatio[OptParam] * LenMargin; //e.g FinePwrRatio[RdOdt]=5
+ // Find the Best Overall Setting
+ // senSq=0,caleM=1,powerOpHigh=0
+ //
+ FindOptimalTradeOff (
+ MrcData,
+ &calcResultSummary[Channel][Byte],
+ Margins,
+ 24,
+ LenMargin,
+ Scale,
+ 0,
+ AveN,
+ IncEnds,
+ 1,
+ PwrLimitsABC,
+ 0,
+ GuardBand
+ );
+ //
+ // Get the best index considering the GuardBand
+ //
+ Best = calcResultSummary[Channel][Byte].Best + calcResultSummary[Channel][Byte].GuardBand;
+ NoiseTicks = 3;
+ NoSignal = FALSE;
+ for (Test = 0; Test < NumTests; Test++) {
+ if ((calcResultSummary[Channel][Byte].Ticks[Test] / 10) > NoiseTicks) {
+ NoSignal = FALSE;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n NumTests = %d Best =%d ch=%d byte=%d calcResultSummary[Channel][Byte].Ticks[Test]=%d NoiseTicks=%d\n",NumTests,Best,Channel,Byte,calcResultSummary[Channel][Byte].Ticks[Test],NoiseTicks);
+ //
+ break;
+ }
+ }
+
+ if (NoSignal) {
+ Best = 0;//set to min
+ calcResultSummary[Channel][Byte].Best = 0;
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n Best =%d ch=%d byte=%d \n",Best,Channel,Byte);
+ // Update CR
+ //
+ if ((OptParam == OptDimmOdt) && (ChannelOut->DimmCount == 2)) {
+ Shift = 0x20;
+ } else {
+ Shift = 0;
+ }
+ //
+ // Best += (Shift - Start);
+ //
+ Best -= (Shift - Start); // update take offset
+ if (!SkipOptUpdate) {
+ UpdateOptParamOffset (MrcData, Channel, localR[Channel], Byte, OptParam, Best, 1);
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " localR[Channel]=%x Best =%d ch=%d byte=%d \n",localR[Channel],Best,Channel,Byte);
+ //
+ BestOff->Offset[Channel][Byte] = Best;
+ } // end byte
+ } // End of Calculating best value (ch)
+#ifdef MRC_DEBUG_PRINT
+ //
+ // printing the results
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((MrcChannelExist (Outputs, Channel))) {
+ if (!((MRC_BIT0 << Channel) & ChBitMask)) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ if (!(ChannelOut->ValidRankBitMask & localR[Channel])) {
+ continue;
+ }
+
+ IncEndsForPrint =
+ (
+ OptParam == OptDimmOdt ||
+ OptParam == OptDimmOdtWr ||
+ OptParam == OptDimmRon ||
+ OptParam == OptRxEq ||
+ IncEnds
+ );
+ printPerCh = (OptParam == OptDimmOdt || OptParam == OptDimmOdtWr || OptParam == OptDimmRon);
+ //
+ // lower bytes
+ //
+ PrintCalcResultTableCh (
+ MrcData,
+ calcResultSummary,
+ TestList,
+ NumTests,
+ Stop - Start + 1,
+ -Start,
+ IncEndsForPrint,
+ OptParam,
+ OptPower,
+ Channel,
+ localR[Channel],
+ Scale[NumTests],
+ 0,
+ printPerCh,
+ NoPrint
+ );
+ //
+ // higher bytes
+ //
+ if (!printPerCh) {
+ PrintCalcResultTableCh (
+ MrcData,
+ calcResultSummary,
+ TestList,
+ NumTests,
+ Stop - Start + 1,
+ -Start,
+ IncEndsForPrint,
+ OptParam,
+ OptPower,
+ Channel,
+ localR[Channel],
+ Scale[NumTests],
+ 1,
+ printPerCh,
+ NoPrint
+ );
+ }
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+ //
+ // Propgate new CR setting
+ //
+ // @todo: redundant :there is one inside updateComps
+ //
+ if (CPUComp) {
+ DdrMiscControl0.Data = Outputs->MiscControl0;
+ DdrMiscControl0.Bits.ForceCompUpdate = 1;
+ MrcWriteCR (MrcData, DDRSCRAM_CR_DDRMISCCONTROL0_REG, DdrMiscControl0.Data);
+ }
+ //
+ // Update the LastPass points in host
+ //
+ for (Test = 0; Test < NumTests; Test++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChBitMask)) {
+ continue;
+ }
+
+ ResultType = GetMarginResultType (TestList[Test]);
+ for (Byte = 0; Byte < NumBytes; Byte++) {
+ //
+ // save the margins in best offset point for each byte/ch in rank 0/1
+ //
+ (*MarginByte)[ResultType][0][Channel][Byte][0] =
+ SaveMargin[Test][BestOff->Offset[Channel][Byte] - Start][Channel][Byte][0];
+ (*MarginByte)[ResultType][0][Channel][Byte][1] =
+ SaveMargin[Test][BestOff->Offset[Channel][Byte] - Start][Channel][Byte][1];
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "best offset= %d ;byte=%d ;(*MarginByte)[ResultType][0][Channel][Byte][0] -%d (*MarginByte)[ResultType][0][Channel][Byte][1] -%d add=%d\n",BestOff->Offset[Channel][Byte],Byte,(U16) (*MarginByte)[ResultType][0][Channel][Byte][0] , (*MarginByte)[ResultType][0][Channel][Byte][1],((U16) (*MarginByte)[ResultType][0][Channel][Byte][0] + (U16)(*MarginByte)[ResultType][0][Channel][Byte][1]));
+ //
+ }
+ }
+
+ Status = ScaleMarginByte (MrcData, Outputs->MarginResult, TestList[Test], 0);
+ }
+
+ BestOff->NumTests = sizeof (TestList);
+ for (Test = 0; Test < NumTests; Test++) {
+ ResultType = GetMarginResultType (TestList[Test]);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChBitMask)) {
+ continue;
+ }
+ //
+ // track minimum eye width per ch
+ //
+ for (Byte = 0; Byte < NumBytes; Byte++) {
+ if (Byte == 0) {
+ BestOff->Margins[Test][Channel] = (U16) ((*MarginByte)[ResultType][0][Channel][0][0] +
+ (*MarginByte)[ResultType][0][Channel][0][1]);
+ } else if (BestOff->Margins[Test][Channel] >
+ ((*MarginByte)[ResultType][0][Channel][Byte][0] + (*MarginByte)[ResultType][0][Channel][Byte][1])
+ ) {
+ BestOff->Margins[Test][Channel] = (U16) ((*MarginByte)[ResultType][0][Channel][Byte][0] +
+ (*MarginByte)[ResultType][0][Channel][Byte][1]);
+ }
+ }
+
+ BestOff->TestList[Test][Channel] = TestList[Test];
+
+ //
+ // Normalize margins
+ // BestOff->Margins[Test][Channel] *= Scale[Test]; //Scale??
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "after scale - BestOff->Margins[ch=%d][%s]= %d \n",Channel,MarginTypesString[TestList[Test]],BestOff->Margins[Test][Channel]);
+ //
+ }
+ //
+ // if (BestOff->Margins[Test][Channel]<=20) {//set OptParam to max in case off no eye
+ // for (Byte = 0; Byte < NumBytes; Byte++) {
+ // if(OptParam == OptRxBias) UpdateOptParamOffset (MrcData, Channel, 0, Byte, OptRxBias, 15, 1);
+ // if(OptParam == OptWrDS) UpdateOptParamOffset (MrcData, Channel, 0, Byte, OptWrDS, 7, 1);
+ // }
+ // }
+ //
+ }
+ //
+ // Clean up
+ //
+ Outputs->EnDumRd = 0;
+
+ return;
+
+}
+
+/**
+ This function implements Read ODT training.
+ Optimize Read ODT strength for performance & power
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - If it succeded return mrcSuccess
+**/
+MrcStatus
+MrcReadODTTraining (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcStatus Status;
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcCpuModel CpuModel;
+ U8 *TestList;
+ U8 TestListDdr3[] = { RdV, RdT };
+ U8 TestListSize;
+ U8 ScaleDdr3[] = { 1, 2, 1, 0, 0 };
+ U8 *Scale;
+ U16 PwrLimits[5]; // Eye width/height
+ S8 Start;
+ S8 Stop;
+ S16 OffLimit;
+ S16 OffLimitDn;
+ U16 OdtLimit;
+ U8 OdtLimitDn;
+ U16 Rleg;
+ S8 StatLegs;
+ U8 OdtLegsDis;
+ S8 CurrentVref;
+ S8 CurrentComp;
+ OptOffsetChByte BestOff;
+ BOOL RdCenter;
+ U8 RecenterLC;
+ DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0;
+ DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1;
+ U8 RdTATestType;
+ U8 Index;
+#ifdef ULT_FLAG
+ U8 TestListLpddr[] = { RdV, RdT, RcvEnaX };
+ U8 ScaleLpddr[] = { 1, 2, 2, 1, 0 };
+ BOOL Lpddr;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ CpuModel = Inputs->CpuModel;
+ Status = mrcSuccess;
+ RdCenter = 1;
+ RecenterLC = 17;
+ Start = -16;
+ MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0]));
+
+ //
+ // find a start offset where we below 180ohm to protect against OS/US
+ //
+ DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG);
+ CurrentComp = (S8) DdrCrDataComp1.Bits.RcompOdtUp;
+ OdtLimitDn = 30; //ohm
+ TestList = TestListDdr3;
+ TestListSize = sizeof (TestListDdr3);
+ Scale = ScaleDdr3;
+ RdTATestType = RdRdTA;
+
+#ifdef ULT_FLAG
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+ if (CpuModel == cmHSW_ULT) {
+ OdtLimit = 230;
+ if (Lpddr) {
+ RdTATestType = RdRdTA_All;
+ OdtLimitDn = 80; //ohm
+ TestList = TestListLpddr;
+ Scale = ScaleLpddr;
+ TestListSize = sizeof (TestListLpddr);
+ }
+ } else
+#endif //ULT_FLAG
+ {
+ OdtLimit = 180;
+ }
+
+ for (Index = 0; Index < TestListSize; Index++) {
+ PwrLimits[Index] = UpmPwrLimitValue (MrcData, TestList[Index], PowerLimit);
+ }
+
+ DdrCrCompCtl0.Data = Outputs->CompCtl0;
+ OdtLegsDis = (U8) DdrCrCompCtl0.Bits.DisableOdtStatic;
+ CurrentVref = (S8) DdrCrCompCtl0.Bits.DqOdtVref;
+ StatLegs = 4 * 4; // we enable only 1/3 segment for odt
+ if (CurrentVref & 0x10) {
+ CurrentVref -= 0x20; // 2's complement
+ }
+
+ Rleg = CalcRdOdt (MrcData, CurrentVref) * (StatLegs * (!OdtLegsDis) + CurrentComp);
+ OffLimit = (Rleg / OdtLimit) - StatLegs * (!OdtLegsDis) - CurrentComp;
+
+ //
+ // Find max ODT offset
+ //
+ OffLimitDn = (Rleg / OdtLimitDn) - StatLegs * (!OdtLegsDis) - CurrentComp;
+ Stop = (U8) OffLimitDn;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ " current code = %d OdtLegsDis = %d Rleg = %d CurrentVref = %d OffLimit = %d Start = %d stop = %d\n",
+ CurrentComp,
+ OdtLegsDis,
+ Rleg,
+ CurrentVref,
+ OffLimit,
+ Start,
+ Stop
+ );
+
+ if (OffLimit > Start) {
+ Start = (S8) OffLimit;
+ }
+
+ if (Stop > (23 + Start)) {
+ Stop = (S8) (23 + Start); // Only 24 offsets in the margin array.
+ }
+ if (Stop > 15) {
+ Stop = 15;
+ }
+
+ //
+ // Function Call for RdODT
+ //
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ 0x3,
+ 0xF,
+ OptRdOdt,
+ TestList,
+ TestListSize,
+ Scale,
+ PwrLimits,
+ Start,
+ Stop, // Stop
+ 17, // Loopcount increased from 15 to better match RMT margins
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOptUpdate
+ RdTATestType, // RdRd2Test
+ 0 // GuardBand
+ );
+
+ if (RdCenter) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Timing\n");
+ Status = DataTimeCentering2D (
+ MrcData,
+ Outputs->MarginResult, // prev. margin results
+ 0x3,
+ RdT,
+ 0, // EnPerBit,
+ 0, // EnRxDutyCycle
+ 0, // ResetPerBit
+ RecenterLC,
+ 0 // En2D
+ );
+ }
+
+ return Status;
+}
+
+/**
+ This function implements Dimm Odt training.
+ Optimize Dimm Odt value for performance/power
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - If it succeded return mrcSuccess
+**/
+MrcStatus
+MrcDimmODT1dTraining (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcStatus Status;
+ MrcOutput *Outputs;
+ U8 TestList[] = { RdV, RdT, WrV, WrT }; // ie 4,1
+ U8 TestListWr[] = { WrV, WrT }; // ie 4,1
+ U8 Scale[] = { 1, 2, 1, 2, 0 };
+ U8 ScaleWr[] = { 1, 2, 0, 0, 0 };
+ U16 PwrLimits[] = { 2480, 2240, 2480, 2240, 0 }; // just margin consideration
+ U8 dimm;
+ U8 Channel;
+ U8 ChannelMask;
+ OptOffsetChByte BestOff;
+
+ Outputs = &MrcData->SysOut.Outputs;
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((MrcChannelExist (Outputs, Channel))) {
+ ChannelMask = MRC_BIT0 << Channel;
+ if (Outputs->Controller[0].Channel[Channel].DimmCount == 2) {
+ //
+ // DimmODT Rtt Nom - 120,60,40,30
+ // run Rtt nom with the Rtt write 0x20=60 ohm
+ //
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ ChannelMask,
+ 0xF,
+ OptDimmOdt,
+ TestList,
+ sizeof (TestList),
+ Scale,
+ PwrLimits,
+ 1, // Start
+ 4, // Stop
+ OPT_PARAM_1D_LC,
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOdtUpdate
+ 0, // RdRd2Test
+ 0 // GuardBand
+ );
+ for (dimm = 0; dimm < 2; dimm++) {
+ //
+ // Function Call for DimmODT Write - 120,60
+ //
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ ChannelMask,
+ 0x3 << (dimm * 2),
+ OptDimmOdtWr,
+ TestListWr,
+ sizeof (TestListWr),
+ ScaleWr,
+ PwrLimits,
+ 1, // Start
+ 2, // Stop
+ OPT_PARAM_1D_LC,
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOdtUpdate
+ 0, // RdRd2Test
+ 0 // GuardBand
+ );
+ }
+ } else {
+ //
+ // 1DPC (only write) - off,120,60
+ //
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ ChannelMask,
+ 0xF,
+ OptDimmOdt,
+ TestListWr,
+ sizeof (TestListWr),
+ ScaleWr,
+ PwrLimits,
+ 0, // Start
+ 2, // Stop
+ OPT_PARAM_1D_LC,
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOdtUpdate
+ 0, // RdRd2Test
+ 0 // GuardBand
+ );
+ }
+ }
+ }
+
+ Status = mrcSuccess;
+
+ return Status;
+}
+
+/**
+ This function is the Write Drive Strength training entry point.
+ This step will optimize write drive strength for performance & power.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - If it succeeds return mrcSuccess
+**/
+MrcStatus
+MrcWriteDriveStrength (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcStatus Status;
+ U8 OptParamLC;
+ U8 RecenterLC;
+ BOOL Recenter;
+
+ Status = mrcSuccess;
+ OptParamLC = OPT_PARAM_LOOP_COUNT;
+ RecenterLC = OPT_PARAM_1D_LC;
+ Recenter = 1;
+
+ Status = TrainWriteDriveStrength (MrcData, 0x3, RecenterLC, OptParamLC, Recenter);
+
+ return Status;
+}
+
+/**
+ This function implements the Write Drive Strength optimization for performance and power.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] ChBitMask - Channel mask to perform training on the Opt Param test list.
+ @param[in] RecenterLC - The loopcount for Write Time recentering.
+ @param[in] OptParamLC - The loopcount for training the Opt Param test list.
+ @param[in] Recenter - Switch which determines if the step recenters Write Timing.
+
+ @retval If it succeeds return mrcSuccess
+**/
+MrcStatus
+TrainWriteDriveStrength (
+ IN MrcParameters *const MrcData,
+ IN const U8 ChBitMask,
+ IN const U8 RecenterLC,
+ IN const U8 OptParamLC,
+ IN const BOOL Recenter
+ )
+{
+ MrcStatus Status;
+ MrcOutput *Outputs;
+ const MrcDebug *Debug;
+ U8 TestList[] = { WrV, WrT };
+ U8 Scale[] = { 1, 2, 1, 0, 0 };
+ U16 PwrLimits[5];
+ OptOffsetChByte BestOff;
+
+ Status = mrcSuccess;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0]));
+ PwrLimits[0] = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit);
+ PwrLimits[1] = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit);
+
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ ChBitMask,
+ 0xf,
+ OptWrDS,
+ TestList,
+ sizeof (TestList),
+ Scale,
+ PwrLimits,
+ -13, // Start
+ 10, // Stop
+ OptParamLC,
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOdtUpdate
+ 0, // RdRd2Test
+ 1 // GuardBand
+ );
+
+ if (Recenter) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Timing\n");
+ Status = DataTimeCentering2D (
+ MrcData,
+ Outputs->MarginResult, // prev. margin results
+ 0x3,
+ WrT,
+ 0, // EnPerBit,
+ 0, // EnRxDutyCycle
+ 0, // ResetPerBit
+ RecenterLC,
+ 0 // En2D
+ );
+ }
+
+ return Status;
+}
+
+/**
+ This function implements Write Slew Rate training.
+ Optimize Write Slew Rate for performance & power
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval mrcSuccess
+**/
+MrcStatus
+MrcWriteSlewRate (
+ IN MrcParameters *const MrcData
+ )
+{
+#ifdef ULT_FLAG
+ U8 TestList[] = { WrV, WrT };
+ U8 Scale[] = { 1, 2, 0, 0, 0 };
+ U16 PwrLimits[] = { 2480, 2240, 0, 0, 0 }; // no power consideration
+ U16 GlobalPwrLimit;
+ OptOffsetChByte BestOff;
+
+ if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) {
+ GlobalPwrLimit = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit);
+ PwrLimits[0] = MAX (PwrLimits[0], GlobalPwrLimit);
+ GlobalPwrLimit = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit);
+ PwrLimits[1] = MAX (PwrLimits[1], GlobalPwrLimit);
+
+ TrainDDROptParam (
+ MrcData,
+ &BestOff,
+ 0x3,
+ 0xf,
+ OptSComp,
+ TestList,
+ sizeof (TestList),
+ Scale,
+ PwrLimits,
+ -15, // Start
+ 8, // Stop
+ OPT_PARAM_1D_LC,
+ 1, // Repeats
+ 0, // NoPrint
+ 0, // SkipOdtUpdate
+ 0, // RdRd2Test
+ 0 // GuardBand
+ );
+ }
+#endif
+
+ return mrcSuccess;
+}
+
+/**
+ Updates a given ch/Rank/byte combination with a new value for OptParam
+ OptParam can be: WrDS, RdOdt, TComp, SComp, RxEq, TxEq, RxBias or DimmOdt
+ OptParam == OptDefault restore values from Host except Dimms Odt's
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel index to work on.
+ @param[in] Ranks - Condenses down the results from multiple ranks
+ @param[in] Byte - Byte index to work on.
+ @param[in] OptParam - Defines the OptParam Offsets.
+ Supported OptParam = [0: WrDS, 1: RdODT, 2: SComp, 3: TComp, 4: TxEq,
+ 5: RxEq, 6: RxBias, 7: DimmOdt, 8: DimmOdtWr]
+ @param[in] Off - Offset
+ @param[in] UpdateHost - Desides if MrcData has to be updated
+
+ @retval Nothing
+**/
+void
+UpdateOptParamOffset (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 Ranks,
+ IN const U8 Byte,
+ IN const U8 OptParam,
+ IN S16 Off,
+ IN const U8 UpdateHost
+ )
+{
+ const U16 RttNomMRSEncodingConst[] = {0x00, 0x10, 0x01, 0x11, 0x81, 0x80}; // RttNom Off,120,60,40,30,20 Ohms
+ const U16 RttWrMRSEncodingConst[] = {0x00, 0x02, 0x01}; // RttWr RttNom,120,60 Ohms
+ const U16 RttDimmRonEncodingConst[] = {0x00, 0x02}; // Dimm Ron 240/6,240/7 Oms
+ const MrcDebug *Debug;
+#ifdef ULT_FLAG
+ const U8 LpddrRonEnc[] = {0x1,0x2,0x3}; //{34,40,48};
+ const U8 LpddrOdtEnc[] = {0x0,0x2,0x3}; //{0,120,240};
+ BOOL Lpddr;
+#endif // ULT_FLAG
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ U16 *MrReg;
+ MrcStatus Status;
+ BOOL Type;
+ U8 Rank;
+ U8 RankMask;
+ U8 Value;
+ U8 Index;
+ U16 MRValue;
+ U16 RttNomMRSEncoding[sizeof (RttNomMRSEncodingConst) / sizeof (RttNomMRSEncodingConst[0])];
+ U16 RttWrMRSEncoding[sizeof (RttWrMRSEncodingConst) / sizeof (RttWrMRSEncodingConst[0])];
+ U16 RttWr, RttNom, RttNomMask;
+ U16 DimmRon;
+ U16 RttWrMask;
+ U16 DimmRonMask;
+ U32 Offset;
+ S16 OffCode;
+ S16 OffMin;
+ S16 OffMax;
+ DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetComp;
+ DDRSCRAM_CR_DDRMISCCONTROL0_STRUCT DdrMiscControl0;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT DdrCrDataControl1;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ MrcOemMemoryCpy ((U8 *) RttNomMRSEncoding, (U8 *) RttNomMRSEncodingConst, sizeof (RttNomMRSEncoding));
+ MrcOemMemoryCpy ((U8 *) RttWrMRSEncoding, (U8 *) RttWrMRSEncodingConst, sizeof (RttWrMRSEncoding));
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+#endif // ULT_FLAG
+
+ //
+ // Compensation Offsets
+ //
+ Type =
+ (
+ (OptParam == OptWrDS) ||
+ (OptParam == OptRdOdt) ||
+ (OptParam == OptTComp) ||
+ (OptParam == OptSComp) ||
+ (OptParam == OptDefault)
+ );
+ if (Type) {
+ if (OptParam == OptWrDS) {
+ OffMin = -32;
+ OffMax = 31;
+ } else {
+ OffMin = -16;
+ OffMax = 15;
+ }
+
+ if (Off > OffMax) {
+ Off = OffMax;
+ } else if (Off < OffMin) {
+ Off = OffMin;
+ }
+
+ DdrCrDataOffsetComp.Data = ChannelOut->DataCompOffset[Byte];
+
+ if (OptParam == OptWrDS) {
+ DdrCrDataOffsetComp.Bits.DqDrvUpCompOffset = Off;
+ DdrCrDataOffsetComp.Bits.DqDrvDownCompOffset = Off;
+ } else if (OptParam == OptRdOdt) {
+ DdrCrDataOffsetComp.Bits.DqOdtUpCompOffset = Off;
+ DdrCrDataOffsetComp.Bits.DqOdtDownCompOffset = Off;
+ } else if (OptParam == OptTComp) {
+ DdrCrDataOffsetComp.Bits.DqTcoCompOffset = Off;
+ } else if (OptParam == OptSComp) {
+ DdrCrDataOffsetComp.Bits.DqSlewRateCompOffset = Off;
+ }
+
+ Offset = DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG +
+ ((DDRDATA1CH0_CR_DDRCRDATAOFFSETCOMP_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG) * Byte) +
+ ((DDRDATA0CH1_CR_DDRCRDATAOFFSETCOMP_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, DdrCrDataOffsetComp.Data);
+ if (UpdateHost) {
+ ChannelOut->DataCompOffset[Byte] = DdrCrDataOffsetComp.Data;
+ }
+ //
+ // Propagate new value and force comp update
+ //
+ DdrMiscControl0.Data = Outputs->MiscControl0;
+ DdrMiscControl0.Bits.ForceCompUpdate = 1;
+ MrcWriteCR (MrcData, DDRSCRAM_CR_DDRMISCCONTROL0_REG, DdrMiscControl0.Data);
+ }
+ //
+ // Equalization Settings
+ //
+ Type = ((OptParam == OptTxEq) || (OptParam == OptRxEq) || (OptParam == OptDefault));
+ if (Type) {
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && ((Ranks & (MRC_BIT0 << Rank)))) {
+ //
+ // TxEq[5:4] = Emphasize = [3, 6, 9, 12] legs
+ // TxEq[3:0] = Deemphasize = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 4*Rsvd] legs
+ //
+ if (OptParam == OptTxEq) {
+ if (Off > 11) {
+ Off = 11;
+ }
+
+ if (Off < 0) {
+ Off = 0;
+ }
+
+ OffCode = Off | TXEQFULLDRV; // Use 12 Emphasize legs (not trained)
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel=%d,Rank= %d update to %x \n",Channel,Rank,OffCode);
+ UpdateTxT (MrcData, Channel, Rank, Byte, 2, OffCode);
+ if (UpdateHost) {
+ ChannelOut->TxEq[Rank][Byte] = (S8) OffCode;
+ }
+ }
+ //
+ // RxEQ[4:0] CR Decoding (pF/kOhm)
+ // [2:0]
+ // [4:3] 0 1 2 3 4 5-7
+ // 0 0.5/.02 0.5/1.0 0.5/.50 0.5/.25 0.5/.12 rsvd
+ // 1 1.0/.02 1.0/1.0 1.0/.50 1.0/.25 1.0/.12 rsvd
+ // 2 1.5/.02 1.5/1.0 1.5/.50 1.5/.25 1.5/.12 rsvd
+ // 3 2.0/.02 2.0/1.0 2.0/.50 2.0/.25 2.0/.12 rsvd
+ // Sweep = 0-19 [4:3] = (Sweep/5) [2:0] = (Sweep%5)
+ //
+ if (OptParam == OptRxEq) {
+ if (Off > 19) {
+ Off = 19;
+ }
+
+ if (Off < 0) {
+ Off = 0;
+ }
+
+ Value = (U8) (((Off / 5) << 3) + (Off % 5));
+ UpdateRxT (MrcData, Channel, Rank, Byte, 2, Value);
+ if (UpdateHost) {
+ ChannelOut->RxEq[Rank][Byte] = Value;
+ }
+ }
+
+ if (OptParam == OptDefault) {
+ UpdateTxT (MrcData, Channel, Rank, Byte, 0xff, 0);
+ UpdateRxT (MrcData, Channel, Rank, Byte, 0xff, 0);
+ }
+ }
+ }
+ }
+ //
+ // RX Amplifier BIAS
+ //
+ if ((OptParam == OptRxBias) || (OptParam == OptDefault)) {
+ if (Off > 7) {
+ Off = 7;
+ }
+
+ if (Off < 0) {
+ Off = 0;
+ }
+ //
+ // Mapping: [0: 0.44, 1: 0.66, 2: 0.88, 3: 1.00, 4: 1.33, 5: 1.66, 6: 2.00, 7: 2.33]
+ //
+ DdrCrDataControl1.Data = ChannelOut->DqControl1[Byte].Data;
+ if (OptParam == OptRxBias) {
+ DdrCrDataControl1.Bits.RxBiasCtl = Off;
+ }
+
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte) +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, DdrCrDataControl1.Data);
+ if (UpdateHost) {
+ ChannelOut->DqControl1[Byte].Data = DdrCrDataControl1.Data;
+ }
+ }
+ //
+ // Update Dimm Ron value
+ //
+ if ((OptParam == OptDimmRon)) {
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ if (Lpddr) {
+ DimmRonMask = (U16)~(MRC_BIT3 | MRC_BIT2 | MRC_BIT1 | MRC_BIT0);
+ Index = (U8) Off;
+ Index = MIN (Index, sizeof (LpddrRonEnc) / sizeof (LpddrRonEnc[0]) - 1);
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = MRC_BIT0 << Rank;
+ if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && (Ranks & RankMask)) {
+ MrReg = &ChannelOut->Dimm[Rank / 2].Rank[(Rank % 2)].MR[mrMR0];
+ //
+ // Program Dimm Ron
+ //
+ DimmRon = LpddrRonEnc[Index];
+ MRValue = (MrReg[mrMR3] & DimmRonMask) | DimmRon;
+ Status = MrcIssueMrw (
+ MrcData,
+ Channel,
+ Rank,
+ mrMR3,
+ MRValue,
+ FALSE, // InitMrw
+ FALSE // ChipSelect2N
+ );
+ if (UpdateHost) {
+ MrReg[mrMR3] = MRValue;
+ }
+ }
+ }
+ } else
+#endif // ULT_FLAG
+ {
+ //
+ // DIMM Ron Encoding RttNom[A5,A1]
+ //
+ DimmRonMask = (U16)~(MRC_BIT5 | MRC_BIT1);
+ Index = (U8) Off;
+ Index = MIN (Index, 1);
+ //
+ // can be 0 or 1
+ //
+ DimmRon = RttDimmRonEncodingConst[Index];
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = MRC_BIT0 << Rank;
+ if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && (Ranks & RankMask)) {
+ MrReg = &ChannelOut->Dimm[Rank / 2].Rank[(Rank % 2)].MR[mrMR0];
+ //
+ // Program Dimm Ron
+ //
+ MRValue = (MrReg[mrMR1] & DimmRonMask) | DimmRon;
+ Status = MrcWriteMRS (MrcData, Channel, RankMask, mrMR1, MRValue);
+ if (UpdateHost) {
+ MrReg[mrMR1] = MRValue;
+ }
+ }
+ }
+ }
+ }
+ //
+ // DIMM ODT Values
+ //
+ if ((OptParam == OptDimmOdt) || (OptParam == OptDimmOdtWr)) {
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ //
+ // We have only Odt write
+ //
+ RttWrMask = (U16)~(MRC_BIT1 | MRC_BIT0);
+ Index = (U8) Off;
+ Index = MIN (Index, sizeof (LpddrOdtEnc) / sizeof (LpddrOdtEnc[0]) - 1);
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = MRC_BIT0 << Rank;
+ if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && (Ranks & RankMask)) {
+ MrReg = &ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR11;
+ MRValue = *MrReg;
+ //
+ // Program Dimm DS
+ //
+ RttWr = LpddrOdtEnc[Index];
+ MRValue = (MRValue & RttWrMask) | RttWr;
+ Status = MrcIssueMrw (
+ MrcData,
+ Channel,
+ Rank,
+ mrMR11,
+ MRValue,
+ FALSE, // InitMrw
+ FALSE // ChipSelect2N
+ );
+ if (UpdateHost) {
+ *MrReg = MRValue;
+ }
+ }
+ }
+
+ return;
+ }
+#endif
+ //
+ // DIMM ODT Encoding RttNom[A9,A6,A2] RttWr[A10, A9]
+ //
+ RttNomMask = (U16)~(MRC_BIT9 | MRC_BIT6 | MRC_BIT2);
+ RttWrMask = (U16)~(MRC_BIT10 | MRC_BIT9);
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ RankMask = MRC_BIT0 << Rank;
+ if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && (Ranks & RankMask)) {
+ MrReg = &ChannelOut->Dimm[Rank / 2].Rank[(Rank % 2)].MR[mrMR0];
+
+ //
+ // Program RTT WR
+ //
+ Index = (U8) ((OptParam == OptDimmOdt) ? (Off >> 4) : Off);
+ Index = MIN (Index, 2);
+ RttWr = RttWrMRSEncoding[Index] << 9;
+ MRValue = (MrReg[mrMR2] & RttWrMask) | RttWr;
+ Status = MrcWriteMRS (MrcData, Channel, RankMask, mrMR2, MRValue);
+ if (UpdateHost) {
+ MrReg[mrMR2] = MRValue;
+ }
+ //
+ // Program RTT NOM
+ //
+ if (OptParam == OptDimmOdtWr) {
+ continue;
+ }
+
+ Index = ((U8) Off & 0xF);
+ if (Index > 5) {
+ Index = 5;
+ }
+
+ RttNom = RttNomMRSEncoding[Index] << 2;
+ MRValue = (MrReg[mrMR1] & RttNomMask) | RttNom;
+ Status = MrcWriteMRS (MrcData, Channel, RankMask, mrMR1, MRValue);
+ if (UpdateHost) {
+ MrReg[mrMR1] = MRValue;
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/**
+ Slightly penalize any Asymmetry in margin
+
+ @param[in] NegEdge - Negative edge of the margin
+ @param[in] PosEdge - Positive edge of the margin
+
+ @retval p2p - Width/Height reduced by the asymmetric difference in margin.
+**/
+U16
+EffectiveMargin (
+ IN const U16 NegEdge,
+ IN const U16 PosEdge
+ )
+{
+ S16 p2p;
+ U16 p2pDiff;
+
+ p2p = 2 * (PosEdge + NegEdge);
+ p2pDiff = PosEdge - NegEdge;
+
+ if (PosEdge > NegEdge) {
+ p2p -= p2pDiff;
+ } else {
+ p2p += p2pDiff;
+ }
+
+ return p2p / 2;
+}
+
+/**
+ This function does a running average on Margins in two dimentional fashion.
+
+ @param[in,out] Margins - Margins to average
+ @param[in] Test - Selects the Margins to average
+ @param[in] MLen - Determines the Y-Dimension lengths
+ @param[in] XDim - Determines the X-Dimension lengths
+ @param[in] XMin - Used to skip the first elements in the Margin when averaging.
+ @param[in] CScale - Used to place more weight on the center point.
+
+ @retval Nothing
+**/
+void
+RunningAverage2D (
+ IN OUT U16 Margins[2][24],
+ IN const U8 Test,
+ IN const U8 MLen,
+ IN const U8 XDim,
+ IN const U8 XMin,
+ IN const U8 CScale
+)
+
+{
+ U8 XMax;
+ U8 YMax;
+ U16 TMargins[24];
+ U8 i;
+ U8 x;
+ U8 y;
+ U8 xo;
+ U8 yo;
+ U8 XOff;
+ S8 YOff;
+
+ XMax = XDim - 1;
+ YMax = ((MLen + XDim - 1) / XDim) - 1; // Ceiling to int in case the matrix is not fully populated
+
+ for (i = 0; i < MLen; i++) {
+ x = (i % XDim);
+ y = (i / XDim);
+
+ //
+ // Center Point
+ //
+ TMargins[i] = Margins[Test][i] * (CScale - 1); // Also add margin at the centerpoint below
+ //
+ // Sum up surrounding results
+ //
+ for (xo = 0; xo < 3; xo++) {
+ XOff = x + xo - 1;
+ //
+ // Avoid negative numbers on XOff
+ //
+ if ((x == 0) && (xo == 0)) {
+ XOff = 0;
+ }
+ //
+ // (x < XMin) allows averaging across points (1;0) and (2;0)
+ //
+ if ((XOff < XMin) && (x < XMin)) {
+ XOff = x; // RxEq special case. Skip averaging on Col0/Col1
+ }
+
+ if (XOff > XMax) {
+ XOff = XMax;
+ }
+
+ for (yo = 0; yo < 3; yo++) {
+ YOff = y + yo - 1;
+ if (YOff < 0) {
+ YOff = 0;
+ }
+
+ if (YOff > YMax) {
+ YOff = YMax;
+ }
+ //
+ // Avoid averaging with unpopulated matrix elements when dealing with partially populated matrices
+ //
+ if ((XDim * YOff + XOff) > (MLen - 1)) {
+ YOff = YOff - 1;
+ }
+
+ TMargins[i] += Margins[Test][XDim * YOff + XOff];
+ }
+ }
+ }
+ //
+ // Copy TempMargins back over to real margins
+ //
+ for (i = 0; i < MLen; i++) {
+ Margins[Test][i] = TMargins[i] / (8 + CScale); // Added div to maintain margin scaling
+ }
+
+ return;
+}
+
+/**
+ Updates a given ch/Rank/byte combination with a new value for OptParam
+ OptParam can be: WrDS, RdOdt, TComp, SComp, RxEq, TxEq, RxBias or DimmOdt
+
+ # Margins: Upto 4 arrays that contain lenMargin elements
+ # Index to the array represents some arbitrary parameter value that we are optimizing
+ # Scale is 4 element array that scales the relative importance on Margins[0] vs. [1] ...
+ # ex: To make Margins[0] twice as important, set Scale = [1, 2, 2, 2]
+ # Since the search optimizes the lowest margin, increasing 1/2/3 makes 0 more important
+ # This function can be used to optimize only Margin[0] by setting Scale = [1, 0, 0, 0]
+ # EnSq = 1 uses a squared function to make the tradeoff between 0/1/2/3 steeper
+ # If AveN > 0, pre-processes the results with a N point running average filter
+ # IncEnds: By setting to 1, the running average will also include the end points
+ # ScaleM: Allows the middle point of the running average to be scaled up
+ #
+ # In addition to optimizing for margin, this function can also optimize for power
+ # PwrLimit is a 4 element array that sets level where pwr is more important than margin
+ # Find any points where ((Margin[0]>PwrLimit[0]) & (Margin[1]>PwrLimit[1]) & ... )
+ # If such points exists and PwrOptHigh = 1, returns point with the highest X value
+ # If such points exists and PwrOptHigh = 0, returns point with the lowest X value
+ # If you don't want to optimize for power, set PwrLimitA and PwrLimitB to large number
+ # Power Optimize still uses the running average filter
+ #
+ # To avoid overflow, this function will automatic scale margins to fit in uint32
+
+ @param[in] MrcData - The global MRC data structure.
+ @param[in,out] OptResByte - Structure containing the optimized results.
+ @param[in] inputMargins - Margins we are optimizing
+ @param[in] MarginsLength - The length of inputMargins
+ @param[in] LenMargin - The length of inputMargins we are optimizing (0 - LenMargin -1).
+ @param[in] Scale - Controls the scaling of the input margin: 1-1, 1-2, ... and so on.
+ @param[in] EnSq - Enables the square root term in the optimization functions.
+ @param[in] AveN - The number of points used for the averaging filter.
+ @param[in] IncEnds - Controls if the endpoints are to be included.
+ @param[in] ScaleM - Controls the scaling of the middle point in 1-D average filter.
+ @param[in] PwrLimit - The power limit above which we only trade-off for power and not margin.
+ @param[in] PwrOptHigh - Controls returning the highest or lowest optimization point.
+ @param[in] GuardBand - Signed offest to check if margin drop is acceptable. Save good guardband
+ in OptResByte.
+
+ @retval Nothing.
+**/
+void
+FindOptimalTradeOff (
+ IN MrcParameters *const MrcData,
+ IN OUT OptResultsPerByte *OptResByte,
+ IN void *inputMargins,
+ IN U8 MarginsLength,
+ IN S8 LenMargin,
+ IN U8 *Scale,
+ IN U8 EnSq,
+ IN U8 AveN,
+ IN U8 IncEnds,
+ IN U8 ScaleM,
+ IN U16 *PwrLimit,
+ IN U8 PwrOptHigh,
+ IN S8 GuardBand
+ )
+
+{
+ const MrcDebug *Debug;
+ U8 NumArr; // Arrays to keep track of results
+ U32 PostMar[5][MaxOptOff]; // Margin array after scaling & averaging
+ U32 MaxPost[5]; // Variables for Results
+ U32 SMaxPost[5];
+ U32 MinPost[5];
+ U32 Signal[5];
+ U32 Noise[5];
+ U32 Ratio[5];
+ U16 PwrLimitPost[5];
+ U32 ScaleMin;
+ U8 Nby2;
+ U8 EqOrder;
+ U8 xArr;
+ U8 yArr;
+ U8 x;
+ U8 i;
+ U8 Off;
+ S8 xEff;
+ S32 n;
+ U8 NumBits;
+ U32 localY;
+ U8 Shift;
+ U8 Adder;
+ U8 Start;
+ U8 Stop;
+ U64 Result;
+ U64 rlocal;
+ U64 MaxR;
+ U64 MinR;
+ U64 SNRTotal;
+ U64 MarginLimit;
+ U8 BestX;
+ U8 PowerX;
+ U8 FoundPwrOpt;
+ U8 NumCalcArr;
+ S8 StepSize;
+ U8 MarginDropPercent;
+ U32 MinPost1;
+ BOOL GoodPower;
+ U16 *Margins;
+ OptResultsPerByte *calcResults;
+
+ MarginDropPercent = 10; // 10% loss of margin is a bad guardband offset.
+ NumArr = 5;
+ Result = 0;
+ rlocal = 0;
+ MaxR = 0;
+ MinR = 0;
+ SNRTotal = 0;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ MrcOemMemorySetDword (MaxPost, 1, sizeof (MaxPost) / sizeof (U32));
+ MrcOemMemorySetDword (SMaxPost, 1, sizeof (SMaxPost) / sizeof (U32));
+ MrcOemMemorySetDword (MinPost, 0xFFFFFFFF, sizeof (MinPost) / sizeof (U32));
+ MrcOemMemorySetDword (Signal, 0, sizeof (Signal) / sizeof (U32));
+ MrcOemMemorySetDword (Noise, 0, sizeof (Noise) / sizeof (U32));
+ MrcOemMemorySetDword (Ratio, 0, sizeof (Ratio) / sizeof (U32));
+ MrcOemMemorySetWord (PwrLimitPost, 0, sizeof (PwrLimitPost) / sizeof (U16));
+
+ //
+ // Initialize PostMar with zeroes
+ //
+ MrcOemMemorySet ((U8 *) PostMar, 0, sizeof (PostMar));
+
+ calcResults = OptResByte;
+ Margins = (U16 *) inputMargins;
+ MrcOemMemorySet ((U8 *) calcResults, 0, sizeof (OptResultsPerByte));
+ //
+ // Avoid division by zero.
+ //
+ if (AveN == 0) {
+ AveN = 1;
+ }
+ Nby2 = (AveN >> 1);
+ EqOrder = 0; // Is the optimization equation: X^1, X^2, X^5
+
+ //
+ // Process Raw Margins Results
+ //
+ for (xArr = 0; xArr < NumArr; xArr++) {
+ //
+ // Scale PwrLimit to match PostMar results
+ //
+ PwrLimitPost[xArr] = PwrLimit[xArr] * (AveN + ScaleM - 1) * Scale[xArr];
+
+ for (x = 0; x < LenMargin; x++) {
+ //
+ // Calculate the Running Average Filter
+ //
+ if (Scale[xArr] == 0) {
+ //
+ // not in the game
+ //
+ MinPost[xArr] = PostMar[xArr][x] = 1;
+ } else {
+ if (x == 0) {
+ //
+ // update EqOrder once for each xArr value with a non-zero scale factor e.g.:so for {RdT,RdV,0,0} it will be =2
+ //
+ EqOrder += 1;
+ }
+
+ for (Off = 0; Off < AveN; Off++) {
+ xEff = x + Off - Nby2;
+ if (xEff < 0) {
+ PostMar[xArr][x] += *(Margins + xArr * MarginsLength + 0); // Margins[xArr][0];
+ } else if (xEff >= LenMargin) {
+ PostMar[xArr][x] += *(Margins + xArr * MarginsLength + LenMargin - 1);
+ } else if (x == xEff) {
+ PostMar[xArr][x] += ScaleM * *(Margins + xArr * MarginsLength + xEff);
+ } else {
+ PostMar[xArr][x] += *(Margins + xArr * MarginsLength + xEff);
+ }
+ }
+
+ if (MaxPost[xArr] < PostMar[xArr][x]) {
+ MaxPost[xArr] = PostMar[xArr][x];
+ }
+
+ if (MinPost[xArr] > PostMar[xArr][x]) {
+ MinPost[xArr] = PostMar[xArr][x];
+ }
+ //
+ // signal delta pre/post average filter
+ //
+ n = (PostMar[xArr][x] -*(Margins + xArr * MarginsLength + x) * (AveN + ScaleM - 1));
+ Noise[xArr] += (n * n);
+ }
+
+ calcResults->Margins[xArr][x].EW = PostMar[xArr][x] / (AveN + ScaleM - 1);
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Margins[%d][%d] =%d\n",xArr,x,calcResults->Margins[xArr][x].EW);
+ //
+ }
+
+ if (Scale[xArr] == 0) {
+ continue;
+ }
+ //
+ // Calculate SNR for this margin result
+ // For stdev, need sqrt function. Use log domain to change exponential to mult
+ // Make both Signal and Noise a % of (Max+Min)/2
+ // *100 for signal&noise = not go to zero
+ //
+ Signal[xArr] = ((MaxPost[xArr] - MinPost[xArr]) * 200 * 100) / (MaxPost[xArr] + MinPost[xArr]);
+ Noise[xArr] /= LenMargin;
+ if (Noise[xArr] != 0) {
+ Noise[xArr] = Mrceexp (MrcNaturalLog (100 * Noise[xArr]) / 2); // result is 100x
+ }
+
+ Noise[xArr] = (Noise[xArr] * 2 * 100) / (MaxPost[xArr] + MinPost[xArr]);
+
+ if (Noise[xArr] == 0) {
+ Ratio[xArr] = (Signal[xArr] * 1000);
+ } else {
+ Ratio[xArr] = (Signal[xArr] * 1000) / Noise[xArr];
+ }
+
+ SMaxPost[xArr] = MaxPost[xArr];
+
+ //
+ // update global results
+ //
+ calcResults->Scale[xArr] = Scale[xArr];
+ calcResults->Signal[xArr] = Signal[xArr];
+ calcResults->Noise[xArr] = Noise[xArr];
+ calcResults->Ratio[xArr] = Ratio[xArr];
+ calcResults->MaxPost[xArr] = MaxPost[xArr] / (AveN + ScaleM - 1);
+ calcResults->MinPost[xArr] = MinPost[xArr] / (AveN + ScaleM - 1);
+ //
+ // 10x the tick diff.
+ //
+ calcResults->Ticks[xArr] = (U16) (MaxPost[xArr] - MinPost[xArr]) / (AveN + ScaleM - 1) / (Scale[xArr]);
+ }
+ //
+ // Sort Array
+ //
+ MrcBsort (SMaxPost, NumArr);
+
+ //
+ // Calculate Number of Bits Required to represent this number. Make sure to handle care of EnSq
+ //
+ NumBits = 0;
+
+ for (xArr = 0; xArr < NumArr; xArr++) {
+ if (xArr < (NumArr - 1)) {
+ //
+ // if EnSq we do Max^2 so the num get twice the bits...
+ //
+ localY = SMaxPost[xArr];
+ if (EnSq) {
+ localY = (localY * localY);
+ }
+
+ NumBits += MrcLog2 ((U32) localY);
+ } else {
+ NumBits += MrcLog2 ((U32) SMaxPost[xArr]);
+ }
+ }
+
+ NumBits += 11; // reserved another 10 bits for division in order to format for printing ; 3 for adding - up to 8
+ //
+ // EqOrder for square terms
+ //
+ if (EnSq) {
+ EqOrder = (EqOrder + (EqOrder - 1));
+ }
+ //
+ // Handle Potential Saturation
+ //
+ if (NumBits > 64) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Warning number of bits exceeds 64 bit : %d \n", NumBits);
+ //
+ // Shift all numbers to reduce final result to be less than 32 bits. Round Up
+ //
+ Shift = (NumBits - 64 + EqOrder - 1) / EqOrder;
+ //
+ // RoundUp Adder
+ //
+ Adder = (1 << (Shift - 1));
+ //
+ // Divide by (1<<Shift) and Round Up
+ //
+ for (xArr = 0; xArr < NumArr; xArr++) {
+ MaxPost[xArr] = (MaxPost[xArr] + Adder) >> Shift;
+ PwrLimitPost[xArr] = (PwrLimitPost[xArr] + Adder) >> Shift;
+ for (x = 0; x < LenMargin; x++) {
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "PostMar[%d][%d] before Shift : %d Adder : %d Shift : %d\n",xArr,x,PostMar[xArr][x],Shift,Adder);
+ //
+ PostMar[xArr][x] = (PostMar[xArr][x] + Adder) >> Shift;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "after: %d\n",PostMar[xArr][x]);
+ //
+ }
+ }
+ }
+ //
+ // Calculate Square terms:
+ //
+ if (EnSq) {
+ for (xArr = 0; xArr < NumArr; xArr++) {
+ MaxPost[xArr] = MaxPost[xArr] * MaxPost[xArr];
+ }
+ }
+ //
+ // Set Limits for Search
+ //
+ Start = 0;
+ Stop = LenMargin;
+ if ((IncEnds == 0) && (LenMargin > AveN)) {
+ //
+ // most commonly
+ //
+ if (Nby2 > 0) {
+ Start++;
+ Stop--;
+ }
+ }
+ //
+ // Find the Best Point to Use
+ //
+ Result = 0;
+ MaxR = 0;
+ MinR = ~(0ULL);
+ BestX = 0;
+ PowerX = 0;
+ FoundPwrOpt = 0;
+
+ for (x = Start; x < Stop; x++) {
+ //
+ // Find Optimal Point from Margin Point of View
+ // Combine the points using the formula:
+ // Max0*Max1*Max2*Post3 + Max1*Max2*Max3*Post0 + Max2*Max3*Max0*Post1 +
+ // Max3*Max0*Max1*Post2 + Scale*min(Post0,Post1,Post2,Post3)^EqOrder
+ // Scale = 1 + (10*(SMaxPost[0]-SMaxPost[1]))/SMaxPost[NumArr-1]
+ //
+ Result = 0;
+ MinPost1 = 0xFFFFFFFF;
+ GoodPower = 1;
+ for (xArr = 0; xArr < NumArr; xArr++) {
+ if (Scale[xArr] == 0) {
+ continue; // not need to calculate those
+ }
+ //
+ // Find Min of all PostMar at offset x
+ // Does this point meet the min power Margin requirements?
+ //
+ if (Scale[xArr] > 0) {
+ if (MinPost1 > PostMar[xArr][x]) {
+ MinPost1 = PostMar[xArr][x];
+ }
+
+ if (PostMar[xArr][x] < PwrLimitPost[xArr]) {
+ GoodPower = 0; // not ! //@todo: delete this power limit for this routing
+ }
+ }
+ //
+ // Calculate this portion of result
+ //
+ rlocal = 1;
+ for (yArr = 0; yArr < NumArr; yArr++) {
+ if (Scale[yArr] == 0) {
+ continue; // not need to calculate those
+ }
+
+ if (xArr == yArr) {
+ continue;
+ } else {
+ rlocal = MrcOemMemoryMultiplyU64ByU32 (rlocal, MaxPost[yArr]);
+ }
+ }
+
+ Result += MrcOemMemoryMultiplyU64ByU32 (rlocal, PostMar[xArr][x]);
+ }
+
+ NumCalcArr = 0;
+ for (xArr = 0; xArr < NumArr; xArr++) {
+ //
+ // required for following step.
+ //
+ if (Scale[xArr] != 0) {
+ NumCalcArr++;
+ }
+ }
+ //
+ // Add in (MinPost ^ EqOrder)
+ // If NumCalcArr is 0, set it to 1 so that it still in the range of array size.
+ //
+ if (NumCalcArr == 0) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Error: wrong input parameter caused NumCalcArr = 0 when calling FindOptimalTradeOff()\n"
+ );
+ NumCalcArr = 1;
+ }
+
+ ScaleMin = 1 + (10 * (SMaxPost[0] - SMaxPost[1])) / SMaxPost[NumCalcArr - 1];
+ if (ScaleMin > 5) {
+ ScaleMin = 5;
+ }
+
+ ScaleMin = 1;
+ rlocal = ScaleMin;
+ for (i = 0; i < EqOrder; i++) {
+ rlocal = MrcOemMemoryMultiplyU64ByU32 (rlocal, MinPost1);
+ }
+
+ Result += rlocal;
+
+ if (Result < MinR) {
+ MinR = Result;
+ }
+
+ if (Result > MaxR) {
+ MaxR = Result;
+ BestX = x; // save first highest function result offset
+ }
+
+ calcResults->Result[x] = Result;
+ //
+ // Find Optimal Point from Power Point of View
+ //
+ if (GoodPower) {
+ //
+ // are all the point meet margins requirements for all Tests ?
+ //
+ if (FoundPwrOpt == 0) {
+ FoundPwrOpt = 1; // power optimization is possible
+ PowerX = x; // first point passing to power limits
+ } else {
+ if ((PwrOptHigh == 1) && (x > PowerX)) {
+ PowerX = x; // we take the less power save point
+ }
+
+ if ((PwrOptHigh == 0) && (x < PowerX)) {
+ PowerX = x; // @todo: how can it be ? x is alwaye increasing
+ }
+ }
+ }
+ } // end shmoo offsets
+ if ((MaxR + MinR) == 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "warninig : MaxR+MinR are Zero !!!\n");
+ }
+ //
+ // Record for debug purposes.
+ // more simple: 1000*(max-min)/((max+min)/2)
+ //
+ SNRTotal = MrcOemMemoryDivideU64ByU64 (MrcOemMemoryMultiplyU64ByU32 ((MaxR - MinR), 2000), (MaxR + MinR + 1));
+ //
+ // Are we optimizing for Power or Margin?
+ //
+ if (FoundPwrOpt) {
+ if ((PwrOptHigh == 1) && (BestX < PowerX)) {
+ BestX = PowerX;
+ }
+ //
+ // if ((PwrOptHigh==0) && (BestX>PowerX)) BestX = PowerX;//give the more power saving offset that meet power limits
+ //
+ }
+
+ calcResults->Best = BestX;
+ calcResults->SNRTotal = SNRTotal;
+ calcResults->MaxR = MaxR;
+ calcResults->MinR = MinR;
+ //
+ // Apply a guard band to the best setting, clamped at edges of the search.
+ //
+ if (GuardBand != 0) {
+ //
+ // Determine step direction and limit to the search edge.
+ //
+ if (GuardBand < 0) {
+ StepSize = 1;
+ Off = ((BestX + GuardBand) < Start) ? Start : (BestX + GuardBand);
+ } else {
+ StepSize = -1;
+ Off = ((BestX + GuardBand) >= Stop) ? (Stop - 1) : (BestX + GuardBand);
+ }
+ //
+ // Check each test for margin drop of MarginDropPercent.
+ // If any test fails, we step towards the original selection.
+ //
+ MarginLimit = MrcOemMemoryMultiplyU64ByU32 (calcResults->Result[BestX], (100 - MarginDropPercent));
+ MarginLimit = MrcOemMemoryDivideU64ByU64 (MarginLimit, 100);
+ for(; (Off != BestX); Off += StepSize) {
+ if (calcResults->Result[Off] > MarginLimit) {
+ break;
+ }
+ }
+
+ calcResults->GuardBand = Off - (S8) BestX;
+ }
+
+ return;
+}
+
+/**
+ This function implements Turn Around Timing training.
+ Optimize TA ODT Delay and Duration
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - If it succeeds return mrcSuccess.
+**/
+MrcStatus
+MrcTurnAroundTiming (
+ IN MrcParameters *const MrcData
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcInput *Inputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U8 Channel;
+ U8 RankMaskCh;
+ U8 RankMask;
+ BOOL RunDD;
+ BOOL RunDR;
+ U8 ParamList[4]; // List of parameters to margin
+ U8 TestListRd[2];
+ U8 TestListWr[2];
+ U8 GuardBand;
+ U8 NomWR2RD;
+ U8 Update;
+ U8 LoopCount;
+ S8 ClkShifts[2];
+ U32 Offset;
+
+ Status = mrcSuccess;
+ RankMaskCh = 0;
+ Update = 1;
+ LoopCount = 12;
+ RunDD = FALSE;
+ RunDR = FALSE;
+ NomWR2RD = 0;
+ RankMask = 0xF;
+ ParamList[0] = RdV;
+ ParamList[1] = RdT;
+ ParamList[2] = WrV;
+ ParamList[3] = WrT;
+ TestListRd[0] = RdV;
+ TestListRd[1] = RdT;
+ TestListWr[0] = WrV;
+ TestListWr[1] = WrT;
+ ClkShifts[0] = -7;
+ ClkShifts[1] = 7;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Inputs = &MrcData->SysIn.Inputs;
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ if (!ChannelOut->ValidRankBitMask) {
+ continue;
+ }
+
+ RankMaskCh = ChannelOut->ValidRankBitMask;
+ RunDD = RunDD || (ChannelOut->DimmCount == 2);
+ RunDR = RunDR || ((RankMaskCh & 0xC) == 0xC) || ((RankMaskCh & 0x3) == 0x3);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Channel %d: RunDR = 0x%x, RunDD = 0x%x, RankMaskCh = 0x%x\n",
+ Channel,
+ RunDR,
+ RunDD,
+ RankMaskCh
+ );
+
+ //
+ // Use nominal values (previuosly programmed) +1 an -1 to test.
+ //
+ NomWR2RD = (U8)
+ (
+ (ChannelOut->MchbarBANKRANKB & MCHBAR_CH0_CR_TC_BANK_RANK_B_tWRRD_dr_MSK) >>
+ MCHBAR_CH0_CR_TC_BANK_RANK_B_tWRRD_dr_OFF
+ );
+ }
+ //
+ // Program SAFE values for ODT and SAmp
+ //
+ GuardBand = 1;
+ UpdateSampOdtTiming (MrcData, GuardBand);
+
+ //
+ // Sweep ODT values but do not apply optimized value yet (Data Collection Only)
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running mcodts\n");
+ Status = TrainDDROptParamCliff (
+ MrcData,
+ mcodts,
+ TestListRd,
+ sizeof (TestListRd),
+ 0,
+ 2 + GuardBand,
+ LoopCount,
+ Update,
+ Outputs->MarginResult,
+ ClkShifts,
+ sizeof (ClkShifts),
+ 0,
+ RankMask,
+ GuardBand
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running mcodtd\n");
+ Status = TrainDDROptParamCliff (
+ MrcData,
+ mcodtd,
+ TestListRd,
+ sizeof (TestListRd),
+ (-1 - GuardBand),
+ 0,
+ LoopCount,
+ Update,
+ Outputs->MarginResult,
+ ClkShifts,
+ sizeof (ClkShifts),
+ 0,
+ RankMask,
+ GuardBand
+ );
+
+ //
+ // Restore SAFE values when ONLY collecting data
+ //
+ if (Update == 0) {
+ UpdateSampOdtTiming (MrcData, GuardBand);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ if (!ChannelOut->ValidRankBitMask) {
+ continue;
+ }
+ Offset = MCHBAR_CH0_CR_TC_BANK_RANK_D_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_D_REG - MCHBAR_CH0_CR_TC_BANK_RANK_D_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ChannelOut->MchbarBANKRANKD);
+ }
+ }
+ //
+ // Sweep DD Timing but do not apply optimized value yet (Data Collection Only)
+ //
+ if (RunDD) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DDRD2RD\n");
+ Status = TrainDDROptParamCliff (
+ MrcData,
+ ddrd2rd,
+ TestListRd,
+ sizeof (TestListRd),
+ 6,
+ 7,
+ LoopCount,
+ Update,
+ Outputs->MarginResult,
+ ClkShifts,
+ sizeof (ClkShifts),
+ 0,
+ RankMask,
+ 0
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DDWR2WR\n");
+ Status = TrainDDROptParamCliff (
+ MrcData,
+ ddwr2wr,
+ TestListWr,
+ sizeof (TestListWr),
+ 7,
+ 8,
+ LoopCount,
+ Update,
+ Outputs->MarginResult,
+ ClkShifts,
+ sizeof (ClkShifts),
+ 0,
+ RankMask,
+ 0
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DDWR2RD\n");
+ Status = TrainDDROptParamCliff (
+ MrcData,
+ ddwr2rd,
+ ParamList,
+ sizeof (ParamList),
+ NomWR2RD - 1,
+ NomWR2RD + 1,
+ LoopCount,
+ Update,
+ Outputs->MarginResult,
+ ClkShifts,
+ sizeof (ClkShifts),
+ 0,
+ RankMask,
+ 0
+ );
+ }
+ //
+ // Sweep DR Timing but do not apply optimized value yet (Data Collection Only)
+ //
+ if (RunDR) {
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DRRD2RD\n");
+ Status = TrainDDROptParamCliff (
+ MrcData,
+ drrd2rd,
+ TestListRd,
+ sizeof (TestListRd),
+ 6,
+ 7,
+ LoopCount,
+ Update,
+ Outputs->MarginResult,
+ ClkShifts,
+ sizeof (ClkShifts),
+ 0,
+ RankMask,
+ 0
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DRWR2WR\n");
+ Status = TrainDDROptParamCliff (
+ MrcData,
+ drwr2wr,
+ TestListWr,
+ sizeof (TestListWr),
+ 7,
+ 8,
+ LoopCount,
+ Update,
+ Outputs->MarginResult,
+ ClkShifts,
+ sizeof (ClkShifts),
+ 0,
+ RankMask,
+ 0
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DRWR2RD\n");
+ Status = TrainDDROptParamCliff (
+ MrcData,
+ drwr2rd,
+ ParamList,
+ sizeof (ParamList),
+ NomWR2RD - 1,
+ NomWR2RD + 1,
+ LoopCount,
+ Update,
+ Outputs->MarginResult,
+ ClkShifts,
+ sizeof (ClkShifts),
+ 0,
+ RankMask,
+ 0
+ );
+ }
+ //
+ // Restore SAFE values when ONLY collecting data
+ //
+ if (Update == 0) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ if (ChannelOut->ValidRankBitMask) {
+ Offset = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ChannelOut->MchbarBANKRANKA);
+
+ Offset = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ChannelOut->MchbarBANKRANKB);
+
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ General purpose function to optimize an abritray value, OptParam (see list above)
+ OptParam is generally some timing number that impacts performance or power
+ Expects that as OptParam gets smaller*, margins are flat until we hit a cliff
+ This procedure defines a cliff as a reducution of 4 ticks in eye height/width
+ * In the case of mcodts, higher values are actually worst
+ To stress out the timing, xxDDR_CLK is shifted by +/- 15 PI ticks
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] OptParam - Supports Turnaround Timings and ODT Start / Duration
+ @param[in] TestList - List of margin param to check to make sure timing are okay.
+ @param[in] NumTests - The size of TestList
+ @param[in] Start - Start point for this turn around time setting.
+ @param[in] Stop - Stop point for this turnaround time setting.
+ Note that the Start/Stop values are the real values, not the encoded value
+ @param[in] LoopCount - Length of a given test
+ @param[in] Update - Update the CRs and host structure with ideal values
+ @param[in] ClkShifts - Array of Pi clocks to be shifted
+ @param[in] MarginByte - Byte level margins
+ @param[in] NumR2RPhases - Number of PI clock phases
+ @param[in] rank - rank to work on
+ @param[in] RankMask - RankMask to be optimized
+ @param[in] GuardBand - GuardBand to be added to last pass value (to be a bit conservative).
+
+ @retval MrcStatus - If it succeeds return mrcSuccess
+**/
+MrcStatus
+TrainDDROptParamCliff (
+ IN MrcParameters *const MrcData,
+ IN U8 OptParam,
+ IN U8 TestList[],
+ IN U8 NumTests,
+ IN S8 Start,
+ IN S8 Stop,
+ IN U8 LoopCount,
+ IN U8 Update,
+ IN U32 MarginByte[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES],
+ IN S8 *ClkShifts,
+ IN U8 NumR2RPhases,
+ IN U8 rank,
+ IN U8 RankMask,
+ IN U8 GuardBand
+ )
+{
+ const MRC_REUTAddress REUTAddressConst = {
+ {0, 0, 0, 0}, // Start
+ {7, 0, 0, 1023}, // Stop
+ {0, 0, 0, 0}, // Order
+ {0, 0, 0, 0}, // IncRate
+ {1, 0, 0, 1}}; // IncValue
+ const U8 OptParamDDType[13] = {1, 2, 1, 2, 1, 2, 1, 2, 3, 3, 3, 3, 0}; // Does this test run dr, dd or both?
+ const U8 RankMapping[16] = {15, 15, 15, 4, 15, 3, 15, 1, 15, 15, 15, 15, 5, 2, 15, 0};
+ // Order of rank turnarounds for dr & dd.
+ const U32 RankOrder[2][6] = {{0x32320101, 0x20101010, 0x23232320, 0x20, 0x10, 0x23}, // RankOrder[0]: drsd - same DIMM
+ {0x21303120, 0x2120, 0x3020, 0x20, 0, 0}}; // RankOrder[1]: drdd - diff DIMM
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ MRC_REUTAddress REUTAddress;
+ MRC_WDBPattern WDBPattern; // For 8 bit VA, this walks through each WDB pointer ~ 2X
+ BOOL IsDual;
+ BOOL ODT;
+ BOOL PerByte;
+ BOOL NotRankTraining;
+ BOOL Lpddr;
+ BOOL FindFirstPass;
+ U32 BERStats[4]; // Track BER results
+ U32 RankList;
+ U32 Offset;
+ U32 CRValue;
+ U16 Margins[4][2][2][MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Tests X DR/DD x ClkPhases x Ch X Byte
+ U16 NumCL; // Number of cachelines per SubSeq
+ U16 m;
+ U16 SeqLC;
+ U16 MinMarginLimit;
+ U8 ShiftValue;
+ U8 Channel;
+ U8 ChannelMask;
+ U8 Byte;
+ U16 ByteMask;
+ U8 Rank;
+ U8 ChBitMask;
+ U8 RankCount;
+ U8 ChBitMaskdd;
+ U8 RankMaskCh;
+ U8 drddPresent[2]; // [0]: ChBitMask for dr, [1]: ChBitMask for dd
+ U8 CmdPat;
+ U8 BMap[9]; // Needed for GetBERMarginByte function
+ U8 MarginLimit; // Need to change it to 20%of eye heigth
+ U8 ResetDDR;
+ U8 SelfRefresh;
+ U8 RankInc; // Increment every cacheline (HW adds +1 automatically)
+ U16 ByteFailMask[MAX_CHANNEL]; // Per ch mask indicating which bytes have failed
+ U8 offs[MAX_CHANNEL];
+ U8 Param;
+ U8 iparam;
+ U16 ByteDone;
+ U8 dd;
+ U8 test0;
+ U8 v0;
+ U8 Mode;
+ U8 RankOrderIndex;
+ U8 UpdateHostMargin;
+ U8 Done;
+ U8 MaxMargin;
+ U8 ResultType;
+ U8 WDBIncRate; // Number of cachelines between incrementing WDB.
+ U8 LoopEnd;
+ S8 Inc;
+ S8 Off;
+ S8 Index;
+ S8 LastPass[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Lass Pass Value for off
+ S8 Begin;
+ S8 End;
+ S8 ChLastPass;
+ S8 ActualGuardBand;
+#ifdef MRC_DEBUG_PRINT
+ S8 ChLastPass1[MAX_CHANNEL];
+#endif // MRC_DEBUG_PRINT
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_STRUCT ReutChSeqRankL2PMapping;
+
+ Status = mrcSuccess;
+ Done = 0;
+ Rank = 0;
+ drddPresent[0] = 0;
+ drddPresent[1] = 0;
+ MarginLimit = (rtl == OptParam) ? 10 : 20; // Drop of X% in margin means failure
+ ResetDDR = 1;
+ SelfRefresh = 0;
+ WDBIncRate = 13;
+ NumCL = 128;
+ //
+ // For {8,5,4,3,2} ranks, this covers each rank ~ {3,5,6,8,12}X
+ // For 8 bit VA, this walks through each WDB pointer ~ 2X
+ //
+ WDBPattern.IncRate = 0;
+ WDBPattern.Start = 0;
+ WDBPattern.Stop = 7;
+ WDBPattern.DQPat = 0;
+ MrcOemMemorySetWord (ByteFailMask, 0, sizeof (ByteFailMask) / sizeof(ByteFailMask[0]));
+ MrcOemMemorySet (offs, 0, sizeof (offs));
+ MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats));
+ MrcOemMemoryCpy ((U8 *) &REUTAddress, (U8 *) &REUTAddressConst, sizeof (REUTAddress));
+ for (Byte = 0; Byte < (sizeof (BMap) / sizeof (BMap[0])); Byte++) {
+ BMap[Byte] = Byte;
+ }
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ NotRankTraining = (OptParam == rtl);
+ FindFirstPass = (OptParam == rtl); // FindFirstPass logic only works for RTL!
+ ODT = (OptParam == rdodtd) || (OptParam == wrodtd) || (OptParam == mcodtd) || (OptParam == mcodts);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "\nNotRankTraining = %u, ODT = %d\n", NotRankTraining, ODT);
+
+ //
+ // Decide which channels need to be run and program NumCachelines
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ if (ChannelOut->ValidRankBitMask) {
+ ChannelMask = MRC_BIT0 << Channel;
+ RankMaskCh = ChannelOut->ValidRankBitMask;
+ IsDual = ((RankMaskCh & 0xC) == 0xC) || ((RankMaskCh & 0x3) == 0x3);
+
+ //
+ // Continue if no ranks in this channel
+ //
+ if ((RankMaskCh & RankMask) == 0) {
+ continue;
+ }
+
+ if ((OptParamDDType[OptParam] & 0x2) && (ChannelOut->DimmCount == 2)) {
+ drddPresent[1] |= ChannelMask; // dd parameter and channel has 2 DIMMs
+ }
+
+ if (((OptParamDDType[OptParam] & 0x1) && IsDual) || NotRankTraining) {
+ drddPresent[0] |= ChannelMask; // dr parameter and channel has a dual rank
+ }
+
+ if (ODT && ((drddPresent[0] & (1 << Channel)) == 0)) {
+ //
+ // ODT matters when Single rank
+ // dr parameter and channel has a dual rank
+ //
+ drddPresent[0] |= ChannelMask;
+ }
+ }
+ }
+
+ ChBitMask = drddPresent[1] | drddPresent[0]; // Chanel is present if it has either a dr or dd
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "drddPresent[0] = 0x%x, drddPresent[1] = 0x%x, ChBitMask = 0x%x\n",
+ drddPresent[0],
+ drddPresent[1],
+ ChBitMask
+ );
+
+ //
+ // There is nothing to optimize for this parameter
+ //
+ if ((ChBitMask == 0) || (Stop <= Start)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "ChBitMask = %d, Start = 0x%x, Stop = 0x%x\n", ChBitMask, Start, Stop);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "No need to optimized TA, OptParam = %d\n", OptParam);
+ return mrcFail;
+ }
+ //
+ // Setup the REUT Test
+ //
+ SeqLC = LoopCount;
+ RankInc = 0;
+ Outputs->DQPat = TurnAround;
+ if ((OptParam == ddwr2rd) || (OptParam == drwr2rd)) {
+ CmdPat = PatWrRdTA;
+ Outputs->DQPat = TurnAroundWR;
+ RankInc = 1;
+ } else if (ODT) {
+ CmdPat = PatODTTA;
+ Outputs->DQPat = TurnAroundODT;
+ RankInc = 1;
+ } else if (OptParam == rtl) {
+ CmdPat = PatWrRd;
+ //
+ // Less optimistic values since we are updating values and RMT fails
+ //
+ WDBIncRate = 16;
+ NumCL = 4;
+ } else {
+ CmdPat = PatWrRd;
+ }
+
+ WDBPattern.DQPat = Outputs->DQPat;
+ WDBPattern.IncRate = WDBIncRate;
+ REUTAddress.IncRate[0] = RankInc;
+ REUTAddress.IncRate[3] = RankInc;
+
+ //
+ // SOE=0, EnCADB=0, EnCKE=0, SubSeqWait=0
+ //
+ SetupIOTest (MrcData, ChBitMask, CmdPat, NumCL, (U8) SeqLC, &REUTAddress, NSOE, &WDBPattern, 0, 0, 0);
+
+ Outputs->DQPatLC = MRC_BIT0 << (LoopCount - MrcLog2 ((U32) (NumCL - 1)));
+ if (Outputs->DQPatLC < 1) {
+ Outputs->DQPatLC = 1;
+ }
+ //
+ // Optimize parameter per byte. Everything else is per channel
+ //
+ PerByte = (OptParam == mcodts) || (OptParam == mcodtd);
+
+ //
+ // Keep track of which bytes have failed and are we done yet
+ //
+ ByteDone = (1 << Outputs->SdramCount) - 1;
+
+ //
+ // ###########################################################
+ // #### Loop through OptParam X DD X ClkPhases X Params and measure margin #####
+ // ###########################################################
+ //
+ if (OptParam == mcodts) {
+ //
+ // In the case of mcodts, higher values are actually worst.
+ //
+ Begin = Start;
+ End = Stop;
+ Inc = 1;
+ } else {
+ Begin = Stop;
+ End = Start;
+ Inc = -1;
+ }
+
+ ActualGuardBand = (Inc * GuardBand);
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Start = %d, Stop = %d, Begin = %d, End = %d, Inc = %d\n",
+ Start,
+ Stop,
+ Begin,
+ End,
+ Inc
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (OptParam == rtl) ? "Rank = %d\n" : "", rank);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel 0\t\t\t\t\t\t\t\t1\nByte\t");
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE, (
+ Outputs->SdramCount == MAX_SDRAM_IN_DIMM
+ ) ? "0\t1\t2\t3\t4\t\t5\t6\t7\t8\t0\t1\t2\t3\t4\t5\t6\t7\t8\n" :
+ "0\t1\t2\t3\t4\t5\t6\t7\t0\t1\t2\t3\t4\t5\t6\t7\n"
+ );
+
+ //
+ // Init Variables
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ LastPass[Channel][Byte] = Begin - ActualGuardBand;
+ for (iparam = 0; iparam < NumTests; iparam++) {
+ for (dd = 0; dd < 2; dd++) {
+ for (test0 = 0; test0 < NumR2RPhases; test0++) {
+ Margins[iparam][dd][test0][Channel][Byte] = 1280;
+ }
+ }
+ }
+ }
+ }
+ }
+ //
+ // Walk through different OptParam values
+ //
+ for (Off = (S8) Begin; Off != (S8) (End + Inc); Off += Inc) {
+ if (Done) {
+ break;
+ }
+ Index = (Off - Begin) * Inc; // Index = 0, 1, 2..
+ //
+ // Inc can only take a value of +/- 1.
+ //
+ if ((Index == 1) && (TRUE == FindFirstPass)) {
+ Inc *= -1;
+ Off = End;
+ End = Begin - Inc; // One Inc less since we have already done Index 0.
+ Begin = Off - Inc; // One Inc less to get us starting at Index 1
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Find First Pass - Walking backwards.\n Off = %d, Begin = %d, End = %d, Inc = %d, Index = %d\n",
+ Off,
+ Begin,
+ End,
+ Inc,
+ (Off - Begin) * Inc
+ );
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Param^ Offset-> %d\n Actl\t", Off);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ RankMaskCh = ChannelOut->ValidRankBitMask & RankMask;
+ //
+ // if nothing for this channel OR No Ranks in this channel
+ //
+ if (!((MRC_BIT0 << Channel) & ChBitMask) || (RankMaskCh == 0)) {
+#ifdef MRC_DEBUG_PRINT
+ if (Channel == 0) {
+ if (Outputs->SdramCount == (MAX_SDRAM_IN_DIMM - 1)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t");
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t");
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+ continue;
+ }
+ //
+ // For debug purposes program Row start stop to OptParam + Offset value
+ // OptParam in upper BYTE
+ //
+ Offset = 4 + MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG) *
+ Channel
+ );
+ CRValue = MrcReadCR (MrcData, Offset);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "upper SEQ_BASE_ADDR_START BEFORE = 0x%x ", CRValue);
+ CRValue = MrcBitSwap (CRValue, OptParam, 0, 8);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "After = 0x%x\n", CRValue);
+ MrcWriteCR (MrcData, Offset, CRValue);
+ //
+ // Offset in Lower BYTE
+ //
+ Offset -= 4;
+ CRValue = MrcReadCR (MrcData, Offset);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "lower SEQ_BASE_ADDR_START BEFORE = 0x%x ", CRValue);
+ CRValue = MrcBitSwap (CRValue, Off, 24, 8);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "After = 0x%x\n", CRValue);
+ MrcWriteCR (MrcData, Offset, CRValue);
+
+ //
+ // OptParam in upper BYTE
+ //
+ Offset = 4 + MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG) *
+ Channel
+ );
+ CRValue = MrcReadCR (MrcData, Offset);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "upper SEQ_BASE_ADDR_WRAP BEFORE = 0x%x ", CRValue);
+ CRValue = MrcBitSwap (CRValue, OptParam, 0, 8);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "After = 0x%x\n", CRValue);
+ MrcWriteCR (MrcData, Offset, CRValue);
+ //
+ // Offset in Lower BYTE
+ //
+ Offset -= 4;
+ CRValue = MrcReadCR (MrcData, Offset);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "lower SEQ_BASE_ADDR_WRAP BEFORE = 0x%x ", CRValue);
+ CRValue = MrcBitSwap (CRValue, Off, 24, 8);
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "After = 0x%x\n", CRValue);
+ MrcWriteCR (MrcData, Offset, CRValue);
+
+ //
+ // No need to update MrcData host during this step even if not collecting data
+ //
+ LoopEnd = (U8) ((PerByte) ? Outputs->SdramCount : 1);
+ for (Byte = 0; Byte < LoopEnd; Byte++) {
+ UpdateTAParamOffset (MrcData, Channel, Byte, OptParam, Off, 0, 0, RankMaskCh);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ //
+ // Test both: different dr and dd as required
+ //
+ for (dd = 0; dd < 2; dd++) {
+ if (Done) {
+ break;
+ }
+ //
+ // Check if this test type should be run
+ //
+ ChBitMaskdd = drddPresent[dd];
+ if (ChBitMaskdd == 0) {
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (dd == 0) ? "Dual Rank\n" : "Dual Dimm\n");
+ //
+ // Select Ranks in the correct order based on the test type
+ // Need to re-order the ranks based on the value of ddw2r
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMaskdd & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ RankMaskCh = ChannelOut->ValidRankBitMask & RankMask;
+ if (RankMaskCh == 0) {
+ continue; // No Ranks in this channel
+ }
+ //
+ // Initialize variables and read out ordered rank list
+ //
+ ReutChSeqRankL2PMapping.Data = 0;
+ RankCount = 0;
+
+ if (NotRankTraining) {
+ RankList = 0x00003210;
+ } else {
+ RankOrderIndex = RankMapping[RankMaskCh];
+ if (RankOrderIndex == 15) {
+ RankList = 0x00003210;
+ } else {
+ RankList = RankOrder[dd][RankOrderIndex];
+ }
+ }
+
+ while (RankList > 0) {
+ Rank = (RankList & 0xF); // Nibble by Nibble
+ RankList = (RankList >> 4);
+ if (!(RankMaskCh & (MRC_BIT0 << Rank))) {
+ continue;
+ }
+
+ ShiftValue = RankCount *
+ MRC_BIT0 <<
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_Logical_to_Physical_Rank0_Mapping_WID;
+ ReutChSeqRankL2PMapping.Data |= (Rank << ShiftValue);
+ RankCount++;
+ }
+
+ Offset = MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG +
+ (
+ (
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_1_REG -
+ MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG
+ ) * Channel
+ );
+ MrcWriteCR (MrcData, Offset, ReutChSeqRankL2PMapping.Data);
+ Offset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG +
+ (
+ (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG) *
+ Channel
+ ) + 7;
+ MrcWriteCR8 (MrcData, Offset, RankCount - 1);
+ }
+ //
+ // ###################################################
+ // ### Walk through different sets of rank2rank timings ###
+ // ###################################################
+ //
+ for (test0 = 0; test0 < NumR2RPhases; test0++) {
+ if (Done) {
+ break;
+ }
+
+ v0 = ClkShifts[test0];
+
+ //
+ // Program rank offsets differently for dd vs. dr
+ //
+ if (NotRankTraining) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMaskdd & (MRC_BIT0 << Channel))) {
+ offs[Channel] = 0;
+ } else {
+ //
+ // Shift all signals in the channel(Clk/Ctl/Cmd/Dq) by v0
+ //
+ offs[Channel] = v0;
+ }
+ }
+ //
+ // UpdateHost=0, SkipTx=0
+ //
+ ShiftCh2Ch (MrcData, RankMask, offs, ResetDDR, SelfRefresh, 0, 0);
+ } else if (dd == 1) {
+ //
+ // For DD
+ // Shift Clk/DQ on one DIMM by v0 and Clk/DQ on other DIMM by -v0
+ // @todo: CTL picode should be optionally shifted to improve margins
+ //
+ SetCmdMargin (MrcData, ChBitMaskdd, 0x3, WrT, v0, 0, ResetDDR, SelfRefresh);
+ SetCmdMargin (MrcData, ChBitMaskdd, 0xC, WrT, -v0, 0, ResetDDR, SelfRefresh);
+ } else {
+ //
+ // For DR
+ // Shift Clk/DQ on front side by v0 and Clk/DQ on backside by -v0
+ // @todo: CTL picode should be optionally shifted to improve margins
+ //
+ SetCmdMargin (MrcData, ChBitMaskdd, 0x5, WrT, v0, 0, ResetDDR, SelfRefresh);
+ SetCmdMargin (MrcData, ChBitMaskdd, 0xA, WrT, -v0, 0, ResetDDR, SelfRefresh);
+ }
+ //
+ // Test different margin param
+ //
+ for (iparam = 0; iparam < NumTests; iparam++) {
+ Param = TestList[iparam];
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s ", MarginTypesString[Param]);
+ if (Param == 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, " WARNING! UNNECESSARY LOOPS. Param = %d \n", Param);
+ return mrcFail;
+ }
+
+ ResultType = GetMarginResultType (Param);
+
+ //
+ // Get the width/height limit for the parameter
+ //
+ MinMarginLimit = UpmPwrLimitValue (MrcData, Param, UpmLimit);
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d ", MinMarginLimit);
+ // Calculate MaxMargin and Starting Point for margin search
+ //
+ MaxMargin = MAX_POSSIBLE_TIME;
+ if ((Param == RdV) ||
+ (Param == RdFan2) ||
+ (Param == RdFan3) ||
+ (Param == WrV) ||
+ (Param == WrFan2) ||
+ (Param == WrFan3)
+ ) {
+ MaxMargin = MAX_POSSIBLE_VREF;
+ }
+ //
+ // Are we done yet or should we keep testing?
+ //
+ Done = 1;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(ChBitMaskdd & (MRC_BIT0 << Channel))) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ RankMaskCh = ChannelOut->ValidRankBitMask & RankMask;
+ if (RankMaskCh == 0) {
+ continue; // No Ranks in this channel
+ }
+
+ //
+ // When FindFirstPass is used, all Bytes have to have passed before we stop.
+ // We uses ByteFailMask[] to track the passing bytes in this case.
+ //
+ if (PerByte || FindFirstPass) {
+ if (ByteFailMask[Channel] != ByteDone) {
+ Done = 0;
+ }
+ } else {
+ if (ByteFailMask[Channel] == 0) {
+ Done = 0;
+ }
+ }
+ }
+
+ if (Done) {
+ break;
+ }
+
+ Status = GetMarginByte (MrcData, Outputs->MarginResult, Param, 0, 0xF);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "% 3d\t", (S8) v0);
+
+ Mode = 0;
+ Status = MrcGetBERMarginByte (
+ MrcData,
+ Outputs->MarginResult,
+ ChBitMaskdd,
+ rank,
+ 0xFF,
+ Param,
+ Mode,
+ BMap,
+ 1,
+ MaxMargin,
+ 0,
+ BERStats
+ );
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ //
+ // Record Results
+ //
+ UpdateHostMargin = 1;
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ RankMaskCh = ChannelOut->ValidRankBitMask & RankMask;
+ //
+ // if nothing for this channel OR No Ranks in this channel
+ //
+ if (!((MRC_BIT0 << Channel) & ChBitMaskdd) || (RankMaskCh == 0)) {
+#ifdef MRC_DEBUG_PRINT
+ if (Channel == 0) {
+ if (Outputs->SdramCount == (MAX_SDRAM_IN_DIMM - 1)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t");
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t");
+ }
+ }
+#endif // MRC_DEBUG_PRINT
+ continue;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // For this optimization, it makes more sense to look at the full sum
+ //
+ ByteMask = MRC_BIT0 << Byte;
+ m = EffectiveMargin (
+ (U16) MarginByte[ResultType][rank][Channel][Byte][0],
+ (U16) MarginByte[ResultType][rank][Channel][Byte][1]
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d", m);
+
+ //
+ // If previously failed, this is also a failure unless we are looking for
+ // the first passing offset.
+ //
+ if ((ByteFailMask[Channel] & ByteMask) && (FALSE == FindFirstPass)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "#\t");
+ continue;
+ }
+ //
+ // Byte fails if margin is below MinMarginLimit at any time
+ //
+ if (m < MinMarginLimit) {
+ //
+ // If we are looking for pass, continue and do not update LastPass
+ //
+ if (TRUE == FindFirstPass) {
+ if (Index == 0) {
+ //
+ // When training from the most aggressive setting to the conservative setting,
+ // if we fail the first setting we stop.
+ //
+ ByteFailMask[Channel] = ByteDone;
+ }
+ UpdateHostMargin = 0;
+ } else {
+ ByteFailMask[Channel] |= ByteMask;
+ LastPass[Channel][Byte] = Off - Inc - ActualGuardBand;
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "#\t");
+ continue;
+ }
+
+ if (Index == 0) {
+ //
+ // Get the smallest marging at Index 0
+ //
+ if (Margins[iparam][dd][test0][Channel][Byte] > m) {
+ Margins[iparam][dd][test0][Channel][Byte] = m;
+ }
+ } else {
+ //
+ // Check if we dropped more than the percent allowed
+ //
+ if (m < ((Margins[iparam][dd][test0][Channel][Byte] * (100 - MarginLimit)) / 100)) {
+ if (FALSE == FindFirstPass) {
+ ByteFailMask[Channel] |= ByteMask;
+ LastPass[Channel][Byte] = Off - Inc - ActualGuardBand;
+ }
+ UpdateHostMargin = 0;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "#-%d\t",
+ (ABS (m - Margins[iparam][dd][test0][Channel][Byte]) * 100) / Margins[iparam][dd][test0][Channel][Byte]
+ );
+ continue;
+ } else {
+ if (TRUE == FindFirstPass) {
+ if ((ByteFailMask[Channel] & ByteMask) != ByteMask) {
+ LastPass[Channel][Byte] = Off - ActualGuardBand;
+ ByteFailMask[Channel] |= ByteMask;
+ }
+ } else {
+ LastPass[Channel][Byte] = Off - ActualGuardBand;
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ ".%c%d\t",
+ (m > Margins[iparam][dd][test0][Channel][Byte]) ? '+' : '-',
+ (ABS(m - Margins[iparam][dd][test0][Channel][Byte]) * 100) / Margins[iparam][dd][test0][Channel][Byte]
+ );
+ }
+ }
+
+ if (UpdateHostMargin) {
+ Status = ScaleMarginByte (MrcData, Outputs->MarginResult, Param, rank);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ //
+ // Clean up
+ //
+ if (NotRankTraining) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ offs[Channel] = 0;
+ }
+ //
+ // UpdateHost=0, SkipTx=0
+ //
+ ShiftCh2Ch (MrcData, RankMask, offs, ResetDDR, SelfRefresh, 0, 0);
+ } else {
+ SetCmdMargin (MrcData, ChBitMaskdd, RankMask, WrT, 0, 0, ResetDDR, SelfRefresh);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+ }
+
+ //
+ // If we are sweeping agressive settings to conservative settings, we
+ // need to restore original Inc, Begin, and End values to select the
+ // proper offset if bytes have varying offsets values for a parameter
+ // that is NOT specified per Byte.
+ //
+ if (TRUE == FindFirstPass) {
+ Off = End; // Temp storage for swap
+ End = Begin + Inc;
+ Begin = Off + Inc;
+ Inc *= -1;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Find First Pass - Reverting Inc, Begin, and End\n Begin = %d, End = %d, Inc = %d,\n",
+ Begin,
+ End,
+ Inc
+ );
+ }
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Optimal offset per Byte\n\t");
+ //
+ // Print OPTIMAL value
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ RankMaskCh = ChannelOut->ValidRankBitMask & RankMask;
+ ChLastPass1[Channel] = End;
+ //
+ // if nothing for this channel OR No Ranks in this channel
+ //
+ if (!((MRC_BIT0 << Channel) & ChBitMask) || (RankMaskCh == 0)) {
+ if (Channel == 0) {
+ if (Outputs->SdramCount == (MAX_SDRAM_IN_DIMM - 1)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t");
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t");
+ }
+ }
+
+ continue;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", LastPass[Channel][Byte]);
+ if ((Inc == 1) && (ChLastPass1[Channel] > LastPass[Channel][Byte])) {
+ ChLastPass1[Channel] = LastPass[Channel][Byte];
+ }
+
+ if ((Inc == -1) && (ChLastPass1[Channel] < LastPass[Channel][Byte])) {
+ ChLastPass1[Channel] = LastPass[Channel][Byte];
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ RankMaskCh = ChannelOut->ValidRankBitMask & RankMask;
+ //
+ // if nothing for this channel OR No Ranks in this channel
+ //
+ if (!((MRC_BIT0 << Channel) & ChBitMask) || (RankMaskCh == 0)) {
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Optimal offset Channel %d = %d\n", Channel, ChLastPass1[Channel]);
+ }
+#endif // MRC_DEBUG_PRINT
+ //
+ // Program new value
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChBitMask)) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ RankMaskCh = ChannelOut->ValidRankBitMask & RankMask;
+ if (RankMaskCh == 0) {
+ continue; // No Ranks in this channel
+ }
+ //
+ // Start with the most aggressive setting
+ //
+ ChLastPass = End;
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if (Update == 0) {
+ LastPass[Channel][Byte] = Begin;
+ }
+
+ if ((Inc == 1) && (ChLastPass > LastPass[Channel][Byte])) {
+ ChLastPass = LastPass[Channel][Byte];
+ }
+
+ if ((Inc == -1) && (ChLastPass < LastPass[Channel][Byte])) {
+ ChLastPass = LastPass[Channel][Byte];
+ }
+
+ if (PerByte) {
+ UpdateTAParamOffset (MrcData, Channel, Byte, OptParam, LastPass[Channel][Byte], Update, 1, RankMaskCh);
+ }
+ }
+
+ if (PerByte == 0) {
+ UpdateTAParamOffset (MrcData, Channel, 0, OptParam, ChLastPass, Update, 1, RankMaskCh);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Selected Offset for channel %d is = %d\n", Channel, ChLastPass);
+ }
+
+ return Status;
+}
+
+/**
+ Sets commnad margins when moving WrT, WrTBox, or WrV
+ NOTE: ONLY one, ResetDDR or SelfRefresh can be set inside this function
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] ChBitMask - Bit mask of populated channels
+ @param[in] Ranks - Bit Mask of populated ranks
+ @param[in] Param - Input parameter to update
+ @param[in] Value0 - value to be added
+ @param[in] Value1 - value to be added
+ @param[in] ResetDDR - Do we reset DDR?
+ @param[in] SelfRefresh - Do we perform Self refresh?
+
+ @retval MrcStatus - If it succeeds return mrcSuccess
+**/
+void
+SetCmdMargin (
+ IN MrcParameters *const MrcData,
+ IN const U8 ChBitMask,
+ IN const U8 Ranks,
+ IN const U8 Param,
+ IN const U8 Value0,
+ IN const U8 Value1,
+ IN U8 ResetDDR,
+ IN const U8 SelfRefresh
+ )
+{
+ MrcControllerOut *ControllerOut;
+ U8 Channel;
+ U8 RankMaskCh;
+ U8 Offset;
+
+ ControllerOut = &MrcData->SysOut.Outputs.Controller[0];
+ Offset = 0;
+ if (SelfRefresh && ResetDDR) {
+ MRC_DEBUG_MSG (
+ &MrcData->SysIn.Inputs.Debug,
+ MSG_LEVEL_ERROR,
+ "WARNING SelfRefresh OR ResetDDR can be set at once...performing SelfRefresh\n"
+ );
+ ResetDDR = 0;
+ }
+
+ if (SelfRefresh) {
+ EnterSR (MrcData);
+ }
+ //
+ // Change Clock Timing
+ //
+ if ((Param == WrT) || (Param == WrTBox)) {
+ //
+ // Walk though all chs and ranks
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (ChBitMask & (MRC_BIT0 << Channel)) {
+ //
+ // determine which ranks from parameter "Ranks" exist in this channel
+ //
+ RankMaskCh = Ranks & ControllerOut->Channel[Channel].ValidRankBitMask;
+ if (RankMaskCh == 0) {
+ continue; // No Ranks in this channel
+ }
+
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationClock, RankMaskCh, 3, Value0, 0);
+ }
+ }
+ }
+
+ if ((Param == WrV) || (Param == (WrTBox))) {
+ if (Param == WrV) {
+ Offset = Value0;
+ } else {
+ if (Param == WrTBox) {
+ Offset = ((2 * Value1) - 1) * 8;
+ }
+ }
+
+ UpdateVrefWaitTilStable (MrcData, 2, 0, Offset, 0);
+ }
+
+ if (ResetDDR) {
+ MrcResetSequence (MrcData);
+ } else if (SelfRefresh) {
+ ExitSR (MrcData);
+ }
+
+ return;
+}
+
+/**
+ Updates the value for following OptParamCliff variables:
+ drrd2rd=0, ddrd2rd=1, drwr2wr=2, ddwr2wr=3, drrd2wr=4, ddrd2wr=5, drwr2rd=6, ddwr2rd=7,
+ rdodtd=8, wrodtd=9, mcodts=10, mcodtd=11, rtl=12}
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel to update the specificed parameter.
+ @param[in] Byte - Byte to update the specified parameter.
+ @param[in] OptParam - Parameter to update.
+ @param[in] Off - Value to offset the current setting.
+ @param[in] UpdateHost - Switch to update the host structure with the new value.
+ @param[in] SkipPrint - Switch to skip debug prints.
+ @param[in] RankMask - Bit mask of Ranks to update.
+
+ @retval Nothing
+**/
+void
+UpdateTAParamOffset (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 Byte,
+ IN const U8 OptParam,
+ IN const U8 Off,
+ IN const U8 UpdateHost,
+ IN const U8 SkipPrint,
+ IN const U8 RankMask
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ U8 Rank;
+ U8 IOLat;
+ S8 New;
+ U32 Offset1;
+ U32 Offset2;
+ MCHBAR_CH0_CR_TC_BANK_RANK_A_STRUCT TcBankRankA;
+ MCHBAR_CH0_CR_TC_BANK_RANK_B_STRUCT TcBankRankB;
+ MCHBAR_CH0_CR_TC_BANK_RANK_C_STRUCT TcBankRankC;
+ MCHBAR_CH0_CR_TC_BANK_RANK_D_STRUCT TcBankRankD;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT DdrDataControl1;
+ MCHBAR_CH0_CR_SC_IO_LATENCY_STRUCT ScIoLatency;
+ MCHBAR_CH0_CR_SC_ROUNDT_LAT_STRUCT ScRoundtLat;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+
+ switch (OptParam) {
+ case drrd2rd:
+ //
+ // dr RD 2 RD Turn Around offsets
+ //
+ TcBankRankA.Data = ChannelOut->MchbarBANKRANKA;
+ TcBankRankA.Bits.tRDRD_dr = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG + ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankA.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKA = TcBankRankA.Data;
+ }
+ break;
+
+ case ddrd2rd:
+ //
+ // dd RD 2 RD Turn Around offsets
+ //
+ TcBankRankA.Data = ChannelOut->MchbarBANKRANKA;
+ TcBankRankA.Bits.tRDRD_dd = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankA.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKA = TcBankRankA.Data;
+ }
+ break;
+
+ case drwr2wr:
+ //
+ // dr WR 2 WR Turn Around offsets
+ //
+ TcBankRankB.Data = ChannelOut->MchbarBANKRANKB;
+ TcBankRankB.Bits.tWRWR_dr = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankB.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKB = TcBankRankB.Data;
+ }
+ break;
+
+ case ddwr2wr:
+ //
+ // dd WR 2 WR Turn Around offsets
+ //
+ TcBankRankB.Data = ChannelOut->MchbarBANKRANKB;
+ TcBankRankB.Bits.tWRWR_dd = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankB.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKB = TcBankRankB.Data;
+ }
+ break;
+
+ case drrd2wr:
+ //
+ // dr RD 2 WR Turn Around offsets
+ //
+ TcBankRankC.Data = ChannelOut->MchbarBANKRANKC;
+ TcBankRankC.Bits.tRDWR_dr = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_C_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_C_REG - MCHBAR_CH0_CR_TC_BANK_RANK_C_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankC.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKC = TcBankRankC.Data;
+ }
+ break;
+
+ case ddrd2wr:
+ //
+ // dd RD 2 WR Turn Around offsets
+ //
+ TcBankRankC.Data = ChannelOut->MchbarBANKRANKC;
+ TcBankRankC.Bits.tRDWR_dd = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_C_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_C_REG - MCHBAR_CH0_CR_TC_BANK_RANK_C_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankC.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKC = TcBankRankC.Data;
+ }
+ break;
+
+ case drwr2rd:
+ //
+ // dr WR 2 RD Turn Around offsets
+ //
+ TcBankRankB.Data = ChannelOut->MchbarBANKRANKB;
+ TcBankRankB.Bits.tWRRD_dr = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankB.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKB = TcBankRankB.Data;
+ }
+ break;
+
+ case ddwr2rd:
+ //
+ // dd WR 2 RD Turn Around offsets
+ //
+ TcBankRankB.Data = ChannelOut->MchbarBANKRANKB;
+ TcBankRankB.Bits.tWRRD_dd = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankB.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKB = TcBankRankB.Data;
+ }
+ break;
+
+ case rdodtd:
+ TcBankRankD.Data = ChannelOut->MchbarBANKRANKD;
+ TcBankRankD.Bits.Odt_Read_Duration = Off - 6; // Convert into Register values. 2'b00 = BL/2 + 2 (6 DCLKs
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_D_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_D_REG - MCHBAR_CH0_CR_TC_BANK_RANK_D_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankD.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKD = TcBankRankD.Data;
+ }
+ break;
+
+ case wrodtd:
+ TcBankRankD.Data = ChannelOut->MchbarBANKRANKD;
+#ifdef ULT_FLAG
+ if (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) {
+ TcBankRankD.UltBits.Odt_Write_Duration = Off - 6; // Convert into Register values. 2'b00 = BL/2 + 2 (6 DCLKs
+ } else
+#endif // ULT_FLAG
+ {
+ TcBankRankD.Bits.Odt_Write_Duration = Off - 6; // Convert into Register values. 2'b00 = BL/2 + 2 (6 DCLKs
+ }
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_D_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_D_REG - MCHBAR_CH0_CR_TC_BANK_RANK_D_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankD.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKD = TcBankRankD.Data;
+ }
+ break;
+
+ case mcodts:
+ //
+ // MC ODT delay
+ //
+ DdrDataControl1.Data = ChannelOut->DqControl1[Byte].Data;
+ New = MrcSE ((U8) DdrDataControl1.Bits.OdtDelay, 4, 8) + Off; // SignExtend
+ if (New < -4) {
+ New = -4; // RcvEnPi[8:6] - 5 qclk Min
+ } else if (New > 6) {
+ New = 6; // RcvEnPi[8:6] + 5 qclk Max
+ }
+
+ DdrDataControl1.Bits.OdtDelay = New;
+ DdrDataControl1.Bits.SenseAmpDelay = New;
+ Offset1 = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte);
+ MrcWriteCR (MrcData, Offset1, DdrDataControl1.Data);
+ if (UpdateHost) {
+ ChannelOut->DqControl1[Byte].Data = DdrDataControl1.Data;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (SkipPrint) ? "" : "%d\t", New);
+ break;
+
+ case mcodtd:
+ //
+ // Duration
+ //
+ DdrDataControl1.Data = ChannelOut->DqControl1[Byte].Data;
+ New = (U8) DdrDataControl1.Bits.OdtDuration + Off;
+ if (New < 0) {
+ New = 0; // 11 tQCK Min
+ } else if (New > DDRDATA0CH0_CR_DDRCRDATACONTROL1_OdtDuration_MAX) {
+ New = DDRDATA0CH0_CR_DDRCRDATACONTROL1_OdtDuration_MAX; // 18 tQCK Max
+ }
+
+ DdrDataControl1.Bits.OdtDuration = New;
+ DdrDataControl1.Bits.SenseAmpDuration = New;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "mcodtd CRValue = 0x%x\n", DdrDataControl1.Bits.OdtDuration);
+ //
+ Offset1 = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte);
+ MrcWriteCR (MrcData, Offset1, DdrDataControl1.Data);
+ if (UpdateHost) {
+ ChannelOut->DqControl1[Byte].Data = DdrDataControl1.Data;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (SkipPrint) ? "" : "%d\t", DdrDataControl1.Bits.OdtDuration);
+ break;
+
+ case rtl:
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (RankMask & (MRC_BIT0 << Rank)) {
+ //
+ // Update IO Latency & RoundTrip
+ //
+ IOLat = ChannelOut->IoLatency[Rank] - (ChannelOut->RTLatency[Rank] - Off);
+ if ((S8) IOLat < 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "IOLatency reached the Saturation point \n");
+ } else {
+ Offset1 = 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, Offset1);
+ Offset2 = MCHBAR_CH0_CR_SC_ROUNDT_LAT_REG +
+ ((MCHBAR_CH1_CR_SC_ROUNDT_LAT_REG - MCHBAR_CH0_CR_SC_ROUNDT_LAT_REG) * Channel);
+ ScRoundtLat.Data = MrcReadCR (MrcData, Offset2);
+ switch (Rank) {
+ case 0:
+ ScIoLatency.Bits.IOLAT_R0D0 = IOLat;
+ ScRoundtLat.Bits.Lat_R0D0 = Off;
+ break;
+
+ case 1:
+ ScIoLatency.Bits.IOLAT_R1D0 = IOLat;
+ ScRoundtLat.Bits.Lat_R1D0 = Off;
+ break;
+
+ case 2:
+ ScIoLatency.Bits.IOLAT_R0D1 = IOLat;
+ ScRoundtLat.Bits.Lat_R0D1 = Off;
+ break;
+
+ case 3:
+ ScIoLatency.Bits.IOLAT_R1D1 = IOLat;
+ ScRoundtLat.Bits.Lat_R1D1 = Off;
+ break;
+
+ default:
+ break;
+ }
+
+ MrcWriteCR (MrcData, Offset1, ScIoLatency.Data);
+ MrcWriteCR (MrcData, Offset2, ScRoundtLat.Data);
+
+ //
+ // Update host
+ //
+ if (UpdateHost) {
+ ChannelOut->RTLatency[Rank] = Off;
+ ChannelOut->IoLatency[Rank] = IOLat;
+ }
+ }
+ }
+ }
+ break;
+
+ case srrd2rd:
+ //
+ // sr RD 2 RD Turn Around offsets
+ //
+ TcBankRankA.Data = ChannelOut->MchbarBANKRANKA;
+ TcBankRankA.Bits.tRDRD = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG + ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankA.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKA = TcBankRankA.Data;
+ }
+ break;
+
+ case srrd2wr:
+ //
+ // sr RD 2 WR Turn Around offsets
+ //
+ TcBankRankC.Data = ChannelOut->MchbarBANKRANKC;
+ TcBankRankC.Bits.tRDWR = Off;
+ Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_C_REG +
+ ((MCHBAR_CH1_CR_TC_BANK_RANK_C_REG - MCHBAR_CH0_CR_TC_BANK_RANK_C_REG) * Channel);
+ MrcWriteCR (MrcData, Offset1, TcBankRankC.Data);
+ if (UpdateHost) {
+ ChannelOut->MchbarBANKRANKC = TcBankRankC.Data;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ ((OptParam != mcodtd) && (OptParam != mcodts) && (!SkipPrint)) ? "%d\t" : "",
+ Off
+ );
+
+ return;
+}
+
+/**
+ This function applies the new DRAM ODT settings
+ Walks through various optimizations to get the best result with new ODT values
+ This includes WrDS, RdODT, Eq, etc.
+ Updates Best* variables if this point if better than the prior points
+ chDone is both an input and output. Reports which channels have a good enough value
+ if SkipRd is high, it will skip the read related functions (RdODT, RdEq, RdTiming)
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in,out] DimmOptPoints - Structure of all the DIMM ODT optimal settings.
+ @param[in] ChMask - Channel to work on.
+ @param[in] RankMask - Rank to work on.
+ @param[in] skipGRdOdt - Used to skip RdODT.
+ @param[in] RttNom - Rtt_Nom value for each DIMM.
+ @param[in] RttWr - Rtt_Wr value for each DIMM.
+ @param[in] GRdOdt - CPU Global Read ODT.
+ @param[in] OptParamTestList - List of Opt test(Drive Strength, RxBias, TxEq, RxEq) to run.
+ @param[in] OptParamTestListSize - Size of OptParamTestList.
+ @param[in] SubPwrLimits - Switch to apply power limits to the suboptimization.
+ @param[in] skipOptTests - Skips the suboptimization.
+ @param[in] skipOptPrint - Skip printing of the suboptimization steps.
+ @param[in] RdCenter - Switch to recenter read.
+ @param[in] WrCenter - Switch to recenter write.
+ @param[in] inputBestMargin - Array of the best margin for each test.
+ @param[in] MarginsLength - Length of inputBestMargin.
+ @param[in] OffsetPoint - Index inside inputBestMargin to start.
+
+ @retval Nothing.
+**/
+void
+TrainDimmOdtSetting (
+ IN MrcParameters *const MrcData,
+ IN OUT DimmOptPoint *DimmOptPoints,
+ IN U8 ChMask,
+ IN U8 RankMask,
+ IN U8 skipGRdOdt,
+ IN U8 RttNom[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL],
+ IN U8 RttWr[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL],
+ IN S8 GRdOdt,
+ IN U8 *OptParamTestList,
+ IN U8 OptParamTestListSize,
+ IN BOOL SubPwrLimits,
+ IN BOOL skipOptTests,
+ IN BOOL skipOptPrint,
+ IN BOOL RdCenter,
+ IN BOOL WrCenter,
+ IN void *inputBestMargin,
+ IN U8 MarginsLength,
+ IN U8 OffsetPoint
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ DimmOptPoint *PointResults;
+ OptOffsetChByte BestOffArr[SizeOfTCompOffset][MAX_RANK_IN_CHANNEL];
+ TCompOffset OffsetType;
+ U8 dimm;
+ U8 rank;
+ U8 ValidRankMask;
+ U8 LocalRanks[MAX_CHANNEL];
+ U8 ChBitMask;
+ U8 Channel;
+ U8 ParamList[] = { RdV, RdT, WrV, WrT }; // List of parameters to margin
+ U8 TestListRd[] = { RdV, RdT };
+ U8 TestListWr[] = { WrV, WrT };
+ U8 *TestList;
+ U8 TestListSize;
+ U8 TScale[] = { 1, 2, 1, 0, 0 };
+ U8 GScale[] = { 1, 2, 0, 0, 0 };
+ U16 GPwrLimits[] = { 520, 280, 0, 0, 0 };
+ U16 noPwrLimits[] = { 2480, 2240, 0, 0, 0 };
+ U8 *Scale;
+ U16 *PwrLimits;
+ S8 start;
+ S8 stop;
+ U8 i;
+ U8 t;
+ U8 ResultType;
+ U8 RecenterLC;
+ U8 OptParamLC;
+ BOOL clipPowerLmt;
+ U16 *BestMargin;
+ U8 TestResultType[4] = { 0, 0, 0, 0 };
+
+ TestListSize = 0;
+ RecenterLC = 15;
+ OptParamLC = OPT_PARAM_LOOP_COUNT;
+ clipPowerLmt = 1;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ ControllerOut = &Outputs->Controller[0];
+
+ PointResults = DimmOptPoints;
+ BestMargin = (U16 *) inputBestMargin;
+ MrcOemMemorySet ((U8 *) BestOffArr, 0xffff, sizeof (BestOffArr));
+ MrcOemMemorySet ((U8 *) PointResults, 0xffff, sizeof (DimmOptPoint));
+ OffsetType = 0;
+
+ if (SubPwrLimits) {
+ //
+ // Use power limits and Trendline
+ //
+ Scale = TScale;
+ PwrLimits = GPwrLimits;
+ } else {
+ //
+ // No power limits and no TrendLine
+ //
+ Scale = GScale;
+ PwrLimits = noPwrLimits;
+ }
+ //
+ // TrainDDROptParam already check the valid against host chRankBit mask
+ // Walk through channels, check if this point is redundant, set RttNom
+ //
+ ChMask &= Outputs->ValidChBitMask;
+ RankMask &= Outputs->ValidRankMask;
+ ValidRankMask = 0;
+ ChBitMask = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ LocalRanks[Channel] = 0;
+ if (((MRC_BIT0 << Channel) & ChMask)) {
+ LocalRanks[Channel] = RankMask & ControllerOut->Channel[Channel].ValidRankBitMask;
+ if (LocalRanks[Channel]) {
+ ChBitMask |= MRC_BIT0 << Channel; // remove ch with no "active" ranks
+ }
+ }
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ for (dimm = 0; dimm < MAX_DIMMS_IN_CHANNEL; dimm++) {
+ PointResults->ODTSet.RttNom[Channel][dimm] = RttNom[Channel][dimm];
+ PointResults->ODTSet.RttWr[Channel][dimm] = RttWr[Channel][dimm];
+ }
+ }
+
+ PointResults->ODTSet.GRdOdt = GRdOdt;
+ UpdateOdtsValues (MrcData, ChBitMask, PointResults, skipGRdOdt, 0, 1, 1);
+ //
+ // update only DimmOdt and GROdt if not skipped.
+ // Recenter Timing
+ //
+ if (RdCenter) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Vref\n");
+ ReadVoltageCentering2D (
+ MrcData,
+ Outputs->MarginResult,
+ ChBitMask,
+ RdV,
+ 0,
+ 0,
+ RecenterLC,
+ 0
+ );
+ //
+ // We can add if status fail go to next point
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Timing ChBitMask=%x\n", ChBitMask);
+ DataTimeCentering2D (
+ MrcData,
+ Outputs->MarginResult, // prev. margin results
+ ChBitMask,
+ RdT,
+ 0, // EnPerBit,
+ 0, // EnRxDutyCycle
+ 0, // ResetPerBit
+ RecenterLC,
+ 0 // En2D
+ );
+ }
+
+ if (WrCenter) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Vref\n");
+ MrcWriteVoltageCentering2D (MrcData);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Timing ChBitMask=%x\n", ChBitMask);
+ DataTimeCentering2D (
+ MrcData,
+ Outputs->MarginResult, // prev. margin results
+ ChBitMask,
+ WrT,
+ 0, // EnPerBit,
+ 0, // EnRxDutyCycle
+ 0, // ResetPerBit
+ RecenterLC,
+ 0 // En2D
+ );
+ }
+ //
+ // @todo: we could check here if we have some reasonable amount of margin to play with
+ //
+ TestList = ParamList;
+ PointResults->OptParamTestListSize = OptParamTestListSize;
+ for (t = 0; t < OptParamTestListSize; t++) {
+ //
+ // also apply the best offset to hw and host and inside also best offset related margin is saved in host struct
+ //
+ PointResults->OptParamTestList[t] = OptParamTestList[t];
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "OptParamTestList[%d]=%d , %s\n",t,OptParamTestList[t],TOptParamOffsetString[OptParamTestList[t]]);
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "OptParamTestList[%d]=%d OptParamTestListSize=%d\n",t,OptParamTestList[t],OptParamTestListSize);
+ switch (OptParamTestList[t]) {
+ case (OptWrDS):
+ start = -11;
+ stop = 12;
+ TrainDDROptParam (
+ MrcData,
+ &BestOffArr[WrDSOfft][0],
+ ChBitMask,
+ RankMask,
+ OptWrDS,
+ TestListWr,
+ sizeof (TestListWr),
+ Scale,
+ PwrLimits,
+ start,
+ stop,
+ OptParamLC,
+ 1, // Repeats
+ skipOptPrint,
+ skipOptTests,
+ 0, // RdRd2Test
+ 1 // GuardBand
+ );
+ TestList = TestListWr;
+ TestListSize = sizeof (TestListWr);
+ OffsetType = WrDSOfft;
+ PointResults->BestOptOff[WrDSOfft][0] = BestOffArr[WrDSOfft][0];
+ break;
+
+ case (OptRdOdt):
+ start = -10;
+ stop = 6;
+ TrainDDROptParam (
+ MrcData,
+ &BestOffArr[RdOdtOfft][0],
+ ChBitMask,
+ RankMask,
+ OptRdOdt,
+ TestListRd,
+ sizeof (TestListRd),
+ Scale,
+ PwrLimits,
+ start,
+ stop,
+ OptParamLC,
+ 1, // Repeats
+ skipOptPrint,
+ skipOptTests,
+ RdRdTA, // RdRd2Test
+ 0 // GuardBand
+ );
+ TestList = TestListRd;
+ TestListSize = sizeof (TestListRd);
+ OffsetType = RdOdtOfft;
+ PointResults->BestOptOff[RdOdtOfft][0] = BestOffArr[RdOdtOfft][0];
+ break;
+
+ case (OptSComp):
+ case (OptTComp):
+ break;
+
+ case (OptTxEq):
+ start = 0;
+ stop = 11;
+ for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) {
+ if (!((MRC_BIT0 << rank) & RankMask)) {
+ continue; // check if rank at least on one channel
+ }
+
+ TrainDDROptParam (
+ MrcData,
+ &BestOffArr[TxEqOfft][rank],
+ ChBitMask,
+ (MRC_BIT0 << rank),
+ OptTxEq,
+ TestListWr,
+ sizeof (TestListWr),
+ Scale,
+ PwrLimits,
+ start,
+ stop,
+ OptParamLC,
+ 1, // Repeats
+ skipOptPrint,
+ skipOptTests,
+ 0, // RdRd2Test
+ 2 // GuardBand
+ );
+ PointResults->BestOptOff[TxEqOfft][rank] = BestOffArr[TxEqOfft][rank];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << rank) & LocalRanks[Channel])) {
+ continue;
+ //
+ // check if the rank exist in this ch
+ //
+ }
+
+ for (i = 0; i < sizeof (TestListWr); i++) {
+ //
+ // track min margin per ch
+ //
+ if (BestOffArr[TxEqOfft][rank].Margins[i][Channel] < BestOffArr[TxEqOfft][0].Margins[i][Channel]) {
+ BestOffArr[TxEqOfft][0].Margins[i][Channel] = BestOffArr[TxEqOfft][rank].Margins[i][Channel];
+ }
+ }
+ }
+ }
+
+ TestList = TestListWr;
+ TestListSize = sizeof (TestListWr);
+ OffsetType = TxEqOfft;
+ break;
+
+ case (OptRxEq):
+ start = 0;
+ stop = 14;
+ for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) {
+ if (!((MRC_BIT0 << rank) & RankMask)) {
+ continue; // check if rank at least on one channel
+ }
+
+ TrainDDROptParam (
+ MrcData,
+ &BestOffArr[RxEqOfft][rank],
+ ChBitMask,
+ (MRC_BIT0 << rank),
+ OptRxEq,
+ TestListRd,
+ sizeof (TestListRd),
+ Scale,
+ noPwrLimits,
+ start,
+ stop,
+ OptParamLC,
+ 1, // Repeats
+ skipOptPrint,
+ skipOptTests,
+ RdRdTA, // RdRd2Test
+ 0 // GuardBand
+ );
+ PointResults->BestOptOff[RxEqOfft][rank] = BestOffArr[RxEqOfft][rank];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((1 << rank) & LocalRanks[Channel])) {
+ continue; // check if the rank exist in this ch
+ }
+
+ for (i = 0; i < sizeof (TestListRd); i++) {
+ //
+ // track min margin per ch and asign to rank0
+ //
+ if (BestOffArr[RxEqOfft][rank].Margins[i][Channel] < BestOffArr[RxEqOfft][0].Margins[i][Channel]) {
+ BestOffArr[RxEqOfft][0].Margins[i][Channel] = BestOffArr[RxEqOfft][rank].Margins[i][Channel];
+ }
+ }
+ }
+ }
+
+ TestList = TestListRd;
+ TestListSize = sizeof (TestListRd);
+ OffsetType = RxEqOfft;
+ break;
+
+ case (OptRxBias):
+ start = 0;
+ stop = 7;
+ TrainDDROptParam (
+ MrcData,
+ &BestOffArr[RdSAmpOfft][0],
+ ChBitMask,
+ RankMask,
+ OptRxBias,
+ TestListRd,
+ sizeof (TestListRd),
+ Scale,
+ PwrLimits,
+ start,
+ stop,
+ OptParamLC,
+ 1, // Repeats
+ skipOptPrint,
+ skipOptTests,
+ RdRdTA, // RdRd2Test
+ 0 // GuardBand
+ );
+ TestList = TestListRd;
+ TestListSize = sizeof (TestListRd);
+ OffsetType = RdSAmpOfft;
+ PointResults->BestOptOff[RdSAmpOfft][0] = BestOffArr[RdSAmpOfft][0];
+ break;
+
+ case (OptDimmOdt):
+ break;
+
+ case (OptDimmOdtWr):
+ break;
+
+ default:
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "OptParam Test not valid\n");
+
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(LocalRanks[Channel])) {
+ continue; // check if the active rank run this ch
+ }
+ //
+ // run through all BestOff[optParam][0] and track min[RdV,RdT,WrV,WrT]
+ //
+ for (i = 0; i < TestListSize; i++) {
+ ResultType = GetMarginResultType (TestList[i]);
+ TestResultType[ResultType] = TestList[i]; // indicate which test we run and create the reverse dic
+ //
+ //we need to update only last results
+ //
+ PointResults->Test[ResultType][Channel] = BestOffArr[OffsetType][0].Margins[i][Channel];
+ }
+ }
+ } // end for OptParamTest
+ //
+ // assign the point for passing to the FindOptimalTradeOff function
+ //
+ i = 0;
+ PointResults->NumTests = 0;
+ for (t = 0; t < 4; t++) {
+ //
+ // ResultType=GetMarginResultType(TestList[i]);
+ //
+ if (TestResultType[t] == 0) {
+ continue; // can only be 1,2,4,5
+ } else {
+ PointResults->TestList[i] = TestResultType[t];
+ PointResults->NumTests++;
+ //
+ // *(BestMargin+i*MarginsLength+OffsetPoint)=PointResults->Test[t][Channel];
+ // sorting test for TradeOff
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(LocalRanks[Channel])) {
+ continue; // check if the active rank run this ch
+ }
+
+ if (clipPowerLmt) {
+ if (PointResults->Test[t][Channel] > UpmPwrLimitValue (MrcData, TestResultType[t], PowerLimit)) {
+ PointResults->Points2Trade[i][Channel] = UpmPwrLimitValue (MrcData, TestResultType[t], PowerLimit);
+ } else {
+ PointResults->Points2Trade[i][Channel] = PointResults->Test[t][Channel];
+ }
+ } else {
+ PointResults->Points2Trade[i][Channel] = PointResults->Test[t][Channel];
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "PointResults->TestList[%d]=%d PointResults->Test[test index=%d][channel=%d] =%d\n",i,PointResults->TestList[i],t,Channel,PointResults->Test[t][Channel]);
+ //
+ }
+
+ i++;
+ }
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "PointResults->NumTests =%d\n",PointResults->NumTests);
+ //
+ return;
+}
+
+/**
+ This function applies an offset to the global compensation logic.
+ Reruns Compensation and returns the new comp value
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] param - Parameter defining the desired global compensation logic
+ @param[in] offset - Value to apply
+ @param[in] UpdateHost - Desides if MrcData has to be updated
+
+ @retval Returns the new comp value.
+**/
+U32
+UpdateCompGlobalOffset (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 param,
+ IN const U32 offset,
+ IN const U8 UpdateHost
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0;
+ DDRCOMP_CR_DDRCRCOMPCTL1_STRUCT DdrCrCompCtl1;
+ PCU_CR_M_COMP_PCU_STRUCT PcuCrMComp;
+ DDRCOMP_CR_DDRCRDATACOMP0_STRUCT DdrCrDataComp0;
+ DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1;
+ DDRCOMP_CR_DDRCRCMDCOMP_STRUCT DdrCrCmdComp;
+ DDRCOMP_CR_DDRCRCTLCOMP_STRUCT DdrCrCtlComp;
+ DDRCOMP_CR_DDRCRCLKCOMP_STRUCT DdrCrClkComp;
+ DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0_Temp;
+ U32 RegOffset;
+ U8 Channel;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ DdrCrCompCtl0.Data = Outputs->CompCtl0;
+ DdrCrCompCtl1.Data = Outputs->CompCtl1;
+ DdrCrDataControl0.Data = 0;
+
+ //
+ // Update offset in local CR variable
+ //
+ switch (param) {
+ case RdOdt:
+ //
+ // Disable FixOdt feature before changing this param
+ //
+ DdrCrCompCtl0.Bits.FixOdtD = 0;
+ //
+ // Apply Comp Offset to RdOdt
+ //
+ DdrCrCompCtl0.Bits.DqOdtVref = offset;
+ break;
+
+ case WrDS:
+ //
+ // Apply Comp Offset to WrDS-DQ
+ //
+ DdrCrCompCtl0.Bits.DqDrvVref = offset;
+ break;
+
+ case WrDSCmd:
+ //
+ // Apply Comp Offset to WrDS-CMD
+ //
+ DdrCrCompCtl0.Bits.CmdDrvVref = offset;
+ break;
+
+ case WrDSCtl:
+ //
+ // Apply Comp Offset to WrDS-CTL
+ //
+ DdrCrCompCtl0.Bits.CtlDrvVref = offset;
+ break;
+
+ case WrDSClk:
+ //
+ // Apply Comp Offset to WrDS-CLK
+ //
+ DdrCrCompCtl0.Bits.ClkDrvVref = offset;
+ break;
+
+ case SCompDq:
+ //
+ // Apply Comp Offset to Scomp-DQ
+ //
+ DdrCrCompCtl1.Bits.DqScompCells = offset;
+ DdrCrCompCtl1.Bits.DqScompPC = offset >> 4;
+ break;
+
+ case SCompCmd:
+ //
+ // Apply Comp Offset to Scomp-CMD
+ //
+ DdrCrCompCtl1.Bits.CmdScompCells = offset;
+ DdrCrCompCtl1.Bits.CmdScompPC = offset >> 4;
+ break;
+
+ case SCompCtl:
+ //
+ // Apply Comp Offset to Scomp-CTL
+ //
+ DdrCrCompCtl1.Bits.CtlScompCells = offset;
+ DdrCrCompCtl1.Bits.CtlScompPC = offset >> 4;
+ break;
+
+ case SCompClk:
+ //
+ // Apply Comp Offset to Scomp-CLK
+ //
+ DdrCrCompCtl1.Bits.ClkScompCells = offset;
+ DdrCrCompCtl1.Bits.ClkScompPC = offset >> 4;
+ break;
+
+ case DisOdtStatic:
+ //
+ // disable static read Otd legs
+ //
+ DdrCrCompCtl0.Bits.DisableOdtStatic = offset;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ DdrCrDataControl0.Data = ChannelOut->DqControl0.Data;
+ DdrCrDataControl0.Bits.DisableOdtStatic = offset; // apply to bytes fubs
+ RegOffset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, RegOffset, DdrCrDataControl0.Data);
+ if (UpdateHost) {
+ ChannelOut->DqControl0.Data = DdrCrDataControl0.Data;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ //
+ // Update the Comp Offsets and Host Structure
+ //
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL0_REG, DdrCrCompCtl0.Data);
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL1_REG, DdrCrCompCtl1.Data);
+ if (UpdateHost) {
+ Outputs->CompCtl0 = DdrCrCompCtl0.Data;
+ Outputs->CompCtl1 = DdrCrCompCtl1.Data;
+ }
+ //
+ // Run Compensation
+ // Start Comp Engine
+ //
+ PcuCrMComp.Data = 0;
+ PcuCrMComp.Bits.COMP_FORCE = 1;
+ PcuCrMComp.Bits.COMP_INTERVAL = MIN (COMP_INT, PCU_CR_M_COMP_PCU_COMP_INTERVAL_MAX);
+ PcuCrMComp.Bits.COMP_DISABLE = 1;
+ MrcWriteCR (MrcData, PCU_CR_M_COMP_PCU_REG, PcuCrMComp.Data);
+ MrcWait (MrcData, 8 * HPET_1US); // Wait for Comp to Complete
+ if (param == RdOdt) {
+ //
+ // we check if we close to saturation and try dis/en the static legs
+ //
+ DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG);
+ if ((DdrCrDataComp1.Bits.RcompOdtUp < 16) || (DdrCrDataComp1.Bits.RcompOdtUp > 48)) {
+ //
+ // disable/enable static read Otd legs
+ //
+ if (DdrCrDataComp1.Bits.RcompOdtUp < 16) {
+ DdrCrCompCtl0.Bits.DisableOdtStatic = 1;
+ } else {
+ DdrCrCompCtl0.Bits.DisableOdtStatic = 0;
+ }
+ //
+ // Update the Comp Offsets and Host Structure
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ ChannelOut->DqControl0.Bits.DisableOdtStatic = DdrCrCompCtl0.Bits.DisableOdtStatic; // apply to bytes fubs
+ RegOffset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "DdrCrDataControl0.Bits.DisableOdtStatic=%d\n",DdrCrDataControl0.Bits.DisableOdtStatic);
+ //
+ MrcWriteCrMulticast (MrcData, RegOffset, ChannelOut->DqControl0.Data);
+ }
+
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL0_REG, DdrCrCompCtl0.Data);
+ //
+ // host need to always be updated with static state
+ //
+ DdrCrCompCtl0_Temp.Data = Outputs->CompCtl0;
+ DdrCrCompCtl0_Temp.Bits.DisableOdtStatic = DdrCrCompCtl0.Bits.DisableOdtStatic;
+ Outputs->CompCtl0 = DdrCrCompCtl0_Temp.Data;
+ //
+ // Run Compensation
+ // Start Comp Engine
+ //
+ MrcWriteCR (MrcData, PCU_CR_M_COMP_PCU_REG, PcuCrMComp.Data);
+ MrcWait (MrcData, 8 * HPET_1US); // Wait for Comp to Complete
+ }
+
+ }
+ //
+ // Return the new comp code
+ //
+ switch (param) {
+ case DisOdtStatic:
+ case RdOdt:
+ DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG);
+ //
+ // re-Enable FixOdt feature after changing this param
+ //
+ DdrCrCompCtl0.Bits.DqOdtUpDnOff = DdrCrDataComp1.Bits.RcompOdtDown - DdrCrDataComp1.Bits.RcompOdtUp;
+ DdrCrCompCtl0.Bits.FixOdtD = 1;
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL0_REG, DdrCrCompCtl0.Data);
+ if (UpdateHost) {
+ Outputs->CompCtl0 = DdrCrCompCtl0.Data;
+ }
+ return DdrCrDataComp1.Bits.RcompOdtUp;
+
+ case WrDS:
+ case SCompDq:
+ DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG);
+ return (param == WrDS) ? DdrCrDataComp0.Bits.RcompDrvUp : DdrCrDataComp0.Bits.SlewRateComp;
+
+ case WrDSCmd:
+ case SCompCmd:
+ DdrCrCmdComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCMDCOMP_REG);
+ return (param == WrDSCmd) ? DdrCrCmdComp.Bits.RcompDrvUp : DdrCrCmdComp.Bits.Scomp;
+
+ case WrDSCtl:
+ case SCompCtl:
+ DdrCrCtlComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCTLCOMP_REG);
+ return (param == WrDSCtl) ? DdrCrCtlComp.Bits.RcompDrvUp : DdrCrCtlComp.Bits.Scomp;
+
+ case WrDSClk:
+ case SCompClk:
+ DdrCrClkComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCLKCOMP_REG);
+ return (param == WrDSClk) ? DdrCrClkComp.Bits.RcompDrvUp : DdrCrClkComp.Bits.Scomp;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ Programs Delay/Duration for the SenseAmp and MCODT based on RcvEn timing
+ Provide GuardBand > 0 if needed to be more conservative in timing
+ Main goal is to optimize power
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] GuardBand - Input parameter with more conservative value
+
+ @retval Nothing
+**/
+void
+UpdateSampOdtTiming (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 GuardBand
+ )
+
+{
+ MrcOutput *Outputs;
+ MrcDebug *Debug;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ U16 *CurRcvEn;
+ U8 Channel;
+ U8 Byte;
+ U8 rank;
+ U16 MaxRcvEn;
+ U16 MinRcvEn;
+ U32 Offset;
+ U32 SWakeUp;
+ U32 SAWakeUppS; // Round up to nearest Qclk
+ S8 SOn; // SenseAmpDelay
+ S8 OOn; // OdtDelay
+ S32 SOff; // SenseAmpDuration
+ S32 OOff; // OdtDuration
+ DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT *DqControl1;
+
+ SAWakeUppS = 1250;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ ControllerOut = &Outputs->Controller[0];
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "UpdateSampOdtTiming: GuardBand = %d\n", GuardBand);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Ch %d\tOdtOn\tOdtOff\tSAmpOn\tSAmpOff\n", Channel);
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ MaxRcvEn = 0;
+ MinRcvEn = 512;
+
+ for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) {
+ if (MrcRankInChannelExist (MrcData, rank, Channel)) {
+ CurRcvEn = &ChannelOut->RcvEn[rank][Byte];
+ if (MaxRcvEn < *CurRcvEn) {
+ MaxRcvEn = *CurRcvEn;
+ }
+
+ if (MinRcvEn > *CurRcvEn) {
+ MinRcvEn = *CurRcvEn;
+ }
+ }
+ }
+ //
+ // Round Max to nearest cycle
+ //
+ MaxRcvEn = (MaxRcvEn >> 6) + 1;
+
+ //
+ // SENSE AMP CAN ONLY BE ON WHEN ODT IS ON FOR EOS REASONS.
+ // SWakeUp = (U32)( (SAWakeUppS + Outputs->Qclkps - 1) / Outputs->Qclkps );
+ // SOn = MinRcvEn - SWakeUp - GuardBand;
+ // OOn = MinRcvEn - 2 - GuardBand;
+ //
+ SWakeUp = (U32) ((64 * SAWakeUppS) / Outputs->Qclkps); // Convert to PI codes
+ //
+ // Turn On ODT & Samp at least 2 Qclks before earlier RcvEn Rise
+ //
+ if (SWakeUp < 128) {
+ SWakeUp = 128; // at least 2-Qclks
+ }
+
+ OOn = SOn = (S8) ((MinRcvEn - SWakeUp) >> 6) - GuardBand;
+ //
+ // SenseAmp Delay
+ //
+ if (SOn < -4) {
+ SOn = -4; // RcvEnPi[8:6] - 5 qclk
+ } else if (SOn > 6) {
+ SOn = 6; // RcvEnPi[8:6] + 5 qclk
+ }
+ //
+ // OdtDelay
+ //
+ if (OOn < -4) {
+ OOn = -4; // RcvEnPi[8:6] - 5 qclk
+ } else if (OOn > 6) {
+ OOn = 6; // RcvEnPi[8:6] + 5 qclk
+ }
+ //
+ // Turn Off Samp 1 qclk after postamble
+ // Turn Off ODT 1 qclk after postamble
+ // Program the duration to leave Odt/Samp On
+ // OnBeforeRcvEn BL+Post AfterPost CR Encoding
+ //
+ SOff = (MaxRcvEn - SOn) + (8 + 1) + 1 + GuardBand - 11;
+ OOff = (MaxRcvEn - OOn) + (8 + 1) + 1 + GuardBand - 11;
+
+ if (SOff < 0) {
+ SOff = 0; // 11 tQCK Min
+ } else if (SOff > 7) {
+ SOff = 7; // 18 tQCK Max
+ }
+
+ if (OOff < 0) {
+ OOff = 0; // 11 tQCK Min
+ } else if (OOff > 7) {
+ OOff = 7; // 18 tQCK mAx
+ }
+
+ DqControl1 = &ChannelOut->DqControl1[Byte];
+ DqControl1->Bits.OdtDelay = OOn;
+ DqControl1->Bits.OdtDuration = OOff;
+ DqControl1->Bits.SenseAmpDelay = SOn;
+ DqControl1->Bits.SenseAmpDuration = SOff;
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte) +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, DqControl1->Data);
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d:\t%d\t%d\t%d\t%d\n",
+ Byte,
+ DqControl1->Bits.OdtDelay,
+ DqControl1->Bits.OdtDuration,
+ DqControl1->Bits.SenseAmpDelay,
+ DqControl1->Bits.SenseAmpDuration
+ );
+ }
+ }
+ }
+
+ return;
+}
+
+/**
+ Turns off unused portions of the slave DLL to save power
+
+ @param[in,out] MrcData - Include all MRC global data.
+
+ @retval Nothing
+**/
+void
+UpdateSlaveDLLLength (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ U8 *CurRxDqs;
+ U32 Offset;
+ U8 Channel;
+ U8 byte;
+ U8 rank;
+ U8 MaxPi;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ MaxPi = 0;
+ for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) {
+ if (MrcRankInChannelExist (MrcData, rank, Channel)) {
+ CurRxDqs = &ChannelOut->RxDqsP[rank][byte];
+ if (MaxPi < *CurRxDqs) {
+ MaxPi = *CurRxDqs;
+ }
+
+ CurRxDqs = &ChannelOut->RxDqsN[rank][byte];
+ if (MaxPi < *CurRxDqs) {
+ MaxPi = *CurRxDqs;
+ }
+ }
+ }
+ //
+ // Update SlaveDLL Length for power Savings
+ // Calculate which segments to turn off:
+ // NEW (OFF: 0, PI<48: 0x2, PI<32: 0x4, PI<16: 0x6)
+ // results are: 0, 2 , 4 or 6
+ //
+ ChannelOut->DqControl1[byte].Bits.SdllSegmentDisable = ((7 - (MaxPi >> 3)) &~MRC_BIT0);
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * byte) +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, ChannelOut->DqControl1[byte].Data);
+ }
+ }
+ }
+
+ return;
+}
+
+#ifdef TRAD_FLAG
+/**
+ Update Internal clocks on setting if needed.
+
+ @param[in,out] MrcData - Include all MRC global data.
+
+ @retval Nothing
+**/
+void
+UpdateInternalClksOn (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ U32 Offset;
+ U8 Channel;
+ U8 Byte;
+ S8 SOn; // SenseAmpDelay
+ S8 OOn; // OdtDelay
+ S32 SOff; // SenseAmpDuration
+ S32 OOff; // OdtDuration
+ U8 InternalClkOn;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ InternalClkOn = 0;
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ OOn = MrcSE ((U8) ChannelOut->DqControl1[Byte].Bits.OdtDelay, 4, 8);
+ OOff = ChannelOut->DqControl1[Byte].Bits.OdtDuration;
+ SOn = MrcSE ((U8) ChannelOut->DqControl1[Byte].Bits.SenseAmpDelay, 4, 8);
+ SOff = ChannelOut->DqControl1[Byte].Bits.SenseAmpDuration;
+
+ //
+ // Check if OdtDelay + OdtDuration >= 7 or if SADelay + SADuration >= 7
+ //
+ if (((OOn + OOff) >= 7) || ((SOn + SOff) >= 7)) {
+ InternalClkOn = 1;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "ODTOn = %d, ODTOff = %d\n", OOn, OOff);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "SAOn = %d, SAOff = %d\n", SOn, SOff);
+ break;
+ }
+ }
+
+ ChannelOut->DqControl0.Bits.InternalClocksOn = InternalClkOn;
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ MrcWriteCrMulticast (MrcData, Offset, ChannelOut->DqControl0.Data);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "C%dDdrCrDataControl0.Data = 0x%x\n",
+ Channel,
+ ChannelOut->DqControl0.Data
+ );
+ }
+ }
+
+ return;
+}
+#endif // TRAD_FLAG
+
+/**
+ This function Shifts the CMD timing.
+ NOTE: ONLY one, ResetDDR or SelfRefresh can be set inside this function
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] Ranks - Parameter defining the desired global compensation logic
+ @param[in] offset - per channel Value to shift picode for
+ @param[in] ResetDDR - Do we reset DDR?
+ @param[in] SelfRefresh - Do we perform Self refresh?
+ @param[in] UpdateHost - Determines if MrcData has to be updated
+ @param[in] SkipTx - Determines if TX update should be skipped
+ @todo: SkipTx is NOT USED at this time and we don't skip it anyway
+
+ @retval MrcStatus - If it succeeds return mrcSuccess
+**/
+MrcStatus
+ShiftCh2Ch (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 Ranks,
+ IN const U8 *const offset,
+ IN U8 ResetDDR,
+ IN const U8 SelfRefresh,
+ IN const U8 UpdateHost,
+ IN const U8 SkipTx
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U8 Channel;
+ U8 Rank;
+ U8 RankMaskCh;
+ S32 NewValue;
+ S32 Offset;
+ BOOL Lpddr;
+
+ Status = mrcSuccess;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ if (SelfRefresh && ResetDDR) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "WARNING SelfRefresh OR ResetDDR can be set at once...performing SelfRefresh\n"
+ );
+ ResetDDR = 0;
+ }
+
+ if (SelfRefresh) {
+ EnterSR (MrcData);
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ RankMaskCh = Ranks & ChannelOut->ValidRankBitMask;
+
+ if (RankMaskCh == 0) {
+ continue;
+ }
+
+ Offset = offset[Channel];
+
+ //
+ // Shift CLK (this will shift DQ PIs as well)
+ //
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationClock, RankMaskCh, 3, Offset, UpdateHost);
+
+ //
+ // Shift CTL
+ //
+ NewValue = 0;
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (RankMaskCh & (1 << Rank)) {
+ NewValue = ChannelOut->CtlPiCode[Rank] + Offset;
+ break;
+ }
+ }
+
+ ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCtl, RankMaskCh, 1, NewValue, UpdateHost);
+
+ //
+ // Shift CmdS
+ //
+ ShiftPIforCmdTraining (
+ MrcData,
+ Channel,
+ MrcIterationCmdS,
+ RankMaskCh,
+ 1,
+ ChannelOut->CmdsCmdPiCode[0] + Offset,
+ UpdateHost
+ );
+
+ //
+ // Shift CmdN
+ //
+ ShiftPIforCmdTraining (
+ MrcData,
+ Channel,
+ MrcIterationCmdN,
+ RankMaskCh,
+ 1,
+ ChannelOut->CmdnCmdPiCode[0] + Offset,
+ UpdateHost
+ );
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ //
+ // For LPDDR need to shift CmdS PiCode[1] separately.
+ // Host struct is not updated, so update PiCode[0] manually, and then restore back.
+ //
+ ChannelOut->CmdsCmdPiCode[0] = ChannelOut->CmdsCmdPiCode[0] + Offset;
+ ShiftPIforCmdTraining (
+ MrcData,
+ Channel,
+ MrcIterationCmdS,
+ RankMaskCh,
+ 2,
+ ChannelOut->CmdsCmdPiCode[1] + Offset,
+ UpdateHost
+ );
+ ChannelOut->CmdsCmdPiCode[0] = ChannelOut->CmdsCmdPiCode[0] - Offset;
+ }
+#endif // ULT_FLAG
+ //
+ // Shift CKE
+ //
+ ShiftPIforCmdTraining (
+ MrcData,
+ Channel,
+ MrcIterationCke,
+ RankMaskCh,
+ 1,
+ ChannelOut->CkeCmdPiCode[0] + Offset,
+ UpdateHost
+ );
+ } // for Channel
+ //
+ // Reset DDR is required
+ //
+ if (ResetDDR) {
+ Status = MrcResetSequence (MrcData);
+ } else if (SelfRefresh) {
+ ExitSR (MrcData);
+ }
+
+ return Status;
+}
+
+/**
+ Returns the index into the array OptResult in the MrcOutput structure.
+
+ @param[in] OptParam - Margin parameter
+
+ @retval One of the following values: RdSAmpOfft(0), WrDSOfft (1), RxEqOfft(2), TxEqOfft (3), RdOdtOfft(4)
+**/
+U8
+GetOptResultType (
+ IN U8 OptParam
+ )
+{
+ switch (OptParam) {
+ case OptRxBias:
+ return RdSAmpOfft;
+
+ case OptWrDS:
+ return WrDSOfft;
+
+ case OptRxEq:
+ return RxEqOfft;
+
+ case OptTxEq:
+ return TxEqOfft;
+
+ case OptRdOdt:
+ return RdOdtOfft;
+
+ default:
+ return 0; // Return RdSAmpOfft to point to the beginning of the array
+ }
+}
+
+/**
+ Program DimmOptPoint values on CPU and DIMM sides, such as DIMM ODT, CPU ODT, Ron, Slew Rate, Equalization.
+
+ @param[in,out] MrcData - Include all MRC global data.
+ @param[in] ChMask - Channel to work on.
+ @param[in,out] BestDimmOptPoint - Best DIMM Opt settings used to update hardware
+ @param[in] SkipGRdOdt - Switch to skip updating CPU ODT
+ @param[in] SkipDimmOdts - Switch to skip updating DIMM ODT
+ @param[in] SkipBestOffsets - Switch to skip updating Opt settings
+ @param[in] UpdateHost - Switch to skip updating MRC host structure
+
+ @retval Nothing
+**/
+void
+UpdateOdtsValues (
+ IN OUT MrcParameters *const MrcData,
+ IN U8 ChMask,
+ IN OUT DimmOptPoint *BestDimmOptPoint,
+ IN BOOL SkipGRdOdt,
+ IN BOOL SkipDimmOdts,
+ IN BOOL SkipBestOffsets,
+ IN BOOL UpdateHost
+ )
+{
+ MrcOutput *Outputs;
+ U8 byte;
+ U8 rank;
+ U8 Channel;
+ U8 offset;
+ U8 Dimm;
+ U8 test;
+ U8 TestArray[5];
+ BOOL DebugPrint;
+ U8 OptParam;
+ U8 NumTests;
+ const MrcDebug *Debug;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ NumTests = BestDimmOptPoint->OptParamTestListSize;
+ DebugPrint = 0;
+
+ MrcOemMemorySet (TestArray, 0, sizeof (TestArray));
+ if (SkipBestOffsets) {
+ NumTests = 0;
+ }
+ //
+ // build tests array to update RdSAmpOfft(0), WrDSOfft (1), RxEqOfft(2), TxEqOfft (3), RdOdtOfft(4)
+ //
+ for (test = 0; test < NumTests; test++) {
+ OptParam = BestDimmOptPoint->OptParamTestList[test];
+ TestArray[GetOptResultType (OptParam)] = 1;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Test - %s : %d ,UpdateHost: %d\n",
+ TOptParamOffsetString[OptParam],
+ test,
+ UpdateHost
+ );
+ }
+
+ if (!SkipGRdOdt) {
+ //
+ // update GRdOdt
+ //
+ BestDimmOptPoint->ODTSet.GRdOdtCode = UpdateCompGlobalOffset (
+ MrcData,
+ RdOdt,
+ (U8) BestDimmOptPoint->ODTSet.GRdOdt,
+ UpdateHost
+ );
+ if (DebugPrint) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "best GRdODT aplly is : %d \n",
+ CalcRdOdt (MrcData, BestDimmOptPoint->ODTSet.GRdOdt)
+ );
+ }
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!(MrcChannelExist (Outputs, Channel))) {
+ continue; // Not valid channel
+ }
+
+ if (!((MRC_BIT0 << Channel) & ChMask)) {
+ continue;
+ }
+
+ offset = 1;
+ if ((Outputs->Controller[0].Channel[Channel].DimmCount == 1)) {
+ offset = 0; // disable dynamic odt
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ //
+ // set RttNom=write and RttWr=0
+ //
+ BestDimmOptPoint->ODTSet.RttNom[Channel][Dimm] = BestDimmOptPoint->ODTSet.RttWr[Channel][Dimm];
+ }
+ }
+#ifdef ULT_FLAG
+ if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) {
+ //
+ // On ULT (1DPC) DIMM ODT is connected to Vdd, so RttNom must be disabled
+ //
+ offset = 1;
+ BestDimmOptPoint->ODTSet.RttNom[Channel][0] = 0;
+ BestDimmOptPoint->ODTSet.RttNom[Channel][1] = 0;
+ BestDimmOptPoint->ODTSet.RttWr[Channel][1] = 0;
+ }
+#endif //ULT_FLAG
+
+ //
+ // Apply Best RTT Points
+ //
+ if (!SkipDimmOdts) {
+ UpdateOptParamOffset (
+ MrcData,
+ Channel,
+ 0x3,
+ 0,
+ OptDimmOdt,
+ (S16)
+ (
+ ((offset * BestDimmOptPoint->ODTSet.RttWr[Channel][0]) << 4) +
+ (BestDimmOptPoint->ODTSet.RttNom[Channel][0])
+ ),
+ UpdateHost
+ );
+ UpdateOptParamOffset (
+ MrcData,
+ Channel,
+ 0xC,
+ 0,
+ OptDimmOdt,
+ (S16)
+ (
+ ((offset * BestDimmOptPoint->ODTSet.RttWr[Channel][1]) << 4) +
+ (BestDimmOptPoint->ODTSet.RttNom[Channel][1])
+ ),
+ UpdateHost
+ );
+ if (DebugPrint) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "best RttNom0 aplly is : %d\t\n",
+ ActualDimmOdt[BestDimmOptPoint->ODTSet.RttNom[Channel][0]]
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "best RttNom1 aplly is : %d\t\n",
+ ActualDimmOdt[BestDimmOptPoint->ODTSet.RttNom[Channel][1]]
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "best RttWr0 aplly is : %d\t\n",
+ ActualDimmOdt[BestDimmOptPoint->ODTSet.RttWr[Channel][0]]
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "best RttWr1 aplly is : %d\t\n",
+ ActualDimmOdt[BestDimmOptPoint->ODTSet.RttWr[Channel][1]]
+ );
+ }
+ }
+
+ if (NumTests) {
+ for (byte = 0; byte < Outputs->SdramCount; byte++) {
+ //
+ // Apply Best RdOdt and WrDS
+ // OdtOff = Off[RdOdtOfft][0][Channel][byte] + RdOdtChOffset[Channel];
+ //
+ if (TestArray[RdSAmpOfft]) {
+ //
+ // OptRdOdt->OptRxBias
+ //
+ UpdateOptParamOffset (
+ MrcData,
+ Channel,
+ 0xF,
+ byte,
+ OptRxBias,
+ BestDimmOptPoint->BestOptOff[RdSAmpOfft][0].Offset[Channel][byte],
+ UpdateHost
+ );
+ }
+
+ if (TestArray[WrDSOfft]) {
+ UpdateOptParamOffset (
+ MrcData,
+ Channel,
+ 0xF,
+ byte,
+ OptWrDS,
+ BestDimmOptPoint->BestOptOff[WrDSOfft][0].Offset[Channel][byte],
+ UpdateHost
+ );
+ }
+
+ if (DebugPrint) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "enable=%d best WrDSOfft byte %d is : %d\t\n",
+ TestArray[WrDSOfft],
+ byte,
+ BestDimmOptPoint->BestOptOff[WrDSOfft][0].Offset[Channel][byte]
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "enable=%d best RdSAmpOfft byte %d is : %d\t\n",
+ TestArray[RdSAmpOfft],
+ byte,
+ BestDimmOptPoint->BestOptOff[RdSAmpOfft][0].Offset[Channel][byte]
+ );
+ }
+
+ for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) {
+ if (!MrcRankInChannelExist (MrcData, rank, Channel)) {
+ continue;
+ }
+ //
+ // Apply Best Tx/Rx EQ Codes
+ //
+ if (TestArray[RxEqOfft]) {
+ UpdateOptParamOffset (
+ MrcData,
+ Channel,
+ (MRC_BIT0 << rank),
+ byte,
+ OptRxEq,
+ BestDimmOptPoint->BestOptOff[RxEqOfft][rank].Offset[Channel][byte],
+ UpdateHost
+ );
+ }
+
+ if (TestArray[TxEqOfft]) {
+ UpdateOptParamOffset (
+ MrcData,
+ Channel,
+ (MRC_BIT0 << rank),
+ byte,
+ OptTxEq,
+ BestDimmOptPoint->BestOptOff[TxEqOfft][rank].Offset[Channel][byte],
+ UpdateHost
+ );
+ }
+
+ if (DebugPrint) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "enable=%d best OptRxEq rank%d byte %d is : %d\t\n",
+ TestArray[RxEqOfft],
+ rank,
+ byte,
+ BestDimmOptPoint->BestOptOff[RxEqOfft][rank].Offset[Channel][byte]
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "enable=%d best OptTxEq rank%d byte %d is : %d\t\n",
+ TestArray[TxEqOfft],
+ rank,
+ byte,
+ BestDimmOptPoint->BestOptOff[TxEqOfft][rank].Offset[Channel][byte]
+ );
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ Calculate Power based on Ron and Rodt
+ Includes both static power from Ron/Rodt and dynamic power from Cpad/Cline
+ The power results here are not absolutely correct but give a reasonable estimate (ie: within 2x) with the proper trends
+ Getting absolutely correct power numbers with simple calculations is fairly difficult given the transmission line nature of the system
+ Driver power is calculated as the amount of power drawn from the CPU pin (do we want this to be thermal power instead?) based on the Ron and ODTeff
+ ODTeff is calculated as both the real, resistive ODT on the bus in parallel with the effective impendence of the cap on the line
+ This effective impedance is how AC power is included in the measurements
+ This better models the real system behavior where the power consumed due to dynamic power reduces as termination strength increases
+ ODT power is calculated as a purely DC term based on Ron and Rodt
+ The final power reported back is a scaled version of the CPU and DRAM power
+ This allows one to weight the CPU vs. DRAM power differently in the optimization function based on what is more important
+ CPU power is generally more important since it can be translated into additional performance
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[out] Results - Results of the Power power calculations
+ @param[in] RonCpu - RON CPU value (ohm)
+ @param[in] RonDimm - RON DIMM value (ohm)
+ @param[in] Rodtcpu - RODT CPU value
+ @param[in] Rodtdram - RODT DRAM value
+ @param[in] Wodtdram - WODT DRAM value
+
+ @retval Nothing
+**/
+void
+CalcPower (
+ IN MrcParameters *MrcData,
+ OUT MrcPower *Results,
+ IN U16 RonCpu,
+ IN U8 RonDimm,
+ IN U16 Rodtcpu,
+ IN U16 Rodtdram,
+ IN U16 Wodtdram
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ U16 CapTotal;
+ U32 CapOdt;
+ U32 Rodt;
+ U32 Vx;
+ U32 Vy;
+ U32 Ix;
+ U32 Iy;
+ U32 DrvPwr;
+ U32 ACPowerRd;
+ U32 ACPowerWr;
+ //
+ // Power Results;
+ //
+ U16 ScaleCpuPwr;
+ U16 ScaleDramPwr;
+ U16 LineLength; // cm
+ U16 Cpad; // pF
+ U32 Derating;
+ U32 ACPower;
+ U32 Vswing;
+ U16 CapPerLength; // pF/cm
+ U16 Freq; // Ghz
+ U16 FreqEff;
+ U16 Pi; // 3.14;
+ U16 Vdd; // 1.5; mV
+ U16 SRDimm; // 15ohm serial resistance
+ U16 NormFactor;
+
+ Vx = 0;
+ Vy = 0;
+ Ix = 0;
+ Iy = 0;
+ ScaleCpuPwr = 1;
+ ScaleDramPwr = 1;
+ LineLength = 10;
+ Cpad = 4;
+ CapPerLength = 2;
+ Pi = 3;
+ SRDimm = 15;
+ NormFactor = 100; // if 1000 we get mW
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ Vdd = Outputs->VddVoltage[Inputs->MemoryProfile];
+ Freq = (U16) (Outputs->Frequency);
+ Freq /= 100; // in 10xGhz
+ //
+ // capacitance for AC power
+ // Cut real cap in half and add 10pF offset to better match curves - results x100 pf
+ // Fixed frequency at 500 MHz(~Data Rate/4 assuming random 1100 type data) - resutls is 100x Ghz
+ // In general, most of the simulations show fairly flat AC power vs. frequency
+ //
+ CapTotal = (Cpad + LineLength * CapPerLength) * 45;
+ FreqEff = 50;
+ CapOdt = 10000000 / (2 * Pi * CapTotal * FreqEff); // Scale Up by 2.5x to better match curves
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "RonDimm=%d Rodtcpu=%d Wodtdram=%d Rodtdram=%d RonCpu=%d CapOdt=%d\n",RonDimm,Rodtcpu,Wodtdram,Rodtdram,RonCpu,CapOdt);
+ // for read
+ //
+ Rodt = (Rodtcpu * (Rodtdram + SRDimm)) / (Rodtcpu + (Rodtdram + SRDimm));
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "(Rodtdram+SRDimm)=%d Rodtcpu=%d Rodt=%d \n",(Rodtdram+SRDimm),Rodtcpu,Rodt);
+ Derating = 1000 * Rodt / (Rodt + CapOdt); // Derate ACPower based on ratio of Real ODT vs. "0DT" due to cap
+ Ix = Vdd / 2 / (RonDimm + SRDimm + Rodt); // mA
+ Vx = Vdd - Ix * (RonDimm + SRDimm); // voltage after dimm driver+15ohm
+ DrvPwr = Ix * Ix * (RonDimm + SRDimm) / NormFactor; // dimm Ron static power
+ Vswing = 2 * Vx - Vdd; // for ACpower= Vh-Vl
+ //
+ // Calculate power associated with swing that cap - mV/1000*pf/100*Ghz/100000
+ //
+ ACPower = Vswing * Vswing / 1000 * CapTotal * FreqEff / 100 / 100 / NormFactor;
+ ACPowerRd = ACPower * Derating / 1000; // mW
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "read ACPowerRd=%d ACPower=%d Derating=%d Vswing=%d Rodt=%d Ix=%d,Vx=%d,PwrDrv=%d,FreqEff=%d CapOdt=%d Rodt=%d ,DrvPwr=%d\n",ACPowerRd,ACPower,Derating,Vswing,Rodt,Ix,Vx,DrvPwr,FreqEff,CapOdt,Rodt,DrvPwr);
+ Results->CpuPwrRd = ((Vdd - Vx) * (Vdd - Vx) + Vx * Vx) / (2 * Rodtcpu * NormFactor); // mW @todo:add RxBias?
+ Iy = (Vx - Vdd / 2) / (Rodtdram + SRDimm); // current in to the NT dimm
+ Vy = Vx - Iy * (SRDimm); // voltage after 15 ohm inside the dimm
+ Results->DimmPwrRd = Iy *
+ Iy *
+ SRDimm /
+ NormFactor +
+ ((Vdd - Vy) * (Vdd - Vy) + Vy * Vy) /
+ (2 * Rodtdram * NormFactor) +
+ DrvPwr; // mW
+ //
+ // for write
+ //
+ Rodt = (Wodtdram + SRDimm) * (Rodtdram + SRDimm) / ((Wodtdram + SRDimm) + (Rodtdram + SRDimm));
+ Derating = 1000 * Rodt / (Rodt + CapOdt); // De-rate ACPower based on ratio of Real ODT vs. "0DT" due to cap
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rodt=%d Derating=%d ",Rodt,Derating);
+ Ix = Vdd / 2 / (RonCpu + Rodt); // mA
+ Vx = Vdd - Ix * (RonCpu); // voltage after cpu driver
+ DrvPwr = Ix * Ix * (RonCpu) / NormFactor; // cpu Ron static power
+ Vswing = 2 * Vx - Vdd; // for ACpower
+ //
+ // Calculate power associated with swing that cap
+ //
+ ACPower = Vswing * Vswing / 1000 * CapTotal * FreqEff / 100 / 100 / NormFactor;
+ ACPowerWr = ACPower * Derating / 1000; // mW
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "write ACPowerWr=%d ACPower=%d Derating=%d Vswing=%d Rodt=%d Ix=%d,Vx=%d,PwrDrv=%d,FreqEff=%d CapOdt=%d Rodt=%d ,DrvPwr=%d\n",ACPowerWr,ACPower,Derating,Vswing,Rodt,Ix,Vx,DrvPwr,FreqEff,CapOdt,Rodt,DrvPwr);
+ Results->CpuPwrWr = DrvPwr; // mW
+ Iy = (Vx - Vdd / 2) / (Wodtdram + SRDimm); // current in to the T dimm
+ Vy = Vx - Iy * (SRDimm); // voltage after 15 ohm inside the dimm
+ Results->DimmPwrWrT = Iy * Iy * SRDimm / NormFactor + ((Vdd - Vy) * (Vdd - Vy) + Vy * Vy) / (2 * Wodtdram * NormFactor); // mW
+ Iy = (Vx - Vdd / 2) / (Rodtdram + SRDimm); // current in to the NT dimm
+ Vy = Vx - Iy * (SRDimm); // voltage after 15 ohm inside the dimm
+ Results->DimmPwrWrNT = Iy * Iy * SRDimm / NormFactor + ((Vdd - Vy) * (Vdd - Vy) + Vy * Vy) / (2 * Rodtdram * NormFactor); // mW
+
+ //
+ // ScaleCpuPwr and ScaleDramPwr allows one to tradeoff CPU vs. DRAM power
+ //
+ Results->ACPowerRd = ACPowerRd;
+ Results->ACPowerWr = ACPowerWr;
+ Results->TotPwr = (U16)
+ (
+ 60 * (Results->CpuPwrRd * ScaleCpuPwr + Results->DimmPwrRd * ScaleDramPwr + ACPowerRd * ScaleDramPwr) + 40 *
+ (
+ (Results->DimmPwrWrT + Results->DimmPwrWrNT) *
+ ScaleDramPwr +
+ Results->CpuPwrWr *
+ ScaleCpuPwr +
+ ACPowerWr *
+ ScaleCpuPwr
+ )
+ ) / 100;
+ Results->ACPower = (60 * ACPowerRd + 40 * ACPowerWr) / 100;
+ Results->CpuPower = (U16)
+ (
+ 60 * (Results->CpuPwrRd * ScaleCpuPwr ) + 40 *
+ (
+ Results->CpuPwrWr *
+ ScaleCpuPwr +
+ ACPowerWr *
+ ScaleCpuPwr
+ )
+ ) / 100;
+
+ Results->DimmPwr = (U16)
+ (
+ 60 * (Results->DimmPwrRd * ScaleDramPwr + ACPowerRd * ScaleDramPwr) + 40 *
+ (
+ (Results->DimmPwrWrT + Results->DimmPwrWrNT) *
+ ScaleDramPwr
+ )
+ ) / 100;
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " PwrTotal=%d DimmPwrR=%d DimmPwrWrT=%d DimmPwrWrNT=%d PwrDrvR=%d Rodt=%d PwrDrvW=%d\n",Results->TotPwr,Results->DimmPwrRd,Results->DimmPwrWrT,Results->DimmPwrWrNT,ACPowerRd,Rodt,ACPowerWr);
+ return;
+}
+
+/**
+ Calculate Power Trend line based on Cpu and Dimms Ron and Odt's
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel to work on.
+ @param[in] DimmMask - DIMMs to work on.
+ @param[in,out] DimmOptPoints - Structure of all the DIMM ODT optimal settings.
+ @param[in] Points2calc - Data to build the trendline on.
+ @param[in] ArrayLength - Array length of Points2calc.
+ @param[in] LenMargin - The length of inputMargins we are optimizing (0 - LenMargin -1).
+ @param[in] TestList - TestList index in Points2cal: WrVref, RdVref, WrT, RdT
+ @param[in] Scale - Scale to apply per test to Points2calc
+ @param[in] TestListSize - Size of TestList/Scale
+ @param[in] PwrCalc1d - Determines if the power test is 1-D or 2-D.
+ @param[in] PWRTrendSlope - Determines how aggressive the T-line will be.(0%-100%)
+
+ @retval Nothing
+**/
+void
+CalcPowerTrend (
+ IN MrcParameters *MrcData,
+ IN U8 Channel,
+ IN U8 DimmMask,
+ IN OUT void *DimmOptPoints,
+ IN void *Points2calc,
+ IN U8 ArrayLength,
+ IN U8 LenMargin,
+ IN U8 *TestList,
+ IN U8 *Scale,
+ IN U8 TestListSize,
+ IN BOOL PwrCalc1d,
+ IN U8 PWRTrendSlope
+ )
+{
+ const MrcDebug *Debug;
+ MrcChannelOut *ChannelOut;
+ U16 MaxPoints[4];
+ U16 MinPoints[4];
+ U16 MaxPwr;
+ U16 MinPwr;
+ U8 off;
+ U8 test;
+ U8 dimm;
+ U8 TestParam;
+ MrcPower PwrRes;
+ U16 AveOfMax;
+ U16 X;
+ S16 MinRatio;
+ S16 Ratio;
+ U16 Slope;
+ U16 SlopeOver100;
+ U16 Rodtcpu;
+ U8 RonDimm;
+ U8 RonCpu;
+ U16 Rodtdram;
+ U16 Wodtdram;
+ U16 *Points;
+ U16 *PointsElement;
+ DimmOptPoint *DimmPoints;
+ U8 dimmcount;
+ BOOL is1DPC;
+ U16 AvgROdt;
+
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Points = (U16 *) Points2calc;
+ is1DPC = (ChannelOut->DimmCount == 1);
+ MaxPwr = 0;
+ MinPwr = 0xffff;
+ Wodtdram = 0;
+
+ MrcOemMemorySet ((U8 *) &PwrRes, 0, sizeof (PwrRes));
+ MrcOemMemorySetWord (MaxPoints, 0, sizeof (MaxPoints) / sizeof (U16));
+ MrcOemMemorySetWord (MinPoints, 0xFFFF, sizeof (MinPoints) / sizeof (U16));
+
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TestListSize=%d\n",TestListSize);
+ //
+ for (off = 0; off < LenMargin; off++) {
+ //
+ // sorting the min max power points
+ //
+ for (test = 0; test < TestListSize; test++) {
+ //
+ // sorting the min max margin points for each test
+ //
+ PointsElement = (Points + ArrayLength * test + off);
+ if (MaxPoints[test] < *PointsElement) {
+ MaxPoints[test] = *PointsElement;
+ }
+
+ if (MinPoints[test] > *PointsElement) {
+ MinPoints[test] = *PointsElement;
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "*(Points+ArrayLength*test+off)=%d (Points+ArrayLength*test+off)=%x\n",*(Points+ArrayLength*test+off),(Points+ArrayLength*test+off));
+ //
+ }
+
+ if (!PwrCalc1d) {
+ DimmPoints = (DimmOptPoint *) DimmOptPoints;
+ RonDimm = 0;
+ RonCpu = 30;
+ Rodtcpu = CalcRdOdt (MrcData, (DimmPoints + off)->ODTSet.GRdOdt);
+ dimmcount = 0;
+ AvgROdt = 0;
+ for (dimm = 0; dimm < MAX_DIMMS_IN_CHANNEL; dimm++) {
+ if (!((MRC_BIT0 << dimm) & DimmMask)) {
+ continue;
+ }
+ //
+ // read from MR1 the DimmRon
+ //
+ RonDimm += (U8) CalcOptPower (MrcData, Channel, 2 * dimm, 0, OptDimmRon, 0, 0, 1);
+ Rodtdram = ActualDimmOdt[(DimmPoints + off)->ODTSet.RttNom[Channel][dimm]];
+ Wodtdram = ActualDimmOdt[(DimmPoints + off)->ODTSet.RttWr[Channel][dimm]];
+ if (is1DPC) { // in 1DPC channel always use only one of the terminations
+ if (Wodtdram == 0) {
+ Wodtdram = Rodtdram;
+ }
+ Rodtdram = 0x3fff; // put 8k ohm as infinity
+ }
+
+ if (Rodtdram == 0) {
+ Rodtdram = 0x3fff;
+ }
+
+ if (Wodtdram == 0) {
+ Wodtdram = Rodtdram; // in 2DPC with RttW=0
+ }
+
+ AvgROdt += Rodtdram;
+ dimmcount++;
+ }
+
+ AvgROdt = (dimmcount != 0) ? AvgROdt / dimmcount : AvgROdt;
+ RonDimm = (dimmcount != 0) ? RonDimm / dimmcount : RonDimm;
+ if ((120 < AvgROdt) && (AvgROdt < 0x3fff)) {
+ Rodtdram = 240; // the mix case of one open and one not
+ } else {
+ Rodtdram = AvgROdt; // for write average not needed because its by dimm
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "input for power calc Wodtdram=%d Rodtdram=%d RonDimm=%d \n",Wodtdram,Rodtdram,RonDimm);
+ //
+ PointsElement = (Points + ArrayLength * TestListSize + off);
+ CalcPower (MrcData, &PwrRes, RonCpu, RonDimm, Rodtcpu, Rodtdram, Wodtdram);
+ *PointsElement = PwrRes.TotPwr;
+ (DimmPoints + off)->PowerCalc = PwrRes;
+
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rodtcpu=%d Wodtdram=%d EqRodtdram=%d Calcpower=%d\n",Rodtcpu,Wodtdram,Rodtdram,*(Points+ArrayLength*test+off));
+ //
+ } else {
+ PointsElement = (Points + ArrayLength * TestListSize + off);
+ }
+
+ if (MaxPwr < *PointsElement) {
+ MaxPwr = *PointsElement;
+ }
+
+ if (MinPwr > *PointsElement) {
+ MinPwr = *PointsElement;
+ }
+
+ if (LenMargin == 1) {
+ MaxPwr = *PointsElement;
+ MinPwr = 0;
+ }
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "MaxPwr=%d MinPwr=%d\n",MaxPwr,MinPwr);
+ //
+ AveOfMax = 0;
+ MinRatio = 0x7fff;
+ for (test = 0; test < TestListSize; test++) {
+ AveOfMax += MaxPoints[test];
+ //
+ // map Test to TestParam
+ //
+ TestParam = TestList[test];
+ Ratio = (100 * (MaxPoints[test] / Scale[test] - UpmPwrLimitValue (MrcData, TestParam, UpmLimit))) /
+ (UpmPwrLimitValue (MrcData, TestParam, PowerLimit) - UpmPwrLimitValue (MrcData, TestParam, UpmLimit));
+ if (MinRatio > Ratio) {
+ MinRatio = Ratio;
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "AveOfMax=%d MinRatio=%d MaxPoints[%d]=%d Scale[%d]=%d \n",AveOfMax,MinRatio,test,MaxPoints[test],test,Scale[test]);
+ //
+ }
+
+ AveOfMax = AveOfMax / TestListSize;
+ //
+ // if MaxPoint > UPM Limit: PwrTrend should be flat
+ // if MaxPoints == PwrLimit: PwrTrend should have slope going from AveOfMax to (1-PWRTrendSlope/100)*AveOfMax
+ // PwrTrend will be a linear slope going from (MinPwr, (1- PWRTrendSlope/100)*AveOfMax) to (MaxPwr, AveOfMax)
+ //
+ Slope = (PWRTrendSlope * MinRatio) / 100;
+ SlopeOver100 = 0;
+ if (Slope > 100) {
+ //
+ // could only happen if no power limits
+ //
+ SlopeOver100 = Slope - 100;
+ Slope = 100;
+ }
+
+ for (off = 0; off < LenMargin; off++) {
+ PointsElement = (Points + ArrayLength * TestListSize + off);
+ if (MinRatio < 0) {
+ *PointsElement = 1;
+ } else {
+ if (MaxPwr == MinPwr) {
+ X = 0; // no power consideration and not divide by zero
+ } else {
+ //
+ // % of where you are between Min and Max Pwr. X=0 should be MaxPwr and 100 should be MinPwr
+ //
+ X = 100 - 100 * (*PointsElement - MinPwr) / (MaxPwr);
+ }
+ //
+ // Create a linear line based on Power from (1 - PWRTrendSlope / 100) * AveOfMax to AveOfMax
+ // Adding a specicial case for TX XTalk: If PWRTrendSlope = 0 and ArrayLength = BIT_TX_XTALK_RANGE
+ // just multiply power numbers by AveOfMax.
+ //
+ if ((PWRTrendSlope == 0) && (ArrayLength == BIT_TX_XTALK_RANGE)) {
+ *PointsElement = *PointsElement * AveOfMax / 100;
+ } else {
+ *PointsElement = AveOfMax * (100 - Slope + (((Slope + SlopeOver100) * X) / 100)) / 100;
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "power trend Points[%d][%d]=%d\n",TestListSize,off,*(Points+ArrayLength*TestListSize+off));
+ //
+ }
+ }
+}
+
+#ifdef MRC_DEBUG_PRINT
+
+#if 0 // This function is not used right now
+/**
+ Prints OptParam values from CRs and Host structure for all ch/Rank/byte as well as
+ the Best optimization value (if requested)
+ OptWrDS = 0
+ OptRdOd = 1
+ OptSCom = 2
+ OptTComp = 3
+ OptTxEq = 4
+ OptRxEq = 5
+ OptRxBias = 6
+ OptDimmOdt = 7
+ OptDimmOdtWr = 8
+ OptDimmRon = 9
+ OptDefault = 10
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] ChMask - Channel Mask to print the summary for
+ @param[in] RankMask - Rank Mask to print the summary for (in case Rank is not applicable set RankMask = 0xF)
+ @param[in] OptParam - Defines the OptParam Offsets. OptDefault reports all parameters
+ @param[in] OptOff - Structure containg the best offest and margins for the OptParam.
+ If OptOffsetChByte is not available, NullPtr needs to be passed (void *NullPtr)
+ @param[in] OptResult - True/False: Whether to print the Best optimization value
+
+ @retval Nothing
+**/
+void
+ReadOptParamOffsetSum (
+ IN MrcParameters *const MrcData,
+ IN U8 ChMask,
+ IN U8 RankMask,
+ IN const U8 OptParam,
+ IN OptOffsetChByte *OptOff,
+ IN BOOL OptResult
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 Param;
+ U8 NumBytes;
+ U8 ChannelMask;
+ S16 OffArr[2];
+ S16 Best;
+ BOOL PerRank;
+ BOOL SkipByte;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ ChannelMask = Outputs->ValidChBitMask & ChMask;
+ NumBytes = (U8) Outputs->SdramCount;
+ MrcOemMemorySetWord ((U16 *) OffArr, (U16) 0, sizeof (OffArr) / sizeof (OffArr[0]));
+
+ for (Param = OptWrDS; Param < OptDefault; Param++) {
+ if (OptParam == Param || OptParam == OptDefault) {
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nOffsets for Optimization Parameter %s\n", TOptParamOffsetString[Param]);
+ PerRank =
+ (
+ Param == OptTxEq ||
+ Param == OptRxEq ||
+ Param == OptDimmOdt ||
+ Param == OptDimmOdtWr ||
+ Param == OptDimmRon
+ );
+ SkipByte = (Param == OptDimmRon || Param == OptDimmOdt || Param == OptDimmOdtWr);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!((MRC_BIT0 << Channel) & ChannelMask)) {
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %d\n", Channel);
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((MRC_BIT0 << Rank) & RankMask) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+
+ if (PerRank) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rank %d\n", Rank);
+ } else if (Rank > 0) {
+ continue;
+ }
+
+ if (!SkipByte) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Byte\t");
+ for (Byte = 0; Byte < NumBytes; Byte++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Byte);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+
+ if (OptResult) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Opt/CR/Host\t");
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CR/Host\t");
+ }
+
+ if (!SkipByte) {
+ for (Byte = 0; Byte < NumBytes; Byte++) {
+ ReadOptParamOffset (MrcData, &OffArr[0], Channel, Rank, Byte, Param);
+
+ if (OptResult) {
+ Best = OptOff->Offset[Channel][Byte];
+ if (Best != OffArr[0] || Best != OffArr[1]) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\nError: Mismatch in Param %s in Channel %d Rank %d Byte %d is found: Best=%d CR=%d Host=%d\n",
+ TOptParamOffsetString[Param],
+ Channel,
+ Rank,
+ Byte,
+ Best,
+ OffArr[0],
+ OffArr[1]
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d/%d/%d\t", Best, OffArr[0], OffArr[1]);
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d/%d\t", OffArr[0], OffArr[1]);
+ }
+ }
+ } else {
+ ReadOptParamOffset (MrcData, &OffArr[0], Channel, Rank, 0, Param);
+
+ if (Param == OptDimmRon || Param == OptDimmOdtWr) {
+ if (OptResult) {
+ Best = OptOff->Offset[Channel][0];
+ if (Best != OffArr[1]) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\nError: Mismatch in Param %s in Channel %d Rank %d is found: Best=%d Host=%d\n",
+ TOptParamOffsetString[Param],
+ Channel,
+ Rank,
+ Best,
+ OffArr[1]
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d/NA/%d", Best, OffArr[1]);
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "NA/%d", OffArr[1]);
+ }
+ } else if (Param == OptDimmOdt) {
+ if (OptResult) {
+ Best = OptOff->Offset[Channel][0];
+ if (Best != OffArr[0]) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\nError: Mismatch in Param %s in Channel %d Rank %d is found: Best=%d Host=%d\n",
+ TOptParamOffsetString[Param],
+ Channel,
+ Rank,
+ Best,
+ OffArr[0]
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d/NA/%d", Best, OffArr[0]);
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "NA/%d", OffArr[0]);
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ Reads OptParam value from CRs and Host structure for a given ch/Rank/byte combination
+ OptParam can be: WrDS, RdOdt, TComp, SComp, RxEq, TxEq, RxBias, DIMM Ron, DIMM RttNom or DIMM RttWr
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[out] FinalVal - Pointer to the array consisting of CR value and Host value for a particular
+ OptParam and given ch/Rank/byte combination.
+ @param[in] Channel - Channel index to work on.
+ @param[in] Rank - Rank index to work on (valid only for TxEq and RxEq, for others is ignored)
+ @param[in] Byte - Byte index to work on.
+ @param[in] OptParam - Defines the OptParam Offsets. Supported OptParam =
+ [0: WrDS, 1: RdODT, 2: SComp, 3: TComp, 3: TxEq,
+ 4: RxEq, 5: RxBias, 6: DimmOdt, 7: DimmOdtWr]
+
+ @retval Nothing
+**/
+void
+ReadOptParamOffset (
+ IN MrcParameters *const MrcData,
+ OUT S16 *FinalVal,
+ IN const U8 Channel,
+ IN const U8 Rank,
+ IN const U8 Byte,
+ IN const U8 OptParam
+ )
+{
+ const U16 RttNomMRSEncodingConst[] = {0x00, 0x10, 0x01, 0x11, 0x81, 0x80}; // RttNom Off,120,60,40,30,20 Ohms
+ const U16 RttWrMRSEncodingConst[] = {0x00, 0x02, 0x01}; // RttWr RttNom,120,60 Ohms
+ const MrcDebug *Debug;
+#ifdef ULT_FLAG
+ const U8 LpddrRonEnc[] = {0x1,0x2,0x3}; //{34,40,48};
+ const U8 LpddrOdtEnc[] = {0x0,0x2,0x3}; //{0,120,240};
+ BOOL Lpddr;
+ U16 DimmRonMask;
+#endif // ULT_FLAG
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ U16 *MrReg;
+ BOOL Type;
+ U8 Index;
+ U16 MRValue;
+ U16 RttNomMRSEncoding[sizeof (RttNomMRSEncodingConst) / sizeof (RttNomMRSEncodingConst[0])];
+ U16 RttWrMRSEncoding[sizeof (RttWrMRSEncodingConst) / sizeof (RttWrMRSEncodingConst[0])];
+ U16 RttWr;
+ U16 RttNom;
+ U16 RttNomMask;
+ U16 RttWrMask;
+ U32 Offset;
+ S16 UpOff;
+ S16 DnOff;
+ DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetCompCr;
+ DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetCompHost;
+ DDRDATA0CH0_CR_TXTRAINRANK0_STRUCT CrTxTrainRank;
+ DDRDATA0CH0_CR_RXTRAINRANK0_STRUCT CrRxTrainRank;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT DdrCrDataControl1Cr;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT DdrCrDataControl1Host;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ MrcOemMemoryCpy ((U8 *) RttNomMRSEncoding, (U8 *) RttNomMRSEncodingConst, sizeof (RttNomMRSEncoding));
+ MrcOemMemoryCpy ((U8 *) RttWrMRSEncoding, (U8 *) RttWrMRSEncodingConst, sizeof (RttWrMRSEncoding));
+
+#ifdef ULT_FLAG
+ Lpddr = Outputs->DdrType == MRC_DDR_TYPE_LPDDR3;
+#endif
+ //
+ // Compensation Offsets
+ //
+ Type = ((OptParam == OptWrDS) || (OptParam == OptRdOdt) || (OptParam == OptTComp) || (OptParam == OptSComp));
+ if (Type) {
+
+ Offset = DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG +
+ ((DDRDATA1CH0_CR_DDRCRDATAOFFSETCOMP_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG) * Byte) +
+ ((DDRDATA0CH1_CR_DDRCRDATAOFFSETCOMP_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG) * Channel);
+ DdrCrDataOffsetCompCr.Data = MrcReadCR (MrcData, Offset);
+ DdrCrDataOffsetCompHost.Data = ChannelOut->DataCompOffset[Byte];
+
+ if (OptParam == OptWrDS) {
+ UpOff = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqDrvUpCompOffset;
+ DnOff = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqDrvDownCompOffset;
+ if (UpOff != DnOff) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "DqDrvUpCompOffset %d is not equal to DqDrvDownCompOffset for Channel=%d, Byte=%d\n",
+ UpOff,
+ DnOff,
+ Channel,
+ Byte
+ );
+ }
+
+ FinalVal[0] = UpOff;
+ UpOff = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqDrvUpCompOffset;
+ DnOff = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqDrvDownCompOffset;
+ if (UpOff != DnOff) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "DqDrvUpCompOffset %d is not equal to DqDrvDownCompOffset for Channel=%d, Byte=%d in Host Structure\n",
+ UpOff,
+ DnOff,
+ Channel,
+ Byte
+ );
+ }
+
+ FinalVal[1] = UpOff;
+
+ if (FinalVal[0] & 0x20) { // 6-bit 2's complement
+ FinalVal[0] -= 0x40;
+ }
+ if (FinalVal[1] & 0x20) { // 6-bit 2's complement
+ FinalVal[1] -= 0x40;
+ }
+ } else if (OptParam == OptRdOdt) {
+ UpOff = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqOdtUpCompOffset;
+ DnOff = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqOdtDownCompOffset;
+ if (UpOff != DnOff) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "DqOdtUpCompOffset %d is not equal to DqOdtDownCompOffset for Channel=%d, Byte=%d\n",
+ UpOff,
+ DnOff,
+ Channel,
+ Byte
+ );
+ }
+
+ FinalVal[0] = UpOff;
+ UpOff = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqOdtUpCompOffset;
+ DnOff = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqOdtDownCompOffset;
+ if (UpOff != DnOff) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "DqOdtUpCompOffset %d is not equal to DqOdtDownCompOffset for Channel=%d, Byte=%d in Host Structure\n",
+ UpOff,
+ DnOff,
+ Channel,
+ Byte
+ );
+ }
+
+ FinalVal[1] = UpOff;
+
+ if (FinalVal[0] & 0x10) { // 5-bit 2's complement
+ FinalVal[0] -= 0x20;
+ }
+ if (FinalVal[1] & 0x10) { // 5-bit 2's complement
+ FinalVal[1] -= 0x20;
+ }
+ } else if (OptParam == OptTComp) {
+ FinalVal[0] = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqTcoCompOffset;
+ FinalVal[1] = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqTcoCompOffset;
+
+ if (FinalVal[0] & 0x10) { // 5-bit 2's complement
+ FinalVal[0] -= 0x20;
+ }
+ if (FinalVal[1] & 0x10) { // 5-bit 2's complement
+ FinalVal[1] -= 0x20;
+ }
+ } else if (OptParam == OptSComp) {
+ FinalVal[0] = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqSlewRateCompOffset;
+ FinalVal[1] = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqSlewRateCompOffset;
+
+ if (FinalVal[0] & 0x10) { // 5-bit 2's complement
+ FinalVal[0] -= 0x20;
+ }
+ if (FinalVal[1] & 0x10) { // 5-bit 2's complement
+ FinalVal[1] -= 0x20;
+ }
+ }
+
+ if (FinalVal[0] != FinalVal[1]) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Error: Param %s: CR value %d doesn't match Host value %d for Channel=%d, Byte=%d\n",
+ TOptParamOffsetString[OptParam],
+ FinalVal[0],
+ FinalVal[1],
+ Channel,
+ Byte
+ );
+ }
+ }
+ //
+ // Equalization Settings
+ //
+ Type = ((OptParam == OptTxEq) || (OptParam == OptRxEq));
+ if (Type) {
+ //
+ // TxEq[5:4] = Emphasize = [3, 6, 9, 12] legs
+ // TxEq[3:0] = Deemphasize = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 4*Rsvd] legs
+ //
+ if (OptParam == OptTxEq) {
+
+ Offset = DDRDATA0CH0_CR_TXTRAINRANK0_REG +
+ ((DDRDATA0CH1_CR_TXTRAINRANK0_REG - DDRDATA0CH0_CR_TXTRAINRANK0_REG) * Channel) +
+ ((DDRDATA0CH0_CR_TXTRAINRANK1_REG - DDRDATA0CH0_CR_TXTRAINRANK0_REG) * Rank) +
+ ((DDRDATA1CH0_CR_TXTRAINRANK0_REG - DDRDATA0CH0_CR_TXTRAINRANK0_REG) * Byte);
+
+ CrTxTrainRank.Data = MrcReadCR (MrcData, Offset);
+ FinalVal[0] = (S16) (S32) CrTxTrainRank.Bits.TxEqualization;
+ FinalVal[1] = (S16) (S32) ChannelOut->TxEq[Rank][Byte];
+ FinalVal[0] &= 0xF; // Read Deemphasize portion only
+ FinalVal[1] &= 0xF; // Read Deemphasize portion only
+ }
+ //
+ // RxEQ[4:0] CR Decoding (pF/kOhm)
+ // [2:0]
+ // [4:3] 0 1 2 3 4 5-7
+ // 0 0.5/.02 0.5/1.0 0.5/.50 0.5/.25 0.5/.12 rsvd
+ // 1 1.0/.02 1.0/1.0 1.0/.50 1.0/.25 1.0/.12 rsvd
+ // 2 1.5/.02 1.5/1.0 1.5/.50 1.5/.25 1.5/.12 rsvd
+ // 3 2.0/.02 2.0/1.0 2.0/.50 2.0/.25 2.0/.12 rsvd
+ // Sweep = 0-19 [4:3] = (Sweep/5) [2:0] = (Sweep%5)
+ //
+ if (OptParam == OptRxEq) {
+ Offset = DDRDATA0CH0_CR_RXTRAINRANK0_REG +
+ ((DDRDATA0CH1_CR_RXTRAINRANK0_REG - DDRDATA0CH0_CR_RXTRAINRANK0_REG) * Channel) +
+ ((DDRDATA0CH0_CR_RXTRAINRANK1_REG - DDRDATA0CH0_CR_RXTRAINRANK0_REG) * Rank) +
+ ((DDRDATA1CH0_CR_RXTRAINRANK0_REG - DDRDATA0CH0_CR_RXTRAINRANK0_REG) * Byte);
+
+ CrRxTrainRank.Data = MrcReadCR (MrcData, Offset);
+ FinalVal[0] = (S16) (S32) CrRxTrainRank.Bits.RxEq;
+ FinalVal[1] = (S16) (S32) ChannelOut->RxEq[Rank][Byte];
+ FinalVal[0] = ((FinalVal[0] >> 3) * 5) + (FinalVal[0] & 0x7); // Multiply Cap portion by 5 and add Res portion
+ FinalVal[1] = ((FinalVal[1] >> 3) * 5) + (FinalVal[1] & 0x7); // Multiply Cap portion by 5 and add Res portion
+ }
+
+ if (FinalVal[0] != FinalVal[1]) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Error: Param %s: CR value %d doesn't match Host value %d for Channel=%d, Rank=%d, Byte=%d\n",
+ TOptParamOffsetString[OptParam],
+ FinalVal[0],
+ FinalVal[1],
+ Channel,
+ Rank,
+ Byte
+ );
+ }
+ }
+ //
+ // RX Amplifier BIAS
+ //
+ if ((OptParam == OptRxBias)) {
+ //
+ // Mapping: [0: 0.44, 1: 0.66, 2: 0.88, 3: 1.00, 4: 1.33, 5: 1.66, 6: 2.00, 7: 2.33]
+ //
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte) +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel);
+
+ DdrCrDataControl1Cr.Data = MrcReadCR (MrcData, Offset);
+ DdrCrDataControl1Host.Data = ChannelOut->DqControl1[Byte].Data;
+ FinalVal[0] = (S16) (S32) DdrCrDataControl1Cr.Bits.RxBiasCtl;
+ FinalVal[1] = (S16) (S32) DdrCrDataControl1Host.Bits.RxBiasCtl;
+
+ if (FinalVal[0] != FinalVal[1]) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Error: Param %s: CR value %d doesn't match Host value %d for Channel=%d, Byte=%d\n",
+ TOptParamOffsetString[OptParam],
+ FinalVal[0],
+ FinalVal[1],
+ Channel,
+ Byte
+ );
+ }
+ }
+ //
+ // Dimm Ron value
+ //
+ if ((OptParam == OptDimmRon)) {
+ //
+ // DIMM Ron Encoding DriverImpCtrl[A5,A1]
+ //
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ MrReg = &ChannelOut->Dimm[Rank / 2].Rank[(Rank % 2)].MR[mrMR0];
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ DimmRonMask = (MRC_BIT3 | MRC_BIT2 | MRC_BIT1 | MRC_BIT0);
+ MRValue = (MrReg[mrMR3] & DimmRonMask);
+
+ for (Index = 0; Index < (sizeof (LpddrRonEnc) / sizeof (LpddrRonEnc[0])); Index++) {
+ if (MRValue == LpddrRonEnc[Index]) {
+ FinalVal[1] = (S16) (S8) Index;
+ }
+ }
+ } else
+#endif
+ {
+ MRValue = MrReg[mrMR1];
+ FinalVal[1] = (S16) ((MRValue >> 1) & 0x1);
+ }
+ }
+ }
+ //
+ // DIMM ODT Values
+ //
+ if ((OptParam == OptDimmOdt) || (OptParam == OptDimmOdtWr)) {
+ //
+ // DIMM ODT Encoding RttNom[A9,A6,A2] RttWr[A10, A9] LPDDR - No RttNom
+ //
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ RttWrMask = (MRC_BIT1 | MRC_BIT0);
+ MRValue = (ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR11 & RttWrMask);
+
+ for (Index = 0; Index < (sizeof (LpddrOdtEnc) / sizeof (LpddrOdtEnc[0])); Index++) {
+ if (MRValue == LpddrOdtEnc[Index]) {
+ FinalVal[1] = (S16) (S8) Index;
+ }
+ }
+
+ FinalVal[0] = 0;
+ } else
+#endif
+ {
+ MrReg = &ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR0];
+ RttNomMask = (MRC_BIT9 + MRC_BIT6 + MRC_BIT2);
+ RttWrMask = (MRC_BIT10 + MRC_BIT9);
+ RttWr = (MrReg[mrMR2] & RttWrMask) >> 9;
+ RttNom = (MrReg[mrMR1] & RttNomMask) >> 2;
+
+ for (Index = 0; Index < sizeof (RttNomMRSEncodingConst) / sizeof (RttNomMRSEncodingConst[0]); Index++) {
+ if (RttNom == RttNomMRSEncoding[Index]) {
+ FinalVal[0] = (S16) (S8) Index;
+ }
+ }
+
+ for (Index = 0; Index < sizeof (RttWrMRSEncodingConst) / sizeof (RttWrMRSEncodingConst[0]); Index++) {
+ if (RttWr == RttWrMRSEncoding[Index]) {
+ FinalVal[1] = (S16) (S8) Index;
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ This function will print out the last margin data collected of the Param passed in.
+ It will print both edges of all the requested bytes, Ranks and Channels.
+ NOTE: The function will not check to see if the Rank/Channel exists. It will print out the
+ values stored in the margin array regardless of population status.
+
+ @param[in] MrcData - Global MRC data.
+ @param[in] Param - Parameter of MRC_MarginTypes of which to print the margin.
+ @param[in] ChannelMask - Bit mask of channels to print.
+ @param[in] RankMask - Bit mask of ranks to print.
+ @param[in] ByteMask - Bit mask of bytes to print.
+
+ @retval Nothing.
+**/
+void
+MrcPrintLastMargins (
+ IN MrcParameters *const MrcData,
+ IN const U8 Param,
+ IN const U8 ChannelMask,
+ IN const U8 RankMask,
+ IN const U16 ByteMask
+ )
+{
+ MrcDebug const *Debug;
+ MrcOutput *Outputs;
+ char *EdgeString;
+ MrcMarginResult LastResultParam;
+ U32 (*LastMargins)[MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES];
+ U8 Channel;
+ U8 Rank;
+ U8 Byte;
+ U8 Edge;
+
+ LastResultParam = GetMarginResultType (Param);
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ LastMargins = Outputs->MarginResult;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%s Last Margins:\n",
+ MarginTypesString[Param]
+ );
+
+ EdgeString = ((Param == RdV) || (Param == WrV)) ? "H" : "R";
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Byte\t");
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if ((1 << Byte) & ByteMask) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "% 10d", Byte);
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nEdge\t");
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if ((1 << Byte) & ByteMask) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " L %s", EdgeString);
+ }
+ }
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((1 << Rank) & RankMask) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((1 << Channel) & ChannelMask) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nR%d.C%d\t", Rank, Channel);
+ for(Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if ((1 << Byte) & ByteMask) {
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "% 5d", LastMargins[LastResultParam][Rank][Channel][Byte][Edge]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // End of table
+}
+#endif // #if 0
+
+/**
+ This function implements switch to print the correct format and data for the
+ OptResultsPerByte struct members.
+
+ @param[in] Debug - Debug pointer for printing.
+ @param[in] Data - Pointer to OptResultsPerByte struct.
+ @param[in] TypeIndex - Member of OptResultsPerByte to print.
+ @param[in] TestIndex - Some parameters store multiple test results to be printed.
+ @param[in] MidPoint - Used to convert from zero-based indexing to the selected value
+
+ @retval Nothing.
+**/
+void
+MrcOptResultsPerBytePrint (
+ IN const MrcDebug *const Debug,
+ IN OptResultsPerByte *Data,
+ IN U8 TypeIndex,
+ IN U8 TestIndex,
+ IN S8 MidPoint
+ )
+{
+ switch (TypeIndex) {
+ case (MrcOptResultBest):
+ (TestIndex == 0) ? MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "=== %d ===", Data->Best - MidPoint) :
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ break;
+
+ case (MrcOptResultGrdBnd):
+ (TestIndex == 0) ? MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "*** %d ***", Data->GuardBand) :
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ break;
+
+ case(MrcOptResultOffSel):
+ (TestIndex == 0) ? MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "--> %d <--", Data->Best - MidPoint + Data->GuardBand) :
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ break;
+
+ case (MrcOptResultScale):
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Data->Scale[TestIndex]);
+ break;
+
+ case (MrcOptResultSignal):
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d.%d%s\t", Data->Signal[TestIndex] / 100, Data->Signal[TestIndex] % 100 / 10, "%");
+ break;
+
+ case (MrcOptResultNoise):
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d.%d%s\t", Data->Noise[TestIndex] / 100, Data->Noise[TestIndex] % 100 / 10, "%");
+ break;
+
+ case (MrcOptResultRatio):
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d.%d\t", Data->Ratio[TestIndex] / 1000, Data->Ratio[TestIndex] % 1000 / 100);
+ break;
+
+ case (MrcOptResultMaxPost):
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Data->MaxPost[TestIndex]);
+ break;
+
+ case (MrcOptResultMinPost):
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Data->MinPost[TestIndex]);
+ break;
+
+ case (MrcOptResultTicks):
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d.%d\t", Data->Ticks[TestIndex] / 10, Data->Ticks[TestIndex] % 10);
+ break;
+
+ case (MrcOptResultSnrTot):
+ (TestIndex == 0) ? MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " %d.%d%s\t",
+ (U32) Data->SNRTotal / 100,
+ (U32) Data->SNRTotal % 100 / 10,
+ "%"
+ ) : MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ break; // assuming we dont exceed 32 bits
+
+ default:
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "OptResultPerByteDbgStr Switch exceeded number of cases defined\n");
+ }
+}
+
+
+/**
+ This function prints the Optimize margin result table
+ e.g: calcResultSummary[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]
+
+ @param[in] MrcData - MRC data structure
+ @param[in] calcResultSummary - The data array [MAX_CHANNEL][MAX_SDRAM_IN_DIMM]
+ @param[in] TestList - Test list
+ @param[in] NumTest - Number of test
+ @param[in] NumOffsets - Number of offsets
+ @param[in] MidPoint - Middle point
+ @param[in] IncEnds - Print ends points
+ @param[in] OptParam - Used to convert to the Opt param string for printing
+ @param[in] OptPower - Opt Power values to be printed
+ @param[in] Channel - Channel to print
+ @param[in] Ranks - Ranks to print
+ @param[in] TrendLine - Switch to print the trend line
+ @param[in] Nibble - take low/high bytes
+ @param[in] perCh - Switch to only print 1 Byte of data
+ @param[in] noPrint - Boolean used to disable printing of results
+
+ @retval Nothing
+**/
+void
+PrintCalcResultTableCh (
+ IN MrcParameters *const MrcData,
+ IN OptResultsPerByte calcResultSummary[MAX_CHANNEL][MAX_SDRAM_IN_DIMM],
+ IN U8 *TestList,
+ IN U8 NumTest,
+ IN U8 NumOffsets,
+ IN S8 MidPoint,
+ IN BOOL IncEnds,
+ IN U8 OptParam,
+ IN U16 *OptPower,
+ IN U8 Channel,
+ IN U8 Ranks,
+ IN BOOL TrendLine,
+ IN U8 Nibble,
+ IN BOOL perCh,
+ IN BOOL noPrint
+ )
+{
+ const MrcDebug *Debug;
+ OptResultsPerByte *data;
+ S8 Off;
+ S8 Start;
+ S8 Stop;
+ U8 i;
+ U8 j;
+ U8 b;
+ U8 FirstByte;
+ U8 NumBytes;
+ U8 NumTestPlus;
+ U32 Result;
+ BOOL Format64Results;
+ U8 Param;
+
+ Format64Results = 1;
+ //
+ // Display result in %/Delta , 0-displat raw 64bit result in HEX
+ //
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Start = (!IncEnds);
+ Stop = NumOffsets - (!IncEnds);
+ if (noPrint) {
+ return ;
+
+ }
+
+ FirstByte = (Nibble) ? 4 : 0;
+ NumBytes = FirstByte + 4 + Nibble * MrcData->SysOut.Outputs.SdramCount % 8;
+ if (perCh) {
+ NumBytes = 1;
+ }
+
+ NumTestPlus = (TrendLine) ? NumTest + 1 : NumTest;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\n<======== optimize %s ========>Plot results ",
+ TOptParamOffsetString[OptParam]
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "<Channel=%d><rank/s=0x%x><Nibble=%s> across settings :(Start=%d,Stop=%d)\n",
+ Channel,
+ Ranks,
+ (Nibble) ? "High" : "Low",
+ Start - MidPoint,
+ Stop - MidPoint - 1
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Bytes\t");
+ for (b = FirstByte; b < NumBytes; b++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", b);
+ for (i = 0; i < NumTestPlus + 1; i++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t"); // tab insertion
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // row end here !
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Offset\t"); // row starts here !
+ if (OptPower[Stop - 1] != 0) {//WA: need to add param to enable this print
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", 3 + TOptParamOffsetString[OptParam]);
+ }
+
+ for (b = FirstByte; b < NumBytes; b++) {
+ for (i = 0; i < NumTest; i++) {
+ //
+ // Test types header
+ //
+ Param = TestList[i];
+ if (Param > CmdV) {
+ Param = (Param % 16) + 4;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", MarginTypesString[Param]);
+ }
+
+ if (TrendLine) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", "T.line");
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Opt.func\t"); // more header..
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n"); // row end here !
+ for (Off = Start; Off < Stop; Off++) {
+ //
+ // row starts here !
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Off - MidPoint);
+ if (OptPower[Stop - 1] != 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", OptPower[Off]);
+ }
+
+ for (b = FirstByte; b < NumBytes; b++) {
+ if (b < MAX_SDRAM_IN_DIMM) {
+ data = &calcResultSummary[Channel][b];
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Error: calcResultSummary array out of bounds! %d > %d \n",
+ b,
+ MAX_SDRAM_IN_DIMM - 1
+ );
+ return;
+ }
+
+ for (i = 0; i < NumTestPlus; i++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", data->Margins[i][Off].EW);
+ }
+
+ if (Format64Results) {
+ Result = (U32) (MrcOemMemoryDivideU64ByU64 (MrcOemMemoryMultiplyU64ByU32 (data->Result[Off], 200), data->MaxR));
+ Result /= 2;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t\t", Result);
+ }
+
+ if (!Format64Results) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%08x-%08x\t\t",
+ (U32) MrcOemMemoryRightShiftU64 (data->Result[Off],
+ 32),
+ (U32) (data->Result[Off])
+ );
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // row end here !
+ }
+
+ for (i = 0; i < (sizeof (OptResultDbgStrings) / sizeof (*OptResultDbgStrings)); i++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", OptResultDbgStrings[i]);
+ for (b = FirstByte; b < NumBytes; b++) {
+ if (b < MAX_SDRAM_IN_DIMM) {
+ data = &calcResultSummary[Channel][b];
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Error: calcResultSummary array out of bounds! %d > %d \n",
+ b,
+ MAX_SDRAM_IN_DIMM - 1
+ );
+ return;
+ }
+
+ for (j = 0; j < NumTestPlus; j++) {
+ MrcOptResultsPerBytePrint (Debug, data, i, j, MidPoint);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t"); // tab insertion
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ } // row end here !
+ return;
+}
+
+/**
+ This function prints the Optimize margin result table
+ e.g: calcResultSummary[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]
+
+ @param[in] MrcData - MRC data structure
+ @param[in] calcResultSummary - The data array [MAX_CHANNEL][MAX_SDRAM_IN_DIMM]
+ @param[in] DimmOptPoints - add argument and description to function comment
+ @param[in] TestList - Test list
+ @param[in] NumTest - Number of test
+ @param[in] NumOffsets - Number of offsets
+ @param[in] MidPoint - Middle point
+ @param[in] IncEnds - Print ends points
+ @param[in] OptParam - Used to convert to the Opt param string for printing
+ @param[in] Channel - Channel to print
+ @param[in] Ranks - Ranks to print
+ @param[in] TrendLine - Switch to print the trend line
+ @param[in] Nibble - take low/high bytes
+ @param[in] perCh - Switch to only print 1 Byte of data
+
+ @retval Nothing
+**/
+void
+PrintODTResultTable (
+ IN MrcParameters *const MrcData,
+ IN OptResultsPerByte *calcResultSummary,
+ IN DimmOptPoint *DimmOptPoints,
+ IN U8 NumOffsets,
+ IN S8 MidPoint,
+ IN BOOL IncEnds,
+ IN U8 OptParam,
+ IN U8 Channel,
+ IN U8 Ranks,
+ IN BOOL TrendLine,
+ IN U8 Nibble,
+ IN BOOL perCh
+ )
+{
+ const char *OdtStrings[] = {
+ "RttNom0",
+ "RttNom1",
+ "RttWr0",
+ "RttWr1",
+ "RdOdt",
+ "Pwr[mW]",
+ "Cpu Rd",
+ "Dim Rd",
+ "Cpu Wr",
+ "DimW-T",
+ "DimW-NT",
+ "ACPower"
+ };
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ OptResultsPerByte *data;
+ S8 Off;
+ S8 Start;
+ S8 Stop;
+ U8 i;
+ U8 j;
+ U8 b;
+ U8 r;
+ U8 FirstByte;
+ U8 NumBytes;
+ U8 NumTestPlus;
+ U8 *TestList;
+ U8 Param;
+ U32 Result;
+ U8 OptResultType;
+ BOOL Format64Results; // Display result in %/MaxR , 0-display raw 64bit result in HEX
+ BOOL printOptSetting;
+ U64 delta;
+
+ Format64Results = 1;
+ printOptSetting = 1;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ Start = (!IncEnds);
+ Stop = NumOffsets - (!IncEnds);
+ TestList = DimmOptPoints[0].TestList;
+ FirstByte = (Nibble) ? 4 : 0;
+ NumBytes = FirstByte + 4 + Nibble * MrcData->SysOut.Outputs.SdramCount % 8;
+ Ranks &= Outputs->Controller[0].Channel[Channel].ValidRankBitMask;
+
+ if (perCh) {
+ NumBytes = 1;
+ }
+
+ NumTestPlus = (TrendLine) ? DimmOptPoints[0].NumTests + 1 : DimmOptPoints[0].NumTests;
+ //
+ // RttNomOffset = (MrcData->Outputs.Channel[Channel].DimmCount == 1) ? 0 : RttOffset; // if 2DPC - RttNom 40,30,20 Ohms
+ //
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\n<======== optimize %s ========>Plot results ",
+ TOptParamOffsetString[OptParam]
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "<Channel=%d><rank/s=0x%x> across settings :(Start=%d,Stop=%d)\n",
+ Channel,
+ Ranks,
+ Start - MidPoint,
+ Stop - MidPoint - 1
+ );
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "ODT\t");
+ // for (b = 0; b < (sizeof(OdtStrings)/sizeof(*OdtStrings)); b++) {
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t",OdtStrings[b] );
+ // for (i = 0; i < NumTestPlus+1; i++) MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");//tab insertion
+ // }
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");//row end here!
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Offset\t"); // row starts here!
+ for (b = 0; b < (sizeof (OdtStrings) / sizeof (*OdtStrings)); b++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", OdtStrings[b]);
+ }
+
+ for (i = 0; i < DimmOptPoints[0].NumTests; i++) {
+ //
+ // Test types header
+ //
+ Param = TestList[i];
+ if (Param > CmdV) {
+ Param = (Param % 16) + 4;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", MarginTypesString[Param]);
+ }
+
+ if (TrendLine) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", "T.line");
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Opt.func\t"); // more header
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n");
+
+ for (Off = Start; Off < Stop; Off++) {
+ //
+ // row starts here !
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Off - MidPoint);
+ for (b = 0; b < (sizeof (OdtStrings) / sizeof (*OdtStrings)); b++) {
+ if (b == 0) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ActualDimmOdt[DimmOptPoints[Off].ODTSet.RttNom[Channel][0]]);
+ }
+
+ if (b == 1) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ActualDimmOdt[DimmOptPoints[Off].ODTSet.RttNom[Channel][1]]);
+ }
+
+ if (b == 2) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ActualDimmOdt[DimmOptPoints[Off].ODTSet.RttWr[Channel][0]]);
+ }
+
+ if (b == 3) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ActualDimmOdt[DimmOptPoints[Off].ODTSet.RttWr[Channel][1]]);
+ }
+
+ if (b == 4) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", CalcRdOdt (MrcData, DimmOptPoints[Off].ODTSet.GRdOdt));
+ }
+
+ if (b == 5) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d.%d\t",
+ DimmOptPoints[Off].PowerCalc.TotPwr / 10,
+ DimmOptPoints[Off].PowerCalc.TotPwr % 10
+ );
+ }
+
+ if (b == 6) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d.%d\t",
+ DimmOptPoints[Off].PowerCalc.CpuPwrRd / 10,
+ DimmOptPoints[Off].PowerCalc.CpuPwrRd % 10
+ );
+ }
+
+ if (b == 7) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d.%d\t",
+ DimmOptPoints[Off].PowerCalc.DimmPwrRd / 10,
+ DimmOptPoints[Off].PowerCalc.DimmPwrRd % 10
+ );
+ }
+
+ if (b == 8) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d.%d\t",
+ DimmOptPoints[Off].PowerCalc.CpuPwrWr / 10,
+ DimmOptPoints[Off].PowerCalc.CpuPwrWr % 10
+ );
+ }
+
+ if (b == 9) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d.%d\t",
+ DimmOptPoints[Off].PowerCalc.DimmPwrWrT / 10,
+ DimmOptPoints[Off].PowerCalc.DimmPwrWrT % 10
+ );
+ }
+
+ if (b == 10) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d.%d\t",
+ DimmOptPoints[Off].PowerCalc.DimmPwrWrNT / 10,
+ DimmOptPoints[Off].PowerCalc.DimmPwrWrNT % 10
+ );
+ }
+
+ if (b == 11) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d.%d\t",
+ DimmOptPoints[Off].PowerCalc.ACPower / 10,
+ DimmOptPoints[Off].PowerCalc.ACPower % 10
+ );
+ }
+ }
+
+ data = calcResultSummary;
+ for (i = 0; i < NumTestPlus; i++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", data->Margins[i][Off].EW);
+ }
+
+ delta = data->MaxR - data->MinR + 1; // +1 to not divide by 0
+ if (Format64Results) {
+ Result = (U32) (MrcOemMemoryDivideU64ByU64 (MrcOemMemoryMultiplyU64ByU32 (data->Result[Off], 200), data->MaxR));
+ Result /= 2;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t\t", Result);
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%08x-%08x\t\t",
+ (U32) MrcOemMemoryRightShiftU64 (data->Result[Off],
+ 32),
+ (U32) (data->Result[Off])
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // row end here !
+ if (printOptSetting) {
+ for (i = 0; i < DimmOptPoints[0].OptParamTestListSize; i++) {
+ OptResultType = GetOptResultType (DimmOptPoints[0].OptParamTestList[i]);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s \t", TOptParamOffsetString[DimmOptPoints[0].OptParamTestList[i]]);
+ if ((OptResultType == RxEqOfft) || (OptResultType == TxEqOfft)) {
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ //
+ for (r = 0; r < MAX_RANK_IN_CHANNEL; r++) {
+ if (!(Ranks & (0x1 << r))) {
+ continue;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "<Rank %d>|", r);
+ for (b = 0; b < Outputs->SdramCount; b++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%02d|",
+ DimmOptPoints[Off].BestOptOff[OptResultType][r].Offset[Channel][b]
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");
+ }
+ } else {
+ for (b = 0; b < Outputs->SdramCount; b++) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d|",
+ DimmOptPoints[Off].BestOptOff[OptResultType][0].Offset[Channel][b]
+ );
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // row end here !
+ }
+ }
+
+ }
+
+ for (i = 0; i < (sizeof (OptResultDbgStrings) / sizeof (*OptResultDbgStrings)); i++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", OptResultDbgStrings[i]);
+ for (b = FirstByte; b < NumBytes; b++) {
+ data = calcResultSummary;
+ for (j = 0; j < NumTestPlus; j++) {
+ MrcOptResultsPerBytePrint (Debug, data, i, j, MidPoint);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t"); // tab insertion
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ } // row end here !
+}
+
+/**
+ This function prints the Optimize margin result table
+ e.g: MarginResult[Test][Offset][Channel][Byte][sign]
+
+ @param[in] MrcData - MRC data structure
+ @param[in] ChMask - Channels to print
+ @param[in] ResultArray - Array with saved margin results
+ @param[in] TestNum - Test index
+ @param[in] OffsetsNum - number of offsets
+ @param[in] MidPoint - Zero point
+ @param[in] Edges - 1 edge or 2 edge
+ @param[in] OptParam - Used to convert to the Opt param string for printing
+ @param[in] Param - Margin type to be printed.
+ @param[in] PowerLimits - Power limits to print.
+ @param[in] noPrint - Used to skip printing.
+
+ @retval Nothing
+**/
+void
+PrintResultTableByte4by24 (
+ IN MrcParameters *MrcData,
+ IN U8 ChMask,
+ IN U16 ResultArray[4][24][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES],
+ IN U16 TestNum,
+ IN U8 OffsetsNum,
+ IN U8 MidPoint,
+ IN U8 Edges,
+ IN U8 OptParam,
+ IN U8 Param,
+ IN U16 *PowerLimits,
+ IN BOOL noPrint
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ U8 Channel;
+ U8 Byte;
+ S8 Off;
+ S8 Start;
+ S8 Stop;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ Start = -MidPoint;
+ Stop = OffsetsNum - MidPoint - 1;
+ if (Param > CmdV) {
+ Param = (Param % 16) + 4;
+ }
+
+ if (noPrint) {
+ return;
+
+ }
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\nTest number : %d - %s ,Plot results across OptParam=%s settings:(Start=%d,Stop=%d) w/ power limits(width): %d \nChannel\t0 1\nByte\t",
+ TestNum,
+ MarginTypesString[Param],
+ TOptParamOffsetString[OptParam],
+ Start,
+ Stop,
+ PowerLimits[TestNum]
+ );
+ if (Outputs->SdramCount == 8) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "0\t1\t2\t3\t4\t5\t6\t7\t0\t1\t2\t3\t4\t5\t6\t7");
+ } else if (Outputs->SdramCount == 9) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "0\t1\t2\t3\t4\t5\t6\t7\t0\t1\t2\t3\t4\t5\t6\t7\t8");
+ }
+ //
+ // Sweep through OpParam settings
+ //
+ for (Off = Start; Off < Stop + 1; Off++) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n %d:\t", Off);
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ //
+ // spaces for non populated channel
+ //
+ if (!((0x1 << Channel) & ChMask)) {
+ if (Channel == 0) {
+ if (Outputs->SdramCount == 8) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t");
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ");
+ }
+ }
+
+ continue;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if (Edges > 1) {
+ if (Byte < MAX_SDRAM_IN_DIMM) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%d-%d\t",
+ ResultArray[TestNum][Off - Start][Channel][Byte][0],
+ ResultArray[TestNum][Off - Start][Channel][Byte][1]
+ );
+ }
+ } else {
+ if (Byte < MAX_SDRAM_IN_DIMM) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ResultArray[TestNum][Off - Start][Channel][Byte][0]);
+ }
+ }
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // New line after the end of the table
+
+ return;
+}
+#endif // MRC_DEBUG_PRINT
+
+/**
+ This function returns the UPM or PWR limit value for the specified parameter
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] Param - Margin type
+ @param[in] LimitType - Type of limit: UpmLimit or PowerLimit
+
+ @retval Returns the UPM or PWR limit
+**/
+U16
+UpmPwrLimitValue (
+ IN MrcParameters *const MrcData,
+ IN U8 Param,
+ IN U8 LimitType
+ )
+{
+ MrcOutput *Outputs;
+ MrcUpmPwrRetrainLimits *MrcLimits;
+ U32 Index;
+ U16 Limit;
+#ifdef ULT_FLAG
+ U8 Channel;
+#endif // ULT_FLAG
+
+ Limit = 0;
+ Outputs = &MrcData->SysOut.Outputs;
+ MrcLimits = Outputs->UpmPwrRetrainLimits.Pointer;
+
+ for (Index = 0; Index < MRC_NUMBER_UPM_PWR_RETRAIN_MARGINS; Index++) {
+ if (Param == MrcLimits[Index].Param) {
+ Limit = MrcLimits[Index].ParamLimit[LimitType];
+ break;
+ }
+ }
+
+#ifdef ULT_FLAG
+ if ((MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) &&
+ (Outputs->DdrType == MRC_DDR_TYPE_DDR3) &&
+ (Param == WrV) &&
+ (LimitType != RetrainLimit)
+ ) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ if (Outputs->Controller[0].Channel[Channel].Dimm[0].ReferenceRawCard == rcF) {
+ Limit += 200; // Add 20 ticks for WrV on HSW ULT with DDR3L and raw card F
+ break;
+ }
+ }
+ }
+ }
+#endif // ULT_FLAG
+
+ return Limit;
+}
+
+/**
+ This function will adjust the requested Limit Type of the margin parameter by the signed offset passed in.
+
+ @param[in] MrcData - MRC global data.
+ @param[in] Param - Margin parameter type to adjust.
+ @param[in] LimitType - MRC_MARGIN_LIMIT_TYPE to adjust.
+ @param[in] Offset - The adjustment value.
+
+ @retval U16 - The new value of Param[MRC_MARGIN_LIMIT_TYPE]
+**/
+U16
+MrcUpdateUpmPwrLimits (
+ IN OUT MrcParameters * const MrcData,
+ IN U8 Param,
+ IN U8 LimitType,
+ IN S8 Offset
+ )
+{
+ MrcUpmPwrRetrainLimits *MrcLimits;
+ U32 Index;
+ S32 UpdatedValue;
+
+ MrcLimits = MrcData->SysOut.Outputs.UpmPwrRetrainLimits.Pointer;
+ UpdatedValue = 0;
+
+ for (Index = 0; Index < MRC_NUMBER_UPM_PWR_RETRAIN_MARGINS; Index++) {
+ if (Param == MrcLimits[Index].Param) {
+ UpdatedValue = MrcLimits[Index].ParamLimit[LimitType];
+ break;
+ }
+ }
+
+ UpdatedValue += Offset;
+ UpdatedValue = MAX (UpdatedValue, 0);
+ UpdatedValue = MIN (UpdatedValue, 0xFFFF);
+
+ MrcLimits[Index].ParamLimit[LimitType] = (U16) UpdatedValue;
+
+ return (U16) UpdatedValue;
+}
+
+/**
+ This function returns the Actual Cpu Driver Impedance (1 segment) in ohm.
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] Offset - Vref Offset (+-8).
+
+ @retval Returns the CPU driver impedance value (for 1 segment)
+**/
+U16
+CalcDrvImp (
+ IN MrcParameters *const MrcData,
+ IN S8 Offset
+ )
+{
+ U16 Result;
+ U8 Rext;
+
+ Rext = 75;
+
+#ifdef ULT_FLAG
+ if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) {
+ Rext = 120; // RCOMP1 resistor is 120 Ohm on HSW-ULT boards
+ }
+#endif // ULT_FLAG
+
+ //
+ // If Offset == -32, return 0;
+ //
+ if (Offset == -32) {
+ Result = 0;
+ } else {
+ Result = Rext * (32 - Offset) / (32 + Offset);
+ }
+
+ return Result;
+}
+
+/**
+ This function returns the Actual Cpu Odt termination in ohm.
+
+ @param[in] MrcData - Pointer to MRC global data.
+ @param[in] Offset - Vref Offset (+-16).
+
+ @retval Returns the Odt termination value.
+**/
+U16
+CalcRdOdt (
+ IN MrcParameters *const MrcData,
+ IN S8 Offset
+ )
+{
+ U16 Result;
+ U8 Rext;
+
+#ifdef ULT_FLAG
+ if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) {
+ Rext = 100; // 200 / 2
+ } else
+#endif //ULT_FLAG
+ {
+ Rext = 50; // 100 / 2
+ }
+
+ Result = (Rext * 96 / (Offset + 48) - Rext);
+
+ return Result;
+}
+
+/**
+ Calculate Power for the selected Opt param based on
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel to work on
+ @param[in] Rank - Rank to work on
+ @param[in] Byte - Byte to work on
+ @param[in] OptParam - The Opt Parameter to work on
+ @param[in] Offset - The Offset to work on
+ @param[in] CurrentComp - The current Comp code for OptParam
+ @param[in] ReadHost - Switch to read current offset and CompCode from Host structure.
+
+ @retval Calc power in mW
+**/
+U32
+CalcOptPower (
+ IN MrcParameters *MrcData,
+ IN U8 Channel,
+ IN U8 Rank,
+ IN U8 Byte,
+ IN U8 OptParam,
+ IN S8 Offset,
+ IN S8 CurrentComp,
+ IN BOOL ReadHost
+ )
+{
+ U32 Power;
+ U16 Rleg;
+ S8 StatLegs;
+ U8 OdtLegsDis;
+ S8 CurrentVref;
+ U8 RxVselect;
+ U8 RxCBSelect;
+ S8 RxFselect;
+ U8 RxDefault;
+ extern const U8 RxBiasTable[2][5][4];
+ U8 RxPowerScale[] = { 33, 66, 88, 100, 133, 166, 200, 233 };
+ U32 Vcc;
+ U32 CPURXPower;
+ MrcVddSelect Vdd;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0;
+ DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetComp;
+ DDR3_MODE_REGISTER_1_STRUCT Ddr3ModeRegister1;
+ DDRCOMP_CR_DDRCRDATACOMP0_STRUCT DdrCrDataComp0;
+ DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1;
+#ifdef ULT_FLAG
+ BOOL Lpddr;
+ U16 DimmRon;
+ extern const U8 RxBiasTableUlt[2][3][4];
+
+ Lpddr = (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3);
+#endif //ULT_FLAG
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ DdrCrDataComp0.Data = 0;
+ DdrCrDataComp1.Data = 0;
+ DdrCrDataOffsetComp.Data = 0;
+ Power = 0;
+ Vdd = Outputs->VddVoltage[Inputs->MemoryProfile];
+
+ if ((OptParam == OptWrDS) || (OptParam == OptRdOdt) || (OptParam == OptSComp)) {
+ DdrCrDataOffsetComp.Data = ChannelOut->DataCompOffset[Byte];
+ if (OptParam == OptRdOdt) {
+ DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG);
+ } else {
+ DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG);
+ }
+ }
+
+ if (OptParam == OptWrDS) {
+ DdrCrCompCtl0.Data = Outputs->CompCtl0;
+ //
+ // Added Driver RCOMP Vref for driver impedance calculation
+ //
+ CurrentVref = (S8) DdrCrCompCtl0.Bits.DqDrvVref;
+ if (CurrentVref & 0x8) {
+ CurrentVref -= 0x10; // 2's complement
+ }
+
+ if (ReadHost) {
+ CurrentComp = (S8) DdrCrDataComp0.Bits.RcompDrvUp;
+ Offset = (S8) DdrCrDataOffsetComp.Bits.DqDrvUpCompOffset;
+
+ if (Offset & 0x20) {
+ Offset-= 0x40; // 2's complement
+ }
+ }
+
+ StatLegs = 3 * 4 * 4; // seg*legs*4 - for calc set to 48
+ Rleg = CalcDrvImp (MrcData, CurrentVref) / 3 * (StatLegs + 3 * CurrentComp); // RCOMP Vref added to Rleg calculation
+ Power = Rleg / (StatLegs + 3 * (CurrentComp + (Offset))); // in ohm
+ }
+
+ if (OptParam == OptRdOdt) {
+ DdrCrCompCtl0.Data = Outputs->CompCtl0;
+ OdtLegsDis = (U8) DdrCrCompCtl0.Bits.DisableOdtStatic;
+ CurrentVref = (S8) DdrCrCompCtl0.Bits.DqOdtVref;
+ StatLegs = 4 * 4; // we enable only 1/3 segment for odt 4 legs time 4
+
+ if (CurrentVref & 0x10) {
+ CurrentVref -= 0x20; // 2's complement
+ }
+
+ if (ReadHost) {
+ CurrentComp = (S8) DdrCrDataComp1.Bits.RcompOdtUp;
+ Offset = (S8) DdrCrDataOffsetComp.Bits.DqOdtUpCompOffset;
+ if (Offset & 0x10) {
+ Offset-= 0x20; // 2's complement
+ }
+ }
+ //
+ // Avoid division by zero.
+ //
+ if (CurrentComp == 0) {
+ CurrentComp = 1;
+ }
+ Rleg = CalcRdOdt (MrcData, CurrentVref) * (StatLegs * (!OdtLegsDis) + CurrentComp);
+ Power = Rleg / (StatLegs * (!OdtLegsDis) + (CurrentComp + (Offset))); // in ohm
+ }
+
+ if (OptParam == OptSComp) {
+ if (ReadHost) {
+ Offset = (S8) DdrCrDataOffsetComp.Bits.DqSlewRateCompOffset;
+ if (Offset & 0x10) {
+ Offset -= 0x20; // 2's complement
+ }
+ }
+ Power = 50 + Offset; // simple linear T-line
+ }
+
+ if (OptParam == OptTxEq) {
+ Power = Offset; // simple linear T-line
+ }
+
+ if (OptParam == OptRxEq) {
+ Power = 100 + (5 * (Offset / 5)); // modulo 5 T-line
+ }
+
+ if (OptParam == OptDimmRon) {
+ //
+ // calc the DimmRon [ohm]
+ //
+ if (ReadHost) {
+ if (Rank < MAX_RANK_IN_CHANNEL) {
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ DimmRon = ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR3];
+ Offset = (U8) 0xF & (DimmRon - 1); //{0x1,0x2,0x3}; //{34,40,48};
+ } else
+#endif //ULT_FLAG
+ {
+ Ddr3ModeRegister1.Data = ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR1];
+ Offset = (U8) Ddr3ModeRegister1.Bits.ODImpedanceLow;
+ }
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Error: ChannelOut->Dimm array out of bounds! %d > %d\n",
+ Rank / 2,
+ MAX_DIMMS_IN_CHANNEL - 1
+ );
+ return 0;
+ }
+ }
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ if (Offset > 6) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Offset %d causes negative unsigned number or divide by 0. Dividing by 1.\n",
+ Offset
+ );
+ Offset = 6;
+ }
+
+ Power = 240 / (7 - Offset);
+ } else
+#endif //ULT_FLAG
+ {
+ if (Offset < -5) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Offset %d causes negative unsigned number or divide by 0. Dividing by 1.\n",
+ Offset
+ );
+ Offset = -5;
+ }
+
+ Power = 240 / (6 + Offset);
+ }
+ }
+
+ if (OptParam == OptRxBias) {
+ //
+ // RX BIAS calculations
+ //
+ Vcc = 1050;
+ RxVselect = 0;
+ if (Vdd > VDD_1_35) {
+ RxVselect = 1; // Set HiVdd bit if Vdd is over 1.35v
+ }
+ //
+ // RX BIAS calculations
+ //
+ GetRxFselect (MrcData, &RxFselect, &RxCBSelect);
+
+#ifdef ULT_FLAG
+ if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) {
+ RxFselect = MIN (RxFselect, RXF_SELECT_MAX_ULT); // Maximum 1600 MHz
+ RxDefault = RxBiasTableUlt[RxVselect][RxFselect][RxCBSelect]; // Read setting from array lookup table
+ } else
+#endif // ULT_FLAG
+ {
+ RxDefault = RxBiasTable[RxVselect][RxFselect][RxCBSelect]; // Read setting from array lookup table
+ }
+
+ CPURXPower = Vdd * 1200 / 1000 + Vcc * 1250 / 1000; // mW
+ CPURXPower /= 1000;
+ if (ReadHost) {
+ Offset = (U8) ChannelOut->DqControl1[Byte].Bits.RxBiasCtl;
+ }
+
+ CPURXPower = (RxPowerScale[Offset] * CPURXPower) / RxPowerScale[RxDefault];
+ Power = (U16) CPURXPower;
+ }
+
+ return Power;
+}
+
+/**
+ This function prints out the Margin eye diagram for ParamT/ParamV.
+
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - Channel to margin.
+ @param[in] Ranks - Bit mask of Ranks to margin.
+ @param[in] ParamT - Time parameter to margin.
+ @param[in] ParamV - Voltage parameter to margin.
+ @param[in] Start - Starting point for margining.
+ @param[in] Stop - Stopping point for margining.
+ @param[in] Repeats - Number of times to repeat the test to average out any noise.
+ @param[in] NoPrint - Switch to skip printing.
+
+ @retval Nothing
+**/
+void
+EyeMargin (
+ IN MrcParameters *const MrcData,
+ IN U8 Channel,
+ IN U8 Ranks,
+ IN U8 ParamT,
+ IN U8 ParamV,
+ IN S8 Start,
+ IN S8 Stop,
+ IN U16 SearchLimits,
+ IN U8 LoopCount,
+ IN U8 Repeats,
+ IN BOOL NoPrint
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ MrcControllerOut *ControllerOut;
+ MrcStatus Status;
+ U32 (*MarginByte)[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES];
+ U32 BERStats[4];
+ U16 SaveMargin[63][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES]; //40 Points X Ch X Byte X Hi/Lo
+ BOOL Eye[63][108];
+ BOOL Lines[108];
+ U8 MaxH=108;
+ U8 MaxW=63;
+ U8 i,j;
+ U16 MinEdge;
+ U16 Mode;
+ U8 ResultTypeV = 0;
+ U8 ChBitMask;
+ U8 Byte;
+ U8 Rank;
+ U8 Edge;
+ U8 FirstRank;
+ U8 NumBytes;
+ U8 BMap[9]; // Need by GetBERMarginByte
+ U8 MaxMarginV;
+ U8 localR[MAX_CHANNEL];
+ U8 Rep;
+ S8 Index;
+ U8 IndexOff;
+ S8 Off;
+ U8 byteMax[MAX_CHANNEL];
+ U32 Offset;
+ U64 CrValue64;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ MarginByte = &Outputs->MarginResult;
+ Ranks &= Outputs->ValidRankMask;
+ ControllerOut = &Outputs->Controller[0];
+ IndexOff = 0;
+ CrValue64 = 0x0ULL; //64 bit Data bit mask
+
+ MrcOemMemorySet ((U8 *) localR, 0, sizeof(localR));
+ MrcOemMemorySet ((U8 *) Eye, 0, sizeof(Eye));
+ MrcOemMemorySet ((U8 *) Lines, 0, sizeof(Lines));
+ MrcOemMemorySet ((U8 *) SaveMargin, 0, sizeof(SaveMargin));
+ MrcOemMemorySetDword (BERStats, 0, sizeof(BERStats) / sizeof (U32));
+ for (Byte = 0; Byte < sizeof (BMap) / sizeof (BMap[0]); Byte++) {
+ BMap[Byte] = Byte;
+ }
+
+ Outputs->EnDumRd = 0;
+ SetupIOTestBasicVA(MrcData, 1<<Channel, LoopCount, 0, 0, 0,8); //set test to all channels
+ //
+ // Select All Ranks for REUT test
+ //
+ ChannelOut = &ControllerOut->Channel[Channel];
+ localR[Channel] = ChannelOut->ValidRankBitMask & Ranks;
+ //
+ // use ChBitMask from here down - if ch is set that mean at least 1 rank for testing, also remove ch w/o active ranks
+ //
+ ChBitMask = SelectReutRanks (MrcData, Channel, localR[Channel], 0);
+
+ if (ChBitMask == 0) {
+ return ;
+ }
+
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ ChannelOut->DataOffsetTrain[Byte] = 0;
+ }
+ //
+ // Find the first selected rank
+ //
+ FirstRank = 0;
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if ((1 << Rank) & localR[Channel]) {
+ FirstRank = Rank; // could be in any channel
+ break;
+ }
+ }
+ //
+ // Store margin results for
+ //
+ NumBytes = (U8) Outputs->SdramCount;
+
+ //
+ // Loop through all Test Params and Measure Margin
+ // Find MaxMargin for this channel
+ //
+ byteMax[Channel] = Stop;
+ if (ParamT == RdT) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ byteMax[Channel] = MrcCalcMaxRxMargin (MrcData, Channel, Ranks, Byte, 0, byteMax[Channel]);
+ }
+ }
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_ERR_DATA_MASK_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_ERR_DATA_MASK_REG - MCHBAR_CH0_CR_REUT_CH_ERR_DATA_MASK_REG) * Channel);
+
+ MrcWriteCR64 (MrcData, Offset, CrValue64);
+ MaxMarginV = MAX_POSSIBLE_VREF;
+ if (MAX_POSSIBLE_TIME < Stop) {
+ Stop = MAX_POSSIBLE_TIME;
+ }
+
+ if (-MAX_POSSIBLE_TIME > Start) {
+ Start = -MAX_POSSIBLE_TIME;
+ }
+
+ IndexOff = MaxW / 2 + Start;
+ //
+ // No need to search too far
+ //
+ if (MaxMarginV > SearchLimits) {
+ MaxMarginV = (U8) (SearchLimits);
+ }
+
+ for (Off = Start; Off < Stop + 1; Off++) {
+ Index = Off - Start;
+ //
+ // change margin ParamT
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ Status = ChangeMargin (MrcData, ParamT, Off, 0, 0, Channel, localR[Channel], Byte, 0, 1, 0, MrcRegFileStart);
+ }
+ ResultTypeV = GetMarginResultType (ParamV); // rxv=0 rxt=1
+ //
+ // Assign to last pass margin results by reference
+ // get lowest margin from all ch/rankS/byte save in FirstRank
+ //
+ Status = GetMarginByte (
+ MrcData,
+ Outputs->MarginResult,
+ ParamV,
+ FirstRank,
+ Ranks
+ );
+ for (Rep = 0; Rep < Repeats; Rep++) {
+ //
+ // Run Margin Test - margin_1d with chosen param
+ // run on all ranks but change param only for firstRank??
+ //
+ Mode = 0;
+ Status = MrcGetBERMarginByte (
+ MrcData,
+ Outputs->MarginResult,
+ ChBitMask,
+ FirstRank,
+ FirstRank,
+ ParamV,
+ Mode,
+ BMap,
+ 1,
+ MaxMarginV,
+ 0,
+ BERStats
+ );
+ //
+ // Record Results
+ //
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if ((Index > 62) || (Index < 0)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: SaveMargin array out of bounds! %d", Index);
+ return;
+ }
+
+ if (Rep == 0) {
+ SaveMargin[Index][Channel][Byte][Edge] = 0;
+ }
+
+ SaveMargin[Index][Channel][Byte][Edge] += (U16) (*MarginByte)[ResultTypeV][FirstRank][Channel][Byte][Edge];
+ }
+ }
+ }
+
+ for (Edge = 0; Edge < MAX_EDGES; Edge++) {
+ MinEdge = 0xFFFF;
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ if ((Index > 62) || (Index < 0)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: SaveMargin array out of bounds! %d", Index);
+ return;
+ }
+
+ SaveMargin[Index][Channel][Byte][Edge] /= Repeats;
+ if (MinEdge > SaveMargin[Index][Channel][Byte][Edge]) {
+ MinEdge = SaveMargin[Index][Channel][Byte][Edge];
+ }
+ }
+
+ if (((Index + IndexOff) > 62) ||
+ ((Index + IndexOff) < 0) ||
+ ((MaxH / 2 - (MinEdge - 1) / 10) > 107) ||
+ ((MaxH / 2 - (MinEdge - 1) / 10) < 0) ||
+ ((MaxH / 2 + (MinEdge - 1) / 10) > 107) ||
+ ((MaxH / 2 + (MinEdge - 1) / 10) < 0)
+ ) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: Eye or Lines array out of bounds!\n");
+ return;
+ }
+
+ if (Edge) {
+ Eye[Index + IndexOff][MaxH / 2 - (MinEdge - 1) / 10] = 1;
+ Lines[MaxH / 2 - (MinEdge - 1) / 10] = 1;
+ } else {
+ Eye[Index + IndexOff][MaxH / 2 + (MinEdge - 1) / 10] = 1;
+ Lines[MaxH / 2 + (MinEdge - 1) / 10] = 1;
+ }
+ }
+ }//end of offset
+ //
+ // Print the box
+ //
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "Plot Eye across ParamT = %s ParamV = %s settings:(Start=%d,Stop=%d) LC = %d Channel = %d Ranks = 0x%x\n",
+ MarginTypesString[ParamT],
+ MarginTypesString[ParamV],
+ Start,
+ Stop,
+ LoopCount,
+ Channel,
+ Ranks
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t------------------------------- +++++++++++++++++++++++++++++++\n");
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t332222222222111111111100000000000000000001111111111222222222233\n");
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Vref\t109876543210987654321098765432101234567890123456789012345678901\n");
+ for (i = 0; i < MaxH; i++) {
+ if (Lines[i]) {
+ //
+ // print only fail lines
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%3d:\t", MaxH / 2 - i); // per ch
+ for (j = 0; j < MaxW; j++) {
+ if (Eye[j][i]) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s", "#"); // per ch
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s", ((j == (MaxW) / 2) || (i == (MaxH) / 2)) ? "+" : " "); // per ch
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");//per ch
+ }
+ }
+ //
+ // Clean up after test
+ //
+ ChannelOut = &ControllerOut->Channel[Channel];
+ MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount);
+ Status = ChangeMargin (MrcData, ParamT, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileCurrent);
+
+}
+
+/**
+ This function fill the input array (e.g array[ch][rank]) with the power calculation
+ per rank/ch for current sys. setting.
+
+ @param[in] MrcData - MRC data struct;
+ @param[in,out] PwrChRank - Array to fill;
+
+ @retval Nothing
+**/
+void
+CalcSysPower (
+ IN MrcParameters *const MrcData,
+ IN OUT MrcPower PwrChRank[MAX_CHANNEL][MAX_RANK_IN_CHANNEL]
+ )
+{
+ const MrcDebug *Debug;
+ MrcChannelOut *ChannelOut;
+ MrcOutput *Outputs;
+ MrcOdtPowerSaving *OdtPowerSaving;
+ U8 Rank;
+ U8 Byte;
+ U8 Channel;
+ BOOL is1DPC;
+ BOOL ChCalcDone;
+ U16 ROdtCpu;
+ U8 RonDimm;
+ U16 RonCpu;
+ U16 Rodtdram;
+ U16 Wodtdram;
+ U16 RxBiasPwr;
+ U8 TotalRankCount;
+ U32 PwrAvgRd;
+ U32 PwrAvgWr;
+ const U8 RttNomDic[6] = {0,60,120,40,20,30}; //accordingly to DDR3 spec
+ const U8 RttWrDic[3] = {0,60,120}; //accordingly to DDR3 spec
+ DDR3_MODE_REGISTER_1_STRUCT Ddr3ModeRegister1;
+ DDR3_MODE_REGISTER_2_STRUCT Ddr3ModeRegister2;
+#ifdef ULT_FLAG
+ U16 LpddrMr3; //dimm DS
+ U16 LpddrMr11; //dimm ODT
+ const U8 LpddrRonDic[4] = {0,34,40,48};
+ const U8 LpddrOdtDic[4] = {0,0,120,240};
+#endif // ULT_FLAG
+
+ Outputs = &MrcData->SysOut.Outputs;
+ OdtPowerSaving = &Outputs->OdtPowerSavingData;
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ RxBiasPwr = 0;
+ TotalRankCount = 0;
+ PwrAvgRd = 0;
+ PwrAvgWr = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChCalcDone = 0;
+ RonCpu = 0;
+ ROdtCpu = 0;
+ RxBiasPwr = 0;
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ is1DPC = (ChannelOut->DimmCount == 1);
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+#ifdef ULT_FLAG
+ if (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3) {
+ if (Rank >= MAX_RANK_IN_DIMM) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: ChannelOut array out of bounds!\n");
+ return ;
+ }
+
+ LpddrMr3 = ChannelOut->Dimm[0].Rank[Rank].MR[mrMR3];
+ LpddrMr11 = ChannelOut->Dimm[0].Rank[Rank].MR11;
+ RonDimm = LpddrRonDic[0x3 & LpddrMr3];
+ Wodtdram = LpddrOdtDic[0x3 & LpddrMr11];
+ Rodtdram = 0x3FFF; // put 8k ohm as infinity - in lpddr there is no nomOdt
+ } else
+#endif // ULT_FLAG
+ {
+ Ddr3ModeRegister1.Data = ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR1];
+ Ddr3ModeRegister2.Data = ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR2];
+ RonDimm = 240 / (6 + (U8) Ddr3ModeRegister1.Bits.ODImpedanceLow);
+ Rodtdram = RttNomDic[(Ddr3ModeRegister1.Bits.OdtRttValueHigh << 2) |
+ (Ddr3ModeRegister1.Bits.OdtRttValueMid << 1) |
+ Ddr3ModeRegister1.Bits.OdtRttValueLow];
+ Wodtdram = RttWrDic[Ddr3ModeRegister2.Bits.DynamicOdt];
+ }
+ if (!ChCalcDone){
+ //
+ //Ron CPU - take average all bytes only per ch
+ //
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++){
+ RonCpu += (U16) CalcOptPower (MrcData, Channel, 0, Byte, OptWrDS, 0, 0, 1);//read from host
+ ROdtCpu += (U16) CalcOptPower (MrcData, Channel, 0, Byte, OptRdOdt, 0, 0, 1);//read from host
+ RxBiasPwr += (U16) CalcOptPower (MrcData, Channel, 0, Byte, OptRxBias, 0, 0, 1);//read from host
+ }
+ RonCpu /= (U16) Outputs->SdramCount;
+ ROdtCpu /= (U16) Outputs->SdramCount;
+ RxBiasPwr /= (U16) Outputs->SdramCount;
+ ChCalcDone = 1;
+ }
+
+ if (is1DPC) {
+ //
+ // in 1DPC channel always use only one of the terminations
+ //
+ if (Wodtdram == 0) {
+ Wodtdram = Rodtdram;
+ Rodtdram = 0x3FFF; // put 8k ohm as infinity
+ } else {
+ Rodtdram = 0x3FFF; // put 8k ohm as infinity
+ }
+ }
+
+ if (Rodtdram == 0) {
+ Rodtdram = 0x3FFF;
+ }
+
+ if (Wodtdram == 0) {
+ Wodtdram = Rodtdram; // in 2DPC where RttW=0
+ }
+
+ CalcPower (MrcData, &PwrChRank[Channel][Rank], RonCpu, RonDimm, ROdtCpu, Rodtdram, Wodtdram);
+ //
+ // add RxBias to CPU and Total
+ //
+ PwrChRank[Channel][Rank].CpuPower += RxBiasPwr;
+ PwrChRank[Channel][Rank].TotPwr += RxBiasPwr;
+ PwrAvgRd += PwrChRank[Channel][Rank].CpuPwrRd + PwrChRank[Channel][Rank].DimmPwrRd +
+ PwrChRank[Channel][Rank].ACPowerRd;
+ PwrAvgWr += PwrChRank[Channel][Rank].CpuPwrWr + PwrChRank[Channel][Rank].DimmPwrWrT +
+ PwrChRank[Channel][Rank].DimmPwrWrNT + PwrChRank[Channel][Rank].ACPowerWr;
+ TotalRankCount++;
+ }
+ }
+ }
+
+ if (TotalRankCount == 0) {
+ TotalRankCount = 1; // Prevent divide by 0
+ }
+
+ PwrAvgRd /= TotalRankCount;
+ PwrAvgRd += RxBiasPwr;
+ PwrAvgWr /= TotalRankCount;
+ //
+ // update Mrc struct with Base line numbers
+ //
+ if (OdtPowerSaving->BaseFlag == FALSE) {
+ OdtPowerSaving->BaseSavingRd = (U16) PwrAvgRd;
+ OdtPowerSaving->BaseSavingWr = (U16) PwrAvgWr;
+ OdtPowerSaving->BaseSavingCmd = 0; // currently no power train for CMD
+ } else {
+ OdtPowerSaving->MrcSavingRd = (U16) PwrAvgRd;
+ OdtPowerSaving->MrcSavingWr = (U16) PwrAvgWr;
+ OdtPowerSaving->MrcSavingCmd = 0; // currently no power train for CMD
+ }
+
+ return;
+}
+
+/**
+ This function optimize the digital offsets by reducing the digital
+ offset and apply the difference to the global one.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Param - Parameter defining the desired digital compensation offset.
+ @param[in] UpdateHost - Decides if MrcData is to be updated.
+
+ @retval The new comp value.
+**/
+U32
+OptimizeCompOffset (
+ IN MrcParameters *const MrcData,
+ IN const U8 Param,
+ IN const U8 UpdateHost
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ DDRCOMP_CR_DDRCRDATACOMP0_STRUCT DdrCrDataComp0;
+ DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1;
+ DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0;
+ DDRCOMP_CR_DDRCRCOMPCTL1_STRUCT DdrCrCompCtl1;
+ DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetComp;
+ U8 GlobalParam;
+ U8 CurrCompVref;
+ S8 NewCompVref;
+ U8 CurrentComp;
+ U8 NewComp;
+ S8 Sign;
+ U8 Done;
+ S16 AvgOffset;
+ U8 Offset;
+ U8 StartDelta;
+ U8 CurrDelta;
+ U8 MinDelta;
+ U8 Off;
+ U8 BestVrefOff;
+ U8 SignBit;
+ U8 Byte;
+ U8 Channel;
+ U8 NumCh;
+ U8 ReservedCodes;
+ S8 MaxCompVref;
+ S8 MinCompVref;
+ U8 DqSCompPC;
+ U8 CurrDqSCompPC;
+ U8 CompCodes[MAX_CHANNEL][MAX_SDRAM_IN_DIMM];
+
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ DdrCrCompCtl0.Data = Outputs->CompCtl0;
+ DdrCrCompCtl1.Data = Outputs->CompCtl1;
+
+ DdrCrDataOffsetComp.Data = 0;
+ ReservedCodes = 3;
+ NewComp = 0;
+ Offset = 0;
+ SignBit = 0;
+ DqSCompPC = 0;
+ CurrDqSCompPC = 0;
+
+ switch (Param) {
+ case OptWrDS:
+ DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG);
+ CurrentComp = (U8) DdrCrDataComp0.Bits.RcompDrvUp;
+ CurrCompVref = MrcSE ((U8) DdrCrCompCtl0.Bits.DqDrvVref, 4, 8);
+ MaxCompVref = (1 << (DDRCOMP_CR_DDRCRCOMPCTL0_DqDrvVref_WID - 1)) - 1;
+ MinCompVref = (-1) * 1 << (DDRCOMP_CR_DDRCRCOMPCTL0_DqDrvVref_WID - 1);
+ GlobalParam = WrDS;
+ break;
+
+ case OptRdOdt:
+ DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG);
+ CurrentComp = (U8) DdrCrDataComp1.Bits.RcompOdtUp;
+ CurrCompVref = MrcSE ((U8) DdrCrCompCtl0.Bits.DqOdtVref, 5, 8);
+ MaxCompVref = (1 << (DDRCOMP_CR_DDRCRCOMPCTL0_DqOdtVref_WID - 1)) - 1;
+ MinCompVref = (-1) * 1 << (DDRCOMP_CR_DDRCRCOMPCTL0_DqOdtVref_WID - 1);
+ GlobalParam = RdOdt;
+ break;
+
+ case OptSComp:
+ DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG);
+ CurrentComp = (U8) DdrCrDataComp0.Bits.SlewRateComp;
+ CurrDqSCompPC = (U8) DdrCrCompCtl1.Bits.DqScompPC;
+ CurrCompVref = (U8) DdrCrCompCtl1.Bits.DqScompCells;
+ MaxCompVref = (1 << (DDRCOMP_CR_DDRCRCOMPCTL1_DqScompCells_WID)) - 1;
+ MinCompVref = 4;
+ GlobalParam = SCompDq;
+ break;
+
+ default:
+ CurrentComp = 0;
+ CurrCompVref = 0;
+ MaxCompVref = 0;
+ MinCompVref = 0;
+ GlobalParam = 0;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Invalid Param : %d", Param);
+ break;
+ }
+
+ AvgOffset = 0;
+ NumCh = 0;
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if ((MrcChannelExist (Outputs, Channel))) {
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ DdrCrDataOffsetComp.Data = ChannelOut->DataCompOffset[Byte];
+ if (Param == OptWrDS) {
+ Offset = (U8) DdrCrDataOffsetComp.Bits.DqDrvUpCompOffset;
+ SignBit = 6;
+ }
+
+ if (Param == OptRdOdt) {
+ Offset = (U8) DdrCrDataOffsetComp.Bits.DqOdtUpCompOffset;
+ SignBit = 5;
+ }
+
+ if (Param == OptSComp) {
+ Offset = (U8) DdrCrDataOffsetComp.Bits.DqSlewRateCompOffset;
+ SignBit = 5;
+ }
+
+ AvgOffset += (S8) MrcSE (Offset, SignBit, 8);
+ CompCodes[Channel][Byte] = CurrentComp + MrcSE (Offset, SignBit, 8);
+ }
+
+ NumCh++;
+ }
+ }
+
+ Sign = (AvgOffset < 0) ? -1 : 1;
+ //
+ // Calculate the average offset and round to the nearest integer.
+ //
+ AvgOffset = (AvgOffset + Sign * NumCh * ((U8) Outputs->SdramCount) / 2) / (NumCh * ((U8) Outputs->SdramCount));
+
+ if (AvgOffset == 0) {
+ return CurrentComp;
+ }
+ //
+ // Find the CompVref minimum of the delta between (CurrentComp + AvgOffset) to NewComp.
+ // Take care of keeping 3 code reserved.
+ // Exit if no vref range left.
+ //
+ Done = 0;
+ Off = 1;
+ BestVrefOff = CurrCompVref;
+ NewComp = CurrentComp;
+ DqSCompPC = CurrDqSCompPC;
+ StartDelta = ABS ((S8) AvgOffset);
+ MinDelta = StartDelta;
+ if (Param == OptSComp) {
+ Sign *= -1;
+ }
+
+ while (!Done) {
+ NewCompVref = CurrCompVref + (Sign * Off);
+ if ((MinCompVref > NewCompVref) || (NewCompVref > MaxCompVref)) {
+ Done = 1;
+ }
+ //
+ // Reserve 3 comp codes
+ //
+ if ((ReservedCodes > NewComp) || (NewComp > (63 - ReservedCodes))) {
+ Done = 1;
+ }
+
+ if (Param == OptSComp) {
+ if ((NewCompVref + 1) > 16) {
+ DqSCompPC = 0;
+ }
+
+ NewCompVref = (DqSCompPC << 4) + NewCompVref;
+ }
+
+ if (!Done) {
+ NewComp = (U8) UpdateCompGlobalOffset (MrcData, GlobalParam, NewCompVref, 0);
+ CurrDelta = ABS (CurrentComp + (S8) AvgOffset - NewComp);
+ if (CurrDelta < StartDelta) {
+ if (CurrDelta < MinDelta) {
+ MinDelta = CurrDelta;
+ BestVrefOff = NewCompVref;
+ if (MinDelta == 0) {
+ Done = 1;
+ }
+ }
+ } else {
+ Done = 1;
+ }
+ }
+
+ Off++;
+ }
+ //
+ // update new compVref setting
+ //
+ if (BestVrefOff != CurrCompVref) {
+ NewComp = (U8) UpdateCompGlobalOffset (MrcData, GlobalParam, BestVrefOff, UpdateHost);
+ //
+ // Update all bytes with new offset: Offset + code - newcode = +newoffset
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ if ((MrcChannelExist (Outputs, Channel))) {
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ UpdateOptParamOffset (MrcData, Channel, 0, Byte, Param, CompCodes[Channel][Byte] - NewComp, UpdateHost);
+ }
+ }
+ }
+ } else {
+ //
+ // Restore CompVref
+ //
+ if (Param == OptSComp) {
+ NewCompVref = (CurrDqSCompPC << 4) + CurrCompVref;
+ }
+
+ NewComp = (U8) UpdateCompGlobalOffset (MrcData, GlobalParam, CurrCompVref, UpdateHost);
+ }
+
+ return NewComp;
+}
+
+/**
+ This step performs Comp Offset optimization on the param list defined in this function.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - mrcSuccess
+**/
+MrcStatus
+MrcOptimizeComp (
+ IN MrcParameters *const MrcData
+ )
+{
+ const U8 ParamList[] = { OptWrDS, OptRdOdt, OptSComp };
+ U8 Param;
+
+ for (Param = 0; Param < sizeof (ParamList); Param++) {
+ OptimizeCompOffset (MrcData, ParamList[Param], 1);
+ }
+
+ return mrcSuccess;
+}
+
+/**
+ This function calculates the percent of power saving from the power optimization steps and
+ updates the proper registers in the PCU. To get the correct base line for this calculation,
+ this routing needs to run first time early in the training in order to update the MrcStruct
+ with the base line. After the power training steps, it will run again to get the actual
+ percent of power saving.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - mrcSuccess
+
+**/
+MrcStatus
+MrcPowerSavingMeter (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcDebug const *Debug;
+ MrcPower PwrChRank[MAX_CHANNEL][MAX_RANK_IN_CHANNEL];
+ MrcOdtPowerSaving *OdtPowerSaving;
+ U8 PercentRd;
+ U8 PercentWr;
+ U8 PercentCmd;
+ PCU_CR_MRC_ODT_POWER_SAVING_PCU_STRUCT CrMrcOdtPowerSavingPcu;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ OdtPowerSaving = &MrcData->SysOut.Outputs.OdtPowerSavingData;
+ CalcSysPower (MrcData, PwrChRank);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "\tBaseLine\tMrcSaving\nAvgRd\t%d\t\t%d\nAvgWr\t%d\t\t%d\n",
+ OdtPowerSaving->BaseSavingRd,
+ OdtPowerSaving->MrcSavingRd,
+ OdtPowerSaving->BaseSavingWr,
+ OdtPowerSaving->MrcSavingWr
+ );
+
+ if (OdtPowerSaving->BaseFlag) {
+ //
+ // Calculate power saving and update PCU regs
+ //
+ if (OdtPowerSaving->BaseSavingRd > OdtPowerSaving->MrcSavingRd) {
+ PercentRd = (U8) (((U32) (OdtPowerSaving->BaseSavingRd - OdtPowerSaving->MrcSavingRd) * 256) / OdtPowerSaving->BaseSavingRd);
+ } else {
+ PercentRd = 0;
+ }
+
+ if (OdtPowerSaving->BaseSavingWr > OdtPowerSaving->MrcSavingWr) {
+ PercentWr = (U8) (((U32) (OdtPowerSaving->BaseSavingWr - OdtPowerSaving->MrcSavingWr) * 256) / OdtPowerSaving->BaseSavingWr);
+ } else {
+ PercentWr = 0;
+ }
+
+ if (OdtPowerSaving->BaseSavingCmd > OdtPowerSaving->MrcSavingCmd) {
+ PercentCmd = (U8) (((U32) (OdtPowerSaving->BaseSavingCmd - OdtPowerSaving->MrcSavingCmd) * 256) / OdtPowerSaving->BaseSavingCmd);
+ } else {
+ PercentCmd = 0;
+ }
+
+ CrMrcOdtPowerSavingPcu.Bits.MRC_Saving_Rd = PercentRd;
+ CrMrcOdtPowerSavingPcu.Bits.MRC_Saving_Wt = PercentWr;
+ CrMrcOdtPowerSavingPcu.Bits.MRC_Saving_Cmd = PercentCmd;
+
+ MrcWriteCR (MrcData, PCU_CR_MRC_ODT_POWER_SAVING_PCU_REG, CrMrcOdtPowerSavingPcu.Data);
+ } else {
+ OdtPowerSaving->BaseFlag = TRUE;
+ }
+
+ return mrcSuccess;
+}
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcDdr3.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcDdr3.c
new file mode 100644
index 0000000..654a332
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcDdr3.c
@@ -0,0 +1,1572 @@
+/** @file
+ This file includes all the DDR3 specific characteristic functions.
+
+@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 "MrcDdr3.h"
+
+#ifdef ULT_FLAG
+///
+/// Only 1DPC is supported on HSW-ULT
+///
+const TOdtValue MbUltOdtTable[MAX_DIMMS_IN_CHANNEL][2] = {
+/// 1DPC 1R, 1DPC 2R
+ {{120,0}, {120,0}},
+};
+
+const TOdtValue User1UltOdtTable[MAX_DIMMS_IN_CHANNEL][2] = {
+/// 1DPC 1R, 1DPC 2R
+ {{120,0}, {120,0}},
+};
+
+#endif //ULT_FLAG
+
+#ifdef TRAD_FLAG
+const TOdtValue MbTradOdtTable[MAX_DIMMS_IN_CHANNEL][oiNotValid] = {
+/// 1DPC 1R, 1DPC 2R, 2DPC 1R 1R, 2DPC 1R 2R, 2DPC 2R 1R, 2DPC 2R 2R
+ {{00,40}, {00,40}, {60,30}, {60,30}, {60,30}, {60,30}},
+ {{00,40}, {00,40}, {60,30}, {60,30}, {60,30}, {60,30}}
+};
+
+const TOdtValue DtTradOdtTable[MAX_DIMMS_IN_CHANNEL][oiNotValid] = {
+/// 1DPC 1R, 1DPC 2R, 2DPC 1R 1R, 2DPC 1R 2R, 2DPC 2R 1R, 2DPC 2R 2R
+ {{60,00}, {60,00}, {60,30}, {60,30}, {60,30}, {60,30}},
+ {{60,00}, {60,00}, {60,30}, {60,30}, {60,30}, {60,30}}
+};
+
+const TOdtValue User1TradOdtTable[MAX_DIMMS_IN_CHANNEL][oiNotValid] = {
+/// 1DPC 1R, 1DPC 2R, 2DPC 1R 1R, 2DPC 1R 2R, 2DPC 2R 1R, 2DPC 2R 2R
+ {{60,60}, {60,60}, {60,30}, {60,30}, {60,30}, {60,30}},
+ {{60,60}, {60,60}, {60,30}, {60,30}, {60,30}, {60,30}}
+};
+
+const TOdtValue User2TradOdtTable[MAX_DIMMS_IN_CHANNEL][oiNotValid] = {
+/// 1DPC 1R, 1DPC 2R, 2DPC 1R 1R, 2DPC 1R 2R, 2DPC 2R 1R, 2DPC 2R 2R
+ {{60,60}, {60,60}, {60,40}, {60,40}, {60,40}, {60,40}},
+ {{60,60}, {60,60}, {60,40}, {60,40}, {60,40}, {60,40}}
+};
+#endif // TRAD_FLAG
+
+//
+// Module external functions
+//
+/**
+@brief
+ this function reverses MA and BA bits for Rank1
+
+ @param[in] BA - MRS command to be sent
+ @param[in] MA - Value to be sent
+
+ @retval Proper MA and BA BITS.
+**/
+U32
+MrcMirror (
+ IN U8 BA,
+ IN U16 MA
+ )
+{
+ U16 ma357;
+ U16 ma468;
+
+ //
+ // UDIMM/SODIMM reverses the following bits on Rank1
+ // A3 - 4, A5 - 6, A7 - 8
+ // BA0 - 1
+ //
+ ma357 = 0xA8 & MA;
+ ma468 = 0x150 & MA;
+ MA = (0xFE07 & MA) | (ma357 << 1) | (ma468 >> 1);
+ BA = (0x4 & BA) | ((0x2 & BA) >> 1) | ((0x1 & BA) << 1);
+
+ return (BA << 24) + MA;
+}
+
+/**
+@brief
+ this function writes to CADB
+
+ @param[in] MrcData - include all the MRC data
+ @param[in] Channel - Channel to send command to
+ @param[in] RankMask - Rank mask for which command will be sent to.
+ @param[in] CMD - 0: MRS, 1: REF, 2: PRE, 3: ACT, 4: WR, 5: RD, 6: ZQ, 7: NOP
+ @param[in] BA - MRS command to be sent
+ @param[in] MA - Value to be sent
+ @param[in] Delay - Delay in Dclocks
+
+ @retval MrcStatus
+**/
+MrcStatus
+MrcWriteCADBCmd (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 RankMask,
+ IN const U8 CMD,
+ IN const U8 BA,
+ IN const U16 *const MA,
+ IN const U8 Delay
+ )
+{
+ const MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcChannelOut *ChannelOut;
+ MrcStatus Status;
+ U32 Offset;
+ U8 Stop;
+ U8 Dimm;
+ U8 Rank;
+ U8 AddressMirrored; // bitMask
+ MCHBAR_CH0_CR_REUT_CH_PAT_CADB_PROG_STRUCT ReutChPatCadbProg;
+ MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MRS_STRUCT ReutChPatCadbMrs;
+ MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_STRUCT ReutChSeqCfg;
+ MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_STRUCT ReutChSeqCfgSave;
+ MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_0_STRUCT ReutChSeqCtl;
+ MCDFXS_CR_REUT_GLOBAL_ERR_MCMAIN_STRUCT ReutGlobalErr;
+ MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ ChannelOut = &Outputs->Controller[0].Channel[Channel];
+ Status = mrcSuccess;
+ Stop = 0;
+ AddressMirrored = 0;
+
+ //
+ // Clear DDR qualifier during reset sequence
+ //
+ MrcWriteCR8 (MrcData, MCSCHEDS_CR_DFT_MISC_REG + 1, 0);
+
+ //
+ // Check for AddressMirrored on each DIMM present
+ //
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ if (ChannelOut->Dimm[Dimm].AddressMirrored == TRUE) {
+ AddressMirrored |= (MRC_BIT0 << Dimm);
+ }
+ }
+ //
+ // Pointer will be dynamically incremented after a write to CADB_PROG register
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_WRITE_POINTER_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_WRITE_POINTER_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_WRITE_POINTER_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, 0);
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel) & RankMask) {
+
+ ReutChPatCadbProg.Data = 0;
+ ReutChPatCadbProg.Bits.CADB_Data_Bank = BA;
+ ReutChPatCadbProg.Bits.CADB_Data_Address = MA[RANK_TO_DIMM_NUMBER (Rank)];
+
+ //
+ // Check if Rank 1 and if DIMM requires AddressMirrored
+ //
+ if ((Rank % 2) && (AddressMirrored & ((Rank / 2) + 1))) {
+ //
+ // Remainder is 1 only for Rank1 of each DIMM
+ //
+ ReutChPatCadbProg.Data = MrcMirror (BA, MA[RANK_TO_DIMM_NUMBER (Rank)]);
+ }
+
+ ReutChPatCadbProg.Bits.CADB_Data_CKE = 0xF;
+ ReutChPatCadbProg.Bits.CADB_Data_Control = CMD;
+ ReutChPatCadbProg.Bits.CADB_Data_CS = ~(MRC_BIT0 << Rank);
+
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_PROG_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_PROG_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_PROG_REG) * Channel);
+ MrcWriteCR64 (MrcData, Offset, ReutChPatCadbProg.Data);
+
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "CH%d Rank %d ReutChPatCadbProg: 0x%08X%08X\n", Channel, Rank, ReutChPatCadbProg.Data32[1], ReutChPatCadbProg.Data32[0]);
+
+ Stop += 1;
+ }
+ }
+
+ if (Stop == 0) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "MrcWriteCADBCmd: Channel %d Ranks %d ValidRankBitMask 0x%X\n",
+ Channel,
+ RankMask,
+ ChannelOut->ValidRankBitMask
+ );
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "MrcWriteCADBCmd: Not a valid Rank in RankBitMask\n");
+ Status = mrcFail;
+ return Status;
+ }
+ //
+ // Execute MRS Mode
+ //
+ Offset = MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MRS_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_PAT_CADB_MRS_REG - MCHBAR_CH0_CR_REUT_CH_PAT_CADB_MRS_REG) * Channel);
+ ReutChPatCadbMrs.Data = 0;
+ ReutChPatCadbMrs.Bits.MRS_Gap = (Delay == 0) ? 3 : Delay;
+ ReutChPatCadbMrs.Bits.CADB_MRS_End_Pointer = Stop - 1;
+ MrcWriteCR (MrcData, Offset, ReutChPatCadbMrs.Data);
+
+ //
+ // Save before MR command
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * Channel);
+ ReutChSeqCfgSave.Data = MrcReadCR (MrcData, Offset);
+
+ //
+ // Prepare for MRS command
+ //
+ ReutChSeqCfg.Data = ReutChSeqCfgSave.Data;
+ ReutChSeqCfg.Bits.Global_Control = 0;
+ ReutChSeqCfg.Bits.Initialization_Mode = MRS_Mode;
+ MrcWriteCR (MrcData, Offset, (U32) ReutChSeqCfg.Data); // Set MRS Mode w/o Global control
+
+ //
+ // Start test and clear errors
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_0_REG) * Channel);
+ ReutChSeqCtl.Data = 0;
+ ReutChSeqCtl.Bits.Local_Clear_Errors = 1;
+ ReutChSeqCtl.Bits.Local_Start_Test = 1;
+ MrcWriteCR8 (MrcData, Offset, (U8) ReutChSeqCtl.Data);
+
+ //
+ // Wait for Channel_Test_Done_Status for the channel.
+ //
+ // @todo: Infinite loop possible, need timer.
+ //
+ do {
+ ReutGlobalErr.Data = MrcReadCR (MrcData, MCDFXS_CR_REUT_GLOBAL_ERR_MCMAIN_REG);
+ if (1 == ((Channel == 0) ? ReutGlobalErr.Bits.Channel_Error_Status_0 : ReutGlobalErr.Bits.Channel_Error_Status_1)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "ERROR IN MrcWriteCADBCmd: REUT_GLOBAL_ERR 0x%X\n", ReutGlobalErr.Data);
+ return mrcFail;
+ }
+ } while (0 == ((Channel == 0) ? ReutGlobalErr.Bits.Channel_Test_Done_Status_0 : ReutGlobalErr.Bits.Channel_Test_Done_Status_1));
+
+ //
+ // Restore after MR command
+ //
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CTL_MCMAIN_0_REG) * Channel);
+ ReutChSeqCtl.Data = 0;
+ ReutChSeqCtl.Bits.Local_Clear_Errors = 1;
+ MrcWriteCR8 (MrcData, Offset, (U8) ReutChSeqCtl.Data);
+
+ Offset = MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG +
+ ((MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, (U32) ReutChSeqCfgSave.Data);
+ return Status;
+}
+
+/**
+@brief
+ This function sends the proper MRS command for specific ranks as indicated by RankMask
+
+ @param[in] MrcData - include all the MRC data
+ @param[in] Channel - Channel to send command to
+ @param[in] RankMask - Rank mask for which command will be sent to
+ @param[in] MR - MRS command to be sent
+ @param[in] DimmValue - Dimm Values to be sent
+
+ @retval MrcStatus
+**/
+MrcStatus
+MrcWriteMRSAll (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 RankMask,
+ IN const U8 MR,
+ IN const U16 *const DimmValue
+ )
+{
+ //
+ // CMD = [0: MRS, 1: REF, 2: PRE, 3: ACT, 4: WR, 5: RD, 6: ZQ, 7: NOP]
+ //
+ return MrcWriteCADBCmd (MrcData, Channel, RankMask, MRS_CMD, MR, DimmValue, 0);
+}
+
+/**
+@brief
+ This function sends the proper MRS command for specific ranks as indicated by RankMask
+
+ @param[in] MrcData - Include all the MRC data
+ @param[in] Channel - Channel to send command to
+ @param[in] RankMask - Rank mask for which command will be sent to
+ @param[in] MR - MRS command to be sent
+ @param[in] Value - Value to be sent
+
+ @retval MrcStatus
+**/
+MrcStatus
+MrcWriteMRS (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 RankMask,
+ IN const U8 MR,
+ IN const U16 Value
+ )
+{
+ MrcStatus Status;
+ U16 DimmValue[MAX_DIMMS_IN_CHANNEL];
+
+ //
+ // Update proper Dimm Values based on Ranks (rank bit mask)
+ //
+ if (RankMask <= 3) {
+ //
+ // For DIMM 0
+ //
+ DimmValue[0] = Value;
+#if MAX_DIMMS_IN_CHANNEL > 1
+ DimmValue[1] = 0;
+#endif
+ } else {
+ //
+ // DIMM 1
+ //
+ DimmValue[0] = 0;
+#if MAX_DIMMS_IN_CHANNEL > 1
+ DimmValue[1] = Value;
+#endif
+ }
+ //
+ // CMD = [0: MRS, 1: REF, 2: PRE, 3: ACT, 4: WR, 5: RD, 6: ZQ, 7: NOP]
+ //
+ Status = MrcWriteCADBCmd (MrcData, Channel, RankMask, MRS_CMD, MR, DimmValue, 0);
+
+ return Status;
+}
+
+/**
+@brief
+ Issue ZQ calibration command on all ranks.
+ When done, wait appropriate delay depending on the ZQ type.
+
+ @param[in] MrcData - include all the MRC data
+ @param[in] chBitMask - Channel bit mask to be sent to.
+ @param[in] ZqType - Type of ZQ Calibration: see MrcZqType enum
+
+ @retval MrcStatus - mrcSuccess if passes, otherwise an error status
+**/
+MrcStatus
+MrcIssueZQ (
+ IN MrcParameters *const MrcData,
+ IN const U8 chBitMask,
+ IN const MrcZqType ZqType
+ )
+{
+ MrcOutput *Outputs;
+ MrcStatus Status;
+ MrcDebug *Debug;
+ U8 Channel;
+ U8 Dimm;
+ U8 Delay;
+ U16 MaValue;
+ U16 DimmValue[MAX_DIMMS_IN_CHANNEL];
+ U32 OpCode;
+#ifdef MRC_DEBUG_PRINT
+ char *StrZqType;
+#endif // MRC_DEBUG_PRINT
+#ifdef ULT_FLAG
+ U8 Rank;
+ BOOL Lpddr;
+#endif // ULT_FLAG
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ Status = mrcSuccess;
+ Delay = 1;
+
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+#endif //ULT_FLAG
+ MaValue = 0;
+
+ switch (ZqType) {
+ case MRC_ZQ_INIT:
+#ifdef MRC_DEBUG_PRINT
+ StrZqType = "INIT";
+#endif
+ MaValue = MRC_BIT10;
+ OpCode = 0xFF;
+ break;
+
+ case MRC_ZQ_LONG:
+#ifdef MRC_DEBUG_PRINT
+ StrZqType = "LONG";
+#endif
+ MaValue = MRC_BIT10;
+ OpCode = 0xAB;
+ break;
+
+ case MRC_ZQ_SHORT:
+#ifdef MRC_DEBUG_PRINT
+ StrZqType = "SHORT";
+#endif
+ OpCode = 0x56;
+ break;
+
+ case MRC_ZQ_RESET:
+#ifdef MRC_DEBUG_PRINT
+ StrZqType = "RESET";
+#endif
+ OpCode = 0xC3;
+ break;
+
+ default:
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Wrong ZQ type: %d\n", ZqType);
+ return mrcWrongInputParameter;
+ }
+
+ //
+ // Program MA value for all DIMMs
+ //
+ for (Dimm = 0; Dimm < (sizeof (DimmValue) / sizeof (DimmValue[0])); Dimm++) {
+ DimmValue[Dimm] = MaValue;
+ }
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ if ((MRC_BIT0 << Channel) & chBitMask) {
+ if (!(MRC_BIT0 << (Channel + 1) & chBitMask) && (ZqType == MRC_ZQ_SHORT)) {
+ Delay = 7;
+ }
+ //
+ // Issue ZQ calibration command on all ranks of this channel
+ //
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ //
+ // MR10, ZQ calibration
+ //
+ if (!Outputs->LpddrJedecInitDone) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "LPDDR: Issue ZQ %s on ch %d rank %d\n", StrZqType, Channel, Rank);
+ }
+ Status = MrcIssueMrw (MrcData, Channel, Rank, 10, OpCode, FALSE, FALSE);
+ }
+ }
+ } else
+#endif // ULT_FLAG
+ {
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "DDR3: Issue ZQ %s on ch %d\n", StrZqType, Channel);
+ //
+ Status = MrcWriteCADBCmd (MrcData, Channel, 0x0F, ZQ_CMD, 0, DimmValue, Delay);
+ }
+ }
+ }
+ }
+
+ if ((ZqType == MRC_ZQ_INIT) || (ZqType == MRC_ZQ_LONG)) {
+ MrcWait (MrcData, 1 * HPET_1US);
+ }
+
+ return Status;
+}
+
+/**
+@brief
+ This function writes the MR2 register for all the ranks and channels
+
+ @param[in, out] MrcData - general data
+ @param[in] Pasr - Partial array self refresh bit A0-A2
+
+ @retval MrcStatus - mrcSuccess if passes, otherwise an error status
+**/
+MrcStatus
+MrcSetMR2 (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 Pasr
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ TOdtValue *OdtTableIndex;
+ MrcProfile Profile;
+ U8 Channel;
+ U8 Rank;
+ U8 RankMod2;
+ U8 AutoSelfRefresh;
+ U8 SelfRefreshTemp;
+ U16 DimmValue[MAX_DIMMS_IN_CHANNEL];
+ MrcStatus Status;
+ DDR3_MODE_REGISTER_2_STRUCT Ddr3ModeRegister;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Debug = &Inputs->Debug;
+ Status = mrcSuccess;
+ Profile = Inputs->MemoryProfile;
+
+ for (Channel = 0; (Channel < MAX_CHANNEL) && (Status == mrcSuccess); Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ Ddr3ModeRegister.Data = 0;
+ Ddr3ModeRegister.Bits.PartialArraySR = Pasr;
+ //
+ // Subtract 5 because of jedec mr2 CWL table 0 = 5 1=6 2=7 ...
+ //
+ Ddr3ModeRegister.Bits.CasWriteLatency = ChannelOut->Timing[Profile].tCWL - 5;
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank += MAX_RANK_IN_DIMM) {
+ //
+ // loop only for each DIMM
+ //
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ if (Outputs->RestoreMRs) {
+ RankMod2 = Rank % 2;
+ Ddr3ModeRegister.Data = ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR2];
+ } else {
+ //
+ // ASR: Set if both bits 0 and 2 of byte 31 in SPD are set.
+ //
+ // @todo: Need to check and see if we need to set DDR3_MODE_REGISTER_2_STR_OFF based on EXTENDED_TEMP support
+ // If ASR need BIT6 set, else if EXTENDED_TEMP set BIT7.
+ // Need to understand the policy here or should we follow NHM's approach for LFD/CFD.
+ //
+ if (
+ (ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].AutoSelfRefresh == TRUE) &&
+ ((Inputs->RefreshRate2x == FALSE) || (Outputs->AutoSelfRefresh == TRUE))
+ ) {
+ AutoSelfRefresh = 1;
+ SelfRefreshTemp = 0;
+ } else if (ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].SelfRefreshTemp == TRUE) {
+ AutoSelfRefresh = 0;
+ SelfRefreshTemp = 1;
+ } else {
+ AutoSelfRefresh = 0;
+ SelfRefreshTemp = 0;
+ }
+
+ Ddr3ModeRegister.Bits.AutoSelfRefresh = AutoSelfRefresh;
+ Ddr3ModeRegister.Bits.SelfRefreshTemp = SelfRefreshTemp;
+
+ OdtTableIndex = GetOdtTableIndex (MrcData, Channel, RANK_TO_DIMM_NUMBER (Rank));
+ if (OdtTableIndex == NULL) {
+ return mrcFail;
+ }
+
+ Ddr3ModeRegister = UpdateRttWrValue (MrcData, OdtTableIndex->RttWr, Ddr3ModeRegister);
+
+ //
+ // *** must be before the MRC command because of address swizzling bits in SODIMM/UDIMM
+ //
+ SetTcMr2ShadowReg (MrcData, Channel, RANK_TO_DIMM_NUMBER (Rank), Ddr3ModeRegister.Data);
+
+ //
+ // save MR2 for later validation usage
+ //
+ RankMod2 = Rank % 2;
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR2] = Ddr3ModeRegister.Data;
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2 + 1].MR[mrMR2] = Ddr3ModeRegister.Data;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "MrcSetMR2 Channel %u Dimm %u Rank %u = 0x%X\n",
+ // Channel, RANK_TO_DIMM_NUMBER (Rank), Rank, Ddr3ModeRegister.Data);
+ //
+ }
+ //
+ // Update proper DIMM value
+ //
+ DimmValue[RANK_TO_DIMM_NUMBER (Rank)] = Ddr3ModeRegister.Data;
+ }
+ }
+ //
+ // 3rd parameter is really a rank mask of which ranks to send command to.
+ // If need it for all possible and present ranks, it should be 0xF.
+ //
+ Status = MrcWriteMRSAll (MrcData, Channel, 0x0F, mrMR2, DimmValue);
+ }
+ }
+
+ return Status;
+}
+
+/**
+@brief
+ This function writes the MR3 register for all the ranks and channels
+
+ @param[in] MrcData - include all the MRC data
+ @param[in] MPRLoc - MPR Location bit A0-A1
+ @param[in] Mpr - MPR bit A2
+
+ @retval MrcStatus - mrcSuccess if passes, otherwise an error status
+**/
+MrcStatus
+MrcSetMR3 (
+ IN MrcParameters *const MrcData,
+ IN const U8 MPRLoc,
+ IN const U8 Mpr
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ U16 DimmValue[MAX_DIMMS_IN_CHANNEL];
+ U8 Channel;
+ U8 Dimm;
+ MrcStatus Status;
+ DDR3_MODE_REGISTER_3_STRUCT Ddr3ModeRegister;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ Status = mrcSuccess;
+
+ //
+ // Independent channel data
+ //
+ Ddr3ModeRegister.Data = 0;
+ Ddr3ModeRegister.Bits.MprLocation = MPRLoc;
+ Ddr3ModeRegister.Bits.MprOperation = Mpr;
+
+ for (Channel = 0; (Channel < MAX_CHANNEL) && (Status == mrcSuccess); Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ for (Dimm = 0; Dimm < (sizeof (DimmValue) / sizeof (DimmValue[0])); Dimm++) {
+ DimmValue[Dimm] = Ddr3ModeRegister.Data;
+ }
+ //
+ // 3rd parameter is really a rank mask of which ranks to send command to.
+ // If need it for all possible and present ranks, it should be 0xF.
+ //
+ Status = MrcWriteMRSAll (MrcData, Channel, 0x0F, mrMR3, DimmValue);
+ }
+ }
+
+ return Status;
+}
+
+/**
+@brief
+ This function writes the MR1 register for all the ranks and channels
+
+ @param[in, out] MrcData - include all the MRC data
+ @param[in] DLLEnable - DLL enable bit A0
+ @param[in] Odic - Output driver impedance control A5, A1
+ @param[in] AdditiveLatency - Additive latency bit A3-A4
+ @param[in] WlEnable - Write leveling enable bit A7
+ @param[in] Tdqs - TDQS enable bit A11
+ @param[in] Qoff - Qoff bit A12
+
+ @retval MrcStatus - mrcSuccess if passes, otherwise an error status
+**/
+MrcStatus
+MrcSetMR1 (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 DLLEnable,
+ IN const U8 Odic,
+ IN const U8 AdditiveLatency,
+ IN const U8 WlEnable,
+ IN const U8 Tdqs,
+ IN const U8 Qoff
+ )
+{
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ TOdtValue *OdtTableIndex;
+ U16 DimmValue[MAX_DIMMS_IN_CHANNEL];
+ U8 Channel;
+ U8 Rank;
+ U8 RankMod2;
+ U8 RttNom;
+ MrcStatus Status;
+ DDR3_MODE_REGISTER_1_STRUCT Ddr3ModeRegister;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Debug = &Inputs->Debug;
+ Status = mrcSuccess;
+ OdtTableIndex = NULL;
+
+ for (Channel = 0; (Channel < MAX_CHANNEL) && (Status == mrcSuccess); Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ //
+ // independent channel data
+ //
+ Ddr3ModeRegister.Data = 0;
+ Ddr3ModeRegister.Bits.DllEnable = DLLEnable;
+ Ddr3ModeRegister.Bits.ODImpedanceLow = Odic & 1;
+ Ddr3ModeRegister.Bits.ODImpedanceHigh = (Odic & 2) >> 1;
+ Ddr3ModeRegister.Bits.AdditiveLatency = AdditiveLatency;
+ Ddr3ModeRegister.Bits.WriteLeveling = WlEnable;
+ Ddr3ModeRegister.Bits.Tdqs = Tdqs; // @todo: We used to set Tdqs if the DIMM is X8.
+ Ddr3ModeRegister.Bits.Qoff = Qoff;
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank += 2) {
+ //
+ // loop only for each DIMM
+ //
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ if (Outputs->RestoreMRs) {
+ RankMod2 = Rank % 2;
+ Ddr3ModeRegister.Data = ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR1];
+ } else {
+ //
+ // Get the ODT table index.
+ //
+ OdtTableIndex = GetOdtTableIndex (MrcData, Channel, RANK_TO_DIMM_NUMBER (Rank));
+ if (OdtTableIndex == NULL) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "ERROR: Un support board type\n");
+ return mrcFail;
+ }
+ //
+ // Set the RttNom value
+ //
+#ifdef ULT_FLAG
+ if (Inputs->CpuModel == cmHSW_ULT && Outputs->DdrType == MRC_DDR_TYPE_DDR3) {
+ RttNom = 0; // ODT disabled on DDR3 ULT
+ } else
+#endif // ULT_FLAG
+ {
+ RttNom = OdtTableIndex->RttNom;
+ }
+
+ Ddr3ModeRegister = UpdateRttNomValue (MrcData, RttNom, Ddr3ModeRegister);
+
+ //
+ // save MR1 for later validation usage
+ //
+ RankMod2 = Rank % 2;
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR1] = Ddr3ModeRegister.Data;
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2 + 1].MR[mrMR1] = Ddr3ModeRegister.Data;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "MrcSetMR1 Channel %u Dimm %u Rank %u = 0x%X\n",
+ // Channel, RANK_TO_DIMM_NUMBER (Rank), Rank, Ddr3ModeRegister.Data);
+ //
+ }
+ //
+ // Update proper DIMM value
+ //
+ DimmValue[RANK_TO_DIMM_NUMBER (Rank)] = Ddr3ModeRegister.Data;
+ }
+ }
+ //
+ // 3rd parameter is really a rank mask of which ranks to send command to.
+ // If need it for all possible and present ranks, it should be 0xF.
+ //
+ Status = MrcWriteMRSAll (MrcData, Channel, 0x0F, mrMR1, DimmValue);
+ }
+ }
+
+ return Status;
+}
+
+/**
+@brief
+ This function writes the MR0 register for all the ranks
+
+ @param[in, out] MrcData - include all the MRC data
+ @param[in] CommandControl - include the command control params
+ @param[in] BurstLength - Burst length bit A0-A1
+ @param[in] ReadBurstType - Read burst type bit A3
+ @param[in] TestMode - Test mode type bit A7
+ @param[in] DllReset - DLL reset bit A8
+
+ @retval MrcStatus - mrcSuccess if passes, otherwise an error status
+**/
+MrcStatus
+MrcSetMR0 (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 BurstLength,
+ IN const U8 ReadBurstType,
+ IN const U8 TestMode,
+ IN const U8 DllReset
+ )
+{
+ /*
+ CAS Latency
+ A6 A5 A4 A2 CAS Latency
+ 0 0 0 0 Reserved
+ 0 0 1 0 5
+ 0 1 0 0 6
+ 0 1 1 0 7
+ 1 0 0 0 8
+ 1 0 1 0 9
+ 1 1 0 0 10
+ 1 1 1 0 11(Optional for DDR3-1600)
+ CAS = (CAS - 4) <<1
+
+ Write recovery
+ A11 A10 A9 WR(cycles)
+ 0 0 0 16*2
+ 0 0 1 5*2
+ 0 1 0 6*2
+ 0 1 1 7*2
+ 1 0 0 8*2
+ 1 0 1 10*2
+ 1 1 0 12*2
+ 1 1 1 14*2
+ Wr = Wr - 5
+*/
+ const U8 WrTable[] = {1, 2, 3, 4, 0, 5, 0, 6, 0, 7, 0, 0};
+ const MrcInput *Inputs;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcProfile Profile;
+ U16 DimmValue[MAX_DIMMS_IN_CHANNEL];
+ U16 Cas;
+ U16 Wr;
+ U16 Offset;
+ U16 Cl_A2;
+ U8 Channel;
+ U8 Rank;
+ U8 RankMod2;
+ MrcStatus Status;
+ DDR3_MODE_REGISTER_0_STRUCT Ddr3ModeRegister;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Debug = &Inputs->Debug;
+ Status = mrcSuccess;
+ Profile = Inputs->MemoryProfile;
+
+ //
+ // independent channel data
+ //
+ Ddr3ModeRegister.Data = 0;
+ Ddr3ModeRegister.Bits.BurstLength = BurstLength;
+ Ddr3ModeRegister.Bits.ReadBurstType = ReadBurstType;
+ Ddr3ModeRegister.Bits.TestMode = TestMode;
+ Ddr3ModeRegister.Bits.DllReset = DllReset;
+
+ for (Channel = 0; (Channel < MAX_CHANNEL) && (Status == mrcSuccess); Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ Cas = ChannelOut->Timing[Profile].tCL;
+ Wr = ChannelOut->Timing[Profile].tWR;
+
+ //
+ // find the new cas value from the CAS table
+ //
+ if (Cas < 5 || Cas > 16) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "ERROR: CAS value %d is not valid \n", Cas);
+ Status = mrcCasError;
+ }
+
+ if ((Wr < 5) || (Wr > 8 && Wr != 10 && Wr != 12 && Wr != 14 && Wr != 16)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "ERROR: Write recovery Wr value %d is not valid \n", Wr);
+ return mrcWrError;
+ }
+ //
+ // convert CAS to jedec ddr3 values
+ //
+ if (Cas <= 11) {
+ Offset = 4;
+ Cl_A2 = 0;
+ } else {
+ Offset = 12;
+ Cl_A2 = 1;
+ }
+
+ Ddr3ModeRegister.Bits.CasLatencyLow = Cl_A2;
+ Ddr3ModeRegister.Bits.CasLatencyHigh = Cas - Offset;
+
+ //
+ // convert Wr to jedec ddr3 values
+ //
+ Ddr3ModeRegister.Bits.WriteRecovery = WrTable[Wr - 5];
+
+ //
+ // calculate the Ppd
+ // DLL control for PPD: slow (0) for mobile, fast (1) for desktop, open for external input
+ // Note - PM_PDWN_CONFIG_C# should be aligned with this. For slow exit use DLL_off. Otherwise use all others.
+ //
+ Ddr3ModeRegister.Bits.PrechargePdDll =
+ (
+ Inputs->PowerDownMode == pdmNoPowerDown ||
+ Inputs->PowerDownMode == pdmAPD
+ ) ? 1 : 0;
+ if ((Inputs->PowerDownMode != pdmNoPowerDown && Inputs->PowerDownMode != pdmAPD) &&
+ (Inputs->PowerDownMode != pdmPPDDLLOFF)
+ ) {
+ Ddr3ModeRegister.Bits.PrechargePdDll = 0;
+ }
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank += 2) {
+ //
+ // loop only for each DIMM
+ //
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ if (Outputs->RestoreMRs) {
+ RankMod2 = Rank % 2;
+ Ddr3ModeRegister.Data = ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR0];
+ } else {
+ //
+ // save MR0 for later validation usage
+ //
+ RankMod2 = Rank % 2;
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR0] = Ddr3ModeRegister.Data;
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2 + 1].MR[mrMR0] = Ddr3ModeRegister.Data;
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "MrcSetMR0 Channel %u Dimm %u Rank %u = 0x%X\n",
+ // Channel, RANK_TO_DIMM_NUMBER (Rank), Rank, Ddr3ModeRegister.Data);
+ //
+ }
+ //
+ // Update proper DIMM value
+ //
+ DimmValue[RANK_TO_DIMM_NUMBER (Rank)] = Ddr3ModeRegister.Data;
+ }
+ }
+ //
+ // 3rd parameter is really a rank mask of which ranks to send command to.
+ // If need it for all possible and present ranks, it should be 0xF.
+ //
+ Status = MrcWriteMRSAll (MrcData, Channel, 0x0F, mrMR0, DimmValue);
+ }
+ }
+
+ return Status;
+}
+
+/**
+@brief
+ This function return tWLO time. this time is Write leveling output delay.
+
+ @param[in] Frequency - MC frequency.
+
+ @retval tWLO timein nCK.
+**/
+U32
+GetTwloTime (
+ IN const MrcFrequency Frequency
+ )
+{
+ U32 tWLO;
+
+ switch (Frequency) {
+ case f2133:
+ case f1867:
+ tWLO = 8;
+ break;
+
+ case f1600:
+ case f1333:
+ tWLO = 6;
+ break;
+
+ case f1067:
+ tWLO = 5;
+ break;
+
+ case f800:
+ tWLO = 4;
+ break;
+
+ default:
+ tWLO = 0;
+ }
+
+ return tWLO;
+}
+
+/**
+@brief
+ This funtion returns the odt table index for the given Dimm/Channel.
+
+ @param[in] MrcData - Include all the mrc global data.
+ @param[in] Channel - Channel to work on.
+ @param[in] Dimm - Rank to work on.
+
+ @retval OdtValue - iThe pointer to the relevant Odt values.
+**/
+TOdtValue *
+GetOdtTableIndex (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 Dimm
+ )
+{
+ MrcDebug *Debug;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ TOdtIndex OdtIndex;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel];
+ DimmOut = &ChannelOut->Dimm[dDIMM0];
+ OdtIndex = oiNotValid;
+
+ switch (ChannelOut->DimmCount) {
+#ifdef TRAD_FLAG
+ case 2:
+ //
+ // Two dimms per channel.
+ //
+ if ((DimmOut[dDIMM0].RankInDIMM == 1) && (DimmOut[dDIMM1].RankInDIMM == 1)) {
+ OdtIndex = oi2DPC1R1R;
+ } else if ((DimmOut[dDIMM0].RankInDIMM == 1) && (DimmOut[dDIMM1].RankInDIMM == 2)) {
+ OdtIndex = oi2DPC1R2R;
+ } else if ((DimmOut[dDIMM0].RankInDIMM == 2) && (DimmOut[dDIMM1].RankInDIMM == 1)) {
+ OdtIndex = oi2DPC2R1R;
+ } else if ((DimmOut[dDIMM0].RankInDIMM == 2) && (DimmOut[dDIMM1].RankInDIMM == 2)) {
+ OdtIndex = oi2DPC2R2R;
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "ERROR: Invalid 2DPC rank mode\n");
+ }
+ break;
+#endif // TRAD_FLAG
+
+ case 1:
+ //
+ // One dimm per channel.
+ //
+ if ((DimmOut[dDIMM0].RankInDIMM == 1) ||
+ ((DimmOut[dDIMM1].RankInDIMM == 1) && (MAX_DIMMS_IN_CHANNEL > 1))
+ ) {
+ OdtIndex = oi1DPC1R;
+ } else if ((DimmOut[dDIMM0].RankInDIMM == 2) ||
+ ((DimmOut[dDIMM1].RankInDIMM == 2) && (MAX_DIMMS_IN_CHANNEL > 1))
+ ) {
+ OdtIndex = oi1DPC2R;
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "ERROR: Invalid 1DPC rank mode\n");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return (OdtIndex == oiNotValid) ? NULL : SelectTable (MrcData, Dimm, OdtIndex);
+}
+
+/**
+@brief
+ This funtion takes the MR1 register value and updates the odt value
+ inside the register.
+
+ @param[in] MrcData - Include all the MRC general data.
+ @param[in] OdtValue - Selected odt index.
+ @param[in] Ddr3ModeRegister - Register to update.
+
+ @retval Ddr3ModeRegister - Updated register
+**/
+DDR3_MODE_REGISTER_1_STRUCT
+UpdateRttNomValue (
+ IN MrcParameters *const MrcData,
+ IN const U8 OdtValue,
+ IN DDR3_MODE_REGISTER_1_STRUCT Ddr3ModeRegister
+ )
+{
+ const MrcDebug *Debug;
+ U8 A2Value;
+ U8 A6Value;
+ U8 A9Value;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ if (OdtValue == 0) {
+ //
+ // rtt_nom is disable
+ //
+ A2Value = 0;
+ A6Value = 0;
+ A9Value = 0;
+ } else if (OdtValue == 60) {
+ //
+ // RZQ/4
+ //
+ A2Value = 1;
+ A6Value = 0;
+ A9Value = 0;
+ } else if (OdtValue == 120) {
+ //
+ // RZQ/2
+ //
+ A2Value = 0;
+ A6Value = 1;
+ A9Value = 0;
+ } else if (OdtValue == 40) {
+ //
+ // RZQ/6
+ //
+ A2Value = 1;
+ A6Value = 1;
+ A9Value = 0;
+ } else if (OdtValue == 20) {
+ //
+ // RZQ/12
+ //
+ A2Value = 0;
+ A6Value = 0;
+ A9Value = 1;
+ } else if (OdtValue == 30) {
+ //
+ // RZQ/8
+ //
+ A2Value = 1;
+ A6Value = 0;
+ A9Value = 1;
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "ERROR: unsupported odt RttNom value\n");
+ A2Value = 1;
+ A6Value = 1;
+ A9Value = 1;
+ }
+
+ Ddr3ModeRegister.Bits.OdtRttValueLow = A2Value;
+ Ddr3ModeRegister.Bits.OdtRttValueMid = A6Value;
+ Ddr3ModeRegister.Bits.OdtRttValueHigh = A9Value;
+ return Ddr3ModeRegister;
+}
+
+/**
+@brief
+ This function updates the Rtt value in the MR2 value passed in.
+
+ @param[in] MrcData - Include all the MRC general data.
+ @param[in] OdtValue - Selected odt index.
+ @param[in] Ddr3ModeRegister - Register to update.
+
+ @retval Ddr3ModeRegister - Updated MR2 register
+**/
+DDR3_MODE_REGISTER_2_STRUCT
+UpdateRttWrValue (
+ MrcParameters *const MrcData,
+ const U8 OdtValue,
+ DDR3_MODE_REGISTER_2_STRUCT Ddr3ModeRegister
+ )
+{
+ U8 RttValue;
+
+ if ((OdtValue > 120) || ((OdtValue % 60) != 0)) {
+ MRC_DEBUG_MSG (
+ &MrcData->SysIn.Inputs.Debug,
+ MSG_LEVEL_ERROR,
+ "ERROR: unsupported odt RttWr value of %u\n",
+ OdtValue
+ );
+ RttValue = 0;
+ } else {
+ RttValue = OdtValue / 60;
+ }
+
+ Ddr3ModeRegister.Bits.DynamicOdt = RttValue;
+ return Ddr3ModeRegister;
+}
+
+/**
+@brief
+ this funtion select the ODT table according OEM/USER decision.
+ In the MRC have 4 table type Mb,Dt,User1,User2.
+ User1,User2 use as internal usage.
+
+ @param[in] MrcData - Include all the MRC general data.
+ @param[in] Dimm - selected DIMM.
+ @param[in] OdtIndex - selected odt index.
+
+ @retval TOdtValue * - Pointer to the relevant table.
+ The return value is NULL if the table could
+ not be found
+**/
+TOdtValue *
+SelectTable (
+ IN MrcParameters *const MrcData,
+ IN const U8 Dimm,
+ IN const TOdtIndex OdtIndex
+ )
+{
+ const MrcInput *Inputs;
+ TOdtValue *OdtTable;
+ const MrcDebug *Debug;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ OdtTable = NULL;
+ switch (Inputs->BoardType) {
+ case btCRBMB:
+ case btCRBEMB:
+#ifdef ULT_FLAG
+ if (Inputs->CpuModel == cmHSW_ULT) {
+ if (OdtIndex >= 2) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: MbUltOdtTable array out of bounds!\n");
+ return NULL;
+ }
+ OdtTable = (TOdtValue *) &MbUltOdtTable[Dimm][OdtIndex];
+ }
+#endif // ULT_FLAG
+#ifdef TRAD_FLAG
+ if ((Inputs->CpuModel == cmHSW) || (Inputs->CpuModel == cmCRW)) {
+ OdtTable = (TOdtValue *) &MbTradOdtTable[Dimm][OdtIndex];
+ }
+#endif //TRAD_FLAG
+ break;
+
+ case btCRBDT:
+#ifdef TRAD_FLAG
+ if ((Inputs->CpuModel == cmHSW) || (Inputs->CpuModel == cmCRW)) {
+ OdtTable = (TOdtValue *) &DtTradOdtTable[Dimm][OdtIndex];
+ }
+#endif //TRAD_FLAG
+ break;
+
+ case btUser1:
+#ifdef ULT_FLAG
+ if (Inputs->CpuModel == cmHSW_ULT) {
+ if (OdtIndex >= 2) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: User1UltOdtTable array out of bounds!\n");
+ return NULL;
+ }
+ OdtTable = (TOdtValue *) &User1UltOdtTable[Dimm][OdtIndex];
+ }
+#endif // ULT_FLAG
+#ifdef TRAD_FLAG
+ if ((Inputs->CpuModel == cmHSW) || (Inputs->CpuModel == cmCRW)) {
+ OdtTable = (TOdtValue *) &User1TradOdtTable[Dimm][OdtIndex];
+ }
+#endif //TRAD_FLAG
+ break;
+
+ case btUser2:
+#ifdef TRAD_FLAG
+ if ((Inputs->CpuModel == cmHSW) || (Inputs->CpuModel == cmCRW)) {
+ OdtTable = (TOdtValue *) &User2TradOdtTable[Dimm][OdtIndex];
+ }
+#endif //TRAD_FLAG
+ break;
+
+ case btUser4:
+ //
+ // @todo: Need to Port ODT table for Ult
+ //
+#ifdef ULT_FLAG
+ if (Inputs->CpuModel == cmHSW_ULT) {
+ if (OdtIndex >= 2) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: MbUltOdtTable array out of bounds!\n");
+ return NULL;
+ }
+ OdtTable = (TOdtValue *) &MbUltOdtTable[Dimm][OdtIndex];
+ }
+#endif // ULT_FLAG
+#ifdef TRAD_FLAG
+ if ((Inputs->CpuModel == cmHSW) || (Inputs->CpuModel == cmCRW)) {
+ OdtTable = (TOdtValue *) &MbTradOdtTable[Dimm][OdtIndex];
+ }
+#endif //TRAD_FLAG
+ break;
+
+ default:
+ OdtTable = NULL;
+ break;
+ }
+
+ return OdtTable;
+}
+
+#ifdef ULT_FLAG
+
+/**
+@brief
+ Issue LPDDR MRW (Mode Register Write) command using MRH (Mode Register Handler).
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - the channel to work on
+ @param[in] Rank - the rank to work on
+ @param[in] Address - MRW address
+ @param[in] Data - MRW Data
+ @param[in] InitMrw - when TRUE, command is stretched (used before CA training is done)
+ @param[in] ChipSelect2N - when TRUE, use 2N stretch mode for CS (used before CA training is done)
+
+ @retval mrcSuccess - MRW was sent successfully
+ @retval mrcDeviceBusy - timed out waiting for MRH
+**/
+MrcStatus
+MrcIssueMrw (
+ IN MrcParameters *const MrcData,
+ IN U32 Channel,
+ IN U32 Rank,
+ IN U32 Address,
+ IN U32 Data,
+ IN BOOL InitMrw,
+ IN BOOL ChipSelect2N
+ )
+{
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ U32 OffsetMrCommand;
+ U32 OffsetCmdRate;
+ MCHBAR_CH0_CR_LPDDR_MR_COMMAND_STRUCT MrCommand;
+ MCHBAR_CH0_CR_CMD_RATE_STRUCT CmdRate;
+ BOOL Busy;
+ U32 Timeout;
+
+ Timeout = (U32) MrcGetCpuTime () + 10000; // 10 seconds timeout
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+
+ OffsetMrCommand = MCHBAR_CH0_CR_LPDDR_MR_COMMAND_REG +
+ ((MCHBAR_CH1_CR_LPDDR_MR_COMMAND_REG - MCHBAR_CH0_CR_LPDDR_MR_COMMAND_REG) * Channel);
+
+ //
+ // Make sure MRH is not busy
+ //
+ do {
+ MrCommand.Data = MrcReadCR (MrcData, OffsetMrCommand);
+ Busy = (MrCommand.Bits.Busy == 1);
+ } while (Busy && ((U32) MrcGetCpuTime () < Timeout));
+ if (Busy) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Timed out waiting for previous MRH command to finish!\n");
+ return mrcDeviceBusy;
+ }
+
+ OffsetCmdRate = MCHBAR_CH0_CR_CMD_RATE_REG + ((MCHBAR_CH1_CR_CMD_RATE_REG - MCHBAR_CH0_CR_CMD_RATE_REG) * Channel);
+ if (ChipSelect2N) {
+ //
+ // Enable 2N stretch mode for CS
+ //
+ CmdRate.Data = MrcReadCR(MrcData, OffsetCmdRate);
+ CmdRate.Bits.init_mrw_2n_cs = 1;
+ MrcWriteCR (MrcData, OffsetCmdRate, CmdRate.Data);
+ }
+ //
+ // Send the MRW
+ //
+ MrCommand.Bits.Address = Address;
+ MrCommand.Bits.Data = Data;
+ MrCommand.Bits.Write = 1;
+ MrCommand.Bits.Init_MRW = InitMrw;
+ MrCommand.Bits.Rank = Rank;
+ MrCommand.Bits.Busy = 1;
+
+ if (!Outputs->LpddrJedecInitDone) {
+ MRC_DEBUG_MSG (
+ Debug, MSG_LEVEL_NOTE,
+ "MrcIssueMrw on ch %d rank %d: MR%d, Opcode=0x%02X, InitMrw=%d, 2N_CS=%d\n",
+ Channel, Rank, Address, Data, InitMrw, ChipSelect2N
+ );
+ }
+ MrcWriteCR (MrcData, OffsetMrCommand, MrCommand.Data);
+
+ //
+ // Wait till MRH is done sending the command
+ //
+ Timeout = (U32) MrcGetCpuTime () + 10000; // 10 seconds timeout
+ do {
+ MrCommand.Data = MrcReadCR (MrcData, OffsetMrCommand);
+ Busy = (MrCommand.Bits.Busy == 1);
+ } while (Busy && ((U32) MrcGetCpuTime () < Timeout));
+
+ if (Busy) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Timed out sending MRH command!\n");
+ return mrcDeviceBusy;
+ }
+
+ if (ChipSelect2N) {
+ //
+ // Disable 2N stretch mode for CS
+ //
+ CmdRate.Bits.init_mrw_2n_cs = 0;
+ MrcWriteCR (MrcData, OffsetCmdRate, CmdRate.Data);
+ }
+
+ return mrcSuccess;
+}
+
+/**
+@brief
+ Issue LPDDR MRR (Mode Register Read) command using MRH (Mode Register Handler).
+ Use DQ mapping array to deswizzle the MR data.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - the channel to work on
+ @param[in] Rank - the rank to work on
+ @param[in] Address - MRR address
+ @param[out] Data - MRR Data array per SDRAM device
+
+ @retval mrcSuccess - MRR was executed successfully
+ @retval mrcDeviceBusy - timed out waiting for MRH
+**/
+MrcStatus
+MrcIssueMrr (
+ IN MrcParameters *const MrcData,
+ IN U32 Channel,
+ IN U32 Rank,
+ IN U32 Address,
+ OUT U8 Data[4]
+ )
+{
+ MrcInput *Inputs;
+ MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerIn *ControllerIn;
+ MrcControllerOut *ControllerOut;
+ MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ U32 OffsetMrCommand;
+ U32 OffsetMrrResult;
+ BOOL Busy;
+ U32 CurrCpuBit;
+ U32 CurrCpuByte;
+ U32 CpuByteCnt;
+ U32 DeviceCnt;
+ U32 CurrDramBit;
+ U32 BitVal;
+ MCHBAR_CH0_CR_LPDDR_MR_RESULT_STRUCT MrResult;
+ MCHBAR_CH0_CR_LPDDR_MR_COMMAND_STRUCT MrCommand;
+ U32 Timeout;
+ Timeout = (U32) MrcGetCpuTime () + 10000; // 10 seconds timeout
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerIn = &Inputs->Controller[0];
+ ControllerOut = &Outputs->Controller[0];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ ChannelIn = &ControllerIn->Channel[Channel];
+ CurrCpuByte = 0;
+ MrcOemMemorySet (Data, 0, 4 * sizeof (Data[0]));
+
+ OffsetMrCommand = MCHBAR_CH0_CR_LPDDR_MR_COMMAND_REG +
+ ((MCHBAR_CH1_CR_LPDDR_MR_COMMAND_REG - MCHBAR_CH0_CR_LPDDR_MR_COMMAND_REG) * Channel);
+
+ OffsetMrrResult= MCHBAR_CH0_CR_LPDDR_MR_RESULT_REG +
+ ((MCHBAR_CH1_CR_LPDDR_MR_RESULT_REG - MCHBAR_CH0_CR_LPDDR_MR_RESULT_REG) * Channel);
+
+ //
+ // Make sure MRH is not busy
+ //
+ do {
+ MrCommand.Data = MrcReadCR (MrcData, OffsetMrCommand);
+ Busy = (MrCommand.Bits.Busy == 1);
+ } while (Busy && ((U32) MrcGetCpuTime () < Timeout));
+ if (Busy) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Timed out waiting for previous MRH command to finish!\n");
+ return mrcDeviceBusy;
+ }
+
+ //
+ // Send the MRR
+ //
+ MrCommand.Bits.Address = Address;
+ MrCommand.Bits.Data = 0; // Reading from DRAM
+ MrCommand.Bits.Write = 0; // MRR
+ MrCommand.Bits.Init_MRW = 0; // MRR doesn't support Init_MRW
+ MrCommand.Bits.Rank = Rank;
+ MrCommand.Bits.Busy = 1;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "MrcIssueMrr on ch %d rank %d: MR%d\n", Channel, Rank, Address);
+ MrcWriteCR (MrcData, OffsetMrCommand, MrCommand.Data);
+
+ //
+ // Wait till MRH is done sending the command and getting the result
+ //
+ Timeout = (U32) MrcGetCpuTime () + 10000; // 10 seconds timeout
+ do {
+ MrCommand.Data = MrcReadCR (MrcData, OffsetMrCommand);
+ Busy = (MrCommand.Bits.Busy == 1);
+ } while (Busy && ((U32) MrcGetCpuTime () < Timeout));
+
+ if (Busy) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Timed out sending MRH command!\n");
+ return mrcDeviceBusy;
+ }
+
+ MrResult.Data = MrcReadCR (MrcData, OffsetMrrResult);
+
+ for (DeviceCnt = 0; DeviceCnt < 4; DeviceCnt++) {
+ if ((ChannelOut->Dimm[dDIMM0].SdramWidth == 32) && (1 == (DeviceCnt & 1))) {
+ //
+ // We only know DQ mapping for the lower 16 bits of the x32 devices
+ // So we'll copy their MRR feedback to the upper bytes' place
+ // Hence, we skip the odd dies for x32
+ //
+ Data[DeviceCnt] = Data[DeviceCnt - 1];
+ continue;
+ }
+
+ //
+ // Find which CPU byte is mapped to the relevant DRAM byte
+ //
+ for (CpuByteCnt = 0; CpuByteCnt < Outputs->SdramCount; CpuByteCnt++) {
+ if ((DeviceCnt * 2) == ChannelIn->DqsMapCpu2Dram[CpuByteCnt]) {
+ CurrCpuByte = CpuByteCnt;
+ break;
+ }
+ }
+
+ for (CurrCpuBit = 0; CurrCpuBit < MAX_BITS; CurrCpuBit++) {
+ //
+ // The actual DRAM bit that is connected to the current CPU DQ pin
+ //
+ CurrDramBit = ChannelIn->DqMapCpu2Dram[CurrCpuByte][CurrCpuBit] - 8 * (DeviceCnt * 2); // Subtract 8xDramByte
+
+ BitVal = (MrResult.Data8[DeviceCnt] >> CurrCpuBit) & 1; // The 0/1 value that came from the DRAM bit
+ Data[DeviceCnt] |= (BitVal << CurrDramBit); // Putting the value in the correct place
+ }
+ } // for DeviceCnt
+
+ return mrcSuccess;
+}
+
+/**
+@brief
+ Issue LPDDR PRECHARGE ALL command using CADB.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] Channel - The channel to work on
+ @param[in] RankMask - The rank(s) to work on
+
+ @retval none
+**/
+void
+MrcIssuePrechargeAll (
+ IN MrcParameters *const MrcData,
+ IN const U8 Channel,
+ IN const U8 RankMask
+ )
+{
+ U32 CaHigh;
+ U32 CaLow;
+ U32 CMD;
+ U32 BA;
+ U32 MA;
+
+ CaHigh = 0x1B;
+ CaLow = 0;
+
+ MrcConvertLpddr2Ddr (CaHigh, CaLow, &MA, &BA, &CMD);
+
+// MRC_DEBUG_MSG (&MrcData->SysIn.Inputs.Debug, MSG_LEVEL_ERROR, "MA: 0x%X, BA: 0x%X, CMD: 0x%X\n", MA, BA, CMD);
+
+ MrcWriteCADBCmd (MrcData, Channel, RankMask, (U8) CMD, (U8) BA, (U16 *) &MA, 0);
+}
+
+#endif // ULT_FLAG
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcIoControl.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcIoControl.c
new file mode 100644
index 0000000..dc6fc11
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcIoControl.c
@@ -0,0 +1,62 @@
+/** @file
+ This file is used as a driver to all memory controller IO registers.
+ It includes all the functions that the MRC needs to configure the IO and do the training.
+
+@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 "MrcIoControl.h"
+
+/**
+@brief
+ Reset the MC IO module. The MC hardware will handle creating the 20 dclk pulse
+ after the bit is set and will also clear the bit.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval mrcSuccess - IO Reset was done successfully
+ @retval mrcDeviceBusy - Timed out waiting for the IO to clear the bit
+**/
+MrcStatus
+IoReset (
+ IN MrcParameters *const MrcData
+ )
+{
+ MCDECS_CR_MC_INIT_STATE_G_MCMAIN_STRUCT McInitStateG;
+ BOOL Busy;
+ U32 Timeout;
+
+ Timeout = (U32) MrcGetCpuTime () + 10000; // 10 seconds timeout
+
+ McInitStateG.Data = MrcReadCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG);
+ McInitStateG.Bits.reset_io = 1;
+ MrcWriteCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG, McInitStateG.Data);
+
+ //
+ // Wait until the bit is cleared by hardware
+ //
+ do {
+ McInitStateG.Data = MrcReadCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG);
+ Busy = (McInitStateG.Bits.reset_io == 1);
+ } while (Busy && ((U32) MrcGetCpuTime () < Timeout));
+
+ return (Busy ? mrcDeviceBusy : mrcSuccess);
+}
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcMcConfiguration.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcMcConfiguration.c
new file mode 100644
index 0000000..914491a
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcMcConfiguration.c
@@ -0,0 +1,1405 @@
+/** @file
+ The functions in this file implement the memory controller registers that
+ are not training specific. After these functions are executed, the
+ memory controller will be ready to execute the timing training sequences.
+
+@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 "MrcMcConfiguration.h"
+
+const U8 RxBiasTable[2][5][4] = {
+ /// Vdd low
+ /// 1067 Mhz, 1333 Mhz, 1600 MHz, 1867 MHz, 2133 Mhz
+ { {4, 3, 3, 2}, {4, 4, 3, 2}, {5, 4, 3, 3}, {5, 4, 4, 3}, {5, 4, 4, 3} },
+ /// Vdd hi
+ /// 1067 Mhz, 1333 Mhz, 1600 MHz, 1867 MHz, 2133 Mhz
+ { {4, 3, 3, 2}, {4, 4, 3, 2}, {5, 4, 3, 3}, {5, 4, 4, 3}, {4, 4, 3, 3} }
+};
+
+#ifdef ULT_FLAG
+const U8 RxBiasTableUlt[2][3][4] = {
+ /// Vdd low
+ /// 1067 Mhz, 1333 Mhz, 1600 MHz
+ { {5, 6, 6, 5}, {5, 6, 6, 5}, {4, 6, 6, 6} },
+ /// Vdd hi
+ /// 1067 Mhz, 1333 Mhz, 1600 MHz
+ { {7, 6, 6, 5}, {7, 6, 6, 5}, {7, 6, 6, 6} }
+};
+#endif // ULT_FLAG
+
+/**
+@brief
+ This function calculates the two numbers that get you closest to the slope.
+
+ @param[in] Slope - targeted slope (multiplied by 100 for int match)
+
+ @retval Returns the Slope Index to be programmed for VtSlope.
+**/
+U8
+MrcCalcVtSlopeCode (
+ IN const U16 Slope
+ )
+{
+ const S16 Coding[] = {0, -125, -62, -31, 250, 125, 62, 31};
+ S16 Error;
+ S16 BestError;
+ U8 BestI;
+ U8 BestJ;
+ U8 i;
+ U8 j;
+
+ BestError = 1000;
+ BestI = 0;
+ BestJ = 0;
+ for (i = 0; i < (sizeof (Coding) / sizeof (Coding[0])); i++) {
+ for (j = 0; j < (sizeof (Coding) / sizeof (Coding[0])); j++) {
+ Error = Slope - (Coding[i] + Coding[j]);
+ if (Error < 0) {
+ Error = -Error;
+ }
+
+ if (BestError > Error) {
+ BestError = Error;
+ BestI = i;
+ BestJ = j;
+ }
+ }
+ }
+
+ return (BestI << 3) + BestJ;
+}
+
+/**
+@brief
+ This function performs the memory controller configuration non training sequence.
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - mrcSuccess if successful or an error status
+**/
+MrcStatus
+MrcMcConfiguration (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const U8 StdCASLat[] = {7, 9, 11, 13, 14}; // 1067, 1333, 1600, 1867 & 2133 MHz
+ const U8 ByteStagger[] = {0, 4, 1, 5, 2, 6, 3, 7, 8};
+ const U8 StepSize[] = {64, 96, 64, 64, 64}; // From Design
+ U8 ReferenceR[] = {25, 50, 20, 20, 25}; // Reference resistors on motherboard (+0 Ohm Rstray)
+ const U8 MinCycleStageDelay[] = {46, 70, 70, 46}; // Avoid corner case
+ const U8 TargetRcompConst[] = {33, 50, 20, 20, 29}; // Target values
+ const U8 BufferStageDelayPSConst[] = {59, 53, 53, 53}; // Slew = 1V / (# Stages * StageDelayPS * Derating)
+ const MrcDebug *Debug;
+ MrcStatus Status;
+ MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcChannelIn *ChannelIn;
+ MrcChannelOut *ChannelOut;
+ MrcControllerOut *ControllerOut;
+ MrcDimmOut *DimmOut;
+ MrcCpuModel CpuModel;
+ MrcCpuStepping CpuStepping;
+ MrcProfile Profile;
+ MrcVddSelect Vdd;
+ BOOL Cmd2N;
+ BOOL AutoSelfRefresh;
+ U32 vrefup;
+ U32 Offset;
+ U32 Data32;
+ U32 DisableOdtStatic;
+ S16 CompVref;
+ U16 VssHiSwingTarget;
+ U16 vpanic;
+ U16 SAFE;
+ U16 NS;
+ U16 VssHi; // Target VssHi Voltage
+ U16 Target;
+ U16 Slope;
+ U16 NumStages;
+ U16 lndown;
+ U16 Rdown;
+ U16 vrefdown;
+ U16 vsshiu;
+ U16 vsshid;
+ U16 lnup;
+ U16 Rup;
+ U16 RefiReduction;
+ S8 RxFselect;
+ U8 delta;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U8 Rank;
+ U8 Byte;
+ U8 VddHi;
+ U8 OverClock;
+ U8 MinLatency;
+ U8 Latency[MAX_CHANNEL];
+ U8 ChannelLatency;
+ U8 RxCBSelect;
+ U8 RxB;
+ U8 stagger;
+ U8 Any2dpc;
+ U8 TargetRcomp[sizeof (TargetRcompConst) / sizeof (TargetRcompConst[0])];
+ U8 BufferStageDelayPS[sizeof (BufferStageDelayPSConst) / sizeof (BufferStageDelayPSConst[0])];
+ U8 i;
+ DDRDATA_CR_RXTRAINRANK0_STRUCT RxTrainRank;
+ DDRDATA_CR_TXTRAINRANK0_STRUCT TxTrainRank;
+ DDRDATA_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+ DDRDATA_CR_DDRCRDATACONTROL1_STRUCT DdrCrDataControl1;
+ DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2;
+#ifdef ULT_FLAG
+ BOOL Lpddr;
+ U32 Group;
+ U32 Cke;
+ U32 CkeRankMapping;
+ DDRDATA_CR_DDRCRVSSHICONTROL_STRUCT DdrCrVssHiControl;
+ DDRDATA7CH1_CR_DDRCRVREFCONTROL_STRUCT DdrCrVrefControl;
+ PCU_CR_DDR_PTM_CTL_PCU_STRUCT DdrPtmCtl;
+#endif // ULT_FLAG
+#ifdef TRAD_FLAG
+ DDRDATACH0_CR_DDRCRVSSHIORVREFCONTROL_STRUCT DdrCrVssHiOrVrefControl;
+#endif // TRAD_FLAG
+ DDRCOMP_CR_DDRCRCOMPVSSHICONTROL_STRUCT DdrCrCompVssHiControl;
+ DDRDATA_CR_DDRCRVREFADJUST1_STRUCT DdrCrVrefAdjust;
+ DDRCLKCH0_CR_DDRCRCLKCONTROLS_STRUCT DdrCrClkControls;
+ DDRCMDCH0_CR_DDRCRCMDCONTROLS_STRUCT DdrCrCmdControls;
+ DDRCKECH0_CR_DDRCRCTLCONTROLS_STRUCT DdrCrCkeControls;
+ DDRCTLCH0_CR_DDRCRCTLCONTROLS_STRUCT DdrCrCtlControls;
+ DDRCKECTLCH0_CR_DDRCRCTLPICODING_STRUCT DdrCrCtlPiCoding;
+ DDRCLKCH0_CR_DDRCRCLKRANKSUSED_STRUCT DdrCrClkRanksUsed;
+ DDRCTLCH0_CR_DDRCRCTLRANKSUSED_STRUCT CtlDdrCrCtlRanksUsed;
+ DDRCKECH0_CR_DDRCRCTLRANKSUSED_STRUCT CkeDdrCrCtlRanksUsed;
+ DDRCOMP_CR_DDRCRCOMPVSSHI_STRUCT DdrCrCompVssHi;
+ DDRSCRAM_CR_DDRMISCCONTROL0_STRUCT DdrMiscControl;
+ PCU_CR_M_COMP_PCU_STRUCT CrMCompPcu;
+ DDRDATA_CR_RCOMPDATA1_STRUCT DataRCompData;
+ DDRCMD_CR_DDRCRCMDCOMP_STRUCT CmdDdrCrCmdComp;
+ DDRCKECTL_CR_DDRCRCTLCOMP_STRUCT CkeCtlDdrCrCtlComp;
+ DDRCLK_CR_DDRCRCLKCOMP_STRUCT ClkDdrCrClkComp;
+ DDRCOMP_CR_DDRCRDATACOMP1_STRUCT CompDdrCrDataComp;
+ DDRCOMP_CR_DDRCRCMDCOMP_STRUCT CompDdrCrCmdComp;
+ DDRCOMP_CR_DDRCRCTLCOMP_STRUCT CompDdrCrCtlComp;
+ DDRCOMP_CR_DDRCRCLKCOMP_STRUCT CompDdrCrClkComp;
+ DDRCOMP_CR_DDRCRCOMPOVR_STRUCT CompDdrCrCompOvr;
+ DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT CompDdrCrCompCtl0;
+ DDRCOMP_CR_DDRCRCOMPCTL1_STRUCT CompDdrCrCompCtl1;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Profile = Inputs->MemoryProfile;
+ Status = mrcSuccess;
+ CpuModel = Inputs->CpuModel;
+ CpuStepping = Inputs->CpuStepping;
+ Vdd = Outputs->VddVoltage[Inputs->MemoryProfile];
+ VddHi = 0;
+ OverClock = 0;
+ MinLatency = 24;
+ SAFE = 0;
+ VssHiSwingTarget = 950; // VssHi target voltage in mV
+ vpanic = 24; // Panic Treshold at 24 mV
+ delta = 15; // VssHi change voltage during panic: 15mV
+ RefiReduction = 100; // Init to 100% for no reduction.
+ DisableOdtStatic = DISABLE_ODT_STATIC;
+
+ MrcOemMemorySet (Latency, 0, sizeof (Latency));
+ MrcOemMemoryCpy (TargetRcomp, (U8 *) TargetRcompConst, sizeof (TargetRcomp) / sizeof (TargetRcomp[0]));
+ MrcOemMemoryCpy (
+ BufferStageDelayPS,
+ (U8 *) BufferStageDelayPSConst,
+ sizeof (BufferStageDelayPS) / sizeof (BufferStageDelayPS[0])
+ );
+
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ ControllerOut->DeviceId = (U16) (MrcOemPciRead32 (HOST_BRIDGE_BUS, HOST_BRIDGE_DEVICE, HOST_BRIDGE_FUNCTION, HOST_BRIDGE_DEVID) >> 16);
+ ControllerOut->RevisionId = (U8) (MrcOemPciRead32 (HOST_BRIDGE_BUS, HOST_BRIDGE_DEVICE, HOST_BRIDGE_FUNCTION, HOST_BRIDGE_REVID));
+ }
+
+#ifdef TRAD_FLAG
+ if ((CpuModel == cmHSW) || (CpuModel == cmCRW)) {
+ //
+ // Make sure tCL-tCWL <= 4
+ // This is needed to support ODT properly for 2DPC case
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ if ((ChannelOut->Timing[Profile].tCL - ChannelOut->Timing[Profile].tCWL) > 4) {
+ ChannelOut->Timing[Profile].tCWL = ChannelOut->Timing[Profile].tCL - 4;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "(tCL-tCWL) > 4, CH %u - tCWL has been updated to %u\n",
+ Channel,
+ ChannelOut->Timing[Profile].tCWL
+ );
+ }
+ }
+ }
+ }
+#endif // TRAD_FLAG
+
+ //
+ // Set memory controller frequency
+ //
+ if (MrcOemCheckPoint (MrcData, OemFrequencySet, NULL) == mrcSuccess) {
+ Status = McFrequencySet (MrcData);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ }
+#ifdef SSA_FLAG
+ MrcOemCheckPoint (MrcData, OemFrequencySetDone, NULL);
+#endif // SSA_FLAG
+ Outputs->Qclkps = (U16) (Outputs->MemoryClock / (2 * 1000)); // QCLK period in pico seconds.
+
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ if (CpuModel == cmHSW_ULT) {
+ //
+ // Select the interleaving mode of DQ/DQS pins
+ // This must be the first DDR IO register to be programmed on ULT
+ //
+ DdrMiscControl.Data = 0;
+ DdrMiscControl.Bits.DdrNoChInterleave = (Inputs->DqPinsInterleaved) ? 0 : 1;
+ if (Lpddr) {
+ DdrMiscControl.Bits.LPDDR_Mode = 1;
+
+ //
+ // Initialize the CKE-to-rank mapping for LPDDR
+ //
+ DdrMiscControl.Bits.CKEMappingCh0 = Inputs->CkeRankMapping & 0x0F;
+ DdrMiscControl.Bits.CKEMappingCh1 = (Inputs->CkeRankMapping >> 4) & 0x0F;
+ }
+ MrcWriteCR (MrcData, DDRSCRAM_CR_DDRMISCCONTROL0_REG, DdrMiscControl.Data);
+ DisableOdtStatic = 1;
+ }
+#endif // ULT_FLAG
+ //
+ // Save MRC Version into CR
+ //
+ MrcSetMrcVersion (MrcData);
+
+ Any2dpc = 0;
+ if (Vdd > VDD_1_35) {
+ VddHi = 1; // Set HiVdd bit if Vdd is over 1.35v
+ }
+
+ NS = ~SAFE;
+
+ //
+ // RX BIAS calculations
+ //
+ GetRxFselect (MrcData, &RxFselect, &RxCBSelect);
+
+#ifdef ULT_FLAG
+ if (CpuModel == cmHSW_ULT) {
+ RxFselect = MIN (RxFselect, RXF_SELECT_MAX_ULT); // Maximum 1600 MHz
+ RxB = RxBiasTableUlt[VddHi][RxFselect][RxCBSelect]; // Read setting from array lookup table
+ } else
+#endif // ULT_FLAG
+ {
+ RxB = RxBiasTable[VddHi][RxFselect][RxCBSelect]; // Read setting from array lookup table
+ }
+
+ //
+ // Determine Overclocking
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ Latency[Channel] = (U8) ChannelOut->Timing[Profile].tCL;
+ if (Latency[Channel] < MinLatency) {
+ MinLatency = Latency[Channel];
+ }
+ }
+ }
+
+ if ((Outputs->Frequency > 2133) || (MinLatency < StdCASLat[RxFselect])) {
+ OverClock = 1;
+ }
+ //
+ // Initialize ValidChBitMask and ValidRankMask used during all training steps
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+ ChannelOut = &ControllerOut->Channel[Channel];
+ ChannelIn = &Inputs->Controller[0].Channel[Channel];
+
+ if (ChannelOut->DimmCount == 2) {
+ Any2dpc++;
+ }
+
+ Outputs->ValidChBitMask |= (1 << Channel);
+ Outputs->ValidRankMask |= ChannelOut->ValidRankBitMask;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "C%uValidRankBitMask / ValidRankMask - 0x%x / 0x%x\n",
+ Channel,
+ ChannelOut->ValidRankBitMask,
+ Outputs->ValidRankMask
+ );
+
+ //
+ // Initialize RanksUsed in CLK fub
+ //
+ DdrCrClkRanksUsed.Data = 0;
+ DdrCrClkRanksUsed.Bits.RankEn = ChannelOut->ValidRankBitMask;
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ //
+ // On LPDDR the CLK RanksUsed goes by CLK group instead of by Rank
+ //
+ DdrCrClkRanksUsed.Bits.RankEn = 0;
+ for (Group = 0; Group < 2; Group++) {
+ if (ChannelIn->DQByteMap[MrcIterationClock][Group] != 0) {
+ DdrCrClkRanksUsed.Bits.RankEn |= (1 << Group);
+ }
+ }
+ }
+#endif // ULT_FLAG
+ Offset = DDRCLKCH0_CR_DDRCRCLKRANKSUSED_REG +
+ ((DDRCLKCH1_CR_DDRCRCLKRANKSUSED_REG - DDRCLKCH0_CR_DDRCRCLKRANKSUSED_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, DdrCrClkRanksUsed.Data);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "DDRCLKCH%d_CR_DDRCRCTLRANKSUSED = 0x%X\n", Channel, DdrCrClkRanksUsed.Data);
+
+ //
+ // Initialize RanksUsed in CTL fub - CS (and ODT for LPDDR)
+ //
+ CtlDdrCrCtlRanksUsed.Data = 0;
+ CtlDdrCrCtlRanksUsed.Bits.RankEn = ChannelOut->ValidRankBitMask;
+#ifdef ULT_FLAG
+ if (CpuModel == cmHSW_ULT) {
+ if (Lpddr && Inputs->LpddrDramOdt) {
+ //
+ // ODT is used on rank 0
+ //
+ CtlDdrCrCtlRanksUsed.Bits.OdtDisable = 2;
+ } else {
+ CtlDdrCrCtlRanksUsed.Bits.OdtDisable = 3;
+ }
+ }
+#endif // ULT_FLAG
+ Offset = DDRCTLCH0_CR_DDRCRCTLRANKSUSED_REG +
+ ((DDRCTLCH1_CR_DDRCRCTLRANKSUSED_REG - DDRCTLCH0_CR_DDRCRCTLRANKSUSED_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, CtlDdrCrCtlRanksUsed.Data);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "DDRCTLCH%d_CR_DDRCRCTLRANKSUSED = 0x%X\n", Channel, CtlDdrCrCtlRanksUsed.Data);
+
+ //
+ // Initialize RanksUsed in CKE fub
+ //
+ CkeDdrCrCtlRanksUsed.Data = 0;
+ CkeDdrCrCtlRanksUsed.Bits.RankEn = ChannelOut->ValidRankBitMask;
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ CkeDdrCrCtlRanksUsed.Bits.RankEn = 0;
+ //
+ // Use CKE-to-Rank mapping: [3:0] - Channel 0, [7:4] - Channel 1
+ //
+ CkeRankMapping = (Inputs->CkeRankMapping >> (Channel * 4)) & 0x0F;
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ for (Cke = 0; Cke <= 3; Cke++) {
+ if (((CkeRankMapping >> Cke) & 1) == Rank) {
+ //
+ // This CKE pin is connected to this Rank...
+ //
+ if (ChannelOut->ValidRankBitMask & (1 << Rank)) {
+ //
+ // ...and this rank is enabled
+ //
+ CkeDdrCrCtlRanksUsed.Bits.RankEn |= (1 << Cke);
+ }
+ }
+ }
+ }
+ }
+#endif // ULT_FLAG
+ Offset = DDRCKECH0_CR_DDRCRCTLRANKSUSED_REG +
+ ((DDRCKECH1_CR_DDRCRCTLRANKSUSED_REG - DDRCKECH0_CR_DDRCRCTLRANKSUSED_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, CkeDdrCrCtlRanksUsed.Data);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "DDRCKECH%d_CR_DDRCRCTLRANKSUSED = 0x%X\n", Channel, CkeDdrCrCtlRanksUsed.Data);
+ //
+ // Save for future use in JEDEC Reset, etc.
+ //
+ ChannelOut->ValidCkeBitMask = (U8) CkeDdrCrCtlRanksUsed.Bits.RankEn;
+ } // for Channel
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init Data CRs\n");
+
+
+ //
+ // Initialize Rx and Tx Data CRs
+ //
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (((1 << Rank) & Outputs->ValidRankMask) == 0) {
+ continue;
+ }
+ //
+ // RxDqsN/P_Pi = 32, RcvEn = 64, RxEq = 1
+ //
+ RxTrainRank.Data = 0;
+ RxTrainRank.Bits.RxRcvEnPi = 64;
+ RxTrainRank.Bits.RxDqsPPi = 32;
+ RxTrainRank.Bits.RxDqsNPi = 32;
+ RxTrainRank.Bits.RxEq = 1;
+ //
+ // RxGroup - Broadcast all channels
+ //
+ Offset = DDRDATA_CR_RXTRAINRANK0_REG + ((DDRDATA_CR_RXTRAINRANK1_REG - DDRDATA_CR_RXTRAINRANK0_REG) * Rank);
+ MrcWriteCrMulticast (MrcData, Offset, RxTrainRank.Data);
+
+ //
+ // Rx per bit offset - Middle value. Train later.
+ //
+ Offset = DDRDATA_CR_RXPERBITRANK0_REG + ((DDRDATA_CR_RXPERBITRANK1_REG - DDRDATA_CR_RXPERBITRANK0_REG) * Rank);
+ Data32 = 0x88888888;
+ MrcWriteCrMulticast (MrcData, Offset, Data32);
+
+ //
+ // Set TxEq to full strength, TxDqs = 0 and TxDq = 32,
+ //
+ TxTrainRank.Data = 0;
+ TxTrainRank.Bits.TxEqualization = TXEQFULLDRV | 0x0B;
+ TxTrainRank.Bits.TxDqDelay = 96;
+ TxTrainRank.Bits.TxDqsDelay = 64;
+ Offset = DDRDATA_CR_TXTRAINRANK0_REG + ((DDRDATA_CR_TXTRAINRANK1_REG - DDRDATA_CR_TXTRAINRANK0_REG) * Rank);
+ MrcWriteCrMulticast (MrcData, Offset, TxTrainRank.Data);
+ //
+ // Middle value. Train later.
+ //
+ Offset = DDRDATA_CR_TXPERBITRANK0_REG + ((DDRDATA_CR_TXPERBITRANK1_REG - DDRDATA_CR_TXPERBITRANK0_REG) * Rank);
+ MrcWriteCrMulticast (MrcData, Offset, 0x88888888);
+
+ //
+ // Save in MrcData
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Byte = 0; Byte < MAX_SDRAM_IN_DIMM; Byte++) {
+ ChannelOut->TxDq[Rank][Byte] = (U16) (TxTrainRank.Bits.TxDqDelay);
+ ChannelOut->TxDqs[Rank][Byte] = (U16) (TxTrainRank.Bits.TxDqsDelay);
+ ChannelOut->TxEq[Rank][Byte] = (U8) (TxTrainRank.Bits.TxEqualization);
+
+ ChannelOut->RcvEn[Rank][Byte] = (U16) (RxTrainRank.Bits.RxRcvEnPi);
+ ChannelOut->RxDqsP[Rank][Byte] = (U8) (RxTrainRank.Bits.RxDqsPPi);
+ ChannelOut->RxDqsN[Rank][Byte] = (U8) (RxTrainRank.Bits.RxDqsNPi);
+ ChannelOut->RxEq[Rank][Byte] = (U8) (RxTrainRank.Bits.RxEq);
+ }
+ }
+ }
+ //
+ // Initial value to corresponding 0.
+ //
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_TXXTALK_REG, 0x0);
+ //
+ // Amplifier voltage offset {0: Most negative offset,... 8: 0 offset, ... 15: Most postive offset}
+ //
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_RXOFFSETVDQ_REG, 0x88888888);
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_DDRCRDATAOFFSETTRAIN_REG, 0x0);
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_DDRCRDATAOFFSETCOMP_REG, 0x0);
+
+ //
+ // Disable ODT Static Leg, set Vdd
+ //
+ DdrCrDataControl0.Data = 0;
+ DdrCrDataControl0.Bits.DataVccddqHi = VddHi;
+ DdrCrDataControl0.Bits.DisableOdtStatic = DisableOdtStatic;
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ DdrCrDataControl0.Bits.LPDDR_Mode = 1;
+ //
+ // If C0 or greater, enable EarlyRleak. Otherwise, no Read Conditioning.
+ //
+ if ((CpuModel == cmHSW_ULT) && (CpuStepping >= csHswUltC0)) {
+ DdrCrDataControl0.Bits.EarlyRleakEn = 1; // Mutually exclusive with DdrCrDataControl0.EnReadPreamble
+ }
+ DdrCrDataControl0.Bits.OdtSampExtendEn = 1;
+ }
+#endif // ULT_FLAG
+#ifdef TRAD_FLAG
+ if ((CpuModel == cmHSW) || (CpuModel == cmCRW)) {
+ DdrCrDataControl0.Bits.InternalClocksOn = 1;
+ }
+#endif // TRAD_FLAG
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_DDRCRDATACONTROL0_REG, DdrCrDataControl0.Data);
+ DdrCrDataControl1.Data = 0;
+ if (Inputs->WeaklockEn) {
+ DdrCrDataControl1.Bits.DllWeakLock = NS; // Enable DLL WeakLock
+ }
+
+ DdrCrDataControl1.Bits.DllMask = 1; // 2 qclk DLL mask
+ DdrCrDataControl1.Bits.OdtDelay = 0xE; // Signed value of (-2) has been converted to hex
+ DdrCrDataControl1.Bits.SenseAmpDelay = 0xE; // Signed value of (-2) has been converted to hex
+ DdrCrDataControl1.Bits.SenseAmpDuration = DDRDATA_CR_DDRCRDATACONTROL1_SenseAmpDuration_MAX; // Max Samp Duration.
+ DdrCrDataControl1.Bits.OdtDuration = DDRDATA_CR_DDRCRDATACONTROL1_OdtDuration_MAX; // Max Odt Duration.
+ DdrCrDataControl1.Bits.RxBiasCtl = RxB; // RxBias uses LUT.
+
+#ifdef ULT_FLAG
+#endif // ULT_FLAG
+
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_DDRCRDATACONTROL1_REG, DdrCrDataControl1.Data);
+
+ DdrCrDataControl2.Data = 0; // Define DQControl2
+
+#ifdef ULT_FLAG
+ if (CpuModel == cmHSW_ULT) {
+ DdrCrDataControl2.Bits.RxDqsAmpOffset = DDRDATA0CH0_CR_DDRCRDATACONTROL2_RxDqsAmpOffset_DEF;
+ DdrCrDataControl2.Bits.RxClkStgNum = DDRDATA0CH0_CR_DDRCRDATACONTROL2_RxClkStgNum_MAX;
+ if (Lpddr) {
+ DdrCrDataControl2.Bits.LeakerComp = 3;
+ }
+ }
+#endif // ULT_FLAG
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++){
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ ChannelOut->DqControl0.Data = DdrCrDataControl0.Data;
+ ChannelLatency = 2 * Latency[Channel] - 6;
+ for (Byte = 0; Byte < Outputs->SdramCount; Byte++) {
+ //
+ // These CRs do a lot of RMW.
+ //
+ ChannelOut->DataOffsetTrain[Byte] = 0; // Faster to store the value in host
+ ChannelOut->DataCompOffset[Byte] = 0;
+ ChannelOut->DqControl1[Byte].Data = DdrCrDataControl1.Data;
+
+ //
+ // Stagger byte turnon to reduce dI/dT. In safe mode, turn off stagger feature
+ //
+ stagger = (SAFE) ? 0 : ((ChannelLatency * ByteStagger[Byte]) / (U8) Outputs->SdramCount) & 0x1F;
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Channel) +
+ ((DDRDATA1CH0_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Byte);
+ DdrCrDataControl2.Bits.RxStaggerCtl = stagger;
+ MrcWriteCR (MrcData, Offset, DdrCrDataControl2.Data);
+ ChannelOut->DqControl2[Byte].Data = DdrCrDataControl2.Data;
+ }
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init Data VssHi CRs\n");
+ //
+ // Initialize VssHi CRs
+ //
+ // @todo: Need to verify as I don't have bit definitions for VssHi mode
+ //
+ VssHi = ((U16) Vdd - VssHiSwingTarget); // VssHiSwingTarget = 950 mV, VddmV=1500 mV
+ Target = (VssHi * 192) / (U16) Vdd - 20; // Sets target for VssHi.
+
+ DdrCrCompVssHiControl.Data = 0;
+#ifdef ULT_FLAG
+ if (CpuModel == cmHSW_ULT) {
+ DdrCrVssHiControl.Data = (SAFE) ? // SAFE: Panic at 7*8=56mV, !SAFE: Panic at 24mV, GainBoost.
+ (7 << 18) + (2 << 14) + (2 << 8) + (2 << 6) : // Set BwError and *BWDivider to safe values
+ (1 << 22) + (3 << 18) + (2 << 14) + (1 << 8) + (1 << 6); // Normal values for BwError/*BWDivider
+ DdrCrVssHiControl.Data += (1 << 16); // Enable Panic Driver
+ DdrCrVssHiControl.Data += Target + (0 << 10); // Set loop sample frequency at max
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_DDRCRVSSHICONTROL_REG, DdrCrVssHiControl.Data); // Multicast to both channels
+ //
+ // Set COMP VssHi the same
+ //
+ DdrCrCompVssHiControl.Data = DdrCrVssHiControl.Data;
+ }
+#endif // ULT_FLAG
+#ifdef TRAD_FLAG
+ if ((CpuModel == cmHSW) || (CpuModel == cmCRW)) {
+ DdrCrVssHiOrVrefControl.Data = (SAFE) ? // SAFE: Panic at 7*8=56mV, !SAFE: Panic at 24mV, GainBoost.
+ (7 << 18) + (2 << 14) + (2 << 8) + (2 << 6) : // Set BwError and *BWDivider to safe values
+ (1 << 22) + (3 << 18) + (2 << 14) + (1 << 8) + (1 << 6); // Normal values for BwError/*BWDivider
+ DdrCrVssHiOrVrefControl.Data += (1 << 16); // Enable Panic Driver
+ DdrCrVssHiOrVrefControl.Data += Target + (0 << 10); // Set loop sample frequency at max
+ MrcWriteCrMulticast (MrcData, DDRDATACH0_CR_DDRCRVSSHIORVREFCONTROL_REG, DdrCrVssHiOrVrefControl.Data);
+
+ //
+ // Set COMP VssHi the same
+ //
+ DdrCrCompVssHiControl.Data = DdrCrVssHiOrVrefControl.Data;
+ }
+#endif // TRAD_FLAG
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init Comp VssHi CRs\n");
+ MrcWriteCrMulticast (MrcData, DDRCOMP_CR_DDRCRCOMPVSSHICONTROL_REG, DdrCrCompVssHiControl.Data);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init Dimm Vref CRs\n");
+ //
+ // Initialize Dimm Vref CRs - Use CH1 BYTE 7
+ //
+ // Ideal Slope EQN = (192/128*VccIo/Vdd-1)
+ //
+ Slope = (U16) ((1000 * 192 * Inputs->VccIomV) / (128 * (U16) Vdd) - 1000);
+ Slope = MrcCalcVtSlopeCode (Slope); // Translate ideal slope in CR value
+
+#ifdef ULT_FLAG
+ if (CpuModel == cmHSW_ULT) {
+ //
+ // No Offset. Apply Slope adjustment VT Slope A = 4, VT Slope B = 0, Set SlowBWError = 1
+ //
+ DdrCrVrefControl.Data = (0 << 18) + (0x20 << 12) + (1 << 8);
+ //
+ // Enable HiBW mode, Set Loop Frequency
+ //
+ DdrCrVrefControl.Data += (1 << 10) + (1 << 4);
+ //
+ // Set LoBWDiv, HiBWDiv
+ //
+ DdrCrVrefControl.Data += (3 << 2) + (0 << 0);
+ //
+ // Program DimmVref Control Values
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "DdrCrVrefControl: 0x%X\n", DdrCrVrefControl.Data);
+ MrcWriteCR (MrcData, DDRDATA7CH1_CR_DDRCRVREFCONTROL_REG, DdrCrVrefControl.Data);
+ }
+#endif // ULT_FLAG
+#ifdef TRAD_FLAG
+ if ((CpuModel == cmHSW) || (CpuModel == cmCRW)) {
+ //
+ // No Offset. Apply Slope adjustment, Set SlowBWError = 1
+ //
+ DdrCrVssHiOrVrefControl.Data = (0 << 18) + (Slope << 12) + (1 << 8);
+ //
+ // Enable HiBW mode, Set Loop Frequency
+ //
+ DdrCrVssHiOrVrefControl.Data += (1 << 10) + (3 << 4);
+ //
+ // Set LoBWDiv, HiBWDiv
+ //
+ DdrCrVssHiOrVrefControl.Data += (3 << 2) + (3 << 0);
+ //
+ // Program DimmVref Control Values
+ //
+ MrcWriteCR (MrcData, DDRDATA7CH1_CR_DDRCRVSSHIORVREFCONTROL_REG, DdrCrVssHiOrVrefControl.Data);
+ }
+#endif // TRAD_FLAG
+
+ //
+ // Enable all DimmVref and VddHi based on VddVoltage
+ //
+ DdrCrVrefAdjust.Data = 0;
+ DdrCrVrefAdjust.Bits.EnDimmVrefCA = 1;
+ DdrCrVrefAdjust.Bits.EnDimmVrefCh0 = 1;
+ DdrCrVrefAdjust.Bits.EnDimmVrefCh1 = 1;
+ DdrCrVrefAdjust.Bits.VccddqHiQnnnH = VddHi;
+ DdrCrVrefAdjust.Bits.HiZTimerCtrl = DDRDATA_CR_DDRCRVREFADJUST1_HiZTimerCtrl_MAX;
+ //
+ // Enable DimmVref Drivers with Vref = 0
+ //
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_DDRCRVREFADJUST1_REG, DdrCrVrefAdjust.Data);
+ Outputs->DimmVref = DdrCrVrefAdjust.Data;
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init CLK CRs\n");
+ //
+ // Initialize Clock CRs
+ //
+ DdrCrClkControls.Data = 0;
+ DdrCrClkControls.Bits.DllMask = 1; // Set 2 qclk DLL mask
+ DdrCrClkControls.Bits.VccddqHi = VddHi; // Set Vdd
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ DdrCrClkControls.Bits.LPDDR_Mode = 1;
+ }
+#endif // ULT_FLAG
+ Offset = DDRCLKCH0_CR_DDRCRCLKCONTROLS_REG +
+ ((DDRCLKCH1_CR_DDRCRCLKCONTROLS_REG - DDRCLKCH0_CR_DDRCRCLKCONTROLS_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, DdrCrClkControls.Data);
+
+ DdrCrCmdControls.Data = DdrCrClkControls.Data;
+ DdrCrCtlControls.Data = DdrCrClkControls.Data;
+ // Determine if weaklock can or can not be enabled
+ //
+ if (Inputs->WeaklockEn) {
+ DdrCrCmdControls.Bits.DllWeakLock = NS;
+ DdrCrCtlControls.Bits.DllWeakLock = NS;
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init CMD N and P CRs\n");
+ //
+ // Initialize CmdN/CmdS CRx
+ //
+ DdrCrCmdControls.Bits.EarlyWeakDrive = 3;
+ DdrCrCmdControls.Bits.CmdTxEq = NS & 1; // Enable Early Warning and Cmd DeEmphasis
+ MrcWriteCR (MrcData, DDRCMDCH0_CR_DDRCRCMDCONTROLS_REG +
+ ((DDRCMDCH1_CR_DDRCRCMDCONTROLS_REG - DDRCMDCH0_CR_DDRCRCMDCONTROLS_REG) * Channel), DdrCrCmdControls.Data);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init CKE CRs\n");
+ //
+ // Initialize CKE CRs
+ //
+ // @todo: DONE Either CKE and CTL must be set the same or we have to be using the per channel defines and not a Multicast.
+ //
+ DdrCrCkeControls.Data = DdrCrCmdControls.Data;
+ DdrCrCkeControls.Bits.CtlSRDrv = NS & 2;
+ DdrCrCkeControls.Bits.CtlTxEq = NS & 1; // Enable Weak CKE in SR and Cke DeEmphasis
+ MrcWriteCR (
+ MrcData,
+ DDRCKECH0_CR_DDRCRCTLCONTROLS_REG +
+ ((DDRCKECH1_CR_DDRCRCTLCONTROLS_REG - DDRCKECH0_CR_DDRCRCTLCONTROLS_REG) * Channel),
+ DdrCrCkeControls.Data
+ );
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init CTL CRs\n");
+ //
+ // Initialize CTL CRs
+ //
+ DdrCrCtlControls.Bits.CtlTxEq = (NS & 1); // Enable Weak CKE in SR and Cke DeEmphasis
+ DdrCrCtlControls.Bits.CtlSRDrv = (NS & 2); // Enable Weak CKE in SR and Cke DeEmphasis
+ DdrCrCtlControls.Bits.LaDrvEnOvrd = 1;
+ MrcWriteCR (
+ MrcData,
+ DDRCTLCH0_CR_DDRCRCTLCONTROLS_REG +
+ ((DDRCTLCH1_CR_DDRCRCTLCONTROLS_REG - DDRCTLCH0_CR_DDRCRCTLCONTROLS_REG) * Channel),
+ DdrCrCtlControls.Data
+ );
+ //
+ // Initialize CRs shared between CKE/CTL/CMD/CLK
+ //
+ // PI setting must match value written for TxDQs above.
+ // There are no shared registers for CLK, only CKE/CTL but only CTLPICODING and CTLCOMPOFFSET
+ // Set CTL/CLK PI to 64, and CMD to 96 (64 + 1/2 QCLK), for ideal initial command centering.
+ //
+ DdrCrCtlPiCoding.Data = 0;
+ DdrCrCtlPiCoding.Bits.CtlPiCode0 =
+ DdrCrCtlPiCoding.Bits.CtlPiCode1 =
+ DdrCrCtlPiCoding.Bits.CtlPiCode2 =
+ DdrCrCtlPiCoding.Bits.CtlPiCode3 = 96;
+
+ Offset = DDRCMDCH0_CR_DDRCRCMDPICODING_REG +
+ ((DDRCMDCH1_CR_DDRCRCMDPICODING_REG - DDRCMDCH0_CR_DDRCRCMDPICODING_REG) * Channel);
+#ifdef ULT_FLAG
+ if (CpuModel == cmHSW_ULT) {
+ //
+ // On ULT we have DdrCrCmdPiCoding.CmdPi0Code and CmdPi1Code
+ //
+ MrcWriteCR (MrcData, Offset, DdrCrCtlPiCoding.Data);
+ }
+#endif // ULT_FLAG
+#ifdef TRAD_FLAG
+ if ((CpuModel == cmHSW) || (CpuModel == cmCRW)) {
+ MrcWriteCR8 (MrcData, Offset, (U8) DdrCrCtlPiCoding.Bits.CtlPiCode0);
+ }
+#endif // TRAD_FLAG
+
+ Offset = DDRCKECH0_CR_DDRCRCMDPICODING_REG +
+ ((DDRCKECH1_CR_DDRCRCMDPICODING_REG - DDRCKECH0_CR_DDRCRCMDPICODING_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, DdrCrCtlPiCoding.Data);
+
+ DdrCrCtlPiCoding.Bits.CtlPiCode0 =
+ DdrCrCtlPiCoding.Bits.CtlPiCode1 =
+ DdrCrCtlPiCoding.Bits.CtlPiCode2 =
+ DdrCrCtlPiCoding.Bits.CtlPiCode3 = 64;
+
+ Offset = DDRCKECTLCH0_CR_DDRCRCTLPICODING_REG +
+ ((DDRCKECTLCH1_CR_DDRCRCTLPICODING_REG - DDRCKECTLCH0_CR_DDRCRCTLPICODING_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, DdrCrCtlPiCoding.Data);
+
+ Offset = DDRCLKCH0_CR_DDRCRCLKPICODE_REG +
+ ((DDRCLKCH1_CR_DDRCRCLKPICODE_REG - DDRCLKCH0_CR_DDRCRCLKPICODE_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, DdrCrCtlPiCoding.Data);
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ ChannelOut->ClkPiCode[Rank] = (U8) DdrCrCtlPiCoding.Bits.CtlPiCode0;
+ }
+
+ Offset = DDRCMDCH0_CR_DDRCRCMDCOMPOFFSET_REG +
+ ((DDRCMDCH1_CR_DDRCRCMDCOMPOFFSET_REG - DDRCMDCH0_CR_DDRCRCMDCOMPOFFSET_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0x0); // Zero for now. May offset comp in future
+
+ Offset = DDRCKECTLCH0_CR_DDRCRCTLCOMPOFFSET_REG +
+ ((DDRCKECTLCH1_CR_DDRCRCTLCOMPOFFSET_REG - DDRCKECTLCH0_CR_DDRCRCTLCOMPOFFSET_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0x0); // Zero for now. May offset comp in future
+
+ Offset = DDRCLKCH0_CR_DDRCRCLKCOMPOFFSET_REG +
+ ((DDRCLKCH1_CR_DDRCRCLKCOMPOFFSET_REG - DDRCLKCH0_CR_DDRCRCLKCOMPOFFSET_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0x0); // Zero for now. May offset comp in future
+ }
+ } // End of for Channel...
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init COMP CRs\n");
+ //
+ // Initialize COMP CRs
+ //
+ // 14:11 DqDrv 19:15 DqOdt 23:20 CmdDrv 27:24 CtlDrv 31:28 ClkDrv
+ //
+ Outputs->CompCtl0 = (DisableOdtStatic << 3); // Disable ODT Static Leg
+
+ if ((Any2dpc) && (Inputs->BoardType == btCRBDT)) {
+ TargetRcomp[1] = 60;
+ }
+
+#ifdef ULT_FLAG
+ if (CpuModel == cmHSW_ULT) {
+ //
+ // RCOMP1 resistor is 120 Ohm on ULT boards
+ // This is used for DQ/CLK Ron (drive strength)
+ //
+ ReferenceR[0] = 40;
+ TargetRcomp[0] = 40;
+
+ ReferenceR[4] = 40;
+ }
+#endif // ULT_FLAG
+
+ for (i = 0; i < 5; i++) {
+ CompVref = (StepSize[i] * (ReferenceR[i] - TargetRcomp[i])) / (2 * (ReferenceR[i] + TargetRcomp[i]));
+ if (i == 1) {
+ //
+ // DqOdt is 5 bits
+ //
+ if (CompVref > 15) {
+ CompVref = 15;
+ } else if (CompVref < -16) {
+ CompVref = -16;
+ }
+
+ Outputs->CompCtl0 |= (CompVref & 0x1F) << (15);
+ } else {
+ if (CompVref > 7) {
+ CompVref = 7;
+ } else if (CompVref < -8) {
+ CompVref = -8;
+ }
+
+ if (i == 0) {
+ Outputs->CompCtl0 |= (CompVref & 0xF) << (11);
+ } else {
+ Outputs->CompCtl0 |= (CompVref & 0xF) << (12 + 4 * i);
+ }
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CompVref[%d] = 0x%x\n", i, CompVref);
+ //
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CompCtl0 = 0x%08X\n", Outputs->CompCtl0);
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL0_REG, Outputs->CompCtl0);
+
+ CompDdrCrCompCtl1.Data = 0;
+ CompDdrCrCompCtl1.Bits.VccddqHi = VddHi; // Set Vdd, 2 qclk DLL mask
+ CompDdrCrCompCtl1.Bits.CompClkOn = SAFE; // Override PwrDn in Safe Mode
+
+ Cmd2N = FALSE;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (Outputs->Controller[Controller].Channel[Channel].Timing[Profile].NMode == 2)
+ Cmd2N = TRUE;
+ if (Cmd2N) {
+ break;
+ }
+ }
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ Outputs->Controller[Controller].Channel[Channel].Timing[Profile].NMode = (Cmd2N == TRUE) ? 2 : 1;
+ }
+ }
+
+ //
+ // Calculate how to program the slew rate targets
+ // Buffer Type DQ CMD CTL CLK
+ // Num Stages 3 5 5 3
+ // Slew Rate 4.5 3.0 3.0 5.0
+ // Derating .8 .8 .8 .8
+ //
+
+/*
+ U8 BufferStageDelayPS[4] = {92, 83, 83, 83}; // Slew = 1V(in mv) / (# Stages * StageDelayPS * Derating)
+ U8 MinCycleStageDelay[4] = {46, 70, 70, 46}; // Avoid corner case
+ U8 i;
+ U16 NumStages;
+*/
+ if (Cmd2N == TRUE) {
+ BufferStageDelayPS[1] = 89; // CMD Slew Rate = 1.8 for 2N
+ }
+
+#ifdef ULT_FLAG
+ if (Lpddr) {
+ BufferStageDelayPS[1] = 63; // CMD Slew Rate = 4 V/ns for double-pumped CMD bus
+ }
+#endif // ULT_FLAG
+
+ for (i = 0; i < 4; i++) {
+ //
+ // Calculate DQ, CMD, CTL, CLK
+ // Number of Stages in DLL, rounded to nearest integer
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "BufferStageDelayPS[%d] = %d\n", i, BufferStageDelayPS[i]);
+ //
+ NumStages = (Outputs->Qclkps + BufferStageDelayPS[i] / 2) / BufferStageDelayPS[i];
+ if (NumStages < 5) {
+ NumStages = 5; // Minimum setting > 3
+ }
+ //
+ // Lock DLL ....
+ //
+ Offset = i * (DDRCOMP_CR_DDRCRCOMPCTL1_DqScompCells_WID + DDRCOMP_CR_DDRCRCOMPCTL1_DqScompPC_WID);
+ if ((NumStages > 16) || (BufferStageDelayPS[i] < MinCycleStageDelay[i])) {
+ CompDdrCrCompCtl1.Data += ((NumStages / 2 - 1) << Offset); // ... to a phase
+ } else {
+ CompDdrCrCompCtl1.Data += (16 + NumStages - 1) << Offset; // ... to a cycle
+ }
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Qclkps = %d, NumStages = %d\n",Outputs->Qclkps, NumStages);
+ //
+ }
+
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL1_REG, CompDdrCrCompCtl1.Data);
+ Outputs->CompCtl1 = CompDdrCrCompCtl1.Data;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CompCtl1 = 0x%x\n", CompDdrCrCompCtl1.Data);
+
+ //
+ // Calculate Target Values for VssHi Panic Driver
+ //
+ // Rtarget = Tperiod / Cdie / ln( VssHi / (VssHi - Delta) )
+ //
+
+/*
+ U8 delta = 15; // VssHi change voltage during panic: 15mV
+ U16 lndown;
+ U16 Rdown;
+ U16 vrefdown;
+ U16 vsshiu, vsshid;
+ U16 vpanic = 24; // Panic Treshold at 24 mV
+ U16 lnup;
+ U16 Rup;
+ U32 vrefup;
+*/
+ vsshid = VssHi + vpanic;
+ //
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "vsshid = %d VssHi = %d vpanic = %d \n", vsshid, VssHi, vpanic);
+ // Calculate log to backsolve exp. RC decay
+ // Input should be 100x. Output is 100x
+ //
+ lndown = (U16) MrcNaturalLog ((100 * vsshid) / (vsshid - delta));
+ Rdown = (Outputs->Qclkps * 2000) / (CDIEVSSHI * lndown); // Rdown is 10x.
+ vrefdown = (128 * Rdown) / (Rdown + 10 * RCMDREF); // Multiple RcmdRef by 10x to match Rdown
+ //
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "vrefdown = %d Rdown = %d lndown = %d \n", vrefdown, Rdown, lndown);
+ //
+ vsshiu = (Inputs->VccIomV - VssHi - vpanic); // if VccIO == 1v then VccmV = 1000
+ lnup = (U16) MrcNaturalLog ((100 * vsshiu) / (vsshiu - delta));
+ Rup = (Outputs->Qclkps * 2000) / (CDIEVSSHI * lnup);
+ vrefup = (128 * 10 * RCMDREF) / (10 * RCMDREF + Rup) - 64;
+ //
+ //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "vrefup = %d Rup = %d lnup = %d vsshiu = %d\n", vrefup, Rup, lnup, vsshiu);
+ //
+ DdrCrCompVssHi.Data = 0;
+ DdrCrCompVssHi.Bits.VtSlopeA = 4; // Apply slope correction of 1.5 to VtComp
+ DdrCrCompVssHi.Bits.VtOffset = (128 * 450 / Inputs->VccIomV / 2); // Apply offset correction to VtComp
+ DdrCrCompVssHi.Bits.PanicDrvUpVref = vrefup; // Apply Calculated Vref Values
+ DdrCrCompVssHi.Bits.PanicDrvDnVref = vrefdown;
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPVSSHI_REG, DdrCrCompVssHi.Data);
+ //
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "DdrCrCompVssHi = 0x%x\n", DdrCrCompVssHi.Data);
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init MISC CRs\n");
+ //
+ // Initialize MISC CRs
+ //
+
+ DdrMiscControl.Data = MrcReadCR (MrcData, DDRSCRAM_CR_DDRMISCCONTROL0_REG);
+ DdrMiscControl.Bits.WeakLock_Latency = 12;
+ DdrMiscControl.Bits.WL_SleepCycles = 5;
+ DdrMiscControl.Bits.WL_WakeCycles = 2;
+ Outputs->MiscControl0 = DdrMiscControl.Data;
+ MrcWriteCR (MrcData, DDRSCRAM_CR_DDRMISCCONTROL0_REG, DdrMiscControl.Data);
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ Offset = DDRSCRAM_CR_DDRSCRAMBLECH0_REG +
+ ((DDRSCRAM_CR_DDRSCRAMBLECH1_REG - DDRSCRAM_CR_DDRSCRAMBLECH0_REG) * Channel);
+ MrcWriteCR (MrcData, Offset, 0);// Keep scrambling disabled for training
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Init KEY MC CRs\n");
+ //
+ // Initialize some key MC CRs
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ //
+ // set the valid rank - Either clear or set only populated so no check for vaid channel
+ //
+ Offset = MCHBAR_CH0_CR_MC_INIT_STATE_REG +
+ ((MCHBAR_CH1_CR_MC_INIT_STATE_REG - MCHBAR_CH0_CR_MC_INIT_STATE_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, ChannelOut->ValidRankBitMask);
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Update LS COMP CRs\n");
+
+ //
+ // 1st Disable Perioid Comp and wait for 10us
+ // Set periodic comp = (10uS * 2^COMP_INT)
+ //
+ CrMCompPcu.Data = 0;
+ CrMCompPcu.Bits.COMP_DISABLE = 1;
+ CrMCompPcu.Bits.COMP_FORCE = 1;
+ CrMCompPcu.Bits.COMP_INTERVAL = COMP_INT; // Set COMP_INT to happen every 10mS
+ MrcWriteCR (MrcData, PCU_CR_M_COMP_PCU_REG, CrMCompPcu.Data);
+ MrcWait (MrcData, 10 * HPET_1US);
+
+ //
+ // Override LevelShifter Compensation to 0x4 (From Hua, 3 is not a valid value)
+ //
+ DataRCompData.Data = MrcReadCR (MrcData, DDRDATA_CR_RCOMPDATA1_REG);
+ DataRCompData.Bits.LevelShifterComp = 2;
+ MrcWriteCrMulticast (MrcData, DDRDATA_CR_RCOMPDATA1_REG, DataRCompData.Data);
+ CmdDdrCrCmdComp.Data = MrcReadCR (MrcData, DDRCMD_CR_DDRCRCMDCOMP_REG);
+ CmdDdrCrCmdComp.Bits.LsComp = 2;
+ MrcWriteCrMulticast (MrcData, DDRCMD_CR_DDRCRCMDCOMP_REG, CmdDdrCrCmdComp.Data);
+ CkeCtlDdrCrCtlComp.Data = MrcReadCR (MrcData, DDRCKECTL_CR_DDRCRCTLCOMP_REG);
+ CkeCtlDdrCrCtlComp.Bits.LsComp = 2;
+ MrcWriteCrMulticast (MrcData, DDRCKECTL_CR_DDRCRCTLCOMP_REG, CkeCtlDdrCrCtlComp.Data);
+ ClkDdrCrClkComp.Data = MrcReadCR (MrcData, DDRCLK_CR_DDRCRCLKCOMP_REG);
+ ClkDdrCrClkComp.Bits.LsComp = 2;
+ MrcWriteCrMulticast (MrcData, DDRCLK_CR_DDRCRCLKCOMP_REG, ClkDdrCrClkComp.Data);
+ CompDdrCrDataComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG);
+ CompDdrCrDataComp.Bits.LevelShifterComp = 2;
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG, CompDdrCrDataComp.Data);
+ CompDdrCrCmdComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCMDCOMP_REG);
+ CompDdrCrCmdComp.Bits.LsComp = 2;
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCMDCOMP_REG, CompDdrCrCmdComp.Data);
+ CompDdrCrCtlComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCTLCOMP_REG);
+ CompDdrCrCtlComp.Bits.LsComp = 2;
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCTLCOMP_REG, CompDdrCrCtlComp.Data);
+ CompDdrCrClkComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCLKCOMP_REG);
+ CompDdrCrClkComp.Bits.LsComp = 2;
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCLKCOMP_REG, CompDdrCrClkComp.Data);
+ CompDdrCrCompOvr.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCOMPOVR_REG);
+ CompDdrCrCompOvr.Bits.LsComp = 2;
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPOVR_REG, CompDdrCrCompOvr.Data);
+
+ //
+ // Manually update the comp values
+ //
+ DdrMiscControl.Data = Outputs->MiscControl0;
+ DdrMiscControl.Bits.ForceCompUpdate = 1;
+ MrcWriteCR (MrcData, DDRSCRAM_CR_DDRMISCCONTROL0_REG, DdrMiscControl.Data);
+
+ //
+ // Fix Offset between ODT Up/Dn
+ //
+ CompDdrCrDataComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG);
+ CompDdrCrCompCtl0.Data = Outputs->CompCtl0;
+ //
+ // Calculate (OdtDn - OdtUp) - Will be BITS 9:4
+ //
+ CompDdrCrCompCtl0.Bits.DqOdtUpDnOff = CompDdrCrDataComp.Bits.RcompOdtDown - CompDdrCrDataComp.Bits.RcompOdtUp;
+ CompDdrCrCompCtl0.Bits.FixOdtD = 1; // Enable Fixed Offset between OdtUp/Dn - Will be BIT10
+ Outputs->CompCtl0 = CompDdrCrCompCtl0.Data;
+ MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL0_REG, CompDdrCrCompCtl0.Data);
+
+ //
+ // 2X Refresh
+ //
+ if (
+ (Inputs->RefreshRate2x == TRUE) &&
+ (
+ ((CpuModel == cmHSW) && (CpuStepping >= csHswC0)) ||
+ ((CpuModel == cmCRW) && (CpuStepping >= csCrwC0)) ||
+ ((CpuModel == cmHSW_ULT) && (CpuStepping >= csHswUltC0))
+ )
+ ) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "*** Enabling 2x Refresh ***\n");
+ AutoSelfRefresh = Outputs->AutoSelfRefresh;
+
+ if ((AutoSelfRefresh == FALSE)
+#ifdef ULT_FLAG
+ || (Lpddr == TRUE)
+#endif
+ ){
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Enabling Mailbox 2x Refresh\n");
+ MrcOemEnable2xRefresh (MrcData);
+ }
+
+ //
+ // Percentage reduction of tREFI needed for ASR and LPDDR cases (Mutually Exclusive).
+ //
+ if (AutoSelfRefresh == TRUE) {
+ RefiReduction = 50;
+ }
+#ifdef ULT_FLAG
+ if (Lpddr == TRUE) {
+ DdrPtmCtl.Data = MrcReadCR (MrcData, PCU_CR_DDR_PTM_CTL_PCU_REG);
+ DdrPtmCtl.Bits.DISABLE_DRAM_TS = 0;
+ MrcWriteCR (MrcData, PCU_CR_DDR_PTM_CTL_PCU_REG, DdrPtmCtl.Data);
+ RefiReduction = 97;
+ }
+#endif
+
+ if ((Inputs->BootMode == bmCold) && ((AutoSelfRefresh == TRUE)
+#ifdef ULT_FLAG
+ || (Lpddr == TRUE)
+#endif
+ )) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "%s Detected, Reducing tREFI by %u percent.\n",
+ (AutoSelfRefresh == TRUE) ? "Auto Self Refresh" : "LPDDR",
+ RefiReduction
+ );
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ ChannelOut->Timing[STD_PROFILE].tREFI = (ChannelOut->Timing[STD_PROFILE].tREFI * RefiReduction) / 100;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " C(%d).tREFI = 0x%x\n", Channel, ChannelOut->Timing[STD_PROFILE].tREFI);
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DimmOut->Status == DIMM_PRESENT) {
+ DimmOut->Timing[STD_PROFILE].tREFI = (DimmOut->Timing[STD_PROFILE].tREFI * RefiReduction) / 100;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " C(%d).D(%d).tREFI = 0x%x\n",
+ Channel,
+ Dimm,
+ DimmOut->Timing[STD_PROFILE].tREFI
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Set the DDR voltage in PCU
+ //
+ MrcSetPcuDdrVoltage (MrcData, Vdd);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Timing Config\n");
+ MrcTimingConfiguration (MrcData);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Refresh Config\n");
+ MrcRefreshConfiguration (MrcData);
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Scheduler parameters\n");
+ MrcSchedulerParametersConfig (MrcData);
+
+ //
+ // this function must be in the end.
+ // if one of the function close channel the function execute this close.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Address Decoding Config\n");
+ MrcAdConfiguration (MrcData);
+
+ return Status;
+}
+
+/**
+@brief
+ This function init all the necessary registers for the training.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval mrcSuccess
+**/
+MrcStatus
+MrcPreTraining (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ U32 Offset;
+ U8 Channel;
+ U8 Rank;
+ U8 RankMod2;
+ MCDECS_CR_MAD_DIMM_CH0_MCMAIN_STRUCT CrMadDimmCh;
+ const MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+
+ //
+ // Dump the MR registers for DDR3
+ // LPDDR Jedec Init is done after Early Command Training
+ //
+ if (Outputs->DdrType != MRC_DDR_TYPE_LPDDR3) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) {
+ continue;
+ }
+
+ RankMod2 = Rank % 2;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "MrcSetMR0 Channel %u Rank %u = 0x%X\n",
+ Channel,
+ Rank,
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR0]
+ );
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "MrcSetMR1 Channel %u Rank %u = 0x%X\n",
+ Channel,
+ Rank,
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR1]
+ );
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "MrcSetMR2 Channel %u Rank %u = 0x%X\n",
+ Channel,
+ Rank,
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR2]
+ );
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "MrcSetMR3 Channel %u Rank %u = 0x%X\n",
+ Channel,
+ Rank,
+ ChannelOut->Dimm[RANK_TO_DIMM_NUMBER (Rank)].Rank[RankMod2].MR[mrMR3]
+ );
+ }
+ }
+
+ if (Outputs->EccSupport == TRUE) {
+ Offset = MCDECS_CR_MAD_DIMM_CH0_MCMAIN_REG +
+ ((MCDECS_CR_MAD_DIMM_CH1_MCMAIN_REG - MCDECS_CR_MAD_DIMM_CH0_MCMAIN_REG) * Channel);
+ CrMadDimmCh.Data = MrcReadCR (MrcData, Offset);
+ //
+ // set ECC IO ACTIVE ONLY - NOT IO
+ //
+ CrMadDimmCh.Bits.ECC = emEccIoActive;
+ MrcWriteCR (MrcData, Offset, CrMadDimmCh.Data);
+ //
+ // Wait 4 usec after enabling the ECC IO, needed by HW
+ //
+ MrcWait (MrcData, 4 * HPET_1US);
+ }
+ } // for Channel
+
+ //
+ // Set up Write data Buffer before training steps
+ //
+ SetupWDB (MrcData);
+
+ return mrcSuccess;
+}
+
+/**
+
+@brief
+
+ This function initializes all the necessary registers after main training steps but before LCT.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval mrcSuccess
+
+**/
+MrcStatus
+MrcPostTraining (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcProfile Profile;
+ U8 Channel;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ Profile = MrcData->SysIn.Inputs.MemoryProfile;
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ //
+ // Update CmdN timing, Round Trip Latency and tXP
+ // OldN=3, NewN=2*Cmd2N
+ //
+ UpdateCmdNTiming (MrcData, Channel, 2 * 2, (ControllerOut->Channel[Channel].Timing[Profile].NMode == 2) ? 2 : 0);
+ }
+ }
+
+ return mrcSuccess;
+}
+
+/**
+@brief
+ Program PCU_CR_DDR_VOLTAGE register.
+
+ @param[in] MrcData - Include all MRC global data.
+ @param[in] VddVoltage - Current DDR voltage.
+
+ @retval none
+**/
+void
+MrcSetPcuDdrVoltage (
+ IN OUT MrcParameters *MrcData,
+ IN MrcVddSelect VddVoltage
+ )
+{
+ MrcInput *Inputs;
+ MrcOutput *Outputs;
+ U8 Data8;
+ PCU_CR_DDR_VOLTAGE_PCU_STRUCT DdrVoltage;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+
+ switch (VddVoltage) {
+ case VDD_1_35:
+ Data8 = 1;
+ break;
+
+ case VDD_1_20:
+ Data8 = 3; // @todo For single CA bus set this to 2
+ break;
+
+ default:
+ Data8 = 0;
+ }
+
+ MRC_DEBUG_MSG (&Inputs->Debug, MSG_LEVEL_NOTE, "PCU_CR_DDR_VOLTAGE = 0x%02X\n", Data8);
+ DdrVoltage.Data = 0;
+ DdrVoltage.Bits.DDR_VOLTAGE = Data8;
+ MrcWriteCR (MrcData, PCU_CR_DDR_VOLTAGE_PCU_REG, DdrVoltage.Data);
+}
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcMemoryMap.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcMemoryMap.c
new file mode 100644
index 0000000..2fd1310
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcMemoryMap.c
@@ -0,0 +1,528 @@
+/** @file
+ The functions in this file initializes the physical memory map.
+
+@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 "MrcMemoryMap.h"
+#include "PttHciRegs.h"
+
+/**
+@brief
+ After BIOS determines the total physical memory size.
+ Determines TOM which is defined by the total physical memory size.
+ Determines TOM minus the ME memory size. The ME memory size is calculated from MESEG_BASE and MESEG_MASK.
+ Determines MMIO allocation, which is system configuration dependent.
+
+ Determines TOLUD which is the minimum value by comparing between "4GB minus MMIO size" and "TOM minus ME size".
+ Determines Graphics Stolen Base, BDSM by subtracting the graphics data stolen memory size from TOLUD.
+ Graphics Data Stolen Memory size is given by GMS field in GGC register. It must be define before this stage.
+ Determines Graphics GTT Stolen Base, BGSM by subtracting the GTT graphics stolen memory size from BDSM.
+ GTT Stolen Memory size is given by GGMS field in GGC register. It must be define before this stage.
+ Determines TSEG Base, TSEGMB by subtracting TSEG size from BGSM.
+ TSEG should be defined.
+ Remove the memory hole caused by aligning TSEG to a 8MB boundary.
+ Determine whether Memory Reclaim is available. If "TOM minus ME Stolem Memory Size" is greater than the value of TOLUD, then memory reclaim is available to enable.
+ Determine REMAPBASE if reclaim is enabled. This is the maximum value by comparing between 4GB and "TOM minus ME size".
+ Determine REMAPLIMIT () if reclaim is enabled. This is the value of REMAPBASE plus "the difference between the value in TOLUD register and the lower of either 4GB or 'TOM minus ME Stolen memory size", and then minus 1 boundary.
+ Determine TOUUD. TOUUD indicates the address one byte above the maximum DRAM. If relcaim is disabled, this value is calculated by "TOM minus ME stolen size". Otherwise, this value is set to REMAPLIMIT plus 1MB.
+
+ @param[in, out] MrcData - Include all MRC global data. include also the memory map data.
+
+ @retval MrcStatus - if the reset is succeded.
+**/
+MrcStatus
+MrcSetMemoryMap (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcMemoryMap *MemoryMap;
+ MRC_PCI_000_GGC_STRUCT Ggc;
+ U32 Offset;
+ U32 TsegBaseOrg;
+ U32 TsegBaseDelta;
+ U32 GdxcTop;
+ U32 FtpmTop;
+ U32 MmioSize;
+#ifdef PTT_FLAG
+ U32 PttSts;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ MemoryMap = &Outputs->MemoryMapData;
+
+ //
+ // Find the total memory size
+ //
+ MrcTotalMemory (MrcData);
+
+ //
+ // Set TOM register
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TOM (Total physical memory size) = %u MB\n", MemoryMap->TotalPhysicalMemorySize);
+
+ //
+ // Find the TOM minus ME size only for internal calculations
+ //
+ MemoryMap->TomMinusMe = MemoryMap->TotalPhysicalMemorySize - Inputs->MeStolenSize;
+
+ MmioSize = Inputs->MmioSize;
+ if (Inputs->MemoryTrace) {
+ if (MemoryMap->TotalPhysicalMemorySize <= MEM_4GB) {
+ MmioSize = MAX (MmioSize, MEM_4GB - MemoryMap->TotalPhysicalMemorySize / 2);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Adjusted MmioSize = %Xh\n", MmioSize);
+ }
+ }
+
+ //
+ // Find and set TOLUD.
+ // TOLUD which is the minimum value by comparing between "4GB minus MMIO size" and "TOM minus ME size"
+ //
+ MemoryMap->ToludBase = MIN (MemoryMap->TomMinusMe, MEM_4GB - MmioSize);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TOLUD base = %Xh\n", MemoryMap->ToludBase);
+
+ //
+ // Find and set BDSM Graphics Stolen Base.
+ // Graphics Stolen Base, BDSM by subtracting the graphics data stolen memory size from TOLUD.
+ //
+ MemoryMap->BdsmBase = MemoryMap->ToludBase - Outputs->GraphicsStolenSize;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "BDSM base = %Xh\n", MemoryMap->BdsmBase);
+
+ //
+ // Graphics GTT Stolen Base
+ // Graphics GTT Stolen Base, BGSM by subtracting the GTT graphics stolen memory size from BDSM.
+ //
+ MemoryMap->GttBase = MemoryMap->BdsmBase - Outputs->GraphicsGttSize;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "GTT base = %Xh\n", MemoryMap->GttBase);
+
+ //
+ // Graphics size register init.
+ //
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_GGC_REG);
+ MrcOemMmioRead (Offset, &Ggc.Data, Inputs->PciEBaseAddress);
+ Ggc.Bits.Vamen = (Inputs->GfxIsVersatileAcceleration == TRUE) ? 1 : 0;
+ Ggc.Bits.Ggms = MIN (GGC_GGMS_MAX, Outputs->GraphicsGttSize);
+ //
+ // GMS limitation is 5 bits
+ //
+ if (Outputs->GraphicsStolenSize == 1024) {
+ Ggc.Bits.Gms = 17;
+ } else {
+ Ggc.Bits.Gms = MIN (GGC_GMS_MAX, (Outputs->GraphicsStolenSize / 32));
+ }
+
+ MemoryMap->GraphicsControlRegister = Ggc.Data;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "GGC value = %Xh\n", MemoryMap->GraphicsControlRegister);
+
+ //
+ // TSEG Base
+ // TSEGMB by subtracting TSEG size from BGSM.
+ //
+ MemoryMap->TsegBase = MemoryMap->GttBase - Inputs->TsegSize;
+ TsegBaseOrg = MemoryMap->TsegBase;
+
+ //
+ // Dpr size to program DPR register in update MemoryMap
+ //
+ MemoryMap->DprSize = Inputs->DprSize;
+
+ //
+ // SMRR must be aligned at 8MB boundary.
+ // according to this TSEG base need to be also aligned to 8MB boundary.
+ // Round it down to the nearest 8MB boundary.
+ //
+ MemoryMap->TsegBase &= ~(Inputs->TsegSize - 1);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TSEG base = %Xh\n", MemoryMap->TsegBase);
+
+ //
+ // Remove the hole between top of aligned TSEG and GTT Base:
+ // 1. Calculate Delta = TsegMB - aligned_TsegMB
+ // 2. Walk backwards and adjust BGSM_new = BGSM - DELTA, TOLUD_new = TOLUD - DELTA
+ //
+ TsegBaseDelta = TsegBaseOrg - MemoryMap->TsegBase;
+ if (TsegBaseDelta != 0) {
+ MemoryMap->GttBase = MemoryMap->GttBase - TsegBaseDelta;
+ MemoryMap->BdsmBase = MemoryMap->BdsmBase - TsegBaseDelta;
+ MemoryMap->ToludBase = MemoryMap->ToludBase - TsegBaseDelta;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "GTT base = %Xh\n", MemoryMap->GttBase);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "BDSM base = %Xh\n", MemoryMap->BdsmBase);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TOLUD base = %Xh\n", MemoryMap->ToludBase);
+ }
+ //
+ // test if Reclaim is available
+ // If "TOM minus ME Stolem Memory Size" is greater than the value of TOLUD, then memory reclaim is available to enable
+ //
+ if (Inputs->RemapEnable && (MemoryMap->TomMinusMe > MemoryMap->ToludBase)) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Reclaim Enable\n");
+ MemoryMap->ReclaimEnable = TRUE;
+ //
+ // Remap Base
+ // This is the maximum value by comparing between 4GB and "TOM minus ME size".
+ //
+ MemoryMap->RemapBase = MAX (MEM_4GB, MemoryMap->TomMinusMe);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Remap Base %Xh\n", MemoryMap->RemapBase);
+ //
+ // Remap Limit
+ // This is the value of REMAPBASE plus "the difference between the value in TOLUD register and the lower of either 4GB or 'TOM minus ME Stolen memory size", and then minus 1MB boundary.
+ //
+ MemoryMap->RemapLimit = MemoryMap->RemapBase + (MIN (MEM_4GB, MemoryMap->TomMinusMe) - MemoryMap->ToludBase);
+
+ MemoryMap->TouudBase = MemoryMap->RemapLimit;
+
+ if (!((Inputs->CpuModel == cmHSW) && (Inputs->CpuStepping == csHswA0))) {
+ MemoryMap->RemapLimit -= 0x1;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Remap Limit %Xh\n", MemoryMap->RemapLimit);
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TOUUD Base %Xh\n", MemoryMap->TouudBase);
+ } else {
+ MemoryMap->ReclaimEnable = FALSE;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Reclaim disable \n");
+ //
+ // TOUUD Base
+ // If relcaim is disabled, this value is calculated by "TOM minus ME stolen size".
+ //
+ MemoryMap->TouudBase = MemoryMap->TomMinusMe;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TOUUD Base %Xh\n", MemoryMap->TouudBase);
+ }
+ //
+ // GDXC must be aligned to 8MB boundary. But PSMI must be 16MB alligned
+ // GdxcBase by subtracting Gdxc from BGSM.
+ // @todo: GDXC is below DPRBASE if TXT is enabled, which is below TSEG. Maybe it will be required to be placed anywhere below TOLUD.
+ //
+ if (Outputs->Gdxc.GdxcEnable) {
+ if (Inputs->MemoryTrace) {
+ //
+ // Put GDXC at the top of the second channel
+ //
+ if (MemoryMap->TotalPhysicalMemorySize <= MEM_4GB) {
+ GdxcTop = MemoryMap->TouudBase;
+ } else {
+ GdxcTop = MemoryMap->TomMinusMe;
+ }
+ } else {
+ //
+ // Put GDXC below DPR stolen region.
+ //
+ GdxcTop = MemoryMap->TsegBase - Inputs->DprSize;
+ }
+ //
+ // @todo For C-step we can remove the "minus 1MB" W/A
+ //
+ MemoryMap->GdxcMotSize = Outputs->Gdxc.GdxcMotSize << (23 - 20); // In MB
+ MemoryMap->GdxcMotBase = GdxcTop - MemoryMap->GdxcMotSize - 1; // Minus 1 MB - WA for MOT overflow.
+ MemoryMap->GdxcMotBase &= ~(MRC_BIT4 - 1); // Round down to 16MB boundary
+
+ MemoryMap->GdxcIotSize = Outputs->Gdxc.GdxcIotSize << (23 - 20); // In MB
+ MemoryMap->GdxcIotBase = MemoryMap->GdxcMotBase - MemoryMap->GdxcIotSize;
+ MemoryMap->GdxcIotBase -= 16; // 16MB for PSMI
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "GDXC MOT base %Xh, size %d (%Xh) MB\n",
+ MemoryMap->GdxcMotBase,
+ MemoryMap->GdxcMotSize,
+ MemoryMap->GdxcMotSize
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "GDXC IOT base %Xh, size %d (%Xh) MB\n",
+ MemoryMap->GdxcIotBase,
+ MemoryMap->GdxcIotSize,
+ MemoryMap->GdxcIotSize
+ );
+ if (Inputs->MemoryTrace) {
+ //
+ // Put fTPM below DPR
+ //
+ FtpmTop = MemoryMap->TsegBase - Inputs->DprSize;
+ } else {
+ //
+ // Put fTPM below GDXC.
+ //
+ FtpmTop = MemoryMap->GdxcIotBase;
+ }
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "GDXC DISABLED\n");
+ FtpmTop = MemoryMap->TsegBase - Inputs->DprSize;
+ }
+
+#ifdef PTT_FLAG
+ if (Inputs->CpuModel == cmHSW_ULT) {
+ MrcOemMmioRead (R_PTT_HCI_STS, (U32 *) &PttSts, R_PTT_HCI_BASE_ADDRESS);
+ if ((PttSts & B_PTT_HCI_STS_ENABLED) == B_PTT_HCI_STS_ENABLED) {
+ //
+ // fTPM Stolen size is 4KB
+ //
+ MemoryMap->FtpmStolenBase = (FtpmTop << 20) - 0x1000;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Ftpm Stolen base = %Xh\n", MemoryMap->FtpmStolenBase);
+ }
+ }
+#endif // PTT_FLAG
+
+ MemoryMap->MeStolenBase = MemoryMap->TomMinusMe;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "ME stolen base %Xh\n", MemoryMap->MeStolenBase);
+
+ MemoryMap->MeStolenSize = Inputs->MeStolenSize;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "ME stolen size %Xh\n", MemoryMap->MeStolenSize);
+
+ UpdateMemoryMapRegisters (Inputs->PciEBaseAddress, Inputs->GdxcBaseAddress, MemoryMap);
+ return mrcSuccess;
+}
+
+/**
+@brief
+ This function find the total memory in the system.
+ and write it to TotalPhysicalMemorySize in MrcData structure.
+
+ @param[in, out] MrcData - Include all MRC global data.
+
+ @retval Nothing
+**/
+void
+MrcTotalMemory (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ Outputs->MemoryMapData.TotalPhysicalMemorySize = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ ChannelOut->Capacity = 0;
+ if (MrcChannelExist (Outputs, Channel)) {
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DimmOut->Status == DIMM_PRESENT) {
+ ChannelOut->Capacity += DimmOut->DimmCapacity;
+ }
+ }
+
+ ChannelOut->Capacity = MIN (ChannelOut->Capacity, Outputs->MrcTotalChannelLimit);
+ Outputs->MemoryMapData.TotalPhysicalMemorySize += ChannelOut->Capacity;
+ }
+ }
+ }
+
+ return;
+}
+
+/**
+@brief
+ this function write to the memory init registers.
+
+ @param[in] PciEBaseAddress - Address of the PCI Express BAR
+ @param[in] GdxcBaseAddress - Address of the GDXC BAR
+ @param[in] MemoryMap - Include all the memory map definitions
+
+ @retval Nothing
+**/
+void
+UpdateMemoryMapRegisters (
+ IN const U32 PciEBaseAddress,
+ IN const U32 GdxcBaseAddress,
+ IN const MrcMemoryMap *const MemoryMap
+ )
+{
+ MRC_PCI_000_TOM_STRUCT Tom;
+ MRC_PCI_000_TOLUD_STRUCT Tolud;
+ MRC_PCI_000_TOUUD_STRUCT Touud;
+ MRC_PCI_000_REMAPBASE_STRUCT RemapBase;
+ MRC_PCI_000_REMAPLIMIT_STRUCT RemapLimit;
+ MRC_PCI_000_TSEGMB_STRUCT Tsegmb;
+ MRC_PCI_000_BDSM_STRUCT Bdsm;
+ MRC_PCI_000_BGSM_STRUCT Bgsm;
+ MRC_PCI_000_MESEG_BASE_STRUCT MeSegBase;
+ MRC_PCI_000_MESEG_MASK_STRUCT MeSegMask;
+ MRC_PCI_000_DPR_STRUCT Dpr;
+ U32 Offset;
+
+ //
+ // Write TOM register
+ //
+ Tom.Data = 0;
+ Tom.Data32.Low.Bits.Value = MemoryMap->TotalPhysicalMemorySize;
+ Tom.Data32.High.Bits.Value = MemoryMap->TotalPhysicalMemorySize >> (32 - TOM_TOM_OFF);
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_TOM_REG);
+ MrcOemMmioWrite (Offset, Tom.Data32.Low.Data, PciEBaseAddress);
+ MrcOemMmioWrite (Offset + 4, Tom.Data32.High.Data, PciEBaseAddress);
+
+ //
+ // Write TOLUD register
+ //
+ Tolud.Data = 0;
+ Tolud.Bits.Value = MemoryMap->ToludBase;
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_TOLUD_REG);
+ MrcOemMmioWrite (Offset, Tolud.Data, PciEBaseAddress);
+
+ //
+ // Write TOUUD register
+ //
+ Touud.Data = 0;
+ Touud.Data32.Low.Bits.Value = MemoryMap->TouudBase;
+ Touud.Data32.High.Bits.Value = MemoryMap->TouudBase >> (32 - TOUUD_TOUUD_OFF);
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_TOUUD_REG);
+ MrcOemMmioWrite (Offset, Touud.Data32.Low.Data, PciEBaseAddress);
+ MrcOemMmioWrite (Offset + 4, Touud.Data32.High.Data, PciEBaseAddress);
+
+ if (MemoryMap->ReclaimEnable) {
+ //
+ // Write REMAPBASE register.
+ //
+ RemapBase.Data = 0;
+ RemapBase.Data32.Low.Bits.Value = MemoryMap->RemapBase;
+ RemapBase.Data32.High.Bits.Value = MemoryMap->RemapBase >> (32 - REMAPBASE_REMAPBASE_OFF);
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_REMAPBASE_REG);
+ MrcOemMmioWrite (Offset, RemapBase.Data32.Low.Data, PciEBaseAddress);
+ MrcOemMmioWrite (Offset + 4, RemapBase.Data32.High.Data, PciEBaseAddress);
+
+ //
+ // Write REMAPLIMIT register.
+ //
+ RemapLimit.Data = 0;
+ RemapLimit.Data32.Low.Bits.Value = MemoryMap->RemapLimit;
+ RemapLimit.Data32.High.Bits.Value = MemoryMap->RemapLimit >> (32 - REMAPLIMIT_REMAPLMT_OFF);
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_REMAPLIMIT_REG);
+ MrcOemMmioWrite (Offset, RemapLimit.Data32.Low.Data, PciEBaseAddress);
+ MrcOemMmioWrite (Offset + 4, RemapLimit.Data32.High.Data, PciEBaseAddress);
+ }
+ //
+ // Write TSEGMB register
+ //
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_TSEGMB_REG);
+ Tsegmb.Data = 0;
+ Tsegmb.Bits.Value = MemoryMap->TsegBase;
+ MrcOemMmioWrite (Offset, Tsegmb.Data, PciEBaseAddress);
+
+ //
+ // Program DPR Register with DPR size & DMA Protection Enabled
+ //
+ if(MemoryMap->DprSize != 0){
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_DPR_REG);
+ MrcOemMmioRead (Offset, &Dpr.Data, PciEBaseAddress);
+ Dpr.Bits.Dprsize = MemoryMap->DprSize;
+ Dpr.Bits.Epm = 1;
+ MrcOemMmioWrite (Offset, Dpr.Data, PciEBaseAddress);
+ }
+ //
+ // Write BDSM register
+ //
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_BDSM_REG);
+ Bdsm.Data = 0;
+ Bdsm.Bits.Value = MemoryMap->BdsmBase;
+ MrcOemMmioWrite (Offset, Bdsm.Data, PciEBaseAddress);
+
+ //
+ // Write BGSM register
+ //
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_BGSM_REG);
+ Bgsm.Data = 0;
+ Bgsm.Bits.Value = MemoryMap->GttBase;
+ MrcOemMmioWrite (Offset, Bgsm.Data, PciEBaseAddress);
+
+ //
+ // Enable ME Stolen Memory if the size is not zero
+ //
+ if (MemoryMap->MeStolenSize != 0) {
+ //
+ // Write MESEG_MASK register. Must be written before MESEG_BASE.
+ //
+ MeSegMask.Data = 0;
+ MeSegMask.Data32.Low.Bits.Enable = 1;
+ Offset = 0x80000 - MemoryMap->MeStolenSize;
+ MeSegMask.Data32.Low.Bits.Value = Offset;
+ MeSegMask.Data32.High.Bits.Value = Offset >> (32 - MESEG_MASK_MEMASK_OFF);
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_MESEG_MASK_REG);
+ MrcOemMmioWrite (Offset, MeSegMask.Data32.Low.Data, PciEBaseAddress);
+ MrcOemMmioWrite (Offset + 4, MeSegMask.Data32.High.Data, PciEBaseAddress);
+
+ //
+ // Write MESEG_BASE register
+ //
+ MeSegBase.Data = 0;
+ MeSegBase.Data32.Low.Bits.Value = MemoryMap->MeStolenBase;
+ MeSegBase.Data32.High.Bits.Value = MemoryMap->MeStolenBase >> (32 - MESEG_BASE_MEBASE_OFF);
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_MESEG_BASE_REG);
+ MrcOemMmioWrite (Offset, MeSegBase.Data32.Low.Data, PciEBaseAddress);
+ MrcOemMmioWrite (Offset + 4, MeSegBase.Data32.High.Data, PciEBaseAddress);
+ }
+ //
+ // Write graphics control register
+ //
+ Offset = MrcOemGetPcieDeviceAddress (0, 0, 0, MRC_PCI_000_GGC_REG);
+ MrcOemMmioWrite (Offset, MemoryMap->GraphicsControlRegister, PciEBaseAddress);
+
+ //
+ // Program GDXC Registers
+ // 1st MOT: 0x10, 0x14 and 0x18 (Address Low, Address High and Region)
+ // 2nd IOT: 0x20, 0x24 and 0x28 (Address Low, Address High and Region)
+ //
+ MrcOemMmioWrite (
+ MPCOHTRK_CR_GDXC_MOT_ADDRESS_LO_REG,
+ MemoryMap->GdxcMotBase << 14, // (GdxcMotBase << 20) >> 6, Current Pointer in cache line units
+ GdxcBaseAddress
+ );
+ MrcOemMmioWrite (
+ MPCOHTRK_CR_GDXC_MOT_ADDRESS_HI_REG,
+ (MemoryMap->GdxcMotBase & MRC_BIT18) >> 18, // Bit [18] will be bit [32], so it goes to MOT_ADDRESS_HI.MEM_PTR
+ GdxcBaseAddress
+ );
+ MrcOemMmioWrite (
+ MPCOHTRK_CR_GDXC_MOT_REGION_REG,
+ MemoryMap->GdxcMotBase >> 3 | // (GdxcMotBase << 20) >> 23, MOT_REGION.START_ADDRESS is bits [38:23]
+ (((MemoryMap->GdxcMotBase + MemoryMap->GdxcMotSize) >> 3) << 16),
+ GdxcBaseAddress
+ );
+
+ MrcOemMmioWrite (
+ MPCOHTRK_CR_GDXC_OCLA_ADDRESS_LO_REG,
+ MemoryMap->GdxcIotBase << 14, // (GdxcIotBase << 20) >> 6, Current Pointer in cache line units
+ GdxcBaseAddress
+ );
+ MrcOemMmioWrite (
+ MPCOHTRK_CR_GDXC_OCLA_ADDRESS_HI_REG,
+ MPCOHTRK_CR_GDXC_OCLA_ADDRESS_HI_LOCK_MSK | (MemoryMap->GdxcIotBase & MRC_BIT18) >> 18, // Bit [18] will be bit [32], goes to IOT_ADDRESS_HI.MEM_PTR
+ GdxcBaseAddress
+ );
+ MrcOemMmioWrite (
+ MPCOHTRK_CR_GDXC_OCLA_REGION_REG,
+ MemoryMap->GdxcIotBase >> 3 | // (GdxcIotBase << 20) >> 23, OCLA_REGION.START_ADDRESS is bits [38:23]
+ (((MemoryMap->GdxcIotBase + MemoryMap->GdxcIotSize) >> 3) << 16),
+ GdxcBaseAddress
+ );
+ return;
+}
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcReset.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcReset.c
new file mode 100644
index 0000000..b51b6f3
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcReset.c
@@ -0,0 +1,629 @@
+/** @file
+ The functions in this file implement the DDR3 reset sequence.
+
+@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 "MrcReset.h"
+
+/**
+@brief
+ Perform full JEDEC reset and init sequence.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval MrcStatus - mrcSuccess or reason for failure.
+**/
+MrcStatus
+MrcResetSequence (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcStatus Status;
+ MrcOutput *Outputs;
+#ifdef ULT_FLAG
+ BOOL Lpddr;
+#endif //ULT_FLAG
+
+ Outputs = &MrcData->SysOut.Outputs;
+
+ //
+ // Start with an IO reset
+ //
+ Status = IoReset (MrcData);
+ if (mrcSuccess == Status) {
+ //
+ // Check if rcomp is done and the ddr ready to use
+ //
+ Status = CheckFirstRcompDone (MrcData);
+ if (mrcSuccess == Status) {
+ //
+ // Perform jedec reset.
+ //
+ // If RTT_NOM is to be enabled in MR1, the ODT input signal must be statically held LOW
+ // in our system RTT_NOM is always enable.
+ // Force ODT low
+ //
+ MrcWriteCR (MrcData, MCSCHEDS_CR_REUT_CH_MISC_ODT_CTRL_REG, MCSCHEDS_CR_REUT_CH_MISC_ODT_CTRL_ODT_Override_MAX);
+
+#ifdef ULT_FLAG
+ //
+ // Check if LPDDR3 memory is used
+ //
+ Lpddr = (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3);
+
+ if (Lpddr) {
+ Status = MrcJedecResetLpddr3 (MrcData);
+ //
+ // The rest of JEDEC init will be done in a separate step after Early Command Training,
+ // and ECT will set the LpddrEctDone flag.
+ //
+ if (Outputs->LpddrEctDone) {
+ Status = MrcJedecInitLpddr3 (MrcData);
+ }
+ return Status;
+ }
+#endif // ULT_FLAG
+ MrcJedecReset (MrcData);
+
+ //
+ // Initialize the DIMM MRS registers.
+ //
+ //
+ // Step 6 - Set the MR2 for each rank
+ //
+ Status = MrcSetMR2 (MrcData, 0);
+ if (Status == mrcSuccess) {
+ //
+ // Step 7 - Set the MR3 for each rank
+ //
+ Status = MrcSetMR3 (MrcData, 0, 0);
+ if (Status == mrcSuccess) {
+ //
+ // Step 8 - Set the MR1 for each rank
+ //
+ Status = MrcSetMR1 (MrcData, 0, DIMMRON, 0, 0, 0, 0);
+ if (Status == mrcSuccess) {
+ //
+ // Step 9 - Set the MR0 for each rank
+ //
+ Status = MrcSetMR0 (MrcData, 0, 0, 0, 1);
+ if (Status == mrcSuccess) {
+ //
+ // Step 10 - Issue ZQCL command to start ZQ calibration
+ //
+ Status = MrcIssueZQ (MrcData, 0x3, MRC_ZQ_INIT);
+ if (Status == mrcSuccess) {
+ //
+ // If RTT_NOM is to be enabled in MR1, the ODT input signal must be statically held LOW
+ // in our system RTT_NOM is always enable.
+ // Force ODT low
+ //
+ MrcWriteCR (MrcData, MCSCHEDS_CR_REUT_CH_MISC_ODT_CTRL_REG, 0);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ //
+ // Set flag to restore from host structure instead from look-up table
+ //
+ Outputs->RestoreMRs = TRUE;
+
+ return Status;
+}
+
+/**
+@brief
+ Perform JEDEC DIMM reset sequence.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval - none
+**/
+void
+MrcJedecReset (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ U32 Offset;
+ U32 VddSettleWaitTime;
+ U8 Channel;
+ MCDECS_CR_MC_INIT_STATE_G_MCMAIN_STRUCT McInitStateG;
+#ifdef ULT_FLAG
+ U32 Rcba;
+ U32 PmCfg2;
+#endif // ULT_FLAG
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+ VddSettleWaitTime = 200; // 200us is the minimum w/o the delay needed to allow for DDR3L Change
+
+#ifdef ULT_FLAG
+ //
+ // Get the PCH RCBA from 0:1F:0:F0, and clear the Enable bit
+ //
+ Offset = MrcOemGetPcieDeviceAddress (0, 0x1F, 0, 0xF0);
+ MrcOemMmioRead (Offset, &Rcba, Inputs->PciEBaseAddress);
+ Rcba &= (~1);
+
+ if (Inputs->CpuModel == cmHSW_ULT) {
+ //
+ // Deassert DRAM RESET# via PCH regsiter on ULT
+ //
+ MrcOemMmioRead (R_PCH_RCRB_PM_CFG2, &PmCfg2, Rcba);
+ PmCfg2 |= B_PCH_RCRB_PM_CFG2_DRAM_RESET_CTL;
+ MrcOemMmioWrite (R_PCH_RCRB_PM_CFG2, PmCfg2, Rcba);
+ }
+#endif // ULT_FLAG
+
+ McInitStateG.Data = 0;
+ McInitStateG.Bits.pu_mrc_done = MCDECS_CR_MC_INIT_STATE_G_MCMAIN_pu_mrc_done_DEF;
+ McInitStateG.Bits.ddr_reset = MCDECS_CR_MC_INIT_STATE_G_MCMAIN_ddr_reset_DEF;
+ McInitStateG.Bits.refresh_enable = MCDECS_CR_MC_INIT_STATE_G_MCMAIN_refresh_enable_DEF;
+ McInitStateG.Bits.mc_init_done_ack = MCDECS_CR_MC_INIT_STATE_G_MCMAIN_mc_init_done_ack_DEF;
+ McInitStateG.Bits.mrc_done = MCDECS_CR_MC_INIT_STATE_G_MCMAIN_mrc_done_DEF;
+ McInitStateG.Bits.safe_sr = MCDECS_CR_MC_INIT_STATE_G_MCMAIN_safe_sr_DEF;
+ McInitStateG.Bits.HVM_Gate_DDR_Reset = MCDECS_CR_MC_INIT_STATE_G_MCMAIN_HVM_Gate_DDR_Reset_DEF;
+ McInitStateG.Bits.dclk_enable = MCDECS_CR_MC_INIT_STATE_G_MCMAIN_dclk_enable_DEF;
+ McInitStateG.Bits.reset_io = MCDECS_CR_MC_INIT_STATE_G_MCMAIN_reset_io_DEF;
+
+ MrcWriteCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG, McInitStateG.Data);
+
+ //
+ // Force CKE low
+ //
+ MrcWriteCR (MrcData, MCSCHEDS_CR_REUT_CH_MISC_CKE_CTRL_REG, MCSCHEDS_CR_REUT_CH_MISC_CKE_CTRL_CKE_Override_MAX);
+
+ //
+ // Assert DIMM reset signal - step 1
+ //
+#ifdef ULT_FLAG
+ if (Inputs->CpuModel == cmHSW_ULT) {
+ PmCfg2 &= ~(B_PCH_RCRB_PM_CFG2_DRAM_RESET_CTL);
+ MrcOemMmioWrite (R_PCH_RCRB_PM_CFG2, PmCfg2, Rcba);
+ } else
+#endif // ULT_FLAG
+ {
+ McInitStateG.Data = MrcReadCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG); // Read 'MC_Init_State_G' register.
+ McInitStateG.Bits.ddr_reset = 0;
+ MrcWriteCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG, McInitStateG.Data); // Assert DIMM reset
+ }
+
+ //
+ // Check and Switch DDR3 voltage
+ //
+ if ((Outputs->VddVoltage[Inputs->MemoryProfile] != VDD_INVALID) && (Outputs->VddVoltageDone == FALSE)) {
+ MrcOemVDDVoltageCheckAndSwitch (MrcData, Outputs->VddVoltage[Inputs->MemoryProfile], &VddSettleWaitTime);
+ }
+ //
+ // delay 200 micro sec as jedec ask
+ //
+ MrcWait (MrcData, VddSettleWaitTime * HPET_1US);
+
+ //
+ // De-asserted DIMM reset signal - step 2
+ //
+#ifdef ULT_FLAG
+ if (Inputs->CpuModel == cmHSW_ULT) {
+ PmCfg2 |= B_PCH_RCRB_PM_CFG2_DRAM_RESET_CTL;
+ MrcOemMmioWrite (R_PCH_RCRB_PM_CFG2, PmCfg2, Rcba);
+ } else
+#endif // ULT_FLAG
+ {
+ McInitStateG.Bits.ddr_reset = 1;
+ MrcWriteCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG, McInitStateG.Data); // De-assert DIMM reset
+ }
+
+ //
+ // delay 500 micro sec as jedec ask
+ //
+ MrcWait (MrcData, 500 * HPET_1US);
+
+ //
+ // Enable the DCLK - step 3
+ //
+ McInitStateG.Bits.dclk_enable = 1;
+ MrcWriteCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG, McInitStateG.Data);
+
+ //
+ // wait the 20 nano sec tCKSRX .
+ //
+ MrcWait (MrcData, 1 * HPET_MIN); // Minimum is 69.84 ns
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+
+ //
+ // Set the Valid CKE - step 4
+ //
+ Offset = 2 + MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_MISC_CKE_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, ControllerOut->Channel[Channel].ValidRankBitMask);
+ }
+ }
+ //
+ // wait minimum of Reset CKE Exit time, tXPR - Step 5
+ //
+ // Spec says max (tXS, 5 tCK). 5 tCK is 10 nsec and minimum using HPET is 69.64ns
+ //
+ MrcWait (MrcData, 1 * HPET_MIN); // Minimum is 69.84 ns
+
+ return;
+}
+
+#ifdef ULT_FLAG
+/**
+@brief
+ Perform JEDEC LPDDR3 DIMM reset sequence.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval mrcSuccess if succeeded
+**/
+MrcStatus
+MrcJedecResetLpddr3 (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcStatus Status;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ U32 Offset;
+ U8 Channel;
+ U8 Rank;
+ MCDECS_CR_MC_INIT_STATE_G_MCMAIN_STRUCT McInitStateG;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ ControllerOut = &Outputs->Controller[0];
+
+ //
+ // Force CKE low
+ //
+ MrcWriteCR (MrcData, MCSCHEDS_CR_REUT_CH_MISC_CKE_CTRL_REG, MCSCHEDS_CR_REUT_CH_MISC_CKE_CTRL_CKE_Override_MAX);
+
+ //
+ // Wait till voltages are stable
+ //
+ // @todo
+ // if ((Outputs->VddVoltage[Inputs->MemoryProfile] != VDD_INVALID) && (Outputs->VddVoltageDone == FALSE)) {
+ // MrcOemVDDVoltageCheckAndSwitch (MrcData, Outputs->VddVoltage[Inputs->MemoryProfile], VddSettleWaitTime);
+ // }
+ //
+ // Enable the DCLK
+ //
+ McInitStateG.Data = MrcReadCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG);
+ McInitStateG.Bits.dclk_enable = 1;
+ MrcWriteCR (MrcData, MCDECS_CR_MC_INIT_STATE_G_MCMAIN_REG, McInitStateG.Data);
+
+ //
+ // Wait 20ns before CKE goes high
+ //
+ MrcWait (MrcData, 1 * HPET_MIN); // Minimum is 69.84 ns
+
+ //
+ // Force CKE high on populated ranks
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+
+ //
+ // Set the Valid CKE
+ //
+ Offset = 2 + MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_MISC_CKE_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, ControllerOut->Channel[Channel].ValidCkeBitMask);
+ }
+ }
+ //
+ // Delay 200 micro sec per JEDEC requirement
+ // tINIT3 - minimum idle time after first CKE assertion
+ //
+ MrcWait (MrcData, 200 * HPET_1US);
+
+ //
+ // Send the RESET MRW command to populated ranks
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ if (Outputs->LpddrEctDone) {
+ //
+ // Issue a PRECHARGE ALL command to put all banks to idle state.
+ // MRW can only be issued when all banks are idle.
+ //
+ MrcIssuePrechargeAll (MrcData, Channel, 1 << Rank);
+ MrcWait (MrcData, 1 * HPET_1US);
+ }
+
+ Status = MrcIssueMrw (
+ MrcData,
+ Channel,
+ Rank,
+ 0x3F, // Address = 63
+ 0xFC, // Data is selected so that High and Low phases of CA[9:0] are equal
+ TRUE, // InitMrw
+ TRUE // ChipSelect2N
+ );
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ }
+ }
+ }
+ }
+ //
+ // tINIT5 - Maximum duration of device auto initialization = 10 us
+ //
+ MrcWait (MrcData, 10 * HPET_1US);
+
+ return mrcSuccess;
+}
+
+typedef struct _MRC_LPDDR_MR_DATA {
+ U8 Address;
+ U8 Data;
+} MRC_LPDDR_MR_DATA;
+
+/**
+@brief
+ Perform JEDEC LPDDR3 DIMM init sequence.
+ 1. ZQ Calibration
+ 2. Program MR2, MR1, MR3, MR11
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval mrcSuccess if succeeded
+**/
+MrcStatus
+MrcJedecInitLpddr3 (
+ IN MrcParameters *const MrcData
+ )
+{
+ MrcStatus Status;
+ MrcInput *Inputs;
+ MrcOutput *Outputs;
+ MrcDebug *Debug;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ U8 Channel;
+ U8 Rank;
+ U32 MrIndex;
+ U8 MrData;
+ U32 Index;
+ MRC_LPDDR_MR_DATA MrTable[] = {
+ { 2, 0x40 }, // MR2: nWRE = 1, RL & WL depend on frequency
+ { 1, 0x43 }, // MR1: BL = BL8, nWR = 12
+ { 3, 0x01 }, // MR3: DS = 34.3 Ohm
+ { 11, 0x00 } // MR11: ODT
+ };
+
+ Outputs = &MrcData->SysOut.Outputs;
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ ControllerOut = &Outputs->Controller[0];
+
+ //
+ // Issue ZQ Init calibration on all channels / ranks
+ //
+ Status = MrcIssueZQ (MrcData, 0x3, MRC_ZQ_INIT);
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+
+ if (Outputs->Frequency <= f800) {
+ MrData = 0x14;
+ } else if (Outputs->Frequency <= f1067) {
+ MrData = 0x16;
+ } else if (Outputs->Frequency <= f1200) {
+ MrData = 0x17;
+ } else if (Outputs->Frequency <= f1333) {
+ MrData = 0x18;
+ } else if (Outputs->Frequency <= f1600) {
+ MrData = 0x1A;
+ } else {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "MrcJedecInitLpddr3: Invalid LPDDR frequency!\n");
+ return mrcFrequencyError;
+ }
+
+ MrTable[0].Data = MrData; // MR2
+
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (!MrcChannelExist (Outputs, Channel)) {
+ continue;
+ }
+
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel) == 0) {
+ continue;
+ }
+
+ if ((Rank == 0) && Inputs->LpddrDramOdt) {
+ //
+ // Enable PD Control on Rank 0 only if we have 2 ranks and ODT is used
+ //
+ if (MrcRankInChannelExist (MrcData, 1, Channel)) {
+ MrTable[3].Data = 0x06; // MR11, ODT Enabled, PD Control = 1
+ } else {
+ MrTable[3].Data = 0x02; // MR11, ODT Enabled, PD Control = 0
+ }
+ } else {
+ MrTable[3].Data = 0; // MR11, ODT Disabled
+ }
+
+ //
+ // Send out all the MR commands from the table
+ //
+ for (Index = 0; Index < sizeof (MrTable) / sizeof (MrTable[0]); ++Index) {
+ MrIndex = MrTable[Index].Address;
+
+ if (Outputs->RestoreMRs) {
+ if (MrIndex < MAX_MR_IN_DIMM) {
+ MrData = (U8) ChannelOut->Dimm[0].Rank[Rank % 2].MR[MrIndex];
+ } else {
+ MrData = (U8) ChannelOut->Dimm[0].Rank[Rank % 2].MR11;
+ }
+ } else {
+ //
+ // Save the MR value in the global struct
+ //
+ MrData = MrTable[Index].Data;
+
+ if (MrIndex < MAX_MR_IN_DIMM) {
+ ChannelOut->Dimm[0].Rank[Rank % 2].MR[MrIndex] = MrData;
+ } else if (MrIndex == mrMR11) {
+ ChannelOut->Dimm[0].Rank[Rank % 2].MR11 = MrData;
+ }
+ }
+
+ Status = MrcIssueMrw (
+ MrcData,
+ Channel,
+ Rank,
+ MrIndex,
+ MrData,
+ FALSE, // InitMrw
+ FALSE // ChipSelect2N
+ );
+ if (Status != mrcSuccess) {
+ return Status;
+ }
+ }
+ //
+ // @todo: Read MR8 (compare to SPD values) - optional step
+ //
+ } // for Rank
+ } // for Channel
+
+ Outputs->LpddrJedecInitDone = TRUE;
+ Outputs->RestoreMRs = TRUE;
+ return Status;
+}
+
+#endif // ULT_FLAG
+
+/**
+@brief
+ Wait in a loop until the first RCOMP has been completed.
+ MRC should wait until this bit is set before executing any DDR command.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval mrcDeviceBusy - On Rcomp completion timeout.
+ @retval mrcSuccess - On Rcomp completion.
+**/
+MrcStatus
+CheckFirstRcompDone (
+ IN MrcParameters *const MrcData
+ )
+{
+ MCDECS_CR_RCOMP_TIMER_MCMAIN_STRUCT CrRcompTimer;
+ BOOL Busy;
+ U32 Timeout;
+
+ Timeout = (U32) MrcGetCpuTime () + 10000; // 10 seconds timeout
+
+ do {
+ CrRcompTimer.Data = MrcReadCR (MrcData, MCDECS_CR_RCOMP_TIMER_MCMAIN_REG);
+ Busy = (0 == CrRcompTimer.Bits.First_Rcomp_done) ? TRUE : FALSE;
+ } while (Busy && ((U32) MrcGetCpuTime () < Timeout));
+
+ return ((Busy) ? mrcDeviceBusy : mrcSuccess);
+}
+
+/**
+@brief
+ Perform the required steps to exit self refresh in S3/Warm reset:
+ Download the Read Reg File for all populated ranks.
+ Assert CKE for all the ranks present to pull DIMMs out of Self-Refresh.
+ Issue long ZQ Calibration for all the ranks present in the channel.
+ Set REUT to normal mode for all channels.
+ Set the Power Down Config Register.
+
+ @param[in] MrcData - Include all MRC global data.
+
+ @retval mrcSuccess
+**/
+MrcStatus
+MrcSelfRefreshExit (
+ IN MrcParameters *const MrcData
+ )
+{
+ const MrcOutput *Outputs;
+ U32 Offset;
+ U8 Channel;
+ U8 Rank;
+ DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0;
+
+ Outputs = &MrcData->SysOut.Outputs;
+ //
+ // Download Read Reg File for all populated ranks per channel
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ if (MrcChannelExist (Outputs, Channel)) {
+ Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATA0CH1_CR_DDRCRDATACONTROL0_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ DdrCrDataControl0.Data = MrcReadCR (MrcData, Offset);
+
+ Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG +
+ ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel);
+ DdrCrDataControl0.Bits.ReadRFRd = 1;
+ for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) {
+ if (MrcRankInChannelExist (MrcData, Rank, Channel)) {
+ DdrCrDataControl0.Bits.ReadRFRank = Rank;
+ MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data);
+ }
+ }
+ }
+ }
+
+ //
+ // Pull the DIMMs out of self refresh by asserting CKE high.
+ // The time needed to stabilize the DCLK (~6uS) should be covered
+ // by the last 43 MC CR restores after restoring MC_INIT_STATE
+ // in MrcRestoreTrainingValues().
+ //
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++){
+ if (MrcChannelExist (&MrcData->SysOut.Outputs, Channel)) {
+ Offset = 2 + MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG +
+ ((MCHBAR_CH1_CR_REUT_CH_MISC_CKE_CTRL_REG - MCHBAR_CH0_CR_REUT_CH_MISC_CKE_CTRL_REG) * Channel);
+ MrcWriteCR8 (MrcData, Offset, Outputs->Controller[0].Channel[Channel].ValidRankBitMask);
+ }
+ }
+
+ //
+ // Issue ZQ Long on both channels / all ranks
+ //
+ if (MrcIssueZQ (MrcData, 0x3, MRC_ZQ_LONG) != mrcSuccess) {
+ MRC_DEBUG_MSG (&MrcData->SysIn.Inputs.Debug, MSG_LEVEL_WARNING, "\nZQ Long failed during S3/warm reset\n");
+ }
+
+ return mrcSuccess;
+}