aboutsummaryrefslogtreecommitdiff
path: root/resources/libreboot/patch/kgpe-d16/0040-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch
diff options
context:
space:
mode:
Diffstat (limited to 'resources/libreboot/patch/kgpe-d16/0040-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch')
-rw-r--r--resources/libreboot/patch/kgpe-d16/0040-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch461
1 files changed, 461 insertions, 0 deletions
diff --git a/resources/libreboot/patch/kgpe-d16/0040-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch b/resources/libreboot/patch/kgpe-d16/0040-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch
new file mode 100644
index 00000000..d70b8957
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0040-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch
@@ -0,0 +1,461 @@
+From f26e90bcbbd7b5966ac353a7e7ba63fa188ffac0 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <tpearson@raptorengineeringinc.com>
+Date: Tue, 2 Jun 2015 20:51:59 -0500
+Subject: [PATCH 040/139] northbridge/amd/amdmct: Read SPD data into cache to
+ decrease bootup time
+
+Change-Id: Ic16a927a3f1fc6f7cb1aea36a8abe8cc1999cb52
+Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com>
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 161 +++++++++++++++-------------
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 7 ++
+ 2 files changed, 92 insertions(+), 76 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 4677c73..5344ff9 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -180,7 +180,7 @@ static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+ static void SyncSetting(struct DCTStatStruc *pDCTstat);
+-static u8 crcCheck(u8 smbaddr);
++static uint8_t crcCheck(struct DCTStatStruc *pDCTstat, uint8_t dimm);
+ static void mct_ExtMCTConfig_Bx(struct DCTStatStruc *pDCTstat);
+ static void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat);
+
+@@ -1170,6 +1170,20 @@ static void precise_memclk_delay_fam15(struct MCTStatStruc *pMCTstat, struct DCT
+ precise_ndelay_fam15(pMCTstat, delay_ns);
+ }
+
++static void read_spd_bytes(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dimm)
++{
++ uint16_t addr;
++ uint16_t byte;
++
++ addr = Get_DIMMAddress_D(pDCTstat, dimm);
++ pDCTstat->spd_data.spd_address[dimm] = addr;
++
++ for (byte = 0; byte < 256; byte++) {
++ pDCTstat->spd_data.spd_bytes[dimm][byte] = mctRead_SPD(addr, byte);
++ }
++}
++
+ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+ {
+@@ -1275,7 +1289,7 @@ restartinit:
+ mct_InitialMCT_D(pMCTstat, pDCTstat);
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n");
+- mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/
++ mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node */
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_preInitDCT\n");
+ mct_preInitDCT(pMCTstat, pDCTstat);
+@@ -2435,7 +2449,6 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ u32 dword;
+ u32 dev;
+ u32 val;
+- u16 smbaddr;
+
+ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
+@@ -2455,64 +2468,62 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ for ( i = 0; i< MAX_DIMMS_SUPPORTED; i++) {
+ LDIMM = i >> 1;
+ if (pDCTstat->DIMMValid & (1 << i)) {
+- smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i));
+-
+- val = mctRead_SPD(smbaddr, SPD_MTBDivisor); /* MTB=Dividend/Divisor */
+- MTB16x = ((mctRead_SPD(smbaddr, SPD_MTBDividend) & 0xFF)<<4);
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_MTBDivisor]; /* MTB=Dividend/Divisor */
++ MTB16x = ((pDCTstat->spd_data.spd_bytes[dct + i][SPD_MTBDividend] & 0xff) << 4);
+ MTB16x /= val; /* transfer to MTB*16 */
+
+- byte = mctRead_SPD(smbaddr, SPD_tRPmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRPmin];
+ val = byte * MTB16x;
+ if (Trp < val)
+ Trp = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tRRDmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRRDmin];
+ val = byte * MTB16x;
+ if (Trrd < val)
+ Trrd = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tRCDmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRCDmin];
+ val = byte * MTB16x;
+ if (Trcd < val)
+ Trcd = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tRTPmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRTPmin];
+ val = byte * MTB16x;
+ if (Trtp < val)
+ Trtp = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tWRmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tWRmin];
+ val = byte * MTB16x;
+ if (Twr < val)
+ Twr = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tWTRmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tWTRmin];
+ val = byte * MTB16x;
+ if (Twtr < val)
+ Twtr = val;
+
+- val = mctRead_SPD(smbaddr, SPD_Upper_tRAS_tRC) & 0xFF;
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tRAS_tRC] & 0xFF;
+ val >>= 4;
+ val <<= 8;
+- val |= mctRead_SPD(smbaddr, SPD_tRCmin) & 0xFF;
++ val |= pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRCmin] & 0xFF;
+ val *= MTB16x;
+ if (Trc < val)
+ Trc = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_Density) & 0xF;
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Density] & 0xF;
+ if (Trfc[LDIMM] < byte)
+ Trfc[LDIMM] = byte;
+
+- val = mctRead_SPD(smbaddr, SPD_Upper_tRAS_tRC) & 0xF;
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tRAS_tRC] & 0xF;
+ val <<= 8;
+- val |= (mctRead_SPD(smbaddr, SPD_tRASmin) & 0xFF);
++ val |= (pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRASmin] & 0xFF);
+ val *= MTB16x;
+ if (Tras < val)
+ Tras = val;
+
+- val = mctRead_SPD(smbaddr, SPD_Upper_tFAW) & 0xF;
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tFAW] & 0xF;
+ val <<= 8;
+- val |= mctRead_SPD(smbaddr, SPD_tFAWmin) & 0xFF;
++ val |= pDCTstat->spd_data.spd_bytes[dct + i][SPD_tFAWmin] & 0xFF;
+ val *= MTB16x;
+ if (Tfaw < val)
+ Tfaw = val;
+@@ -2928,7 +2939,7 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ u8 CLactual, CLdesired, CLT_Fail;
+ uint16_t min_frequency_tck16x;
+
+- u8 smbaddr, byte = 0, bytex = 0;
++ u8 byte = 0, bytex = 0;
+
+ CASLatLow = 0xFF;
+ CASLatHigh = 0xFF;
+@@ -2949,28 +2960,27 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+
+ for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) {
+ if (pDCTstat->DIMMValid & (1 << i)) {
+- smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i));
+ /* 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 bytes 14 and 15.
+ */
+- byte = mctRead_SPD(smbaddr, SPD_CASLow);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_CASLow];
+ CASLatLow &= byte;
+- byte = mctRead_SPD(smbaddr, SPD_CASHigh);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_CASHigh];
+ CASLatHigh &= byte;
+ /* Step 2: Determine tAAmin(all) which is the largest tAAmin
+ value for all modules on the memory channel (SPD byte 16). */
+- byte = mctRead_SPD(smbaddr, SPD_MTBDivisor);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_MTBDivisor];
+
+- MTB16x = ((mctRead_SPD(smbaddr, SPD_MTBDividend) & 0xFF)<<4);
++ MTB16x = ((pDCTstat->spd_data.spd_bytes[dct + i][SPD_MTBDividend] & 0xFF)<<4);
+ MTB16x /= byte; /* transfer to MTB*16 */
+
+- byte = mctRead_SPD(smbaddr, SPD_tAAmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tAAmin];
+ if (tAAmin16x < byte * MTB16x)
+ tAAmin16x = byte * MTB16x;
+ /* Step 3: Determine tCKmin(all) which is the largest tCKmin
+ value for all modules on the memory channel (SPD byte 12). */
+- byte = mctRead_SPD(smbaddr, SPD_tCKmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tCKmin];
+
+ if (tCKmin16x < byte * MTB16x)
+ tCKmin16x = byte * MTB16x;
+@@ -3341,7 +3351,6 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
+ u8 byte;
+ u16 word;
+ u32 dword;
+- u16 smbaddr;
+
+ dev = pDCTstat->dev_dct;
+
+@@ -3352,16 +3361,14 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
+ byte -= 3;
+
+ if (pDCTstat->DIMMValid & (1<<byte)) {
+- smbaddr = Get_DIMMAddress_D(pDCTstat, (ChipSel + dct));
+-
+- byte = mctRead_SPD(smbaddr, SPD_Addressing);
++ byte = pDCTstat->spd_data.spd_bytes[ChipSel + dct][SPD_Addressing];
+ Rows = (byte >> 3) & 0x7; /* Rows:0b=12-bit,... */
+ Cols = byte & 0x7; /* Cols:0b=9-bit,... */
+
+- byte = mctRead_SPD(smbaddr, SPD_Density);
++ byte = pDCTstat->spd_data.spd_bytes[ChipSel + dct][SPD_Density];
+ Banks = (byte >> 4) & 7; /* Banks:0b=3-bit,... */
+
+- byte = mctRead_SPD(smbaddr, SPD_Organization);
++ byte = pDCTstat->spd_data.spd_bytes[ChipSel + dct][SPD_Organization];
+ Ranks = ((byte >> 3) & 7) + 1;
+
+ /* Configure Bank encoding
+@@ -3456,46 +3463,42 @@ static void SPDCalcWidth_D(struct MCTStatStruc *pMCTstat,
+ * and determine the width mode: 64-bit, 64-bit muxed, 128-bit.
+ */
+ u8 i;
+- u8 smbaddr, smbaddr1;
+ u8 byte, byte1;
+
+ /* Check Symmetry of Channel A and Channel B DIMMs
+ (must be matched for 128-bit mode).*/
+ for (i=0; i < MAX_DIMMS_SUPPORTED; i += 2) {
+ if ((pDCTstat->DIMMValid & (1 << i)) && (pDCTstat->DIMMValid & (1<<(i+1)))) {
+- smbaddr = Get_DIMMAddress_D(pDCTstat, i);
+- smbaddr1 = Get_DIMMAddress_D(pDCTstat, i+1);
+-
+- byte = mctRead_SPD(smbaddr, SPD_Addressing) & 0x7;
+- byte1 = mctRead_SPD(smbaddr1, SPD_Addressing) & 0x7;
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_Addressing] & 0x7;
++ byte1 = pDCTstat->spd_data.spd_bytes[i + 1][SPD_Addressing] & 0x7;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+- byte = mctRead_SPD(smbaddr, SPD_Density) & 0x0f;
+- byte1 = mctRead_SPD(smbaddr1, SPD_Density) & 0x0f;
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_Density] & 0x0f;
++ byte1 = pDCTstat->spd_data.spd_bytes[i + 1][SPD_Density] & 0x0f;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+- byte = mctRead_SPD(smbaddr, SPD_Organization) & 0x7;
+- byte1 = mctRead_SPD(smbaddr1, SPD_Organization) & 0x7;
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_Organization] & 0x7;
++ byte1 = pDCTstat->spd_data.spd_bytes[i + 1][SPD_Organization] & 0x7;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+- byte = (mctRead_SPD(smbaddr, SPD_Organization) >> 3) & 0x7;
+- byte1 = (mctRead_SPD(smbaddr1, SPD_Organization) >> 3) & 0x7;
++ byte = (pDCTstat->spd_data.spd_bytes[i][SPD_Organization] >> 3) & 0x7;
++ byte1 = (pDCTstat->spd_data.spd_bytes[i + 1][SPD_Organization] >> 3) & 0x7;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+- byte = mctRead_SPD(smbaddr, SPD_DMBANKS) & 7; /* #ranks-1 */
+- byte1 = mctRead_SPD(smbaddr1, SPD_DMBANKS) & 7; /* #ranks-1 */
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_DMBANKS] & 7; /* #ranks-1 */
++ byte1 = pDCTstat->spd_data.spd_bytes[i + 1][SPD_DMBANKS] & 7; /* #ranks-1 */
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+@@ -3676,8 +3679,9 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ status = mctRead_SPD(smbaddr, SPD_ByteUse);
+ if (status >= 0) { /* SPD access is ok */
+ pDCTstat->DIMMPresent |= 1 << i;
+- if (crcCheck(smbaddr)) { /* CRC is OK */
+- byte = mctRead_SPD(smbaddr, SPD_TYPE);
++ read_spd_bytes(pMCTstat, pDCTstat, i);
++ if (crcCheck(pDCTstat, i)) { /* CRC is OK */
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_TYPE];
+ if (byte == JED_DDR3SDRAM) {
+ /*Dimm is 'Present'*/
+ pDCTstat->DIMMValid |= 1 << i;
+@@ -3690,36 +3694,41 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ } else {
+ /*if NV_SPDCHK_RESTRT is set to 1, ignore faulty SPD checksum*/
+ pDCTstat->ErrStatus |= 1<<SB_DIMMChkSum;
+- byte = mctRead_SPD(smbaddr, SPD_TYPE);
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_TYPE];
+ if (byte == JED_DDR3SDRAM)
+ pDCTstat->DIMMValid |= 1 << i;
+ }
+ }
++
++ /* Zero DIMM SPD data cache if DIMM not present / valid */
++ if (!(pDCTstat->DIMMValid & (1 << i)))
++ memset(pDCTstat->spd_data.spd_bytes[i], 0, 256);
++
+ /* Get module information for SMBIOS */
+ if (pDCTstat->DIMMValid & (1 << i)) {
+ pDCTstat->DimmManufacturerID[i] = 0;
+ for (k = 0; k < 8; k++)
+- pDCTstat->DimmManufacturerID[i] |= ((uint64_t)mctRead_SPD(smbaddr, SPD_MANID_START + k)) << (k * 8);
++ pDCTstat->DimmManufacturerID[i] |= ((uint64_t)pDCTstat->spd_data.spd_bytes[i][SPD_MANID_START + k]) << (k * 8);
+ for (k = 0; k < SPD_PARTN_LENGTH; k++)
+- pDCTstat->DimmPartNumber[i][k] = mctRead_SPD(smbaddr, SPD_PARTN_START + k);
++ pDCTstat->DimmPartNumber[i][k] = pDCTstat->spd_data.spd_bytes[i][SPD_PARTN_START + k];
+ pDCTstat->DimmPartNumber[i][SPD_PARTN_LENGTH] = 0;
+ pDCTstat->DimmRevisionNumber[i] = 0;
+ for (k = 0; k < 2; k++)
+- pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)mctRead_SPD(smbaddr, SPD_REVNO_START + k)) << (k * 8);
++ pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)pDCTstat->spd_data.spd_bytes[i][SPD_REVNO_START + k]) << (k * 8);
+ pDCTstat->DimmSerialNumber[i] = 0;
+ for (k = 0; k < 4; k++)
+- pDCTstat->DimmSerialNumber[i] |= ((uint32_t)mctRead_SPD(smbaddr, SPD_SERIAL_START + k)) << (k * 8);
+- pDCTstat->DimmRows[i] = (mctRead_SPD(smbaddr, SPD_Addressing) & 0x38) >> 3;
+- pDCTstat->DimmCols[i] = mctRead_SPD(smbaddr, SPD_Addressing) & 0x7;
+- pDCTstat->DimmRanks[i] = ((mctRead_SPD(smbaddr, SPD_Organization) & 0x38) >> 3) + 1;
+- pDCTstat->DimmBanks[i] = 1ULL << (((mctRead_SPD(smbaddr, SPD_Density) & 0x70) >> 4) + 3);
+- pDCTstat->DimmWidth[i] = 1ULL << ((mctRead_SPD(smbaddr, SPD_BusWidth) & 0x7) + 3);
++ pDCTstat->DimmSerialNumber[i] |= ((uint32_t)pDCTstat->spd_data.spd_bytes[i][SPD_SERIAL_START + k]) << (k * 8);
++ pDCTstat->DimmRows[i] = (pDCTstat->spd_data.spd_bytes[i][SPD_Addressing] & 0x38) >> 3;
++ pDCTstat->DimmCols[i] = pDCTstat->spd_data.spd_bytes[i][SPD_Addressing] & 0x7;
++ pDCTstat->DimmRanks[i] = ((pDCTstat->spd_data.spd_bytes[i][SPD_Organization] & 0x38) >> 3) + 1;
++ pDCTstat->DimmBanks[i] = 1ULL << (((pDCTstat->spd_data.spd_bytes[i][SPD_Density] & 0x70) >> 4) + 3);
++ pDCTstat->DimmWidth[i] = 1ULL << ((pDCTstat->spd_data.spd_bytes[i][SPD_BusWidth] & 0x7) + 3);
+ }
+ /* Check supported voltage(s) */
+- pDCTstat->DimmSupportedVoltages[i] = mctRead_SPD(smbaddr, SPD_Voltage) & 0x7;
++ pDCTstat->DimmSupportedVoltages[i] = pDCTstat->spd_data.spd_bytes[i][SPD_Voltage] & 0x7;
+ pDCTstat->DimmSupportedVoltages[i] ^= 0x1; /* Invert LSB to convert from SPD format to internal bitmap format */
+ /* Check module type */
+- byte = mctRead_SPD(smbaddr, SPD_DIMMTYPE) & 0x7;
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_DIMMTYPE] & 0x7;
+ if (byte == JED_RDIMM || byte == JED_MiniRDIMM) {
+ RegDIMMPresent |= 1 << i;
+ pDCTstat->DimmRegistered[i] = 1;
+@@ -3733,13 +3742,13 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->DimmLoadReduced[i] = 0;
+ }
+ /* Check ECC capable */
+- byte = mctRead_SPD(smbaddr, SPD_BusWidth);
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_BusWidth];
+ if (byte & JED_ECC) {
+ /* DIMM is ECC capable */
+ pDCTstat->DimmECCPresent |= 1 << i;
+ }
+ /* Check if x4 device */
+- devwidth = mctRead_SPD(smbaddr, SPD_Organization) & 0x7; /* 0:x4,1:x8,2:x16 */
++ devwidth = pDCTstat->spd_data.spd_bytes[i][SPD_Organization] & 0x7; /* 0:x4,1:x8,2:x16 */
+ if (devwidth == 0) {
+ /* DIMM is made with x4 or x16 drams */
+ pDCTstat->Dimmx4Present |= 1 << i;
+@@ -3749,7 +3758,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->Dimmx16Present |= 1 << i;
+ }
+
+- byte = (mctRead_SPD(smbaddr, SPD_Organization) >> 3);
++ byte = (pDCTstat->spd_data.spd_bytes[i][SPD_Organization] >> 3);
+ byte &= 7;
+ if (byte == 3) { /* 4ranks */
+ /* if any DIMMs are QR, we have to make two passes through DIMMs*/
+@@ -3784,7 +3793,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+
+ /* check address mirror support for unbuffered dimm */
+ /* check number of registers on a dimm for registered dimm */
+- byte = mctRead_SPD(smbaddr, SPD_AddressMirror);
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_AddressMirror];
+ if (RegDIMMPresent & (1 << i)) {
+ if ((byte & 3) > 1)
+ pDCTstat->MirrPresU_NumRegR |= 1 << i;
+@@ -3793,20 +3802,20 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->MirrPresU_NumRegR |= 1 << i;
+ }
+ /* Get byte62: Reference Raw Card information. We dont need it now. */
+- /* byte = mctRead_SPD(smbaddr, SPD_RefRawCard); */
++ /* byte = pDCTstat->spd_data.spd_bytes[i][SPD_RefRawCard]; */
+ /* Get Byte65/66 for register manufacture ID code */
+- if ((0x97 == mctRead_SPD(smbaddr, SPD_RegManufactureID_H)) &&
+- (0x80 == mctRead_SPD(smbaddr, SPD_RegManufactureID_L))) {
+- if (0x16 == mctRead_SPD(smbaddr, SPD_RegManRevID))
++ if ((0x97 == pDCTstat->spd_data.spd_bytes[i][SPD_RegManufactureID_H]) &&
++ (0x80 == pDCTstat->spd_data.spd_bytes[i][SPD_RegManufactureID_L])) {
++ if (0x16 == pDCTstat->spd_data.spd_bytes[i][SPD_RegManRevID])
+ pDCTstat->RegMan2Present |= 1 << i;
+ else
+ pDCTstat->RegMan1Present |= 1 << i;
+ }
+ /* Get Control word values for RC3. We dont need it. */
+- byte = mctRead_SPD(smbaddr, 70);
++ byte = pDCTstat->spd_data.spd_bytes[i][70];
+ pDCTstat->CtrlWrd3 |= (byte >> 4) << (i << 2); /* C3 = SPD byte 70 [7:4] */
+ /* Get Control word values for RC4, and RC5 */
+- byte = mctRead_SPD(smbaddr, 71);
++ byte = pDCTstat->spd_data.spd_bytes[i][71];
+ pDCTstat->CtrlWrd4 |= (byte & 0xFF) << (i << 2); /* RC4 = SPD byte 71 [3:0] */
+ pDCTstat->CtrlWrd5 |= (byte >> 4) << (i << 2); /* RC5 = SPD byte 71 [7:4] */
+ }
+@@ -6184,14 +6193,14 @@ static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) {
+ * 1010 001111 16 3 10 4GB
+ * 1011 010111 16 3 11 8GB
+ */
+-u8 crcCheck(u8 smbaddr)
++uint8_t crcCheck(struct DCTStatStruc *pDCTstat, uint8_t dimm)
+ {
+ u8 byte_use;
+ u8 Index;
+ u16 CRC;
+ u8 byte, i;
+
+- byte_use = mctRead_SPD(smbaddr, SPD_ByteUse);
++ byte_use = pDCTstat->spd_data.spd_bytes[dimm][SPD_ByteUse];
+ if (byte_use & 0x80)
+ byte_use = 117;
+ else
+@@ -6199,7 +6208,7 @@ u8 crcCheck(u8 smbaddr)
+
+ CRC = 0;
+ for (Index = 0; Index < byte_use; Index ++) {
+- byte = mctRead_SPD(smbaddr, Index);
++ byte = pDCTstat->spd_data.spd_bytes[dimm][Index];
+ CRC ^= byte << 8;
+ for (i=0; i<8; i++) {
+ if (CRC & 0x8000) {
+@@ -6209,5 +6218,5 @@ u8 crcCheck(u8 smbaddr)
+ CRC <<= 1;
+ }
+ }
+- return CRC == (mctRead_SPD(smbaddr, SPD_byte_127) << 8 | mctRead_SPD(smbaddr, SPD_byte_126));
++ return CRC == (pDCTstat->spd_data.spd_bytes[dimm][SPD_byte_127] << 8 | pDCTstat->spd_data.spd_bytes[dimm][SPD_byte_126]);
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 50fbff7..5bb09b4 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -322,6 +322,11 @@ struct MCTStatStruc {
+ ===============================================================================*/
+ #include "mwlc_d.h" /* I have to */
+
++struct amd_spd_node_data {
++ uint8_t spd_bytes[MAX_DIMMS_SUPPORTED][256]; /* [DIMM][byte] */
++ uint8_t spd_address[MAX_DIMMS_SUPPORTED]; /* [DIMM] */
++} __attribute__((packed));
++
+ struct DCTStatStruc { /* A per Node structure*/
+ /* DCTStatStruct_F - start */
+ u8 Node_ID; /* Node ID of current controller */
+@@ -615,6 +620,8 @@ struct DCTStatStruc { /* A per Node structure*/
+ char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH+1];
+ uint16_t DimmRevisionNumber[MAX_DIMMS_SUPPORTED];
+ uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED];
++
++ struct amd_spd_node_data spd_data;
+ } __attribute__((packed));
+
+ struct amd_s3_persistent_mct_channel_data {
+--
+1.9.1
+