summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcDdr3.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcDdr3.c')
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcDdr3.c1572
1 files changed, 1572 insertions, 0 deletions
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