diff options
Diffstat (limited to 'resources/libreboot/patch/kgpe-d16/0044-amd-amdmct-mct_ddr3-Use-training-values-from-previou.patch')
-rw-r--r-- | resources/libreboot/patch/kgpe-d16/0044-amd-amdmct-mct_ddr3-Use-training-values-from-previou.patch | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/resources/libreboot/patch/kgpe-d16/0044-amd-amdmct-mct_ddr3-Use-training-values-from-previou.patch b/resources/libreboot/patch/kgpe-d16/0044-amd-amdmct-mct_ddr3-Use-training-values-from-previou.patch new file mode 100644 index 00000000..e1c7c3f2 --- /dev/null +++ b/resources/libreboot/patch/kgpe-d16/0044-amd-amdmct-mct_ddr3-Use-training-values-from-previou.patch @@ -0,0 +1,781 @@ +From d95a5bb7caa2d95f531ff529269509b9c38bbf72 Mon Sep 17 00:00:00 2001 +From: Timothy Pearson <tpearson@raptorengineeringinc.com> +Date: Thu, 4 Jun 2015 00:11:03 -0500 +Subject: [PATCH 044/143] amd/amdmct/mct_ddr3: Use training values from + previous boot if possible + +DRAM training accounts for most of the romstage startup time, yet +if the hardware configuration has not changed from the previous boot +the previously discovered training values are still valid. Use them +if the DIMM configuration has not changed since the last boot. + +Change-Id: I37ed277b16476d38e4af76c6ae827a575c6b017d +Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com> +--- + src/mainboard/asus/kgpe-d16/cmos.layout | 1 + + src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 206 +++++++++++++++++-------- + src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 6 + + src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 175 ++++++++++++++++++--- + src/northbridge/amd/amdmct/mct_ddr3/s3utils.h | 10 +- + src/northbridge/amd/amdmct/wrappers/mcti_d.c | 4 + + 6 files changed, 308 insertions(+), 94 deletions(-) + +diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout +index e55edc4..7944631 100644 +--- a/src/mainboard/asus/kgpe-d16/cmos.layout ++++ b/src/mainboard/asus/kgpe-d16/cmos.layout +@@ -43,6 +43,7 @@ entries + 458 4 e 11 hypertransport_speed_limit + 462 2 e 12 minimum_memory_voltage + 464 1 e 2 compute_unit_siblings ++465 1 r 0 allow_spd_nvram_cache_restore + 477 1 e 1 ieee1394 + 728 256 h 0 user_data + 984 16 h 0 check_sum +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c +index e60adb7..20e66f2 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c +@@ -39,7 +39,8 @@ + static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA); + static void DQSTiming_D(struct MCTStatStruc *pMCTstat, +- struct DCTStatStruc *pDCTstatA); ++ struct DCTStatStruc *pDCTstatA, ++ uint8_t allow_config_restore); + static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA); + static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat, +@@ -355,8 +356,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs + else if (drive_strength == 0x3) + calibration_code = 0xfff; + } +- } +- else if (ddr_voltage_index & 0x2) { ++ } else if (ddr_voltage_index & 0x2) { + /* 1.35V */ + /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 42 */ + if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { +@@ -390,8 +390,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs + else if (drive_strength == 0x3) + calibration_code = 0xdb6; + } +- } +- else if (ddr_voltage_index & 0x1) { ++ } else if (ddr_voltage_index & 0x1) { + /* 1.5V */ + /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 41 */ + if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { +@@ -426,8 +425,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs + calibration_code = 0xb6d; + } + } +- } +- else if (package_type == PT_C3) { ++ } else if (package_type == PT_C3) { + /* Socket C32 */ + if (ddr_voltage_index & 0x4) { + /* 1.25V */ +@@ -473,8 +471,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs + else if (drive_strength == 0x3) + calibration_code = 0xfff; + } +- } +- else if (ddr_voltage_index & 0x2) { ++ } else if (ddr_voltage_index & 0x2) { + /* 1.35V */ + /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 45 */ + if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { +@@ -518,8 +515,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs + else if (drive_strength == 0x3) + calibration_code = 0xdb6; + } +- } +- else if (ddr_voltage_index & 0x1) { ++ } else if (ddr_voltage_index & 0x1) { + /* 1.5V */ + /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 44 */ + if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { +@@ -619,8 +615,7 @@ static uint32_t fam15h_phy_predriver_cmd_addr_calibration_code(struct DCTStatStr + else if (drive_strength == 0x3) + calibration_code = 0xb64; + } +- } +- else if (ddr_voltage_index & 0x2) { ++ } else if (ddr_voltage_index & 0x2) { + /* 1.35V */ + /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 51 */ + if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { +@@ -654,8 +649,7 @@ static uint32_t fam15h_phy_predriver_cmd_addr_calibration_code(struct DCTStatStr + else if (drive_strength == 0x3) + calibration_code = 0x924; + } +- } +- else if (ddr_voltage_index & 0x1) { ++ } else if (ddr_voltage_index & 0x1) { + /* 1.5V */ + /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 50 */ + if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { +@@ -736,8 +730,7 @@ static uint32_t fam15h_phy_predriver_clk_calibration_code(struct DCTStatStruc *p + else if (drive_strength == 0x3) + calibration_code = 0xff6; + } +- } +- else if (ddr_voltage_index & 0x2) { ++ } else if (ddr_voltage_index & 0x2) { + /* 1.35V */ + /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 54 */ + if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { +@@ -771,8 +764,7 @@ static uint32_t fam15h_phy_predriver_clk_calibration_code(struct DCTStatStruc *p + else if (drive_strength == 0x3) + calibration_code = 0xdad; + } +- } +- else if (ddr_voltage_index & 0x1) { ++ } else if (ddr_voltage_index & 0x1) { + /* 1.5V */ + /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 53 */ + if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { +@@ -841,16 +833,13 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT + else if (MemClkFreq == 0x6) { + /* DDR3-800 */ + calibration_code = 0x10112222; +- } +- else if (MemClkFreq == 0xa) { ++ } else if (MemClkFreq == 0xa) { + /* DDR3-1066 */ + calibration_code = 0x20112222; +- } +- else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) { ++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) { + /* DDR3-1333 - DDR3-1600 */ + calibration_code = 0x30112222; +- } +- else if (MemClkFreq == 0x16) { ++ } else if (MemClkFreq == 0x16) { + /* DDR3-1866 */ + calibration_code = 0x30332222; + } +@@ -860,16 +849,13 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT + if (MemClkFreq == 0x4) { + /* DDR3-667 */ + calibration_code = 0x00112222; +- } +- else if (MemClkFreq == 0x6) { ++ } else if (MemClkFreq == 0x6) { + /* DDR3-800 */ + calibration_code = 0x10112222; +- } +- else if (MemClkFreq == 0xa) { ++ } else if (MemClkFreq == 0xa) { + /* DDR3-1066 */ + calibration_code = 0x20112222; +- } +- else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) { ++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) { + /* DDR3-1333 - DDR3-1600 */ + calibration_code = 0x30112222; + } +@@ -881,20 +867,16 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT + if (MemClkFreq == 0x4) { + /* DDR3-667 */ + calibration_code = 0x10222222; +- } +- else if (MemClkFreq == 0x6) { ++ } else if (MemClkFreq == 0x6) { + /* DDR3-800 */ + calibration_code = 0x20222222; +- } +- else if (MemClkFreq == 0xa) { ++ } else if (MemClkFreq == 0xa) { + /* DDR3-1066 */ + calibration_code = 0x30222222; +- } +- else if (MemClkFreq == 0xe) { ++ } else if (MemClkFreq == 0xe) { + /* DDR3-1333 */ + calibration_code = 0x30222222; +- } +- else if (MemClkFreq == 0x12) { ++ } else if (MemClkFreq == 0x12) { + /* DDR3-1600 */ + if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1)) + calibration_code = 0x30222222; +@@ -1081,8 +1063,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc + || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) { + /* DDR3-667 - DDR3-1333 */ + slow_access = 0; +- } +- else if (MemClkFreq == 0x12) { ++ } else if (MemClkFreq == 0x12) { + /* DDR3-1600 */ + if (rank_count_dimm0 == 1) + slow_access = 0; +@@ -1098,8 +1079,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc + || (MemClkFreq == 0xa)) { + /* DDR3-667 - DDR3-1066 */ + slow_access = 0; +- } +- else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) { ++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) { + /* DDR3-1333 - DDR3-1600 */ + slow_access = 1; + } +@@ -1184,6 +1164,30 @@ static void read_spd_bytes(struct MCTStatStruc *pMCTstat, + } + } + ++#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) ++static void calculate_and_store_spd_hashes(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat) ++{ ++ uint8_t dimm; ++ ++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) { ++ calculate_spd_hash(pDCTstat->spd_data.spd_bytes[dimm], &pDCTstat->spd_data.spd_hash[dimm]); ++ } ++} ++ ++static void compare_nvram_spd_hashes(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat) ++{ ++ uint8_t dimm; ++ ++ pDCTstat->spd_data.nvram_spd_match = 1; ++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) { ++ if (pDCTstat->spd_data.spd_hash[dimm] != pDCTstat->spd_data.nvram_spd_hash[dimm]) ++ pDCTstat->spd_data.nvram_spd_match = 0; ++ } ++} ++#endif ++ + static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA) + { +@@ -1232,6 +1236,8 @@ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat, + */ + u8 Node, NodesWmem; + u32 node_sys_base; ++ uint8_t nvram; ++ uint8_t allow_config_restore; + + uint8_t s3resume = acpi_is_wakeup_s3(); + +@@ -1248,7 +1254,7 @@ restartinit: + + #if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) + printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DCT configuration from NVRAM\n"); +- restore_mct_information_from_nvram(); ++ restore_mct_information_from_nvram(0); + #endif + + printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n"); +@@ -1298,11 +1304,26 @@ restartinit: + node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F; + } + ++ /* If the boot fails make sure training is attempted after reset */ ++ nvram = 0; ++ set_option("allow_spd_nvram_cache_restore", &nvram); ++ + #if IS_ENABLED(DIMM_VOLTAGE_SET_SUPPORT) + printk(BIOS_DEBUG, "mctAutoInitMCT_D: DIMMSetVoltage\n"); + DIMMSetVoltages(pMCTstat, pDCTstatA); /* Set the DIMM voltages (mainboard specific) */ + #endif + ++ /* If DIMM configuration has not changed since last boot restore training values */ ++ allow_config_restore = 1; ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { ++ struct DCTStatStruc *pDCTstat; ++ pDCTstat = pDCTstatA + Node; ++ ++ if (pDCTstat->NodePresent) ++ if (!pDCTstat->spd_data.nvram_spd_match) ++ allow_config_restore = 0; ++ } ++ + for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { + struct DCTStatStruc *pDCTstat; + pDCTstat = pDCTstatA + Node; +@@ -1336,14 +1357,33 @@ restartinit: + CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */ + mctHookAfterCPU(); /* Setup external northbridge(s) */ + ++ /* FIXME ++ * Previous training values should only be used if the current desired ++ * speed is the same as the speed used in the previous boot. ++ * How to get the desired speed at this point in the code? ++ */ ++#if 0 ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { ++ struct DCTStatStruc *pDCTstat; ++ pDCTstat = pDCTstatA + Node; ++ ++ if (pDCTstat->NodePresent) { ++ if (pDCTstat->spd_data.nvram_memclk[0] != pDCTstat->DIMMAutoSpeed) ++ allow_config_restore = 0; ++ } ++ } ++#endif ++ + printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n"); +- DQSTiming_D(pMCTstat, pDCTstatA); /* Get Receiver Enable and DQS signal timing*/ ++ DQSTiming_D(pMCTstat, pDCTstatA, allow_config_restore); /* Get Receiver Enable and DQS signal timing*/ + + printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n"); + UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */ + +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n"); +- mct_OtherTiming(pMCTstat, pDCTstatA); ++ if (!allow_config_restore) { ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n"); ++ mct_OtherTiming(pMCTstat, pDCTstatA); ++ } + + if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/ + goto restartinit; +@@ -1825,7 +1865,7 @@ static void exit_training_mode_fam15(struct MCTStatStruc *pMCTstat, + } + + static void DQSTiming_D(struct MCTStatStruc *pMCTstat, +- struct DCTStatStruc *pDCTstatA) ++ struct DCTStatStruc *pDCTstatA, uint8_t allow_config_restore) + { + u8 nv_DQSTrainCTL; + +@@ -1833,9 +1873,8 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat, + return; + } + +- nv_DQSTrainCTL = mctGet_NVbits(NV_DQSTrainCTL); +- /* FIXME: BOZO- DQS training every time*/ +- nv_DQSTrainCTL = 1; ++ // nv_DQSTrainCTL = mctGet_NVbits(NV_DQSTrainCTL); ++ nv_DQSTrainCTL = !allow_config_restore; + + mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA); + phyAssistedMemFnceTraining(pMCTstat, pDCTstatA); +@@ -1854,15 +1893,16 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat, + } + } + ++ mctHookBeforeAnyTraining(pMCTstat, pDCTstatA); ++ if (!is_fam15h()) { ++ /* TODO: should be in mctHookBeforeAnyTraining */ ++ _WRMSR(0x26C, 0x04040404, 0x04040404); ++ _WRMSR(0x26D, 0x04040404, 0x04040404); ++ _WRMSR(0x26E, 0x04040404, 0x04040404); ++ _WRMSR(0x26F, 0x04040404, 0x04040404); ++ } ++ + if (nv_DQSTrainCTL) { +- mctHookBeforeAnyTraining(pMCTstat, pDCTstatA); +- if (!is_fam15h()) { +- /* TODO: should be in mctHookBeforeAnyTraining */ +- _WRMSR(0x26C, 0x04040404, 0x04040404); +- _WRMSR(0x26D, 0x04040404, 0x04040404); +- _WRMSR(0x26E, 0x04040404, 0x04040404); +- _WRMSR(0x26F, 0x04040404, 0x04040404); +- } + mct_WriteLevelization_HW(pMCTstat, pDCTstatA, FirstPass); + + if (is_fam15h()) { +@@ -1892,18 +1932,25 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat, + exit_training_mode_fam15(pMCTstat, pDCTstatA); + else + mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA); ++ } else { ++ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, FirstPass); + +- /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */ +- mctHookAfterAnyTraining(); +- mctSaveDQSSigTmg_D(); ++ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, SecondPass); + +- MCTMemClr_D(pMCTstat, pDCTstatA); +- } else { +- mctGetDQSSigTmg_D(); /* get values into data structure */ +- LoadDQSSigTmgRegs_D(pMCTstat, pDCTstatA); /* load values into registers.*/ +- /* mctDoWarmResetMemClr_D(); */ +- MCTMemClr_D(pMCTstat, pDCTstatA); ++#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DIMM training configuration from NVRAM\n"); ++ restore_mct_information_from_nvram(1); ++#endif ++ ++ if (is_fam15h()) ++ exit_training_mode_fam15(pMCTstat, pDCTstatA); + } ++ ++ /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */ ++ mctHookAfterAnyTraining(); ++ ++ /* mctDoWarmResetMemClr_D(); */ ++ MCTMemClr_D(pMCTstat, pDCTstatA); + } + + static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, +@@ -3913,6 +3960,8 @@ static void mct_preInitDCT(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat) + { + u8 err_code; ++ uint8_t nvram; ++ uint8_t allow_config_restore; + + /* Preconfigure DCT0 */ + DCTPreInit_D(pMCTstat, pDCTstat, 0); +@@ -3927,6 +3976,27 @@ static void mct_preInitDCT(struct MCTStatStruc *pMCTstat, + pDCTstat->ErrCode = err_code; /* Using DCT0 Error code to update pDCTstat.ErrCode */ + } + } ++ ++#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) ++ calculate_and_store_spd_hashes(pMCTstat, pDCTstat); ++ ++ if (load_spd_hashes_from_nvram(pDCTstat) < 0) { ++ pDCTstat->spd_data.nvram_spd_match = 0; ++ } ++ else { ++ compare_nvram_spd_hashes(pMCTstat, pDCTstat); ++ } ++#else ++ pDCTstat->spd_data.nvram_spd_match = 0; ++#endif ++ ++ /* Check to see if restoration of SPD data from NVRAM is allowed */ ++ allow_config_restore = 0; ++ if (get_option(&nvram, "allow_spd_nvram_cache_restore") == CB_SUCCESS) ++ allow_config_restore = !!nvram; ++ ++ if (!allow_config_restore) ++ pDCTstat->spd_data.nvram_spd_match = 0; + } + + static void mct_initDCT(struct MCTStatStruc *pMCTstat, +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h +index 5bb09b4..539ecc3 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h +@@ -325,6 +325,10 @@ struct MCTStatStruc { + struct amd_spd_node_data { + uint8_t spd_bytes[MAX_DIMMS_SUPPORTED][256]; /* [DIMM][byte] */ + uint8_t spd_address[MAX_DIMMS_SUPPORTED]; /* [DIMM] */ ++ uint64_t spd_hash[MAX_DIMMS_SUPPORTED]; /* [DIMM] */ ++ uint64_t nvram_spd_hash[MAX_DIMMS_SUPPORTED]; /* [DIMM] */ ++ uint8_t nvram_spd_match; ++ uint8_t nvram_memclk[2]; /* [channel] */ + } __attribute__((packed)); + + struct DCTStatStruc { /* A per Node structure*/ +@@ -784,6 +788,8 @@ struct amd_s3_persistent_mct_channel_data { + + struct amd_s3_persistent_node_data { + uint32_t node_present; ++ uint64_t spd_hash[MAX_DIMMS_SUPPORTED]; ++ uint8_t memclk[2]; + struct amd_s3_persistent_mct_channel_data channel[2]; + } __attribute__((packed)); + +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c +index aa23951..fa1873a 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c +@@ -26,8 +26,10 @@ + #include <device/pci_ops.h> + #include <console/console.h> + #include <cbfs.h> ++#include <cbmem.h> + #include <spi-generic.h> + #include <spi_flash.h> ++#include <pc80/mc146818rtc.h> + + #include "s3utils.h" + +@@ -124,6 +126,68 @@ static uint32_t read_amd_dct_index_register_dct(device_t dev, uint8_t node, uint + return read_amd_dct_index_register(dev, index_ctl_reg, index); + } + ++/* Non-cryptographic 64-bit hash function taken from Stack Overflow: ++ * http://stackoverflow.com/a/13326345 ++ * Any 64-bit hash with sufficiently low collision potential ++ * could be used instead. ++ */ ++void calculate_spd_hash(uint8_t *spd_data, uint64_t *spd_hash) ++{ ++ const unsigned long long prime = 2654435789ULL; ++ uint16_t byte; ++ *spd_hash = 104395301; ++ ++ for (byte = 0; byte < 256; byte++) ++ *spd_hash += (spd_data[byte] * prime) ^ (*spd_hash >> 23); ++ ++ *spd_hash = *spd_hash ^ (*spd_hash << 37); ++} ++ ++static struct amd_s3_persistent_data * map_s3nv_in_nvram(void) ++{ ++ ssize_t s3nv_offset; ++ ssize_t s3nv_file_offset; ++ void * s3nv_cbfs_file_ptr; ++ struct amd_s3_persistent_data *persistent_data; ++ ++ /* Obtain CBFS file offset */ ++ s3nv_offset = get_s3nv_file_offset(); ++ if (s3nv_offset == -1) ++ return NULL; ++ ++ /* Align flash pointer to nearest boundary */ ++ s3nv_file_offset = s3nv_offset; ++ s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1); ++ s3nv_offset += CONFIG_S3_DATA_SIZE; ++ s3nv_file_offset = s3nv_offset - s3nv_file_offset; ++ ++ /* Map data structure in CBFS and restore settings */ ++ s3nv_cbfs_file_ptr = cbfs_boot_map_with_leak(S3NV_FILE_NAME, CBFS_TYPE_RAW, NULL); ++ if (!s3nv_cbfs_file_ptr) { ++ printk(BIOS_DEBUG, "S3 state file could not be mapped: %s\n", S3NV_FILE_NAME); ++ return NULL; ++ } ++ persistent_data = (s3nv_cbfs_file_ptr + s3nv_file_offset); ++ ++ return persistent_data; ++} ++ ++#ifdef __PRE_RAM__ ++int8_t load_spd_hashes_from_nvram(struct DCTStatStruc *pDCTstat) ++{ ++ struct amd_s3_persistent_data *persistent_data; ++ ++ persistent_data = map_s3nv_in_nvram(); ++ if (!persistent_data) ++ return -1; ++ ++ memcpy(pDCTstat->spd_data.nvram_spd_hash, persistent_data->node[pDCTstat->Node_ID].spd_hash, sizeof(pDCTstat->spd_data.nvram_spd_hash)); ++ memcpy(pDCTstat->spd_data.nvram_memclk, persistent_data->node[pDCTstat->Node_ID].memclk, sizeof(pDCTstat->spd_data.nvram_memclk)); ++ ++ return 0; ++} ++#endif ++ + #ifdef __RAMSTAGE__ + static uint64_t rdmsr_uint64_t(unsigned long index) { + msr_t msr = rdmsr(index); +@@ -149,6 +213,31 @@ static uint32_t read_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t d + return pci_read_config32(dev, reg); + } + ++static void copy_cbmem_spd_data_to_save_variable(struct amd_s3_persistent_data* persistent_data) ++{ ++ uint8_t node; ++ uint8_t dimm; ++ uint8_t channel; ++ struct amdmct_memory_info *mem_info; ++ mem_info = cbmem_find(CBMEM_ID_AMDMCT_MEMINFO); ++ if (mem_info == NULL) { ++ /* can't find amdmct information in cbmem */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) ++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) ++ persistent_data->node[node].spd_hash[dimm] = 0xffffffffffffffffULL; ++ ++ return; ++ } ++ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) ++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) ++ calculate_spd_hash(mem_info->dct_stat[node].spd_data.spd_bytes[dimm], &persistent_data->node[node].spd_hash[dimm]); ++ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) ++ for (channel = 0; channel < 2; channel++) ++ persistent_data->node[node].memclk[channel] = mem_info->dct_stat[node].Speed; ++} ++ + void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data) + { + uint8_t i; +@@ -441,7 +530,7 @@ static void wrmsr_uint64_t(unsigned long index, uint64_t value) { + wrmsr(index, msr); + } + +-void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data) ++void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data, uint8_t training_only) + { + uint8_t i; + uint8_t j; +@@ -451,6 +540,51 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + uint8_t dct_enabled; + uint32_t dword; + ++ if (training_only) { ++ /* Only restore the Receiver Enable and DQS training registers */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ /* Restore training parameters */ ++ for (i=0; i<4; i++) ++ for (j=0; j<3; j++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]); ++ for (i=0; i<4; i++) ++ for (j=0; j<3; j++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]); ++ ++ for (i=0; i<12; i++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x10 + i, data->f2x9cx10[i]); ++ for (i=0; i<12; i++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x20 + i, data->f2x9cx20[i]); ++ ++ if (IS_ENABLED(CONFIG_DIMM_DDR3)) { ++ for (i=0; i<12; i++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x30 + i, data->f2x9cx30[i]); ++ for (i=0; i<12; i++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x40 + i, data->f2x9cx40[i]); ++ } ++ ++ /* Restore MaxRdLatency */ ++ if (is_fam15h()) { ++ for (i=0; i<4; i++) ++ write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]); ++ } ++ else { ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78); ++ } ++ ++ /* Other timing control registers */ ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x8c, data->f2x8c); ++ } ++ } ++ ++ return; ++ } ++ + /* Load data from data structure into DCTs */ + /* Stage 1 */ + for (node = 0; node < MAX_NODES_SUPPORTED; node++) { +@@ -501,7 +635,8 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + wrmsr_uint64_t(0x00000250, data->msr00000250); + wrmsr_uint64_t(0x00000258, data->msr00000258); + /* FIXME +- * Restoring these MSRs causes a hang on resume ++ * Restoring these MSRs causes a hang on resume due to ++ * destroying CAR while still executing from CAR! + * For now, skip restoration... + */ + // for (i=0; i<8; i++) +@@ -890,6 +1025,8 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + #ifdef __RAMSTAGE__ + int8_t save_mct_information_to_nvram(void) + { ++ uint8_t nvram; ++ + if (acpi_is_wakeup_s3()) + return 0; + +@@ -909,6 +1046,9 @@ int8_t save_mct_information_to_nvram(void) + /* Obtain MCT configuration data */ + copy_mct_data_to_save_variable(persistent_data); + ++ /* Save RAM SPD data at the same time */ ++ copy_cbmem_spd_data_to_save_variable(persistent_data); ++ + /* Obtain CBFS file offset */ + s3nv_offset = get_s3nv_file_offset(); + if (s3nv_offset == -1) +@@ -949,36 +1089,23 @@ int8_t save_mct_information_to_nvram(void) + /* Restore SPI MMIO address */ + pci_write_config32(lpc_dev, 0xa0, spi_mmio_prev); + ++ /* Allow training bypass if DIMM configuration is unchanged on next boot */ ++ nvram = 1; ++ set_option("allow_spd_nvram_cache_restore", &nvram); ++ + return 0; + } + #endif + +-int8_t restore_mct_information_from_nvram(void) ++int8_t restore_mct_information_from_nvram(uint8_t training_only) + { +- ssize_t s3nv_offset; +- ssize_t s3nv_file_offset; +- void * s3nv_cbfs_file_ptr; + struct amd_s3_persistent_data *persistent_data; + +- /* Obtain CBFS file offset */ +- s3nv_offset = get_s3nv_file_offset(); +- if (s3nv_offset == -1) ++ persistent_data = map_s3nv_in_nvram(); ++ if (!persistent_data) + return -1; + +- /* Align flash pointer to nearest boundary */ +- s3nv_file_offset = s3nv_offset; +- s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1); +- s3nv_offset += CONFIG_S3_DATA_SIZE; +- s3nv_file_offset = s3nv_offset - s3nv_file_offset; +- +- /* Map data structure in CBFS and restore settings */ +- s3nv_cbfs_file_ptr = cbfs_boot_map_with_leak(S3NV_FILE_NAME, CBFS_TYPE_RAW, NULL); +- if (!s3nv_cbfs_file_ptr) { +- printk(BIOS_DEBUG, "S3 state file could not be mapped: %s\n", S3NV_FILE_NAME); +- return -1; +- } +- persistent_data = (s3nv_cbfs_file_ptr + s3nv_file_offset); +- restore_mct_data_from_save_variable(persistent_data); ++ restore_mct_data_from_save_variable(persistent_data, training_only); + + return 0; +-} +\ No newline at end of file ++} +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h +index dcddcad..82f73a7 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h ++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h +@@ -20,9 +20,15 @@ + #include "../wrappers/mcti.h" + #include "mct_d.h" + ++void calculate_spd_hash(uint8_t *spd_data, uint64_t *spd_hash); ++ ++#ifdef __PRE_RAM__ ++int8_t load_spd_hashes_from_nvram(struct DCTStatStruc *pDCTstat); ++#endif ++ + #ifdef __RAMSTAGE__ + int8_t save_mct_information_to_nvram(void); + #endif +-int8_t restore_mct_information_from_nvram(void); ++int8_t restore_mct_information_from_nvram(uint8_t training_only); + void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data); +-void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data); +\ No newline at end of file ++void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data, uint8_t training_only); +\ No newline at end of file +diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c +index 9969c4f..5ca8eac 100644 +--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c ++++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c +@@ -400,14 +400,18 @@ static void mctHookAfterCPU(void) + } + + ++#if IS_ENABLED(CONFIG_DIMM_DDR2) + static void mctSaveDQSSigTmg_D(void) + { + } ++#endif + + ++#if IS_ENABLED(CONFIG_DIMM_DDR2) + static void mctGetDQSSigTmg_D(void) + { + } ++#endif + + + static void mctHookBeforeECC(void) +-- +1.7.9.5 + |