diff options
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcDdr3.c')
-rw-r--r-- | ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcDdr3.c | 1572 |
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 |