diff options
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Api/MrcMemoryScrub.c')
-rw-r--r-- | ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Api/MrcMemoryScrub.c | 678 |
1 files changed, 678 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Api/MrcMemoryScrub.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Api/MrcMemoryScrub.c new file mode 100644 index 0000000..e140250 --- /dev/null +++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Api/MrcMemoryScrub.c @@ -0,0 +1,678 @@ +/** @file + This file contains the memory scrubbing and alias checking functions. + +@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 "MrcTypes.h" +#include "MrcApi.h" +#include "MrcMemoryScrub.h" + +/** +@brief + This function sets all the memory to a known value when ECC is enabled and + either we are not in warm boot or we are in warm boot and TXT is set. + + @param[in] MrcData - Include all MRC global data. + + @retval mrcSuccess if the clean succeeded, otherwise an error status. +**/ +MrcStatus +MrcEccClean ( + IN MrcParameters *const MrcData + ) +{ + const MrcInput *Inputs; + const MrcDebug *Debug; + const U8 WrapCarryEn[MrcReutFieldMax] = {0, 0, 0, 0}; + const U8 WrapTriggerEn[MrcReutFieldMax] = {0, 0, 1, 0}; // Trigger Stop on Bank Wrap + const U8 AddrInvertEn[MrcReutFieldMax] = {0, 0, 0, 0}; + MrcControllerOut *ControllerOut; + MrcDimmOut *DimmOut; + MrcOutput *Outputs; + MrcStatus Status; + U32 ReutSubSeqCtl0Data; + U8 Pattern; + U8 PMask; + U8 Rank; + U8 Bank; + U8 Channel; + U8 ActiveChBitMask; + U8 RankToDimm; + MRC_REUTAddress ReutAddress; + MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_STRUCT ReutChSeqCfg; + MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_STRUCT ReutChPatWdbCl; + + Inputs = &MrcData->SysIn.Inputs; + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + Debug = &Inputs->Debug; + Status = mrcSuccess; + Pattern = 0; + PMask = 0; + MrcOemMemorySet ((U8 *) &ReutAddress, 0, sizeof (ReutAddress)); + + if ((Outputs->EccSupport == TRUE) || (Inputs->OemCleanMemory == TRUE)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Scrubbing Memory\n"); + ReutAddress.IncVal[MrcReutFieldCol] = 1; // Each write is 1 cache line which is 8 column addresses worth of data. + ReutAddress.IncVal[MrcReutFieldRow] = 1; // Walk through rows 1 at a time. + + // + // Setup the first cache line to zeros. + // + WriteWDBFixedPattern (MrcData, &Pattern, &PMask, 1, 0); + + // + // Setup Reut for both channels. + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!MrcChannelExist (Outputs, Channel)) { + continue; + } + + // + // Write initial Reut Address Values. + // + MrcProgramSequenceAddress ( + MrcData, + Channel, + ReutAddress.Start, + NULL, // Stop + ReutAddress.Order, + ReutAddress.IncRate, + ReutAddress.IncVal, + WrapTriggerEn, + WrapCarryEn, + AddrInvertEn, + 0, // AddrInvertRate + FALSE + ); + + // + // Set Reut to Write + // + ReutChSeqCfg.Data = 0; + ReutChSeqCfg.Bits.Initialization_Mode = REUT_Testing_Mode; + ReutChSeqCfg.Bits.Global_Control = 1; + ReutChSeqCfg.Bits.Start_Test_Delay = 2; + ReutChSeqCfg.Bits.Address_Update_Rate_Mode = 1; + ReutChSeqCfg.Bits.Stop_Base_Sequence_On_Wrap_Trigger = 1; + MrcWriteCR64 ( + MrcData, + 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 + ); + + // + // Program new loopcount registers based on stepping. + // + if ( + (Inputs->CpuModel == cmHSW && Inputs->CpuStepping >= csHswC0) || + (Inputs->CpuModel == cmCRW && Inputs->CpuStepping >= csCrwC0) || + (Inputs->CpuModel == cmHSW_ULT && Inputs->CpuStepping >= csHswUltC0) || + (Inputs->CpuModel == cmBDW) + ) { + MrcWriteCR ( + MrcData, + 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), + 0 + ); + } + + // + // Set up the Subsequence control. + // + ReutSubSeqCtl0Data = 0; + SetSubsequenceType (MrcData, &ReutSubSeqCtl0Data, BWr); + MrcWriteCR ( + MrcData, + 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), + ReutSubSeqCtl0Data + ); + + // + // Program Write Data Buffer Control. Since we are using 1 cache line, we only need + // to set the increment scale to linear. + // + ReutChPatWdbCl.Data = 0; + ReutChPatWdbCl.Bits.WDB_Increment_Scale = 1; + MrcWriteCR ( + MrcData, + 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), + ReutChPatWdbCl.Data + ); + } + + // + // Run Per Rank + // + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if ((MRC_BIT0 << Rank) & Outputs->ValidRankMask) { + // + // Determine the Active Channels + // + ActiveChBitMask = 0; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ActiveChBitMask |= SelectReutRanks (MrcData, Channel, MRC_BIT0 << Rank, 0); + } + + // + // Counter registers are not large enough to walk through 1 Rank for LPDDR3 support due to 11 column bits. + // Must walk through memory on a bank loop. + // + for (Bank = 0; Bank < 8; Bank++) { + ReutAddress.Start[MrcReutFieldBank] = Bank; + ReutAddress.Stop[MrcReutFieldBank] = Bank; + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (ActiveChBitMask & (MRC_BIT0 << Channel)) { + // + // Update Bank start/stop + // + RankToDimm = RANK_TO_DIMM_NUMBER (Rank); + DimmOut = &ControllerOut->Channel[Channel].Dimm[RankToDimm]; + ReutAddress.Stop[MrcReutFieldRow] = (U16) DimmOut->RowSize - 1; + ReutAddress.Stop[MrcReutFieldCol] = DimmOut->ColumnSize - WDB_CACHE_LINE_SIZE; + ReutAddress.IncRate[MrcReutFieldRow] = DimmOut->ColumnSize / WDB_CACHE_LINE_SIZE; + MrcProgramSequenceAddress ( + MrcData, + Channel, + ReutAddress.Start, + ReutAddress.Stop, + NULL, // Order + ReutAddress.IncRate, + NULL, // IncVal + NULL, // WrapTriggerEn + NULL, // WrapCarryEn + NULL, // AddrInvertEn + 0, // AddrInvertRate + FALSE + ); + } + } + + // + // Run the test + // + Status = MrcRunMemoryScrub (MrcData, ActiveChBitMask); + if (Status != mrcSuccess) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rank %d error!\n", Rank); + break; + } + } + } + + if (Status != mrcSuccess) { + break; + } + } + + // + // Return to normal operation mode + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + ReutChSeqCfg.Data = 0; + ReutChSeqCfg.Bits.Initialization_Mode = NOP_Mode; + MrcWriteCR ( + MrcData, + 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), + (U32) ReutChSeqCfg.Data + ); + } + } + + if (Status != mrcSuccess) { + MrcOemDebugHook (MrcData, MRC_ECC_CLEAN_ERROR); + } + } + + return Status; +} + +/** +@brief + This function performs a memory alias check. + + @param[in] MrcData - The global host structure + + @retval mrcSuccess or error value. +**/ +MrcStatus +MrcAliasCheck ( + IN OUT MrcParameters *const MrcData + ) +{ + const MrcDebug *Debug; + const MrcInput *Inputs; + const U8 WrapCarryEn[MrcReutFieldMax] = {0, 0, 0, 0}; + const U8 WrapTriggerEn[MrcReutFieldMax] = {0, 0, 0, 0}; // Trigger Stop on Bank Wrap + const U8 AddrInvertEn[MrcReutFieldMax] = {0, 0, 0, 0}; + const U16 SdramCapacityTable[] = {256, 512, 1024, 2048, 4096, 8192, 16384, 32768}; // Mb + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcDimmOut *DimmOut; + MrcStatus Status; + MrcDdrType DdrType; + BOOL InvalidSpdAddressingCapacity; + U32 SdramAddressingCapacity; + U32 CrOffset; + U16 SdramCapacity; + U16 WritesPerPage; + U16 ColumnIncValUnaligned; + U8 Rank; + U8 RankToDimm; + U8 Channel; + U8 ActiveChBitMask; + MRC_REUTAddress ReutAddress; + MCDFXS_CR_REUT_CH_SEQ_CFG_MCMAIN_0_STRUCT ReutChSeqCfg; + MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_CTRL_STRUCT ReutChPatWdbCl; + MCHBAR_CH0_CR_REUT_CH_PAT_WDB_CL_MUX_CFG_STRUCT ReutChPatWdbClMuxCfg; + MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_STRUCT ReutSubSeqCtl; + MCDECS_CR_MAD_DIMM_CH0_MCMAIN_STRUCT MadDimmCh[MAX_CHANNEL]; + MCDECS_CR_MAD_DIMM_CH0_MCMAIN_STRUCT MadDimm; + + Outputs = &MrcData->SysOut.Outputs; + Inputs = &MrcData->SysIn.Inputs; + Debug = &Inputs->Debug; + ControllerOut = &Outputs->Controller[0]; + Status = mrcSuccess; + InvalidSpdAddressingCapacity = FALSE; + DdrType = Outputs->DdrType; + // + // Check to see if the SDRAM Addressing * Primary Bus Width == SDRAM capacity. + // If not, report an alias and exit. + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank += MAX_RANK_IN_DIMM) { + if (MrcRankInChannelExist (MrcData, Rank, Channel)) { + RankToDimm = RANK_TO_DIMM_NUMBER (Rank); + DimmOut = &ControllerOut->Channel[Channel].Dimm[RankToDimm]; + SdramAddressingCapacity = (DimmOut->ColumnSize * DimmOut->RowSize); + // + // Since the minimum number of row and coulmn bits are 12 and 9 respectivly, + // we can shift by 20 to get the result in Mb before multiplying by the bus width. + // + SdramAddressingCapacity = SdramAddressingCapacity >> 20; + SdramAddressingCapacity *= DimmOut->Banks; + SdramAddressingCapacity *= (DimmOut->BankGroups > 0) ? DimmOut->BankGroups : 1; + SdramAddressingCapacity *= DimmOut->SdramWidth; + SdramCapacity = SdramCapacityTable[DimmOut->DensityIndex]; + if (SdramCapacity != SdramAddressingCapacity) { + InvalidSpdAddressingCapacity = TRUE; + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_ERROR, + "ERROR: Channel %d Dimm %d SPD SDRAM Adressing Capacity(0x%xMb) does not match SDRAM Capacity(0x%xMb)\nPlease verify:\n", + Channel, + RankToDimm, + SdramAddressingCapacity, + SdramCapacity + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_ERROR, + " Capacity: 0x%x\n RowSize: 0x%x\n ColumnSize: 0x%x\n Banks: 0x%x\n Bank Groups: 0x%x\n Device Width: 0x%x\n", + SdramCapacity, + DimmOut->RowSize, + DimmOut->ColumnSize, + DimmOut->Banks, + DimmOut->BankGroups, + DimmOut->SdramWidth + ); + break; + } + } + } + } + // + // Since we will not hang the system, signal that an Alias could exist and return mrcSuccess. + // + if (TRUE == InvalidSpdAddressingCapacity) { + Outputs->SpdSecurityStatus = MrcSpdStatusAliased; + return Status; + } + + if ((Inputs->CpuModel == cmHSW && Inputs->CpuStepping >= csHswB0) || (Inputs->CpuModel != cmHSW)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Performing Alias Test\n"); + MrcOemMemorySet ((U8 *) &ReutAddress, 0, sizeof (ReutAddress)); + + // + // Determine if we are ECC enabled. If so, disable ECC since the ECC scrub has yet to occur. + // + if (Outputs->EccSupport == TRUE) { + MRC_DEBUG_MSG(Debug, MSG_LEVEL_NOTE, "ECC enabled. Disabling ECC for the test. Must scrub after this!!!\n"); + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + CrOffset = MCDECS_CR_MAD_DIMM_CH0_MCMAIN_REG + + ((MCDECS_CR_MAD_DIMM_CH1_MCMAIN_REG - MCDECS_CR_MAD_DIMM_CH0_MCMAIN_REG) * Channel); + + MadDimmCh[Channel].Data = MrcReadCR (MrcData, CrOffset); + MadDimm.Data = MadDimmCh[Channel].Data; + MadDimm.Bits.ECC = emNoEcc; + MrcWriteCR (MrcData, CrOffset, MadDimm.Data); + } + } + } + + // + // Test Initialization + // + // + // Start with IncRate = 3 so we have 4 column writes per page. This will change with Column Size. + // Must have 4 (reg + 1) writes to move to the next LFSR code for unique values. + // + ReutAddress.IncRate[MrcReutFieldRow] = 3; + // + // IncVal[Col] is chosen to be 1/4 of the minimum column supported to get 4 writes per page. + // Each write is 1 cache line (8 column addresses worth of data). + // IncVal is on a cache line basis when programmed. Account for this here ( >> 3). + // + ColumnIncValUnaligned = MRC_BIT10 >> 2; // divide by 4 + ReutAddress.IncVal[MrcReutFieldCol] = ColumnIncValUnaligned >> 3; // cache line shift + // + // Smallest Row address size is 2^12, but Row_Base_Address_Increment is a 12-bit signed field [0-11]. + // Thus we have to increment by 2^10. + // + ReutAddress.IncVal[MrcReutFieldRow] = MRC_BIT10; + ReutAddress.Stop[MrcReutFieldCol] = 24; // 4 ([0-3] << 3) column writes before wrapping + ReutAddress.Start[MrcReutFieldBank] = 1; + ReutAddress.Stop[MrcReutFieldBank] = 1; + + // + // Setup Reut all present channels. + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!MrcChannelExist (Outputs, Channel)) { + continue; + } + + // + // Write initial Reut Address Values. + // + MrcProgramSequenceAddress ( + MrcData, + Channel, + ReutAddress.Start, + NULL, // Stop + ReutAddress.Order, + ReutAddress.IncRate, + ReutAddress.IncVal, + WrapTriggerEn, + WrapCarryEn, + AddrInvertEn, + 0, + FALSE + ); + + // + // Set Reut to Write + // + ReutChSeqCfg.Data = 0; + ReutChSeqCfg.Bits.Initialization_Mode = REUT_Testing_Mode; + ReutChSeqCfg.Bits.Global_Control = 1; + ReutChSeqCfg.Bits.Start_Test_Delay = 2; + ReutChSeqCfg.Bits.Subsequence_End_Pointer = 1; + + if ( + (Inputs->CpuModel == cmHSW && Inputs->CpuStepping < csHswC0) || + (Inputs->CpuModel == cmCRW && Inputs->CpuStepping < csCrwC0) || + (Inputs->CpuModel == cmHSW_ULT && Inputs->CpuStepping < csHswUltC0) || + (Inputs->CpuModel == cmBDW) + ) { + ReutChSeqCfg.Bits.Loopcount = 1; + } else { + CrOffset = 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, CrOffset, 1); + } + + CrOffset = 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, + CrOffset, + ReutChSeqCfg.Data + ); + + // + // Program Write Data Buffer Control. + // + ReutChPatWdbCl.Data = 0; + ReutChPatWdbCl.Bits.WDB_Increment_Scale = 1; + MrcWriteCR ( + MrcData, + 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), + ReutChPatWdbCl.Data + ); + + ReutChPatWdbClMuxCfg.Bits.ECC_Data_Source_Sel = 1; + ReutChPatWdbClMuxCfg.Bits.Mux2_Control = LFSRMode; + ReutChPatWdbClMuxCfg.Bits.Mux1_Control = LFSRMode; + ReutChPatWdbClMuxCfg.Bits.Mux0_Control = LFSRMode; + CrOffset = 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, CrOffset, ReutChPatWdbClMuxCfg.Data); + } + + // + // Run test Per Dimm + // + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank += MAX_RANK_IN_DIMM){ + if ((MRC_BIT0 << Rank) & Outputs->ValidRankMask) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Testing Dimm %d\n", Rank / 2); + // + // Determine Active Channels + // + ActiveChBitMask = 0; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ActiveChBitMask |= SelectReutRanks (MrcData, Channel, MRC_BIT0 << Rank, 0); + // + // Update Rank stop address based on DIMM SPD if Active. + // + if (ActiveChBitMask & (MRC_BIT0 << Channel)) { + RankToDimm = RANK_TO_DIMM_NUMBER (Rank); + DimmOut = &ControllerOut->Channel[Channel].Dimm[RankToDimm]; + // + // Since we're counting cache lines and won't wrap the row address, + // program Row Stop to RowSize - 1 to avoid resetting the current address. + // Column must wrap. The wrap occurs on the increment which is after writing, + // to that address. Thus, we set wrap to be the last accessed column. + // + WritesPerPage = DimmOut->ColumnSize / ColumnIncValUnaligned; // Should be >= 4 + ReutAddress.Stop[MrcReutFieldRow] = (U16) DimmOut->RowSize - 1; + ReutAddress.Stop[MrcReutFieldCol] = DimmOut->ColumnSize - ColumnIncValUnaligned; + ReutAddress.IncRate[MrcReutFieldRow] = WritesPerPage - 1; // IncRate is +1 the programmed value + + MrcProgramSequenceAddress ( + MrcData, + Channel, + NULL, + ReutAddress.Stop, + NULL, + ReutAddress.IncRate, + NULL, + NULL, + NULL, + NULL, + 0, + FALSE + ); + // + // Set up the Subsequence control. + // + CrOffset = 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); + // + // @todo: Review that the settings programmed here are common between the steppings. + // + ReutSubSeqCtl.Data = 0; + ReutSubSeqCtl.Bits.Subsequence_Type = BWr; + // + // Instead of matching wrap addresses, we will stop on 1 less cache line write from the top. + // This works because when aliasing occurs, the physical addressing size must double for row/col. + // + ReutSubSeqCtl.Bits.Number_of_Cachelines = MrcLog2 (((DimmOut->RowSize / MRC_BIT10) * WritesPerPage) - 1); + MrcWriteCR ( + MrcData, + CrOffset, + ReutSubSeqCtl.Data + ); + + CrOffset += MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_1_REG - MCDFXS_CR_REUT_CH0_SUBSEQ_CTL_MCMAIN_0_REG; + ReutSubSeqCtl.Bits.Reset_Current_Base_Address_To_Start = 1; + ReutSubSeqCtl.Bits.Subsequence_Type = BRd; + MrcWriteCR ( + MrcData, + CrOffset, + ReutSubSeqCtl.Data + ); + } + } + + // + // Run the test + // + Status = MrcRunMemoryScrub (MrcData, ActiveChBitMask); + if (Status != mrcSuccess) { + break; + } + } + } + + if (Outputs->EccSupport == TRUE) { + MRC_DEBUG_MSG(Debug, MSG_LEVEL_NOTE, "ReEnabling ECC Logic. Must scrub after this!\n"); + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + CrOffset = MCDECS_CR_MAD_DIMM_CH0_MCMAIN_REG + + ((MCDECS_CR_MAD_DIMM_CH1_MCMAIN_REG - MCDECS_CR_MAD_DIMM_CH0_MCMAIN_REG) * Channel); + + MrcWriteCR (MrcData, CrOffset, MadDimmCh[Channel].Data); + } + } + } + // + // Wait 4 usec after enabling the ECC IO, needed by HW + // + MrcWait (MrcData, 4 * HPET_1US); + + // + // Return to normal operation mode + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + ReutChSeqCfg.Data = 0; + ReutChSeqCfg.Bits.Initialization_Mode = NOP_Mode; + MrcWriteCR ( + MrcData, + 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), + (U32) ReutChSeqCfg.Data + ); + } + } + } + + if (mrcSuccess != Status) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "*** Alias Detected! See REUT Error above. ***\n"); + Outputs->SpdSecurityStatus = MrcSpdStatusAliased; + Status = mrcSuccess; + } + + return Status; +} + +/** +@brief + This function runs the srcubbing test reporting any timeouts/errors. + + @param[in] MrcData - The global host structure + @param[in] ChBitMask - Bitmask of channels the test is run on. + + @retval mrcSuccess or error value. +**/ +MrcStatus +MrcRunMemoryScrub ( + IN OUT MrcParameters *const MrcData, + IN U8 ChBitMask + ) +{ + const MrcDebug *Debug; + MrcStatus Status; + U8 ErrorStatus; + U8 TestDoneStatus; + MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_STRUCT ReutGlobalCtl; + MCDFXS_CR_REUT_GLOBAL_ERR_MCMAIN_STRUCT ReutGlobalErr; + U32 Timer; + + Status = mrcSuccess; + Debug = &MrcData->SysIn.Inputs.Debug; + + // + // Setup Timer and run the test + // + Timer = (U32) MrcGetCpuTime() + 10000; // 10 Second timeout + ReutGlobalCtl.Data = 0; + ReutGlobalCtl.Bits.Global_Start_Test = 1; + ReutGlobalCtl.Bits.Global_Clear_Errors = 1; + ReutGlobalCtl.Bits.Global_Stop_Test_On_Any_Error = NSOE; + MrcWriteCR (MrcData, MCDFXS_CR_REUT_GLOBAL_CTL_MCMAIN_REG, ReutGlobalCtl.Data); + + // + // Wait until Channel test done status matches ChbitMask or TimeoutCounter value reaches 0; + // + 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) && ((U32) MrcGetCpuTime () < Timer)); + + if ((TestDoneStatus & ChBitMask) != ChBitMask) { + Status = mrcDeviceBusy; + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_ERROR, + "Timeout occured while running the test: ReutGlobalErr: 0x%X.\n", + ReutGlobalErr.Data + ); + } + + ErrorStatus = (U8) ((ReutGlobalErr.Bits.Channel_Error_Status_1 << 1) | ReutGlobalErr.Bits.Channel_Error_Status_0); + if (ErrorStatus & ChBitMask) { + Status = mrcReutSequenceError; + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_ERROR, + "REUT Error: Channel(s):%s%s\n", + (ReutGlobalErr.Bits.Channel_Error_Status_0 == 1) ? " 0" : "", + (ReutGlobalErr.Bits.Channel_Error_Status_1 == 1) ? " 1" : "" + ); + } + + return Status; +} + |