diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/device/dram/ddr3.c | 117 | ||||
-rw-r--r-- | src/include/device/dram/ddr3.h | 12 | ||||
-rw-r--r-- | src/northbridge/intel/sandybridge/raminit.c | 30 |
3 files changed, 158 insertions, 1 deletions
diff --git a/src/device/dram/ddr3.c b/src/device/dram/ddr3.c index fe9de2d8e7..6bfaabcd76 100644 --- a/src/device/dram/ddr3.c +++ b/src/device/dram/ddr3.c @@ -116,6 +116,8 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) /* Don't assume we memset 0 dimm struct. Clear all our flags */ dimm->flags.raw = 0; + dimm->dimms_per_channel = 3; + /* Make sure that the SPD dump is indeed from a DDR3 module */ if (spd[2] != SPD_MEMORY_TYPE_SDRAM_DDR3) { printram("Not a DDR3 SPD!\n"); @@ -180,14 +182,17 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) printram(" Supported voltages:"); if (reg8 & (1 << 2)) { dimm->flags.operable_1_25V = 1; + dimm->voltage = 1250; printram(" 1.25V"); } if (reg8 & (1 << 1)) { dimm->flags.operable_1_35V = 1; + dimm->voltage = 1300; printram(" 1.35V"); } if (!(reg8 & (1 << 0))) { dimm->flags.operable_1_50V = 1; + dimm->voltage = 1500; printram(" 1.5V"); } printram("\n"); @@ -338,6 +343,118 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) return ret; } +/** + * \brief Decode the raw SPD XMP data + * + * Decodes a raw SPD XMP data from a DDR3 DIMM, and organizes it into a + * @ref dimm_attr structure. The SPD data must first be read in a contiguous + * array, and passed to this function. + * + * @param dimm pointer to @ref dimm_attr structure where the decoded data is to + * be stored + * @param spd array of raw data previously read from the SPD. + * + * @param profile select one of the profiles to load + * + * @return @ref spd_status enumerator + * SPD_STATUS_OK -- decoding was successful + * SPD_STATUS_INVALID -- invalid SPD or not a DDR3 SPD + * SPD_STATUS_CRC_ERROR -- CRC did not verify + * SPD_STATUS_INVALID_FIELD -- A field with an invalid value was + * detected. + */ +int spd_xmp_decode_ddr3(dimm_attr *dimm, + spd_raw_data spd, + enum ddr3_xmp_profile profile) +{ + int ret; + u32 mtb; /* medium time base */ + u8 *xmp; /* pointer to XMP profile data */ + + /* need a valid SPD */ + ret = spd_decode_ddr3(dimm, spd); + if (ret != SPD_STATUS_OK) + return ret; + + /* search for magic header */ + if (spd[176] != 0x0C || spd[177] != 0x4A) { + printram("Not a DDR3 XMP profile!\n"); + dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED; + return SPD_STATUS_INVALID; + } + + if (profile == DDR3_XMP_PROFILE_1) { + if (!(spd[178] & 1)) { + printram("Selected XMP profile disabled!\n"); + dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED; + return SPD_STATUS_INVALID; + } + printram(" XMP Profile 1\n"); + xmp = &spd[185]; + + /* Medium Timebase = + * Medium Timebase (MTB) Dividend / + * Medium Timebase (MTB) Divisor */ + mtb = (((u32) spd[180]) << 8) / spd[181]; + + dimm->dimms_per_channel = ((spd[178] >> 2) & 0x3) + 1; + } else { + if (!(spd[178] & 2)) { + printram("Selected XMP profile disabled!\n"); + dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED; + return SPD_STATUS_INVALID; + } + printram(" XMP Profile 2\n"); + xmp = &spd[220]; + + /* Medium Timebase = + * Medium Timebase (MTB) Dividend / + * Medium Timebase (MTB) Divisor */ + mtb = (((u32) spd[182]) << 8) / spd[183]; + + dimm->dimms_per_channel = ((spd[178] >> 4) & 0x3) + 1; + } + printram(" Max DIMMs per channel: %u\n", + dimm->dimms_per_channel); + + printram(" XMP Revision: %u.%u\n", spd[179] >> 4, spd[179] & 0xf); + + /* calculate voltage in mV */ + dimm->voltage = (xmp[0] & 1) * 50; + dimm->voltage += ((xmp[0] >> 1) & 0xf) * 100; + dimm->voltage += ((xmp[0] >> 5) & 0x3) * 1000; + printram(" Requested voltage: %u mV\n", dimm->voltage); + + /* SDRAM Minimum Cycle Time (tCKmin) */ + dimm->tCK = xmp[1] * mtb; + /* CAS Latencies Supported */ + dimm->cas_supported = (xmp[9] << 8) + xmp[8]; + /* Minimum CAS Latency Time (tAAmin) */ + dimm->tAA = xmp[2] * mtb; + /* Minimum Write Recovery Time (tWRmin) */ + dimm->tWR = xmp[8] * mtb; + /* Minimum RAS# to CAS# Delay Time (tRCDmin) */ + dimm->tRCD = xmp[7] * mtb; + /* Minimum Row Active to Row Active Delay Time (tRRDmin) */ + dimm->tRRD = xmp[17] * mtb; + /* Minimum Row Precharge Delay Time (tRPmin) */ + dimm->tRP = xmp[6] * mtb; + /* Minimum Active to Precharge Delay Time (tRASmin) */ + dimm->tRAS = (((xmp[9] & 0x0f) << 8) + xmp[10]) * mtb; + /* Minimum Active to Active/Refresh Delay Time (tRCmin) */ + dimm->tRC = (((xmp[9] & 0xf0) << 4) + xmp[11]) * mtb; + /* Minimum Refresh Recovery Delay Time (tRFCmin) */ + dimm->tRFC = ((xmp[15] << 8) + xmp[14]) * mtb; + /* Minimum Internal Write to Read Command Delay Time (tWTRmin) */ + dimm->tWTR = xmp[20] * mtb; + /* Minimum Internal Read to Precharge Command Delay Time (tRTPmin) */ + dimm->tRTP = xmp[16] * mtb; + /* Minimum Four Activate Window Delay Time (tFAWmin) */ + dimm->tFAW = (((xmp[18] & 0x0f) << 8) + xmp[19]) * mtb; + + return ret; +} + /* * The information printed below has a more informational character, and is not * necessarily tied in to RAM init debugging. Hence, we stop using printram(), diff --git a/src/include/device/dram/ddr3.h b/src/include/device/dram/ddr3.h index de75aee452..0520ead4b7 100644 --- a/src/include/device/dram/ddr3.h +++ b/src/include/device/dram/ddr3.h @@ -161,6 +161,10 @@ typedef struct dimm_attr_st { u32 tFAW; u8 reference_card; + /* XMP: Module voltage in mV */ + u16 voltage; + /* XMP: max DIMMs per channel supported (1-4) */ + u8 dimms_per_channel; } dimm_attr; /** Result of the SPD decoding process */ @@ -171,12 +175,20 @@ enum spd_status { SPD_STATUS_INVALID_FIELD, }; +enum ddr3_xmp_profile { + DDR3_XMP_PROFILE_1 = 0, + DDR3_XMP_PROFILE_2 = 1, +}; + typedef u8 spd_raw_data[256]; u16 spd_ddr3_calc_crc(u8 *spd, int len); int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd_data); int dimm_is_registered(enum spd_dimm_type type); void dram_print_spd_ddr3(const dimm_attr * dimm); +int spd_xmp_decode_ddr3(dimm_attr * dimm, + spd_raw_data spd, + enum ddr3_xmp_profile profile); /** * \brief Read double word from specified address diff --git a/src/northbridge/intel/sandybridge/raminit.c b/src/northbridge/intel/sandybridge/raminit.c index 59f3b3b834..40089e2cdb 100644 --- a/src/northbridge/intel/sandybridge/raminit.c +++ b/src/northbridge/intel/sandybridge/raminit.c @@ -284,7 +284,7 @@ void read_spd(spd_raw_data * spd, u8 addr) static void dram_find_spds_ddr3(spd_raw_data * spd, dimm_info * dimm, ramctr_timing * ctrl) { - int dimms = 0; + int dimms = 0, dimms_on_channel; int channel, slot, spd_slot; memset (ctrl->rankmap, 0, sizeof (ctrl->rankmap)); @@ -295,9 +295,37 @@ static void dram_find_spds_ddr3(spd_raw_data * spd, dimm_info * dimm, FOR_ALL_CHANNELS { ctrl->channel_size_mb[channel] = 0; + dimms_on_channel = 0; + /* count dimms on channel */ for (slot = 0; slot < NUM_SLOTS; slot++) { spd_slot = 2 * channel + slot; spd_decode_ddr3(&dimm->dimm[channel][slot], spd[spd_slot]); + if (dimm->dimm[channel][slot].dram_type == SPD_MEMORY_TYPE_SDRAM_DDR3) + dimms_on_channel++; + } + + for (slot = 0; slot < NUM_SLOTS; slot++) { + spd_slot = 2 * channel + slot; + /* search for XMP profile */ + spd_xmp_decode_ddr3(&dimm->dimm[channel][slot], + spd[spd_slot], + DDR3_XMP_PROFILE_1); + + if (dimm->dimm[channel][slot].dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { + printram("No valid XMP profile found.\n"); + spd_decode_ddr3(&dimm->dimm[channel][slot], spd[spd_slot]); + } else if (dimms_on_channel > dimm->dimm[channel][slot].dimms_per_channel) { + printram("XMP profile supports %u DIMMs, but %u DIMMs are installed.\n", + dimm->dimm[channel][slot].dimms_per_channel, + dimms_on_channel); + spd_decode_ddr3(&dimm->dimm[channel][slot], spd[spd_slot]); + } else if (dimm->dimm[channel][slot].voltage != 1500) { + /* TODO: support other DDR3 voltage than 1500mV */ + printram("XMP profile's requested %u mV is unsupported.\n", + dimm->dimm[channel][slot].voltage); + spd_decode_ddr3(&dimm->dimm[channel][slot], spd[spd_slot]); + } + if (dimm->dimm[channel][slot].dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { // set dimm invalid dimm->dimm[channel][slot].ranks = 0; |