summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/device/dram/ddr3.c117
-rw-r--r--src/include/device/dram/ddr3.h12
-rw-r--r--src/northbridge/intel/sandybridge/raminit.c30
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;