summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/SpdProcessing/MrcSpdProcessing.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/SpdProcessing/MrcSpdProcessing.c')
-rw-r--r--ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/SpdProcessing/MrcSpdProcessing.c4275
1 files changed, 4275 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/SpdProcessing/MrcSpdProcessing.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/SpdProcessing/MrcSpdProcessing.c
new file mode 100644
index 0000000..1717d48
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/SpdProcessing/MrcSpdProcessing.c
@@ -0,0 +1,4275 @@
+/** @file
+ By passing in a SPD data structure and platform support values, an output
+ structure is populated with DIMM configuration information.
+
+@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 "MrcSpdProcessing.h"
+
+#ifdef MRC_DEBUG_PRINT
+const char UnknownString[] = "unknown";
+const char Ddr3String[] = "DDR3";
+const char Ddr4String[] = "DDR4";
+const char RdimmString[] = "RDIMM";
+const char UdimmString[] = "UDIMM";
+const char SodimmString[] = "SO-DIMM";
+const char Sodimm72String[] = "72 bit SO-DIMM";
+const char StdString[] = "Standard";
+#if (SUPPORT_XMP == SUPPORT)
+const char Xmp1String[] = "XMP1";
+const char Xmp2String[] = "XMP2";
+const char XpString[] = " XMP profile %u is %sabled and recommended channel config: %u DIMM per channel\n";
+#endif // SUPPORT_XMP
+const char ErrorString[] = "ERROR: Unsupported ";
+const char SpdValString[] = "SPD value: ";
+const char IsSupString[] = " is supported";
+const char NotSupString[] = " is not supported";
+const char TimeBaseString[] = "Timebase (MTB/FTB)";
+const char tAAString[] = "CAS Latency Time (tAAmin)";
+const char tCKString[] = "SDRAM Cycle Time (tCKmin)";
+const char tWRString[] = "Write recovery time (tWRmin)";
+const char tRCDString[] = "RAS# to CAS# delay time (tRCDmin)";
+const char tRRDString[] = "Row active to row active delay time (tRRDmin)";
+const char tRPString[] = "Row precharge delay time (tRPmin)";
+#if (SUPPORT_LPDDR3 == SUPPORT)
+const char Lpddr3String[] = "LPDDR3";
+const char tRPabString[] = "Row precharge delay time for all banks (tRPab)";
+#endif // SUPPORT_LPDDRn
+const char tRASString[] = "Active to precharge delay time (tRASmin)";
+const char tRCString[] = "Active to active/refresh delay time (tRCmin)";
+const char tRFCString[] = "Refresh recovery delay time (tRFCmin)";
+const char tWTRString[] = "Internal write to read command delay time (tWTRmin)";
+const char tRTPString[] = "Internal read to precharge delay time (tRTPmin)";
+const char tFAWString[] = "Active to active/refresh delay time (tFAWmin)";
+const char tREFIString[] = "Average Periodic Refresh Interval (tREFImin)";
+const char tCWLString[] = "CAS Write Latency (tCWLmin)";
+const char NmodeString[] = "Command rate mode (Nmode)";
+const char VddString[] = "Module voltage VDD (mVolts)";
+const char BestCaseString[] = "Best case value for profiles 0-";
+const char ProfileString[] = "Profile";
+const char HeaderString[] = "Profile Controller Channel Dimm Value";
+const char RrcString[][3] = {
+ " A", " B", " C", " D", " E", " F", " G", " H", " J", " K",
+ " L", " M", " N", " P", " R", " T", " U", " V", " W", " Y",
+ "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AJ", "AK",
+ "AL", "ZZ", "AM", "AN", "AP", "AR", "AT", "AU", "AV", "AW",
+ "AY", "BA", "BB", "BC", "BD", "BE", "BF", "BG", "BH", "BJ",
+ "BK", "BL", "BM", "BN", "BP", "BR", "BT", "BU", "BV", "BW",
+ "BY", "CA", "CB", "ZZ"};
+#endif // MRC_DEBUG_PRINT
+
+const TRangeTable Range[] = {
+ { 0xFFFFFFFF, fUnSupport, (0 << MRC_REF_CLOCK_133) | (0 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_800_TCK_MIN, f800, (1 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_1000_TCK_MIN, f1000, (0 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_1067_TCK_MIN, f1067, (1 << MRC_REF_CLOCK_133) | (0 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_1200_TCK_MIN, f1200, (0 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_1333_TCK_MIN, f1333, (1 << MRC_REF_CLOCK_133) | (0 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_1400_TCK_MIN, f1400, (0 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_1600_TCK_MIN, f1600, (1 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_1800_TCK_MIN, f1800, (0 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_1867_TCK_MIN, f1867, (1 << MRC_REF_CLOCK_133) | (0 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_2000_TCK_MIN, f2000, (0 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_2133_TCK_MIN, f2133, (1 << MRC_REF_CLOCK_133) | (0 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_2200_TCK_MIN, f2200, (0 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_2400_TCK_MIN, f2400, (1 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_2600_TCK_MIN, f2600, (0 << MRC_REF_CLOCK_133) | (1 << MRC_REF_CLOCK_100) },
+ { MRC_DDR3_2667_TCK_MIN, f2667, (1 << MRC_REF_CLOCK_133) | (0 << MRC_REF_CLOCK_100) },
+ { 0, fNoInit, (0 << MRC_REF_CLOCK_133) | (0 << MRC_REF_CLOCK_100) }
+};
+
+const SupportTable PlatformSupport = {
+ {TRAD_SUPPORT_LPDDR3, ULT_SUPPORT_LPDDR3 },
+ {TRAD_SUPPORT_COLUMN_10, ULT_SUPPORT_COLUMN_10},
+ {TRAD_SUPPORT_COLUMN_11, ULT_SUPPORT_COLUMN_11},
+ {TRAD_SUPPORT_COLUMN_12, ULT_SUPPORT_COLUMN_12},
+ {TRAD_VDDMINPOSSIBLE, ULT_VDDMINPOSSIBLE },
+ {TRAD_VDDMAXPOSSIBLE, ULT_VDDMAXPOSSIBLE }
+};
+
+/**
+ @brief
+ Calculate the memory clock value from the current memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Frequency - Memory frequency to convert.
+ @param[out] tCKminIndex - Pointer to the chosen table index.
+
+ @retval Returns the tCK value for the given frequency.
+**/
+U32
+ConvertFreq2Clock (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcFrequency Frequency,
+ OUT S32 *const tCKminIndex
+ )
+{
+ U32 tCKminActual;
+ S32 Index;
+
+ tCKminActual = MRC_DDR3_800_TCK_MIN;
+ for (Index = 0; (U32) Index < (sizeof (Range) / sizeof (TRangeTable)); Index++) {
+ if (Frequency == Range[Index].Frequency) {
+ tCKminActual = Range[Index].tCK;
+ break;
+ }
+ }
+ if (tCKminIndex != NULL) {
+ *tCKminIndex = Index;
+ }
+ return (tCKminActual);
+}
+
+/**
+ @brief
+ Calculate the memory frequency from the memory clock value.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] RefClk - The memory reference clock.
+ @param[in] tCKmin - The tCKmin value to convert.
+ @param[out] tCKminIndex - Pointer to the chosen table index.
+
+ @retval Returns the tCK value for the given frequency.
+**/
+static
+U32
+ConvertClock2Freq (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcRefClkSelect RefClk,
+ IN const U32 tCKmin,
+ OUT S32 *const tCKminIndex
+ )
+{
+ MrcOutput *Outputs;
+ MrcFrequency Frequency;
+ U32 Index;
+
+ Outputs = &MrcData->SysOut.Outputs;
+
+ //
+ // Convert tCK value to the nearest frequency value.
+ // Then find slowest valid frequency for the given reference clock.
+ //
+ Frequency = fNoInit;
+ for (Index = 0; Index < (sizeof (Range) / sizeof (TRangeTable)) - 1; Index++) {
+ if ((tCKmin <= Range[Index].tCK) && (tCKmin > Range[Index + 1].tCK)) {
+ Frequency = Range[Index].Frequency;
+ break;
+ }
+ }
+
+ while (Index) {
+ if ((Range[Index].RefClkFlag & (1 << RefClk)) == 0) {
+ Frequency = Range[--Index].Frequency;
+ } else {
+ break;
+ }
+ }
+ if (tCKminIndex != NULL) {
+ *tCKminIndex = Index;
+ }
+ return (Frequency);
+}
+
+/**
+ @brief
+ Determine if the DIMM slot is filled.
+ If the SPD structure is all zero's, then DIMM is not present.
+
+ @param[in] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in] Size - Amount of data, in bytes.
+
+ @retval TRUE on valid value, otherwise FALSE and the value is set to zero.
+**/
+static
+MrcDimmSts
+DimmPresence (
+ IN const MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN const U32 Size
+ )
+{
+
+ const U8 *p;
+ U32 count;
+ const MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ p = (const U8 *) Spd;
+ count = Size;
+ while (count--) {
+ if (0 != *p++) {
+ return DIMM_PRESENT;
+ }
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, " Warning: No DIMM detected in slot\n");
+ return DIMM_NOT_PRESENT;
+}
+
+/**
+ @brief
+ Determine if the DIMM is valid and supported.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval TRUE on valid value, otherwise FALSE.
+**/
+static
+BOOL
+ValidDimm (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ const SPD_EXTREME_MEMORY_PROFILE *Xmp;
+ BOOL Status;
+ U8 DeviceType;
+ MrcCpuModel CpuModel;
+ SpdVddFlag VddFlag;
+#ifdef MRC_DEBUG_PRINT
+ const U16 BytesUsedConst[] = {0, 128, 176, 256};
+ const MrcDebug *Debug;
+ const char *DramTypeString;
+ const char *ModuleTypeString;
+ const char *ProfileString;
+ SPD_REVISION_STRUCT Revision;
+ U16 BytesUsed;
+ U16 BytesTotal;
+ U16 CrcCoverage;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+#endif // MRC_DEBUG_PRINT
+
+ Status = TRUE;
+ CpuModel = MrcData->SysIn.Inputs.CpuModel;
+
+ DeviceType = Spd->Ddr3.General.DramDeviceType.Bits.Type;
+
+ VddFlag.Bits.Vdd1_35 = Spd->Ddr3.General.ModuleNominalVoltage.Bits.OperationAt1_35;
+
+ switch (DeviceType) {
+#if (SUPPORT_DDR3 == SUPPORT)
+ case MRC_SPD_DDR3_SDRAM_TYPE_NUMBER:
+ DimmOut->DdrType = MRC_DDR_TYPE_DDR3;
+ DimmOut->ModuleType = Spd->Ddr3.General.ModuleType.Bits.ModuleType;
+ Xmp = &Spd->Ddr3.Xmp;
+ if ((CpuModel == cmHSW_ULT) && (VddFlag.Bits.Vdd1_35 == 0)) {
+ Status = FALSE;
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, " DDR3 memory does not support 1.35V operation\n");
+#endif // MRC_DEBUG_PRINT
+ }
+ break;
+#endif // SUPPORT_DDR3
+
+#if (SUPPORT_LPDDR3 == SUPPORT)
+ case MRC_SPD_LPDDR3_SDRAM_TYPE_NUMBER:
+ if ((CpuModel == cmHSW_ULT && PlatformSupport.Lpddr3.UltSupport) ||
+ ((CpuModel == cmHSW || CpuModel == cmCRW) && PlatformSupport.Lpddr3.TradSupport)) {
+ DimmOut->DdrType = MRC_DDR_TYPE_LPDDR3;
+ DimmOut->ModuleType = Spd->Ddr3.General.ModuleType.Bits.ModuleType;
+ Xmp = &Spd->Ddr3.Xmp;
+ break;
+ }
+ // no break;
+#endif // SUPPORT_LPDDR3
+
+
+ default:
+ DimmOut->DdrType = MRC_DDR_TYPE_UNKNOWN;
+ DimmOut->ModuleType = 0;
+ Xmp = NULL;
+ Status = FALSE;
+ break;
+ }
+
+ if (Status) {
+ switch (DimmOut->ModuleType) {
+#if (SUPPORT_RDIMM == SUPPORT)
+ case MRC_MODULE_TYPE_RDIMM:
+ break;
+#endif
+
+#if (SUPPORT_UDIMM == SUPPORT)
+ case MRC_MODULE_TYPE_UDIMM:
+ break;
+#endif
+
+#if (SUPPORT_SODIMM == SUPPORT)
+ case MRC_MODULE_TYPE_SODIMM:
+ case MRC_MODULE_72B_SO_UDIMM:
+ break;
+#endif
+
+ default:
+ Status = FALSE;
+ break;
+ }
+ }
+
+#if (SUPPORT_XMP == SUPPORT)
+ DimmOut->XmpSupport = 0;
+ if (Status) {
+ if ((XMP_ID_STRING != Xmp->Header.XmpId) ||
+ (0x12 != (Xmp->Header.XmpRevision.Data & 0xFE)) ||
+ ((MrcData->SysIn.Inputs.MemoryProfile == XMP_PROFILE1) && (Xmp->Header.XmpOrgConf.Bits.ProfileEnable1 == 0)) ||
+ ((MrcData->SysIn.Inputs.MemoryProfile == XMP_PROFILE2) && (Xmp->Header.XmpOrgConf.Bits.ProfileEnable2 == 0))) {
+ if ((MrcData->SysIn.Inputs.MemoryProfile == XMP_PROFILE1) || (MrcData->SysIn.Inputs.MemoryProfile == XMP_PROFILE2)) {
+ Status = FALSE;
+ }
+ } else {
+ MrcData->SysOut.Outputs.XmpProfileEnable |= 1;
+ }
+ if (XMP_ID_STRING == Xmp->Header.XmpId) {
+ if (0x12 == (Xmp->Header.XmpRevision.Data & 0xFE)) {
+ DimmOut->XmpRevision = Xmp->Header.XmpRevision.Data;
+ }
+ if (Xmp->Header.XmpOrgConf.Bits.ProfileEnable1 != 0) {
+ DimmOut->XmpSupport |= 1;
+ }
+ if (Xmp->Header.XmpOrgConf.Bits.ProfileEnable2 != 0) {
+ DimmOut->XmpSupport |= 2;
+ }
+ }
+ }
+#endif // SUPPORT_XMP
+
+#ifdef MRC_DEBUG_PRINT
+ switch (MrcData->SysIn.Inputs.MemoryProfile) {
+ case STD_PROFILE:
+ case USER_PROFILE:
+ default:
+ ProfileString = StdString;
+ break;
+#if (SUPPORT_XMP == SUPPORT)
+ case XMP_PROFILE1:
+ ProfileString = Xmp1String;
+ break;
+ case XMP_PROFILE2:
+ ProfileString = Xmp2String;
+ break;
+#endif // SUPPORT_XMP
+ }
+
+ switch (DeviceType) {
+#if (SUPPORT_DDR3 == SUPPORT)
+ case MRC_SPD_DDR3_SDRAM_TYPE_NUMBER:
+ DramTypeString = Ddr3String;
+ BytesTotal = 256 * Spd->Ddr3.General.Description.Bits.BytesTotal;
+ BytesUsed = BytesUsedConst[Spd->Ddr3.General.Description.Bits.BytesUsed & 3];
+ CrcCoverage = 125 - (9 * Spd->Ddr3.General.Description.Bits.CrcCoverage);
+ Revision.Data = Spd->Ddr3.General.Revision.Data;
+ break;
+#endif // SUPPORT_DDR3
+
+#if (SUPPORT_LPDDR3 == SUPPORT)
+ case MRC_SPD_LPDDR3_SDRAM_TYPE_NUMBER:
+ if ((CpuModel == cmHSW_ULT && PlatformSupport.Lpddr3.UltSupport) ||
+ ((CpuModel == cmHSW || CpuModel == cmCRW) && PlatformSupport.Lpddr3.TradSupport)) {
+ DramTypeString = Lpddr3String;
+ BytesTotal = 256 * Spd->Ddr3.General.Description.Bits.BytesTotal;
+ BytesUsed = BytesUsedConst[Spd->Ddr3.General.Description.Bits.BytesUsed & 3];
+ CrcCoverage = 125 - (9 * Spd->Ddr3.General.Description.Bits.CrcCoverage);
+ Revision.Data = Spd->Ddr3.General.Revision.Data;
+ break;
+ }
+ // no break;
+#endif // SUPPORT_LPDDR3
+
+
+ default:
+ DramTypeString = UnknownString;
+ BytesTotal = 0;
+ BytesUsed = 0;
+ CrcCoverage = 0;
+ Revision.Data = 0;
+ break;
+ }
+
+ switch (DimmOut->ModuleType) {
+#if (SUPPORT_RDIMM == SUPPORT)
+ case MRC_MODULE_TYPE_RDIMM:
+ ModuleTypeString = RdimmString;
+ break;
+#endif // SUPPORT_RDIMM
+
+#if (SUPPORT_UDIMM == SUPPORT)
+ case MRC_MODULE_TYPE_UDIMM:
+ ModuleTypeString = UdimmString;
+ break;
+#endif // SUPPORT_UDIMM
+
+#if (SUPPORT_SODIMM == SUPPORT)
+ case MRC_MODULE_TYPE_SODIMM:
+ ModuleTypeString = SodimmString;
+ break;
+
+ case MRC_MODULE_72B_SO_UDIMM:
+ ModuleTypeString = SodimmString;
+ break;
+#endif // SUPPORT_SODIMM
+
+ default:
+ ModuleTypeString = UnknownString;
+ break;
+ }
+
+ if (Status) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " %s %s detected, Rev: %u.%u, Size: %u used/%u total, CRC coverage: 0 - %u\n",
+ DramTypeString,
+ ModuleTypeString,
+ Revision.Bits.Major,
+ Revision.Bits.Minor,
+ BytesUsed,
+ BytesTotal,
+ CrcCoverage
+ );
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ " %s %s detected, SPD Dram type %Xh, module type %Xh\n",
+ DramTypeString,
+ ModuleTypeString,
+ DeviceType,
+ DimmOut->ModuleType
+ );
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " DIMM profile %s selected\n", ProfileString);
+#if (SUPPORT_XMP == SUPPORT)
+ if (Xmp == NULL) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Xmp structure is NULL!\n\n");
+ } else {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " XMP String: %Xh, Rev: %u.%u\n",
+ Xmp->Header.XmpId,
+ Xmp->Header.XmpRevision.Bits.Major,
+ Xmp->Header.XmpRevision.Bits.Minor
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ XpString,
+ 1,
+ Xmp->Header.XmpOrgConf.Bits.ProfileEnable1 ? "en" : "dis",
+ Xmp->Header.XmpOrgConf.Bits.ProfileConfig1 + 1
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ XpString,
+ 2,
+ Xmp->Header.XmpOrgConf.Bits.ProfileEnable2 ? "en" : "dis",
+ Xmp->Header.XmpOrgConf.Bits.ProfileConfig2 + 1
+ );
+ }
+#endif // SUPPORT_XMP
+
+#endif // MRC_DEBUG_PRINT
+
+ return Status;
+}
+
+/**
+ @brief
+ Determine if the DIMM SDRAM device width is valid and return the value.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval TRUE on valid value, otherwise FALSE and the value is set to zero.
+**/
+static
+BOOL
+ValidSdramDeviceWidth (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ const MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ DimmOut->SdramWidthIndex = Spd->Ddr3.General.ModuleOrganization.Bits.SdramDeviceWidth;
+
+ switch (DimmOut->SdramWidthIndex) {
+#if (SUPPORT_DEVWIDTH_4 == SUPPORT)
+
+ case MRC_SPD_SDRAM_DEVICE_WIDTH_4:
+ DimmOut->SdramWidth = 4;
+ break;
+#endif
+#if (SUPPORT_DEVWIDTH_8 == SUPPORT)
+
+ case MRC_SPD_SDRAM_DEVICE_WIDTH_8:
+ DimmOut->SdramWidth = 8;
+ break;
+#endif
+#if (SUPPORT_DEVWIDTH_16 == SUPPORT)
+
+ case MRC_SPD_SDRAM_DEVICE_WIDTH_16:
+ DimmOut->SdramWidth = 16;
+ break;
+#endif
+#if (SUPPORT_DEVWIDTH_32 == SUPPORT)
+
+ case MRC_SPD_SDRAM_DEVICE_WIDTH_32:
+ DimmOut->SdramWidth = 32;
+ break;
+#endif
+
+ default:
+ DimmOut->SdramWidth = 0;
+ MRC_DEBUG_MSG (Debug,
+ MSG_LEVEL_ERROR,
+ "%sSDRAM device width, %s%Xh\n",
+ ErrorString,
+ SpdValString,
+ DimmOut->SdramWidthIndex
+ );
+ return FALSE;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " SDRAM device width: %u\n", DimmOut->SdramWidth);
+ return TRUE;
+}
+
+/**
+ @brief
+ Determine if the DIMM SDRAM row address size is valid and return the value.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval TRUE if the row address size is valid, otherwise FALSE and the value is set to zero.
+**/
+static
+BOOL
+ValidRowSize (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ U8 RowBits;
+ U8 RowAddress;
+ const MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ RowAddress = Spd->Ddr3.General.SdramAddressing.Bits.RowAddress;
+
+ switch (RowAddress) {
+#if (SUPPORT_ROW_12 == SUPPORT)
+
+ case MRC_SPD_SDRAM_ROW_12:
+ DimmOut->RowSize = MRC_BIT12;
+ RowBits = 12;
+ break;
+#endif
+#if (SUPPORT_ROW_13 == SUPPORT)
+
+ case MRC_SPD_SDRAM_ROW_13:
+ DimmOut->RowSize = MRC_BIT13;
+ RowBits = 13;
+ break;
+#endif
+#if (SUPPORT_ROW_14 == SUPPORT)
+
+ case MRC_SPD_SDRAM_ROW_14:
+ DimmOut->RowSize = MRC_BIT14;
+ RowBits = 14;
+ break;
+#endif
+#if (SUPPORT_ROW_15 == SUPPORT)
+
+ case MRC_SPD_SDRAM_ROW_15:
+ DimmOut->RowSize = MRC_BIT15;
+ RowBits = 15;
+ break;
+#endif
+#if (SUPPORT_ROW_16 == SUPPORT)
+
+ case MRC_SPD_SDRAM_ROW_16:
+ DimmOut->RowSize = MRC_BIT16;
+ RowBits = 16;
+ break;
+#endif
+ default:
+ DimmOut->RowSize = 0;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "%sSDRAM row size, %s%Xh\n", ErrorString, SpdValString, RowAddress);
+ return FALSE;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Row bits: %u\n", RowBits);
+ return TRUE;
+}
+
+/**
+ @brief
+ Determine if the DIMM SDRAM column address size is valid and return the value.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval TRUE if the column address size is valid, otherwise FALSE and the value is set to zero.
+**/
+static
+BOOL
+ValidColumnSize (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ const MrcDebug *Debug;
+ U8 ColumnBits;
+ U8 ColumnAddress;
+ MrcCpuModel CpuModel;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ CpuModel = MrcData->SysIn.Inputs.CpuModel;
+
+ ColumnAddress = Spd->Ddr3.General.SdramAddressing.Bits.ColumnAddress;
+
+ switch (ColumnAddress) {
+#if (SUPPORT_COLUMN_9 == SUPPORT)
+ case MRC_SPD_SDRAM_COLUMN_9:
+ DimmOut->ColumnSize = MRC_BIT9;
+ ColumnBits = 9;
+ break;
+#endif
+
+#if (SUPPORT_COLUMN_10 == SUPPORT)
+ case MRC_SPD_SDRAM_COLUMN_10:
+ if ((CpuModel == cmHSW_ULT && PlatformSupport.Column10.UltSupport) ||
+ ((CpuModel == cmHSW || CpuModel == cmCRW) && PlatformSupport.Column10.TradSupport)) {
+ DimmOut->ColumnSize = MRC_BIT10;
+ ColumnBits = 10;
+ break;
+ }
+ // no break;
+#endif
+
+#if (SUPPORT_COLUMN_11 == SUPPORT)
+ case MRC_SPD_SDRAM_COLUMN_11:
+ if ((CpuModel == cmHSW_ULT && PlatformSupport.Column11.UltSupport) ||
+ ((CpuModel == cmHSW || CpuModel == cmCRW) && PlatformSupport.Column11.TradSupport)) {
+ DimmOut->ColumnSize = MRC_BIT11;
+ ColumnBits = 11;
+ break;
+ }
+ // no break;
+#endif
+
+#if (SUPPORT_COLUMN_12 == SUPPORT)
+ case MRC_SPD_SDRAM_COLUMN_12:
+ if ((CpuModel == cmHSW_ULT && PlatformSupport.Column12.UltSupport) ||
+ ((CpuModel == cmHSW || CpuModel == cmCRW) && PlatformSupport.Column12.TradSupport)) {
+ DimmOut->ColumnSize = MRC_BIT12;
+ ColumnBits = 12;
+ break;
+ }
+ // no break;
+#endif
+
+ default:
+ DimmOut->ColumnSize = 0;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "%sSDRAM column size, %s%Xh\n", ErrorString, SpdValString, ColumnAddress);
+ return FALSE;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Column bits: %u\n", ColumnBits);
+ return TRUE;
+}
+
+/**
+ @brief
+ Determine if the DIMM SDRAM primary bus width is valid and return the value.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval TRUE on valid value, otherwise FALSE and the value is set to zero.
+**/
+static
+BOOL
+ValidPrimaryWidth (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ U8 Width;
+ const MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ Width = Spd->Ddr3.General.ModuleMemoryBusWidth.Bits.PrimaryBusWidth;
+
+ switch (Width) {
+#if (SUPPORT_PRIWIDTH_8 == SUPPORT)
+
+ case MRC_SPD_PRIMARY_BUS_WIDTH_8:
+ DimmOut->PrimaryBusWidth = 8;
+ break;
+#endif
+#if (SUPPORT_PRIWIDTH_16 == SUPPORT)
+
+ case MRC_SPD_PRIMARY_BUS_WIDTH_16:
+ DimmOut->PrimaryBusWidth = 16;
+ break;
+#endif
+#if (SUPPORT_PRIWIDTH_32 == SUPPORT)
+
+ case MRC_SPD_PRIMARY_BUS_WIDTH_32:
+ DimmOut->PrimaryBusWidth = 32;
+ break;
+#endif
+#if (SUPPORT_PRIWIDTH_64 == SUPPORT)
+
+ case MRC_SPD_PRIMARY_BUS_WIDTH_64:
+ DimmOut->PrimaryBusWidth = 64;
+ break;
+#endif
+
+ default:
+ DimmOut->PrimaryBusWidth = 0;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "%sSDRAM primary bus width, %s%Xh\n", ErrorString, SpdValString, Width);
+ return FALSE;
+ break;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Primary bus width: %u\n", DimmOut->PrimaryBusWidth);
+ return TRUE;
+}
+
+/**
+ Determines if the number of Bank are valid.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval TRUE on valid value, otherwise FALSE.
+**/
+static
+BOOL
+ValidBank (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ const MrcDebug *Debug;
+ U8 BankAddress;
+ U8 BankGroup;
+ U8 ValidCheck;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+ ValidCheck = TRUE;
+ DimmOut->DensityIndex = Spd->Ddr3.General.SdramDensityAndBanks.Bits.Density;
+ BankAddress = Spd->Ddr3.General.SdramDensityAndBanks.Bits.BankAddress;
+ BankGroup = 0;
+ switch (BankAddress) {
+#if (SUPPORT_BANK_8 == SUPPORT)
+ case MRC_SPD_DDR3_SDRAM_BANK_8:
+#endif
+#if (SUPPORT_BANK_16 == SUPPORT)
+ case MRC_SPD_DDR3_SDRAM_BANK_16:
+#endif
+#if (SUPPORT_BANK_32 == SUPPORT)
+ case MRC_SPD_DDR3_SDRAM_BANK_32:
+#endif
+#if (SUPPORT_BANK_64 == SUPPORT)
+ case MRC_SPD_DDR3_SDRAM_BANK_64:
+#endif
+#if ((SUPPORT_BANK_8 == SUPPORT) || (SUPPORT_BANK_16 == SUPPORT) || (SUPPORT_BANK_32 == SUPPORT) || (SUPPORT_BANK_64 == SUPPORT))
+ DimmOut->Banks = MRC_BIT3 << BankAddress;;
+ DimmOut->BankGroups = BankGroup;
+ break;
+#endif
+
+ default:
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "%sSDRAM number of banks, %s%Xh\n",
+ ErrorString,
+ SpdValString,
+ BankAddress
+ );
+ ValidCheck = FALSE;
+ }
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ (TRUE == ValidCheck) ? " %u Banks in %u groups\n" : "",
+ DimmOut->Banks,
+ DimmOut->BankGroups
+ );
+
+ return ValidCheck;
+}
+
+/**
+ @brief
+ Determine if the number of ranks in the DIMM is valid and return the value.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval TRUE on valid value, otherwise FALSE and the value is set to zero.
+**/
+static
+BOOL
+GetRankCount (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ const MrcDebug *Debug;
+ U8 RankCount;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ RankCount = Spd->Ddr3.General.ModuleOrganization.Bits.RankCount;
+
+ DimmOut->RankInDIMM = RankCount + 1;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Ranks: %u\n", DimmOut->RankInDIMM);
+ if (DimmOut->RankInDIMM > MAX_RANK_IN_DIMM) {
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "%snumber of ranks, %s%Xh\n", ErrorString, SpdValString, RankCount);
+ DimmOut->RankInDIMM = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the size of the DIMM, in MBytes.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval TRUE on valid value, otherwise FALSE and the value is set to zero.
+**/
+static
+BOOL
+GetDimmSize (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ const U32 SdramCapacityTable[] = {
+ (256 / 8), (512 / 8), (1024 / 8), (2048 / 8),
+ (4096 / 8), (8192 / 8), (16384 / 8), (32768 / 8)
+ };
+ U32 DimmSize;
+ const MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ if ((DimmOut->SdramWidth > 0) && (DimmOut->DensityIndex < (sizeof (SdramCapacityTable) / sizeof (SdramCapacityTable[0])))) {
+ DimmSize = (((SdramCapacityTable[DimmOut->DensityIndex] * DimmOut->PrimaryBusWidth) / DimmOut->SdramWidth) * DimmOut->RankInDIMM);
+ if ((DimmSize >= DIMMSIZEMIN) && (DimmSize <= DIMMSIZEMAX)) {
+ DimmOut->DimmCapacity = DimmSize;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " DIMM size: %u MByte\n",
+ DimmSize
+ );
+ return TRUE;
+ }
+ }
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "%sDIMM size, valid range %u - %u. ",
+ ErrorString,
+ DIMMSIZEMIN,
+ DIMMSIZEMAX
+ );
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "SDRAM capacity %s%Xh\n",
+ SpdValString,
+ DimmOut->DensityIndex
+ );
+ DimmOut->DimmCapacity = 0;
+ return FALSE;
+}
+
+/**
+ @brief
+ Obtain ECC support Status for this DIMM.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval Returns TRUE.
+**/
+static
+BOOL
+ValidEccSupport (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+#if (SUPPORT_ECC == SUPPORT)
+ U8 BusWidthExtension;
+#endif // SUPPORT_ECC
+ const MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+#if (SUPPORT_ECC == SUPPORT)
+ BusWidthExtension = Spd->Ddr3.General.ModuleMemoryBusWidth.Bits.BusWidthExtension;
+
+ if (MRC_SPD_BUS_WIDTH_EXTENSION_8 == BusWidthExtension) {
+ DimmOut->EccSupport = TRUE;
+ } else
+#endif // SUPPORT_ECC
+ {
+ DimmOut->EccSupport = FALSE;
+ }
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " ECC is %ssupported\n", (DimmOut->EccSupport == FALSE) ? "not " : "");
+ return TRUE;
+}
+
+/**
+ @brief
+ Obtain address mirroring Status for this DIMM.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval Returns TRUE.
+**/
+static
+BOOL
+GetAddressMirror (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ const MrcDebug *Debug;
+ U8 MappingRank1;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ MappingRank1 = Spd->Ddr3.Module.Unbuffered.AddressMappingEdgeConn.Bits.MappingRank1;
+ DimmOut->AddressMirrored = (MappingRank1 != 0) ? TRUE : FALSE;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " DIMM has %saddress mirroring\n", (DimmOut->AddressMirrored == FALSE) ? "no " : "");
+ return TRUE;
+}
+
+/**
+ @brief
+ Obtain thermal and refresh support for this DIMM.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval Returns TRUE.
+**/
+static
+BOOL
+GetThermalRefreshSupport (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ DimmOut->PartialSelfRefresh = Spd->Ddr3.General.ThermalAndRefreshOptions.Bits.PartialArraySelfRefresh;
+ DimmOut->OnDieThermalSensor = Spd->Ddr3.General.ThermalAndRefreshOptions.Bits.OnDieThermalSensor;
+ DimmOut->AutoSelfRefresh = Spd->Ddr3.General.ThermalAndRefreshOptions.Bits.AutoSelfRefresh && Inputs->AutoSelfRefreshSupport;
+ DimmOut->ExtendedTemperRefresh = Spd->Ddr3.General.ThermalAndRefreshOptions.Bits.ExtendedTemperatureRefreshRate;
+ DimmOut->ExtendedTemperRange = Spd->Ddr3.General.ThermalAndRefreshOptions.Bits.ExtendedTemperatureRange;
+
+ DimmOut->SelfRefreshTemp = ((!DimmOut->AutoSelfRefresh) && (DimmOut->ExtendedTemperRange) && (Inputs->ExtTemperatureSupport)) ? TRUE : FALSE;
+
+ MRC_DEBUG_MSG (Debug,
+ MSG_LEVEL_NOTE,
+ " Partial Array Self Refresh%s\n",
+ DimmOut->PartialSelfRefresh ? IsSupString : NotSupString);
+ MRC_DEBUG_MSG (Debug,
+ MSG_LEVEL_NOTE,
+ " On-Die Thermal Sensor Readout%s\n",
+ DimmOut->OnDieThermalSensor ? IsSupString : NotSupString);
+ MRC_DEBUG_MSG (Debug,
+ MSG_LEVEL_NOTE,
+ " Auto Self Refresh%s\n",
+ DimmOut->AutoSelfRefresh ? IsSupString : NotSupString);
+ MRC_DEBUG_MSG (Debug,
+ MSG_LEVEL_NOTE,
+ " Extended Temperature Refresh Rate%s\n",
+ DimmOut->ExtendedTemperRefresh ? IsSupString : NotSupString);
+ MRC_DEBUG_MSG (Debug,
+ MSG_LEVEL_NOTE,
+ " Extended Temperature Range%s\n",
+ DimmOut->ExtendedTemperRange ? IsSupString : NotSupString);
+ return TRUE;
+}
+
+/**
+ @brief
+ Obtain which JEDEC reference design raw card was used as the basis for the DIMM assembly.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+ @param[in] Spd - Pointer to Spd data structure.
+ @param[in, out] DimmOut - Pointer to structure containing DIMM information.
+
+ @retval Returns TRUE.
+**/
+static
+BOOL
+GetReferenceRawCardSupport (
+ IN OUT MrcParameters *const MrcData,
+ IN const MrcSpd *const Spd,
+ IN OUT MrcDimmOut *const DimmOut
+ )
+{
+ const MrcDebug *Debug;
+
+ Debug = &MrcData->SysIn.Inputs.Debug;
+
+ DimmOut->ReferenceRawCard = (Spd->Ddr3.Module.Unbuffered.ReferenceRawCardUsed.Bits.Extension << MRC_SPD_REF_RAW_CARD_SIZE) |
+ Spd->Ddr3.Module.Unbuffered.ReferenceRawCardUsed.Bits.Card;
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " Reference raw card: %u '%s'\n",
+ DimmOut->ReferenceRawCard,
+ (DimmOut->ReferenceRawCard < (sizeof (RrcString) / sizeof (RrcString[0][0]))) ?
+ RrcString[DimmOut->ReferenceRawCard] : UnknownString
+ );
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the CRC16 of the provided SPD data. CRC16 formula is the same
+ one that is used for calculating the CRC16 stored at SPD bytes 126-127.
+ This can be used to detect DIMM change.
+
+ @param[in] Buffer - Pointer to the start of the data.
+ @param[in] Size - Amount of data in the buffer, in bytes.
+ @param[out] Crc - Pointer to location to write the calculated CRC16 value.
+
+ @retval Returns TRUE.
+**/
+BOOL
+GetDimmCrc (
+ IN const U8 *const Buffer,
+ IN const U32 Size,
+ OUT U16 *const Crc
+ )
+{
+ const U8 *Data;
+ U32 Value;
+ U32 Byte;
+ U8 Bit;
+
+ Data = Buffer;
+ Value = CRC_SEED;
+ for (Byte = 0; Byte < Size; Byte++) {
+ Value ^= (U32) *Data++ << 8;
+ for (Bit = 0; Bit < 8; Bit++) {
+ Value = (Value & MRC_BIT15) ? (Value << 1) ^ CRC_XOR_MASK : Value << 1;
+ }
+ }
+
+ *Crc = (U16) Value;
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the medium and fine timebases, using integer math.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if medium timebase is valid, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmTimeBase (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+#if ((SUPPORT_XMP == SUPPORT) || (SUPPORT_DDR3 == SUPPORT) || (SUPPORT_LPDDR3 == SUPPORT))
+ U8 SpdMtbDividend;
+ U8 SpdMtbDivisor;
+ U8 SpdFtbDividend;
+ U8 SpdFtbDivisor;
+#endif
+#if (SUPPORT_XMP == SUPPORT)
+ const SPD_MEDIUM_TIMEBASE *XmpMtb;
+ U32 Index;
+
+#endif // SUPPORT_XMP
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", TimeBaseString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ SpdFtbDividend = Spd->Ddr3.Xmp.Header.FineTimeBase.Bits.Dividend;
+ SpdFtbDivisor = Spd->Ddr3.Xmp.Header.FineTimeBase.Bits.Divisor;
+ XmpMtb = &Spd->Ddr3.Xmp.Header.MediumTimeBase[Index];
+ SpdMtbDividend = XmpMtb->Dividend.Bits.Dividend;
+ SpdMtbDivisor = XmpMtb->Divisor.Bits.Divisor;
+ TimeBase->Ftb = ((DimmOut->XmpRevision == 0x12) || (SpdFtbDivisor == 0)) ? 0 : (SpdFtbDividend * MRC_FREQUENCY_FTB_OFFSET) / SpdFtbDivisor;
+ TimeBase->Mtb = (SpdMtbDivisor == 0) ? 0 : (SpdMtbDividend * MRC_FREQUENCY_MTB_OFFSET) / SpdMtbDivisor;
+ } else {
+ TimeBase->Ftb = 0;
+ TimeBase->Mtb = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ case STD_PROFILE:
+ default:
+ SpdFtbDividend = Spd->Ddr3.General.FineTimebase.Bits.Dividend;
+ SpdFtbDivisor = Spd->Ddr3.General.FineTimebase.Bits.Divisor;
+ SpdMtbDividend = Spd->Ddr3.General.MediumTimebase.Dividend.Bits.Dividend;
+ SpdMtbDivisor = Spd->Ddr3.General.MediumTimebase.Divisor.Bits.Divisor;
+ TimeBase->Ftb = (SpdFtbDivisor == 0) ? 0 : (SpdFtbDividend * MRC_FREQUENCY_FTB_OFFSET) / SpdFtbDivisor;
+ TimeBase->Mtb = (SpdMtbDivisor == 0) ? 0 : (SpdMtbDividend * MRC_FREQUENCY_MTB_OFFSET) / SpdMtbDivisor;
+ break;
+ } //switch
+
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u % 6u %u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ TimeBase->Mtb,
+ TimeBase->Ftb
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the SDRAM minimum cycle time (tCKmin) that this DIMM supports.
+ Then use the lookup table to obtain the frequency closest to the clock multiple.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if the DIMM frequency is supported, otherwise FALSE and the frequency is set to fUnSupport.
+**/
+static
+BOOL
+GetChannelDimmtCK (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ S32 tCKminMtb;
+ S32 tCKminFine;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ const SPD_EXTREME_MEMORY_PROFILE_DATA *Data;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s (fs)\n", tCKString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = fNoInit;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ Calculated = 0;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Data = &Spd->Ddr3.Xmp.Data[Profile - XMP_PROFILE1];
+
+ tCKminMtb = Data->tCKmin.Bits.tCKmin;
+ tCKminFine = (DimmOut->XmpRevision == 0x13) ? Data->tCKminFine.Bits.tCKminFine : 0;
+ Calculated = (MediumTimebase * tCKminMtb) + (FineTimebase * tCKminFine);
+ Calculated = MAX (Outputs->MemoryClockMax, Calculated);
+ } else {
+ Calculated = 0;
+ }
+#endif // SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (Inputs->Ratio > 0) {
+ Calculated = MrcRatioToClock (Inputs->Ratio, Outputs->RefClk, Inputs->BClkFrequency);
+ Calculated = MAX (Outputs->MemoryClockMax, Calculated);
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ tCKminMtb = Spd->Ddr3.General.tCKmin.Bits.tCKmin;
+ tCKminFine = Spd->Ddr3.General.tCKminFine.Bits.tCKminFine;
+
+ Calculated = (MediumTimebase * tCKminMtb) + (FineTimebase * tCKminFine);
+ Calculated = MAX (Outputs->MemoryClockMax, Calculated);
+ break;
+ } //switch
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u % 6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tCK = Actual[Profile];
+ ChannelOut->Timing[Profile].tCK = Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ Outputs->MemoryClock = Actual[Inputs->MemoryProfile];
+
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the Minimum CAS Latency Time (tAAmin) for the given DIMMs.
+ Step 1: Determine the common set of supported CAS Latency values for all modules
+ on the memory channel using the CAS Latencies Supported in SPD.
+ Step 2: Determine tAAmin(all) which is the largest tAAmin value for all modules on the memory channel.
+ Step 3: Determine tCKmin(all) which is the largest tCKmin value for all
+ the modules on the memory channel (Done in function GetChannelDimmtCK).
+ Step 4: For a proposed tCK value between tCKmin and tCKmax, determine the desired CAS Latency.
+ If tCKproposed is not a standard JEDEC value then tCKproposed must be adjusted to the
+ next lower standard tCK value for calculating CLdesired.
+ Step 5: Chose an actual CAS Latency that is greater than or equal to CLdesired and is
+ supported by all modules on the memory channel as determined in step 1. If no such value exists,
+ choose a higher tCKproposed value and repeat steps 4 and 5 until a solution is found.
+ Step 6: Once the calculation of CLactual is completed, the BIOS must also verify that this CAS
+ Latency value does not exceed tAAmax, which is 20 ns for all DDR3 speed grades.
+ If not, choose a lower CL value and repeat steps 5 and 6 until a solution is found.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if the CAS latency has been calculated, otherwise FALSE and the returned value is set to zero.
+**/
+static
+BOOL
+GetChannelDimmtAA (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcProfile Profile;
+ MrcBool Found[MAX_PROFILE];
+ MrcBool UserProfile;
+ MrcBool tCLOverride;
+ BOOL Status;
+ S32 MediumTimeBase;
+ S32 FineTimeBase;
+ S32 tCKminIndex;
+ S32 tCKmin100;
+ S32 tCKminIndexSave;
+ S32 TimingFTB;
+ U32 TimingMTB;
+ U32 tCKmin;
+ U32 CommonCasMask[MAX_PROFILE];
+ U32 CasMask;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+ U32 tCLLimitMin;
+ U32 tCLLimitMax;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+#if (SUPPORT_XMP == SUPPORT)
+ const SPD_EXTREME_MEMORY_PROFILE_DATA *Data;
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ tCKmin = 0;
+ Calculated = 0;
+ Status = FALSE;
+ tCLOverride = FALSE;
+ MediumTimeBase = 0;
+ FineTimeBase = 0;
+ TimingMTB = 0;
+ TimingFTB = 0;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s tCL Mask\n", tAAString, HeaderString);
+
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ UserProfile = (Profile == USER_PROFILE) && (Inputs->MemoryProfile == USER_PROFILE);
+ CommonCasMask[Profile] = ~(0UL);
+ Actual[Profile] = 0;
+ tCLLimitMin = 4;
+ tCLLimitMax = 18;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ tCKmin = ChannelOut->Dimm[Dimm].Timing[Profile].tCK;
+ MediumTimeBase = ChannelOut->TimeBase[Dimm][Profile].Mtb;
+ FineTimeBase = ChannelOut->TimeBase[Dimm][Profile].Ftb;
+ CasMask = 0;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ Data = &Spd->Ddr3.Xmp.Data[Index];
+ tCLLimitMin = 4;
+ tCLLimitMax = 18;
+ TimingMTB = Data->tAAmin.Bits.tAAmin;
+ TimingFTB = (DimmOut->XmpRevision == 0x13) ? Data->tAAminFine.Bits.tAAminFine : 0;
+ CasMask = Data->CasLatencies.Data & MRC_SPD_CL_SUPPORTED_MASK;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimeBase * TimingMTB) + (FineTimeBase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ } else {
+ Calculated = 0;
+ }
+#endif // SUPPORT_XMP
+ break;
+
+ case USER_PROFILE:
+ if (DimmIn->Timing.tCL > 0) {
+ CasMask = ~(0UL);
+ Calculated = DimmIn->Timing.tCL;
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ tCLOverride = TRUE;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+
+ case STD_PROFILE:
+ default:
+ tCLLimitMin = 4;
+ tCLLimitMax = 18;
+ TimingMTB = Spd->Ddr3.General.tAAmin.Bits.tAAmin;
+ TimingFTB = Spd->Ddr3.General.tAAminFine.Bits.tAAminFine;
+ CasMask = Spd->Ddr3.General.CasLatencies.Data & MRC_SPD_CL_SUPPORTED_MASK;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimeBase * TimingMTB) + (FineTimeBase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ break;
+ } //end switch
+
+ CommonCasMask[Profile] &= CasMask;
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u % 8Xh\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated,
+ CasMask
+ );
+ } //if DimmOut->Status
+ } //for Dimm
+ } //for Channel
+ } //for Controller
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Profile %u common set of supported CAS Latency values = %Xh\n", Profile, CommonCasMask[Profile]);
+
+ if ((Profile >= XMP_PROFILE1) && (tCKmin == 0)) {
+ continue;
+ }
+
+ Found[Profile] = FALSE;
+ ConvertClock2Freq (MrcData, Outputs->RefClk, tCKmin, &tCKminIndex);
+ if ((Profile >= XMP_PROFILE1) && (Outputs->RefClk == MRC_REF_CLOCK_133) && (Outputs->Capable100)) {
+ ConvertClock2Freq (MrcData, MRC_REF_CLOCK_100, tCKmin, &tCKmin100);
+ if (tCKmin100 > tCKminIndex) {
+ tCKminIndex = tCKmin100;
+ if (Inputs->MemoryProfile == Profile) {
+ Outputs->RefClk = MRC_REF_CLOCK_100;
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " Profile%u is RefClk 100 capable, switching to RefClk 100\n", Profile);
+ }
+ }
+ do {
+ for (; !Found[Profile] && (Actual[Profile] <= tCLLimitMax); Actual[Profile]++) {
+ if ((UserProfile) ||
+ ((MRC_BIT0 == ((CommonCasMask[Profile] >> (Actual[Profile] - tCLLimitMin)) & MRC_BIT0)) &&
+ ((Actual[Profile] * tCKmin) <= MRC_TaaMAX))) {
+ Found[Profile] = TRUE;
+ if (Profile == Inputs->MemoryProfile) {
+ Outputs->MemoryClock = tCKmin;
+ Status = TRUE;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ DimmOut->Timing[Profile].tCL = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tCL = (U16) Actual[Profile];
+ DimmOut->Timing[Profile].tCK = tCKmin;
+ ChannelOut->Timing[Profile].tCK = tCKmin;
+ } //if
+ } //for Dimm
+ } //for Channel
+ } //for Controller
+ break;
+ } //if
+ } //for Actual[Profile]
+ if (!Found[Profile]) {
+ if (UserProfile && ((Inputs->Ratio > 0) || (tCLOverride == TRUE))) {
+ break;
+ } else {
+ tCKminIndexSave = tCKminIndex;
+ while (--tCKminIndex > 0) {
+ if ((Range[tCKminIndex].RefClkFlag == 3) ||
+ ((Range[tCKminIndex].RefClkFlag == 1) && (Outputs->RefClk == MRC_REF_CLOCK_133)) ||
+ ((Range[tCKminIndex].RefClkFlag == 2) && (Outputs->RefClk == MRC_REF_CLOCK_100))) {
+ tCKmin = Range[tCKminIndex].tCK;
+ ConvertClock2Freq (MrcData, Outputs->RefClk, tCKmin, &tCKminIndex);
+ Actual[Profile] = (tCKmin == 0) ? 0 : ((MediumTimeBase * TimingMTB) + (FineTimeBase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ MRC_DEBUG_MSG (Debug,
+ MSG_LEVEL_WARNING,
+ "Warning: The memory frequency is being downgraded on profile %u, from %u to %u and the new tCL is %u\n",
+ Profile,
+ Range[tCKminIndexSave].Frequency,
+ Range[tCKminIndex].Frequency,
+ Actual[Profile]);
+ break;
+ }
+ }
+ }
+ }
+ } while (!Found[Profile] && (tCKminIndex > 0));
+ } //for Profile
+
+ Outputs->Frequency = ConvertClock2Freq (MrcData, Outputs->RefClk, Outputs->MemoryClock, NULL);
+
+#ifdef MRC_DEBUG_PRINT
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Found[Profile] ? Actual[Profile] : 0);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n Memory clock = %ufs\n", Outputs->MemoryClock);
+#endif
+
+ return (Status);
+}
+
+/**
+ @brief
+ Calculate the minimum tCWL timing value for the given memory frequency.
+ We calculate timings for all profiles so that this information can be passed out of MRC.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtCWL (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+ U32 MaxPossible;
+ U32 TimingMTB;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ MaxPossible = TCWLMAXPOSSIBLE;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tCWLString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ Calculated = 0;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ TimingMTB = Spd->Ddr3.Xmp.Data[Profile - XMP_PROFILE1].tCWLmin.Bits.tCWLmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif // SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tCWL > 0) {
+ Calculated = DimmIn->Timing.tCWL;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ TimingMTB = 0;
+#if (SUPPORT_LPDDR3 == SUPPORT)
+ if (DimmOut->DdrType == MRC_DDR_TYPE_LPDDR3) {
+ //
+ // WL Set A from MR2 spec, adding 1 to take tDQSS into account.
+ // We will subtract this 1 when programming TC_BANK_RANK_D.tWCL
+ //
+ if (tCKmin <= MRC_DDR3_1333_TCK_MIN) {
+ Calculated = 7;
+ } else if (tCKmin <= MRC_DDR3_1067_TCK_MIN) {
+ Calculated = 5;
+ } else if (tCKmin <= MRC_DDR3_800_TCK_MIN) {
+ Calculated = 4;
+ }
+ } else
+#endif // SUPPORT_LPDDR3
+ {
+ if (tCKmin <= MRC_DDR3_2133_TCK_MIN) {
+ Calculated = 10;
+ } else if (tCKmin <= MRC_DDR3_1867_TCK_MIN) {
+ Calculated = 9;
+ } else if (tCKmin <= MRC_DDR3_1600_TCK_MIN) {
+ Calculated = 8;
+ } else if (tCKmin <= MRC_DDR3_1333_TCK_MIN) {
+ Calculated = 7;
+ } else if (tCKmin <= MRC_DDR3_1067_TCK_MIN) {
+ Calculated = 6;
+ } else if (tCKmin <= MRC_DDR3_800_TCK_MIN) {
+ Calculated = 5;
+ }
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, MaxPossible);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tCWL = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tCWL = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+
+#if (MRC_DDR3_LPDDR_SUPPORTED)
+/**
+ @brief
+ Calculate the minimum tFAW timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtFAW (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ U32 TimingMTB;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ const SPD_EXTREME_MEMORY_PROFILE_DATA *Data;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tFAWString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Data = &Spd->Ddr3.Xmp.Data[Profile - XMP_PROFILE1];
+ TimingMTB = ((U32) (Data->tFAWMinUpper.Bits.tFAWminUpper) << 8) | (U32) (Data->tFAWmin.Bits.tFAWmin);
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tFAW > 0) {
+ Calculated = DimmIn->Timing.tFAW;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = ((U32) (Spd->Ddr3.General.tFAWMinUpper.Bits.tFAWminUpper) << 8) | (U32) (Spd->Ddr3.General.tFAWmin.Bits.tFAWmin);
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, TFAWMAXPOSSIBLE);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tFAW = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tFAW = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+#endif
+
+/**
+ @brief
+ Calculate the minimum tRAS timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtRAS (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ U32 TimingMTB;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ const SPD_EXTREME_MEMORY_PROFILE_DATA *Data;
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tRASString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ Data = &Spd->Ddr3.Xmp.Data[Index];
+ TimingMTB = ((U32) (Data->tRASMintRCMinUpper.Bits.tRASminUpper) << 8) | (U32) (Data->tRASmin.Bits.tRASmin);
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tRAS > 0) {
+ Calculated = DimmIn->Timing.tRAS;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = ((U32) (Spd->Ddr3.General.tRASMintRCMinUpper.Bits.tRASminUpper) << 8) | (U32) (Spd->Ddr3.General.tRASmin.Bits.tRASmin);
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, TRASMAXPOSSIBLE);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tRAS = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tRAS = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the minimum tRC timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtRC (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ U32 TimingMTB;
+ S32 TimingFTB;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ const SPD_EXTREME_MEMORY_PROFILE_DATA *Data;
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tRCString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ Data = &Spd->Ddr3.Xmp.Data[Index];
+ TimingMTB = ((U32) (Data->tRASMintRCMinUpper.Bits.tRCminUpper) << 8) | (U32) (Data->tRCmin.Bits.tRCmin);
+ TimingFTB = (DimmOut->XmpRevision == 0x13) ? Data->tRCminFine.Bits.tRCminFine : 0;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (FineTimebase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tRC > 0) {
+ Calculated = DimmIn->Timing.tRC;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = ((U32) (Spd->Ddr3.General.tRASMintRCMinUpper.Bits.tRCminUpper) << 8) | (U32) (Spd->Ddr3.General.tRCmin.Bits.tRCmin);
+ TimingFTB = Spd->Ddr3.General.tRCminFine.Bits.tRCminFine;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (FineTimebase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, TRCMAXPOSSIBLE);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tRC = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tRC = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the minimum tRCD timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtRCD (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ U32 TimingMTB;
+ S32 TimingFTB;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ const SPD_EXTREME_MEMORY_PROFILE_DATA *Data;
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tRCDString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ Data = &Spd->Ddr3.Xmp.Data[Index];
+ TimingMTB = Data->tRCDmin.Bits.tRCDmin;
+ TimingFTB = (DimmOut->XmpRevision == 0x13) ? Data->tRCDminFine.Bits.tRCDminFine : 0;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (FineTimebase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tRCD > 0) {
+ Calculated = DimmIn->Timing.tRCD;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = Spd->Ddr3.General.tRCDmin.Bits.tRCDmin;
+ TimingFTB = Spd->Ddr3.General.tRCDminFine.Bits.tRCDminFine;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (FineTimebase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, TRCDMAXPOSSIBLE);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tRCD = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tRCD = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the minimum tREFI timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtREFI (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+ U32 TimingMTB;
+#if (SUPPORT_XMP == SUPPORT)
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tREFIString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ TimingMTB = Spd->Ddr3.Xmp.Data[Index].tREFImin.Bits.tREFImin;
+ Calculated = (tCKmin == 0) ? 0 : (U32) (MrcOemMemoryDivideU64ByU64 (
+ ((MrcOemMemoryMultiplyU64ByU32 (MediumTimebase, TimingMTB * TREFIMULTIPLIER)) + (tCKmin - 1)),
+ tCKmin));
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tREFI > 0) {
+ Calculated = DimmIn->Timing.tREFI;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ switch (DimmOut->DdrType)
+ {
+#if (MRC_DDR3_LPDDR_SUPPORTED)
+ case MRC_DDR_TYPE_DDR3:
+ TimingMTB = TREFIMIN_DDR3;
+ break;
+#endif
+#if (SUPPORT_LPDDR3 == SUPPORT)
+ case MRC_DDR_TYPE_LPDDR3:
+ TimingMTB = TREFIMIN_LPDDR3;
+ break;
+#endif
+ default:
+ TimingMTB = TREFIMIN_DDR3;
+ break;
+ }
+
+ Calculated = (tCKmin == 0) ? 0 : ((TimingMTB + ((tCKmin / 1000) - 1)) / (tCKmin / 1000));
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, TREFIMAXPOSSIBLE);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tREFI = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tREFI = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the minimum tRFC timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtRFC (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ U32 TimingMTB;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tRFCString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ TimingMTB = Spd->Ddr3.Xmp.Data[Index].tRFCmin.Bits.tRFCmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tRFC > 0) {
+ Calculated = DimmIn->Timing.tRFC;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = Spd->Ddr3.General.tRFCmin.Bits.tRFCmin;
+ //
+ // @todo: Temp w/a for 8GB dimms
+ // if ((DimmOut->DimmCapacity == 8192) && (TimingMTB != 2400)) {
+ // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s %u/%u/%u tRFC MTB = %u .., W/A - changing it to 2400\n", CcdString, Controller, Channel, Dimm, TimingMTB);
+ // TimingMTB = 2400;
+ // }
+ //
+
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, TRFCMAXPOSSIBLE);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tRFC = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tRFC = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+
+
+/**
+ @brief
+ Calculate the minimum tRP timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtRP (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 TimingMTB;
+ S32 TimingFTB;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ U32 Index;
+ const SPD_EXTREME_MEMORY_PROFILE_DATA *Data;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tRPString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ Data = &Spd->Ddr3.Xmp.Data[Index];
+ TimingMTB = Data->tRPmin.Bits.tRPmin;
+ TimingFTB = (DimmOut->XmpRevision == 0x13) ? Data->tRPminFine.Bits.tRPminFine : 0;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (FineTimebase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tRP > 0) {
+ Calculated = DimmIn->Timing.tRP;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = Spd->Ddr3.General.tRPmin.Bits.tRPmin;
+ TimingFTB = Spd->Ddr3.General.tRPminFine.Bits.tRPminFine;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (FineTimebase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, TRPMAXPOSSIBLE);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tRP = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tRP = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+
+#if (SUPPORT_LPDDR3 == SUPPORT)
+/**
+ @brief
+ Calculate the minimum tRPab timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtRPab (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ MrcBool Flag;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 TimingMTB;
+ S32 TimingFTB;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ Flag = FALSE;
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if ((DIMM_PRESENT == DimmOut->Status) && (DimmOut->DdrType == MRC_DDR_TYPE_LPDDR3)) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case USER_PROFILE:
+ if (DimmIn->Timing.tRPab > 0) {
+ Calculated = DimmIn->Timing.tRPab;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = Spd->Ddr3.General.tRPab.Bits.tRPab;
+ TimingFTB = Spd->Ddr3.General.tRPabFine.Bits.tRPabFine;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (FineTimebase * TimingFTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ if ((Calculated >= TRPABMINPOSSIBLE) && ((Calculated - DimmOut->Timing[Profile].tRP) <= 3)) {
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ }
+ if (!Flag) {
+ Flag = TRUE;
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tRPabString, HeaderString);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //Flag
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ if (Flag ) {
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tRPab = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tRPab = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+ }
+
+ return TRUE;
+
+}
+#endif
+
+#if (MRC_DDR3_LPDDR_SUPPORTED)
+/**
+ @brief
+ Calculate the minimum tRRD timing value for the given memory frequency.
+ MRC should not set tRRD below 4nCK for all frequencies.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE
+**/
+static
+BOOL
+GetChannelDimmtRRD (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ U32 TimingMTB;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tRRDString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ TimingMTB = Spd->Ddr3.Xmp.Data[Index].tRRDmin.Bits.tRRDmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tRRD > 0) {
+ Calculated = DimmIn->Timing.tRRD;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = Spd->Ddr3.General.tRRDmin.Bits.tRRDmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ Calculated = MAX (Calculated, TRRDMINPOSSIBLE); // Make sure tRRD is at least 4 tCK
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tRRD = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tRRD = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+#endif
+
+
+#if (MRC_DDR3_LPDDR_SUPPORTED)
+/**
+ @brief
+ Calculate the minimum tRTP timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtRTP (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ U32 TimingMTB;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+ U32 MaxPossible;
+#if (SUPPORT_XMP == SUPPORT)
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ MaxPossible = TRTPMAXPOSSIBLE;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tRTPString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ TimingMTB = Spd->Ddr3.Xmp.Data[Index].tRTPmin.Bits.tRTPmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tRTP > 0) {
+ Calculated = DimmIn->Timing.tRTP;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = Spd->Ddr3.General.tRTPmin.Bits.tRTPmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, MaxPossible);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tRTP = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tRTP = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+#endif
+
+#if (MRC_DDR3_LPDDR_SUPPORTED)
+/**
+ @brief
+ Calculate the minimum tWR timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtWR (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ U32 TimingMTB;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+ U32 MaxPossible;
+#if (SUPPORT_XMP == SUPPORT)
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ MaxPossible = TWRMAXPOSSIBLE;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tWRString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ TimingMTB = Spd->Ddr3.Xmp.Data[Index].tWRmin.Bits.tWRmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tWR > 0) {
+ Calculated = DimmIn->Timing.tWR;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = Spd->Ddr3.General.tWRmin.Bits.tWRmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ //
+ // Special case, tWRmin values of 9, 11, 13, and 15 are not supported by DDR3 Mode Register 0 (MR0).
+ // If we see one of these values, then add one clock to it in order to make it valid.
+ //
+ if ((9 == Calculated) || (11 == Calculated) || (13 == Calculated) || (15 == Calculated)) {
+ Calculated++;
+ } else {
+ Calculated = MIN (Calculated, MaxPossible);
+ }
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tWR = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tWR = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+#endif
+
+#if (MRC_DDR3_LPDDR_SUPPORTED)
+/**
+ @brief
+ Calculate the minimum tWTR timing value for the given memory frequency.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmtWTR (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ U32 TimingMTB;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", tWTRString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 0;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ TimingMTB = Spd->Ddr3.Xmp.Data[Index].tWTRmin.Bits.tWTRmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.tWTR > 0) {
+ Calculated = DimmIn->Timing.tWTR;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ if (tCKmin > 0) {
+ TimingMTB = Spd->Ddr3.General.tWTRmin.Bits.tWTRmin;
+ Calculated = (tCKmin == 0) ? 0 : ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ }
+ break;
+ } //switch
+
+ Calculated = MIN (Calculated, TWTRMAXPOSSIBLE);
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].tWTR = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].tWTR = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+#endif
+
+/**
+ @brief
+ Calculate the minimum command rate mode value for the given channel.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmNmode (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcTimeBase *TimeBase;
+ MrcProfile Profile;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 tCKmin;
+ S32 MediumTimebase;
+ S32 FineTimebase;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+#if (SUPPORT_XMP == SUPPORT)
+ U32 Index;
+ U32 TimingMTB;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", NmodeString, HeaderString);
+
+ //
+ // Find the smallest timing value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = (Profile < XMP_PROFILE1) ? NMODEMINPOSSIBLE : 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = 2;
+ tCKmin = DimmOut->Timing[Profile].tCK;
+ TimeBase = &ChannelOut->TimeBase[Dimm][Profile];
+ MediumTimebase = TimeBase->Mtb;
+ FineTimebase = TimeBase->Ftb;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ TimingMTB = Spd->Ddr3.Xmp.Data[Index].SystemCmdRate.Bits.NMode;
+ if (tCKmin == 0) {
+ Calculated = 0;
+ } else {
+ Calculated = ((MediumTimebase * TimingMTB) + (tCKmin - 1)) / tCKmin;
+ if (Calculated == 0) {
+ Calculated = 2;
+ }
+ }
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (DimmIn->Timing.NMode > 0) {
+ Calculated = DimmIn->Timing.NMode;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ Calculated = NMODEMINPOSSIBLE;
+ break;
+ } //switch
+
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %6u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case timing for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ DimmOut->Timing[Profile].NMode = (U16) Actual[Profile];
+ ChannelOut->Timing[Profile].NMode = (U16) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+
+/**
+ @brief
+ Calculate the VDD voltage value for the given channel.
+
+ @param[in, out] MrcData - Pointer to MrcData data structure.
+
+ @retval TRUE if there are DIMMs present, otherwise FALSE.
+**/
+static
+BOOL
+GetChannelDimmVdd (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ const MrcSpd *Spd;
+ const MrcDebug *Debug;
+ const SPD_EXTREME_MEMORY_PROFILE_DATA *Xmp;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcProfile Profile;
+ SpdVddFlag VddFlag;
+ MrcCpuModel CpuModel;
+ U32 Actual[MAX_PROFILE];
+ U32 Calculated;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+#if (SUPPORT_XMP == SUPPORT)
+ U32 Index;
+#endif
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Outputs = &MrcData->SysOut.Outputs;
+ Debug = &Inputs->Debug;
+ CpuModel = MrcData->SysIn.Inputs.CpuModel;
+
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s:\n %s\n", VddString, HeaderString);
+
+ //
+ // Find the best case voltage value for all the given DIMMs, for all the profiles.
+ //
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ Actual[Profile] = (Profile < XMP_PROFILE1) ? VDD_1_20 : 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Spd = &DimmIn->Spd;
+ Calculated = VDD_1_50;
+ switch (Profile) {
+ case XMP_PROFILE1:
+ case XMP_PROFILE2:
+#if (SUPPORT_XMP == SUPPORT)
+ if (((Profile == XMP_PROFILE1) && ((DimmOut->XmpSupport & 1) != 0)) ||
+ ((Profile == XMP_PROFILE2) && ((DimmOut->XmpSupport & 2) != 0))) {
+ Index = Profile - XMP_PROFILE1;
+ Xmp = &Spd->Ddr3.Xmp.Data[Index];
+ Calculated = XMP_VDD_INCREMENT * Xmp->Vdd.Bits.Decimal;
+ Calculated = MIN (Calculated, XMP_VDD_INTEGER - 1);
+ Calculated += (XMP_VDD_INTEGER * Xmp->Vdd.Bits.Integer);
+ Calculated = MAX (Calculated, XMP_VDD_MIN_POSSIBLE);
+ Calculated = MIN (Calculated, XMP_VDD_MAX_POSSIBLE);
+ } else {
+ Calculated = 0;
+ }
+#endif //SUPPORT_XMP
+ break;
+ case USER_PROFILE:
+ if (Inputs->VddVoltage > 0) {
+ Calculated = Inputs->VddVoltage;
+ break;
+ } else {
+ // In AUTO mode, so no break.
+ }
+ case STD_PROFILE:
+ default:
+ VddFlag.Bits.Vdd1_50 = ~(Spd->Ddr3.General.ModuleNominalVoltage.Bits.OperationAt1_50);
+ VddFlag.Bits.Vdd1_35 = Spd->Ddr3.General.ModuleNominalVoltage.Bits.OperationAt1_35;
+ VddFlag.Bits.Vdd1_25 = Spd->Ddr3.General.ModuleNominalVoltage.Bits.OperationAt1_25;
+#if (VDDMINPOSSIBLE <= 1350)
+ if ((CpuModel == cmHSW_ULT && PlatformSupport.VddMin.UltSupport <= 1350) ||
+ ((CpuModel == cmHSW || CpuModel == cmCRW) && PlatformSupport.VddMin.TradSupport <= 1350 )) {
+ if (VddFlag.Bits.Vdd1_35) {
+ Calculated = VDD_1_35;
+ }
+ }
+#endif // VDDMINPOSSIBLE
+#if (VDDMINPOSSIBLE <= 1200)
+ if ((CpuModel == cmHSW_ULT && PlatformSupport.VddMin.UltSupport <= 1200) ||
+ ((CpuModel == cmHSW || CpuModel == cmCRW) && PlatformSupport.VddMin.TradSupport <= 1200 )) {
+ if (VddFlag.Bits.Vdd1_25) {
+ Calculated = VDD_1_20;
+ }
+ }
+#endif // VDDMINPOSSIBLE
+ if ((Profile == STD_PROFILE) && (Inputs->BoardType == btCRBDT)) {
+ Calculated = VDD_1_50;
+ }
+ break;
+ } //switch
+
+ Actual[Profile] = MAX (Actual[Profile], Calculated);
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ " % 7u % 10u % 8u % 5u %4u\n",
+ Profile,
+ Controller,
+ Channel,
+ Dimm,
+ Calculated
+ );
+ } //DimmOut->Status
+ } //Dimm
+ } //Channel
+ } //Controller
+ } //Profile
+
+ //
+ // Set the best case voltage for all controllers/channels/dimms, for each profile.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %s%u:", BestCaseString, MAX_PROFILE - 1);
+ for (Profile = STD_PROFILE; Profile < MAX_PROFILE; Profile++) {
+ if (((Profile == XMP_PROFILE1) || (Profile == XMP_PROFILE2)) && (!(Outputs->XmpProfileEnable))) {
+ continue;
+ }
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ Outputs->VddVoltage[Profile] = (MrcVddSelect) Actual[Profile];
+ DimmOut->VddVoltage[Profile] = (MrcVddSelect) Actual[Profile];
+ }
+ }
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " %u", Actual[Profile]);
+ }
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");
+
+ return TRUE;
+}
+
+/**
+ @brief
+ Analyze the given DIMM SPD data to determine DIMM presence and configuration.
+
+ @param[in, out] MrcData - Pointer to MRC global data structure.
+ @param[in] Controller - Current controller number.
+ @param[in] Channel - Current channel number.
+ @param[in] Dimm - Current DIMM number.
+
+ @retval mrcSuccess if DIMM is present otherwise mrcDimmNotExist.
+**/
+static
+MrcStatus
+SpdDimmRecognition (
+ IN OUT MrcParameters *const MrcData,
+ IN const U8 Controller,
+ IN U8 Channel,
+ IN U8 Dimm
+ )
+{
+ const SpdRecogCallTable CallTable[] = {
+ {ValidDimm},
+ {ValidSdramDeviceWidth},
+ {ValidPrimaryWidth},
+ {GetRankCount},
+ {ValidBank},
+ {GetDimmSize},
+ {ValidRowSize},
+ {ValidColumnSize},
+ {ValidEccSupport},
+ {GetAddressMirror},
+ {GetThermalRefreshSupport},
+ {GetReferenceRawCardSupport}
+ };
+ const MrcSpd *Spd;
+ const U8 *CrcStart;
+ MrcDimmOut *DimmOut;
+ MrcDimmIn *DimmIn;
+ BOOL Status;
+ U32 CrcSize;
+ U8 Index;
+
+ Spd = &MrcData->SysIn.Inputs.Controller[Controller].Channel[Channel].Dimm[Dimm].Spd;
+ DimmIn = &MrcData->SysIn.Inputs.Controller[Controller].Channel[Channel].Dimm[Dimm];
+ DimmOut = &MrcData->SysOut.Outputs.Controller[Controller].Channel[Channel].Dimm[Dimm];
+ DimmOut->Status = DIMM_NOT_PRESENT;
+ if (DIMM_PRESENT == DimmPresence (MrcData, Spd, sizeof (MrcSpd))) {
+ Status = TRUE;
+ for (Index = 0; (Status == TRUE) && (Index < (sizeof (CallTable) / sizeof (CallTable[0]))); Index++) {
+ Status &= CallTable[Index].mrc_task (MrcData, Spd, DimmOut);
+ }
+ if (Status == FALSE) {
+ DimmOut->Status = DIMM_DISABLED;
+ return mrcDimmNotExist;
+ }
+ DimmOut->Status = DIMM_PRESENT;
+ CrcStart = MrcSpdCrcArea (MrcData, Controller, Channel, Dimm, &CrcSize);
+ GetDimmCrc ((const U8 *const) CrcStart, CrcSize, &DimmOut->Crc);
+ } else {
+ return mrcDimmNotExist;
+ }
+
+ if (DIMM_DISABLED == DimmIn->Status) {
+ DimmOut->Status = DIMM_DISABLED;
+ }
+
+ return mrcSuccess;
+}
+
+/**
+ @brief
+ Calculate the timing of all DIMMs on all channels.
+
+ @param[in, out] MrcData - The MRC "global data".
+
+ @retval mrcSuccess on success, mrcDimmNotExist if no DIMMs found.
+**/
+static
+MrcStatus
+SpdTimingCalculation (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const SpdTimeCallTable CallTable[] = {
+ {GetChannelDimmTimeBase}, // Note: This must be done first as all other calculations are based on this.
+ {GetChannelDimmtCK}, // Note: This must be done second as all other calculations are based on this.
+ {GetChannelDimmtAA},
+ {GetChannelDimmtCWL},
+ {GetChannelDimmtRAS},
+ {GetChannelDimmtRC},
+ {GetChannelDimmtRCD},
+ {GetChannelDimmtREFI},
+ {GetChannelDimmtRFC},
+ {GetChannelDimmtRP},
+#if (SUPPORT_LPDDR3 == SUPPORT)
+ {GetChannelDimmtRPab}, // Note: This must be done after GetChannelDimmtRP
+#endif
+#if (MRC_DDR3_LPDDR_SUPPORTED)
+ {GetChannelDimmtFAW},
+ {GetChannelDimmtRRD},
+ {GetChannelDimmtRTP},
+ {GetChannelDimmtWR},
+ {GetChannelDimmtWTR},
+#endif
+ {GetChannelDimmNmode},
+ {GetChannelDimmVdd}
+ };
+ BOOL Status;
+ U8 Index;
+#if (SUPPORT_FORCE == SUPPORT)
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ U16 Value;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+#endif
+
+ //
+ // Find the "least common denominator" timing across the DIMMs.
+ // tAA must be done first before any other timings are calculated.
+ //
+ Status = TRUE;
+ for (Index = 0; (Status == TRUE) && (Index < (sizeof (CallTable) / sizeof (SpdTimeCallTable))); Index++) {
+ Status &= CallTable[Index].mrc_task (MrcData);
+ }
+
+#if (SUPPORT_FORCE == SUPPORT)
+ if (Status == TRUE) {
+ //
+ // Force tCLmin, tRCDmin, tRPmin to be the same "least common denominator" value.
+ //
+ Value = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &Outputs->Controller[Controller].Channel[Channel].Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ Value = MAX (Value, DimmOut->Timing.tRCD);
+ Value = MAX (Value, DimmOut->Timing.tRP);
+ Value = MAX (Value, DimmOut->Timing.tCL);
+ }
+ }
+ }
+ }
+
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &Outputs->Controller[Controller].Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DIMM_PRESENT == DimmOut->Status) {
+ ChannelOut->Timing.tRCD = Value;
+ ChannelOut->Timing.tRP = Value;
+ ChannelOut->Timing.tCL = Value;
+ DimmOut->Timing.tRCD = Value;
+ DimmOut->Timing.tRP = Value;
+ DimmOut->Timing.tCL = Value;
+ }
+ }
+ }
+ }
+ }
+#endif
+ return (Status == FALSE) ? mrcDimmNotExist : mrcSuccess;
+}
+
+/**
+ @brief
+ Determine the starting address and size of the SPD area to generate a CRC.
+
+ @param[in, out] MrcData - The MRC "global data".
+ @param[in] Controller - Controller index.
+ @param[in] Channel - Channel index.
+ @param[in] Dimm - Dimm index.
+ @param[out] CrcSize - Location to write CRC block size.
+
+ @retval The starting address of the CRC block.
+**/
+const U8 *
+MrcSpdCrcArea (
+ IN OUT MrcParameters *const MrcData,
+ IN U8 Controller,
+ IN U8 Channel,
+ IN U8 Dimm,
+ OUT U32 *const CrcSize
+ )
+{
+ const MrcDimmIn *DimmIn;
+ MrcDimmOut *DimmOut;
+ const U8 *CrcStart;
+
+ DimmIn = &MrcData->SysIn.Inputs.Controller[Controller].Channel[Channel].Dimm[Dimm];
+ DimmOut = &MrcData->SysOut.Outputs.Controller[Controller].Channel[Channel].Dimm[Dimm];
+ CrcStart = NULL;
+
+ CrcStart = (const U8 *) &DimmIn->Spd.Ddr3.ModuleId;
+ *CrcSize = SPD3_MANUF_SIZE;
+ return (CrcStart);
+}
+
+/**
+ @brief
+ Process the SPD information for all DIMMs on all channels.
+
+ @param[in, out] MrcData - The MRC "global data".
+
+ @retval mrcSuccess on success, mrcDimmNotExist if no DIMMs found.
+**/
+MrcStatus
+MrcSpdProcessing (
+ IN OUT MrcParameters *const MrcData
+ )
+{
+ const MrcDebug *Debug;
+ const MrcInput *Inputs;
+ const MrcControllerIn *ControllerIn;
+ const MrcChannelIn *ChannelIn;
+ const MrcDimmIn *DimmIn;
+ MrcStatus Status;
+ MrcOutput *Outputs;
+ MrcControllerOut *ControllerOut;
+ MrcChannelOut *ChannelOut;
+ MrcDimmOut *DimmOut;
+ MrcModuleType ModuleType;
+ U8 Controller;
+ U8 Channel;
+ U8 Dimm;
+ U32 DimmCount;
+ U8 ValidRankBitMask;
+
+ Inputs = &MrcData->SysIn.Inputs;
+ Debug = &Inputs->Debug;
+ Outputs = &MrcData->SysOut.Outputs;
+ Status = mrcDimmNotExist;
+
+ //
+ // Scan thru each DIMM to see if it is a valid DIMM and to get its configuration.
+ //
+ ModuleType = MRC_MODULE_TYPE_UNKNOWN;
+ DimmCount = 0;
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerIn = &Inputs->Controller[Controller];
+ ControllerOut = &Outputs->Controller[Controller];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelIn = &ControllerIn->Channel[Channel];
+ ChannelOut = &ControllerOut->Channel[Channel];
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmIn = &ChannelIn->Dimm[Dimm];
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if (DimmIn->Status == DIMM_ENABLED || DimmIn->Status == DIMM_DISABLED) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_NOTE,
+ "SPD Dimm recognition, %s %u/%u/%u\n",
+ CcdString,
+ Controller,
+ Channel,
+ Dimm
+ );
+ if (mrcSuccess == SpdDimmRecognition (MrcData, Controller, Channel, Dimm)) {
+ DimmCount++;
+ if (MRC_DDR_TYPE_UNKNOWN == Outputs->DdrType) {
+ Outputs->DdrType = DimmOut->DdrType;
+ } else if (Outputs->DdrType != DimmOut->DdrType) {
+ Status = mrcMixedDimmSystem;
+ }
+ if (MRC_MODULE_TYPE_UNKNOWN == ModuleType) {
+ ModuleType = DimmOut->ModuleType;
+ } else if (ModuleType != DimmOut->ModuleType) {
+ Status = mrcMixedDimmSystem;
+ }
+ if (Status == mrcMixedDimmSystem) {
+ MRC_DEBUG_MSG (
+ Debug,
+ MSG_LEVEL_ERROR,
+ "%s configuration, system contains a mix of memory types\n",
+ ErrorString
+ );
+ return (Status);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (DimmCount > 0) {
+ //
+ // Scan thru each channel to see if it is a valid channel and to get its configuration.
+ //
+ MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "SPD Dimm timing calculation\n");
+ if (mrcSuccess == SpdTimingCalculation (MrcData)) {
+ Outputs->EccSupport = TRUE;
+
+ //
+ // Count up the number of valid DIMMs.
+ //
+ ControllerOut = &Outputs->Controller[0];
+ for (Channel = 0; Channel < MAX_CHANNEL; Channel++) {
+ ChannelOut = &ControllerOut->Channel[Channel];
+ ChannelOut->EccSupport = TRUE;
+ for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) {
+ DimmOut = &ChannelOut->Dimm[Dimm];
+ if ((DIMM_PRESENT == DimmOut->Status) || (DIMM_DISABLED == DimmOut->Status)) {
+ ChannelOut->DimmCount++;
+ }
+ if (DIMM_PRESENT == DimmOut->Status) {
+#if (MAX_RANK_IN_CHANNEL > 8)
+#error The next switch statement and ValidRankBitMask needs updated to support additional ranks.
+#endif
+ switch (DimmOut->RankInDIMM) {
+ case 1:
+ ValidRankBitMask = 1;
+ break;
+#if (MAX_RANK_IN_DIMM > 1)
+
+ case 2:
+ ValidRankBitMask = 3;
+ break;
+#endif
+#if (MAX_RANK_IN_DIMM > 2)
+
+ case 3:
+ ValidRankBitMask = 7;
+ break;
+#endif
+#if (MAX_RANK_IN_DIMM > 3)
+
+ case 4:
+ ValidRankBitMask = 15;
+ break;
+#endif
+
+ default:
+ ValidRankBitMask = 0;
+ break;
+ }
+
+ ChannelOut->ValidRankBitMask |= ValidRankBitMask << (Dimm * MAX_RANK_IN_DIMM);
+
+ ChannelOut->EccSupport &= DimmOut->EccSupport;
+ Outputs->EccSupport &= DimmOut->EccSupport;
+ }
+ }
+
+ if ((ChannelOut->DimmCount > 0) && (ChannelOut->ValidRankBitMask > 0)) {
+ ControllerOut->ChannelCount++;
+ ControllerOut->Channel[Channel].Status = CHANNEL_PRESENT;
+ }
+ }
+
+ for (Controller = 0; Controller < MAX_CONTROLLERS; Controller++) {
+ ControllerOut = &Outputs->Controller[Controller];
+ if (ControllerOut->ChannelCount > 0) {
+ ControllerOut->Status = CONTROLLER_PRESENT;
+ Status = mrcSuccess;
+ }
+ }
+ }
+ }
+
+ return Status;
+}