summaryrefslogtreecommitdiffstats
path: root/resources/libreboot/patch/coreboot/33fb4cf0ffb01be8bcb6b488872c87eb50e7d77f/grub/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch
diff options
context:
space:
mode:
authorFrancis Rowe <info@gluglug.org.uk>2016-01-02 17:10:32 (EST)
committer Francis Rowe <info@gluglug.org.uk>2016-01-04 15:28:39 (EST)
commitd1f408f3725aa02bc1d76c4c6aadb4697bd073c0 (patch)
tree7eed036543ae1f8c57b56825880a722a8efbedf1 /resources/libreboot/patch/coreboot/33fb4cf0ffb01be8bcb6b488872c87eb50e7d77f/grub/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch
parent91aec7e72005dcda72d19f2d024a02d8c0f86590 (diff)
downloadlibreboot-d1f408f3725aa02bc1d76c4c6aadb4697bd073c0.zip
libreboot-d1f408f3725aa02bc1d76c4c6aadb4697bd073c0.tar.gz
libreboot-d1f408f3725aa02bc1d76c4c6aadb4697bd073c0.tar.bz2
Use different coreboot revisions and patches per board
The release archives will be bigger, but this is a necessary change that makes libreboot development easier. At present, there are boards maintained in libreboot by different people. By doing it this way, that becomes much easier. This is in contrast to the present situation, where a change to one board potentially affects all other boards, especially when updating to a new version of coreboot. Coreboot-libre scripts, download scripts, build scripts - everything. The entire build system has been modified to reflect this change of development. For reasons of consistency, cbfstool and nvramtool are no longer included in the util archives.
Diffstat (limited to 'resources/libreboot/patch/coreboot/33fb4cf0ffb01be8bcb6b488872c87eb50e7d77f/grub/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch')
-rw-r--r--resources/libreboot/patch/coreboot/33fb4cf0ffb01be8bcb6b488872c87eb50e7d77f/grub/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch474
1 files changed, 474 insertions, 0 deletions
diff --git a/resources/libreboot/patch/coreboot/33fb4cf0ffb01be8bcb6b488872c87eb50e7d77f/grub/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch b/resources/libreboot/patch/coreboot/33fb4cf0ffb01be8bcb6b488872c87eb50e7d77f/grub/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch
new file mode 100644
index 0000000..7a84e72
--- /dev/null
+++ b/resources/libreboot/patch/coreboot/33fb4cf0ffb01be8bcb6b488872c87eb50e7d77f/grub/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch
@@ -0,0 +1,474 @@
+From b2b65511ad56a90e2f206d99d348854d379a719b Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <tpearson@raptorengineeringinc.com>
+Date: Thu, 25 Jun 2015 15:07:34 -0500
+Subject: [PATCH 071/143] cpu/amd: Fix AMD Family 15h ECC initialization
+ reliability issues
+
+Change-Id: I7f009b655f8500aeb22981f7020f1db74cdd6925
+Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com>
+---
+ src/cpu/amd/car/cache_as_ram.inc | 4 +
+ src/cpu/amd/family_10h-family_15h/init_cpus.c | 16 ++++
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 12 +--
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 6 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 21 ++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c | 110 +++++++++++-------------
+ src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c | 6 +-
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 57 +++++++-----
+ 8 files changed, 136 insertions(+), 96 deletions(-)
+
+diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc
+index 9edc41f..5db9224 100644
+--- a/src/cpu/amd/car/cache_as_ram.inc
++++ b/src/cpu/amd/car/cache_as_ram.inc
+@@ -362,12 +362,16 @@ clear_fixed_var_mtrr_out:
+ simplemask CacheSize, 0
+ wrmsr
+
++ jmp_if_fam15h(fam15_skip_dram_mtrr_setup)
++
+ /* Enable memory access for first MBs using top_mem. */
+ movl $TOP_MEM, %ecx
+ xorl %edx, %edx
+ movl $(((CONFIG_RAMTOP) + TOP_MEM_MASK) & ~TOP_MEM_MASK) , %eax
+ wrmsr
+
++fam15_skip_dram_mtrr_setup:
++
+ #if CONFIG_XIP_ROM_SIZE
+
+ /* Enable write base caching so we can do execute in place (XIP)
+diff --git a/src/cpu/amd/family_10h-family_15h/init_cpus.c b/src/cpu/amd/family_10h-family_15h/init_cpus.c
+index 061bba2..d45671c 100644
+--- a/src/cpu/amd/family_10h-family_15h/init_cpus.c
++++ b/src/cpu/amd/family_10h-family_15h/init_cpus.c
+@@ -317,6 +317,22 @@ static void STOP_CAR_AND_CPU(uint8_t skip_sharedc_config, uint32_t apicid)
+ msr = rdmsr(BU_CFG2);
+ msr.lo &= ~(1 << ClLinesToNbDis);
+ wrmsr(BU_CFG2, msr);
++ } else {
++ /* Family 15h or later
++ * DRAM setup is delayed on Fam15 in order to prevent
++ * any DRAM access before ECC check bits are initialized.
++ * Each core also needs to have its initial DRAM map initialized
++ * before it is put to sleep, otherwise it will fail to wake
++ * in ramstage. To meet both of these goals, delay DRAM map
++ * setup until the last possible moment, where speculative
++ * memory access is highly unlikely before core halt...
++ */
++ if (!skip_sharedc_config) {
++ /* Enable memory access for first MBs using top_mem */
++ msr.hi = 0;
++ msr.lo = (CONFIG_RAMTOP + TOP_MEM_MASK) & (~TOP_MEM_MASK);
++ wrmsr(TOP_MEM, msr);
++ }
+ }
+
+ disable_cache_as_ram(skip_sharedc_config); // inline
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 78bc8b3..dda997e 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1458,8 +1458,7 @@ restartinit:
+ HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/
+ mctHookAfterHTMap();
+
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
+- CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctHookAfterCPU\n");
+ mctHookAfterCPU(); /* Setup external northbridge(s) */
+
+ /* FIXME
+@@ -1482,9 +1481,6 @@ restartinit:
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n");
+ 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 */
+-
+ if (!allow_config_restore) {
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n");
+ mct_OtherTiming(pMCTstat, pDCTstatA);
+@@ -1505,6 +1501,12 @@ restartinit:
+ MCTMemClr_D(pMCTstat,pDCTstatA);
+ }
+
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
++ CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
++ UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 11555ae..ac8c934 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -725,8 +725,10 @@ struct amd_s3_persistent_mct_channel_data {
+ uint32_t f2x9cx30[12];
+ uint32_t f2x9cx40[12];
+
+- /* Other (1 dword) */
++ /* Other (3 dwords) */
+ uint32_t f3x58;
++ uint32_t f3x5c;
++ uint32_t f3x60;
+
+ /* Family 15h-specific registers (90 dwords) */
+ uint32_t f2x200;
+@@ -785,7 +787,7 @@ struct amd_s3_persistent_mct_channel_data {
+ uint32_t f2x9cx0d0f0_0_f_31[9]; /* [lane] */
+ uint32_t f2x9cx0d0f8021;
+
+- /* TOTAL: 340 dwords */
++ /* TOTAL: 342 dwords */
+ } __attribute__((packed));
+
+ struct amd_s3_persistent_node_data {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index 740edae..b0ad54b 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -902,6 +902,16 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ uint32_t dev = pDCTstat->dev_dct;
+ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+
++#if DQS_TRAIN_DEBUG > 0
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++#endif
++
++ mem_clk = Get_NB32_DCT(dev, dct, 0x94) & 0x1f;
++ if (fam15h_freq_tab[mem_clk] == 0) {
++ pDCTstat->CH_MaxRdLat[dct] = 0x55;
++ return;
++ }
++
+ /* P is specified in PhyCLKs (1/2 MEMCLKs) */
+ for (nb_pstate = 0; nb_pstate < 2; nb_pstate++) {
+ /* 2.10.5.8.5 (2) */
+@@ -949,7 +959,6 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ t += 800;
+
+ /* 2.10.5.8.5 (10) */
+- mem_clk = Get_NB32_DCT(dev, dct, 0x94) & 0x1f;
+ dword = Get_NB32(pDCTstat->dev_nbctl, (0x160 + (nb_pstate * 4))); /* Retrieve NbDid, NbFid */
+ nb_clk = (200 * (((dword >> 1) & 0x1f) + 0x4)) / (((dword >> 7) & 0x1)?2:1);
+ n = (((((uint64_t)p * 1000000000000ULL)/(((uint64_t)fam15h_freq_tab[mem_clk] * 1000000ULL) * 2)) + ((uint64_t)t)) * ((uint64_t)nb_clk * 1000)) / 1000000000ULL;
+@@ -964,8 +973,16 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ Set_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210, dword);
+
+ /* Save result for later use */
+- pDCTstat->CH_MaxRdLat[dct] = n;
++ pDCTstat->CH_MaxRdLat[dct] = n - 1;
++
++#if DQS_TRAIN_DEBUG > 0
++ printk(BIOS_DEBUG, "%s: CH_MaxRdLat[%d]: %03x\n", __func__, dct, pDCTstat->CH_MaxRdLat[dct]);
++#endif
+ }
++
++#if DQS_TRAIN_DEBUG > 0
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
++#endif
+ }
+
+ static void start_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+index a0482e8..d25ed53 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+@@ -92,13 +92,8 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ uint8_t sync_flood_on_dram_err[MAX_NODES_SUPPORTED];
+ uint8_t sync_flood_on_any_uc_err[MAX_NODES_SUPPORTED];
+
+- uint8_t redirect_ecc_scrub = 0;
+-
+ mctHookBeforeECC();
+
+- if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
+- redirect_ecc_scrub = 1;
+-
+ /* Construct these booleans, based on setup options, for easy handling
+ later in this procedure */
+ OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
+@@ -117,8 +112,11 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ OF_ScrubCTL |= (u32) nvbits << 8;
+ }
+
++ nvbits = mctGet_NVbits(NV_L3BKScrub);
++ OF_ScrubCTL |= (nvbits & 0x1f) << 24; /* L3Scrub = NV_L3BKScrub */
++
+ nvbits = mctGet_NVbits(NV_DramBKScrub);
+- OF_ScrubCTL |= nvbits;
++ OF_ScrubCTL |= nvbits; /* DramScrub = NV_DramBKScrub */
+
+ /* Prevent lockups on DRAM errors during ECC init */
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+@@ -133,6 +131,10 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ dword &= ~(0x1 << 21);
+ Set_NB32(pDCTstat->dev_nbmisc, 0x44, dword);
+
++ /* Clear MC4 error status */
++ pci_write_config32(pDCTstat->dev_nbmisc, 0x48, 0x0);
++ pci_write_config32(pDCTstat->dev_nbmisc, 0x4c, 0x0);
++
+ /* Clear the RAM before enabling ECC to prevent MCE-related lockups */
+ DCTMemClr_Init_D(pMCTstat, pDCTstat);
+ DCTMemClr_Sync_D(pMCTstat, pDCTstat);
+@@ -170,6 +172,9 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ if(LDramECC) { /* if ECC is enabled on this dram */
+ if (OB_NBECC) {
+ mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
++ val = Get_NB32(pDCTstat->dev_dct, 0x110);
++ val |= 1 << 5; /* DctDatIntLv = 1 */
++ Set_NB32(pDCTstat->dev_dct, 0x110, val);
+ dev = pDCTstat->dev_nbmisc;
+ reg = 0x44; /* MCA NB Configuration */
+ val = Get_NB32(dev, reg);
+@@ -180,37 +185,16 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ printk(BIOS_DEBUG, " ECC enabled on node: %02x\n", Node);
+ }
+ } /* this node has ECC enabled dram */
++
++ if (MemClrECC) {
++ DCTMemClr_Sync_D(pMCTstat, pDCTstat);
++ }
+ } else {
+ LDramECC = 0;
+ } /* Node has Dram */
+-
+- if (MemClrECC) {
+- DCTMemClr_Sync_D(pMCTstat, pDCTstat);
+- }
+-
+- if (pDCTstat->LogicalCPUID & (AMD_DR_GT_D0 | AMD_FAM15_ALL)) {
+- /* Set up message triggered C1E */
+- val = pci_read_config32(pDCTstat->dev_nbmisc, 0xd4);
+- val &= ~(0x1 << 15); /* StutterScrubEn = DRAM scrub enabled */
+- val |= (mctGet_NVbits(NV_DramBKScrub)?1:0) << 15;
+- pci_write_config32(pDCTstat->dev_nbmisc, 0xd4, val);
+- }
+ } /* if Node present */
+ }
+
+- /* Restore previous MCA error handling settings */
+- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+- struct DCTStatStruc *pDCTstat;
+- pDCTstat = pDCTstatA + Node;
+-
+- if (NodePresent_D(Node)) {
+- dword = Get_NB32(pDCTstat->dev_nbmisc, 0x44);
+- dword |= (sync_flood_on_dram_err[Node] & 0x1) << 30;
+- dword |= (sync_flood_on_any_uc_err[Node] & 0x1) << 21;
+- Set_NB32(pDCTstat->dev_nbmisc, 0x44, dword);
+- }
+- }
+-
+ if(AllECC)
+ pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
+ else
+@@ -229,19 +213,26 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ /*WE/RE is checked because memory config may have been */
+ if((val & 3)==3) { /* Node has dram populated */
+ if (isDramECCEn_D(pDCTstat)) { /* if ECC is enabled on this dram */
+- if (is_fam15h()) {
+- /* Erratum 505 */
+- fam15h_switch_dct(pDCTstat->dev_map, 0);
+- }
+ dev = pDCTstat->dev_nbmisc;
+ val = curBase << 8;
+ if (OB_ECCRedir) {
+- val |= (1<<0); /* enable redirection */
++ val |= (1 << 0); /* Enable redirection */
+ }
+ Set_NB32(dev, 0x5c, val); /* Dram Scrub Addr Low */
+- val = curBase>>24;
++ val = curBase >> 24;
+ Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
+- Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */
++
++ /* Set scrub rate controls */
++ if (is_fam15h()) {
++ /* Erratum 505 */
++ fam15h_switch_dct(pDCTstat->dev_map, 0);
++ }
++ Set_NB32(dev, 0x58, OF_ScrubCTL); /* Scrub Control */
++ if (is_fam15h()) {
++ fam15h_switch_dct(pDCTstat->dev_map, 1); /* Erratum 505 */
++ Set_NB32(dev, 0x58, OF_ScrubCTL); /* Scrub Control */
++ fam15h_switch_dct(pDCTstat->dev_map, 0); /* Erratum 505 */
++ }
+
+ if (!is_fam15h()) {
+ /* Divisor should not be set deeper than
+@@ -258,36 +249,31 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ }
+ }
+
+- if (is_fam15h()) {
+- uint8_t dct;
+-
+- /* Disable training mode
+- * See fam15EnableTrainingMode for the non-ECC training mode tear-down code
+- */
+- for (dct = 0; dct < 2; dct++) {
+- /* NOTE: Reads use DCT 0 and writes use the current DCT per Erratum 505 */
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, 0, 0x58); /* Scrub Rate Control */
+- dword &= ~(0x1f << 24); /* L3Scrub = NV_L3BKScrub */
+- dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
+- dword &= ~(0x1f); /* DramScrub = NV_DramBKScrub */
+- dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */
+-
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */
+- dword &= ~(0x1); /* ScrubReDirEn = redirect_ecc_scrub */
+- dword |= redirect_ecc_scrub & 0x1;
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */
+-
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */
+- dword &= ~(0x1 << 4); /* L3ScrbRedirDis = 0 */
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */
+- }
++ if (pDCTstat->LogicalCPUID & (AMD_DR_GT_D0 | AMD_FAM15_ALL)) {
++ /* Set up message triggered C1E */
++ val = pci_read_config32(pDCTstat->dev_nbmisc, 0xd4);
++ val &= ~(0x1 << 15); /* StutterScrubEn = DRAM scrub enabled */
++ val |= (mctGet_NVbits(NV_DramBKScrub)?1:0) << 15;
++ pci_write_config32(pDCTstat->dev_nbmisc, 0xd4, val);
+ }
+ } /* this node has ECC enabled dram */
+ } /*Node has Dram */
+ } /*if Node present */
+ }
+
++ /* Restore previous MCA error handling settings */
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (NodePresent_D(Node)) {
++ dword = Get_NB32(pDCTstat->dev_nbmisc, 0x44);
++ dword |= (sync_flood_on_dram_err[Node] & 0x1) << 30;
++ dword |= (sync_flood_on_any_uc_err[Node] & 0x1) << 21;
++ Set_NB32(pDCTstat->dev_nbmisc, 0x44, dword);
++ }
++ }
++
+ if(mctGet_NVbits(NV_SyncOnUnEccEn))
+ setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
+index 596fb23..abc8ae3 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
+@@ -232,9 +232,9 @@ void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat
+ Cache32bTOP = val;
+ pMCTstat->Sub4GCacheTop = val;
+
+- /*======================================================================
+- * Clear variable MTRR values
+- *======================================================================*/
++ /*======================================================================
++ * Clear variable MTRR values
++ *======================================================================*/
+ addr = 0x200;
+ lo = 0;
+ hi = lo;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index fe89af1..b4a084c 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -89,6 +89,28 @@ static uint32_t read_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint3
+ return pci_read_config32(dev, reg);
+ }
+
++static void write_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg, uint32_t value) {
++ if (is_fam15h()) {
++ uint32_t dword;
++#ifdef __PRE_RAM__
++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
++#else
++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
++#endif
++
++ /* Select DCT */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ pci_write_config32(dev_fn1, 0x10c, dword);
++ } else {
++ /* Apply offset */
++ reg += dct * 0x100;
++ }
++
++ pci_write_config32(dev, reg, value);
++}
++
+ static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index)
+ {
+ uint32_t dword;
+@@ -489,29 +511,17 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
+
+ /* Other */
+ /* ECC scrub rate control */
+- data->f3x58 = pci_read_config32(dev_fn3, 0x58);
++ data->f3x58 = read_config32_dct(dev_fn3, node, 0, 0x58);
++
++ /* ECC scrub location */
++ write_config32_dct(dev_fn3, node, 0, 0x58, 0x0); /* Disable sequential scrub to work around non-atomic location read */
++ data->f3x5c = read_config32_dct(dev_fn3, node, 0, 0x5c);
++ data->f3x60 = read_config32_dct(dev_fn3, node, 0, 0x60);
++ write_config32_dct(dev_fn3, node, 0, 0x58, data->f3x58); /* Re-enable sequential scrub */
+ }
+ }
+ }
+ #else
+-static void write_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg, uint32_t value) {
+- if (is_fam15h()) {
+- uint32_t dword;
+- device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
+-
+- /* Select DCT */
+- dword = pci_read_config32(dev_fn1, 0x10c);
+- dword &= ~0x1;
+- dword |= (dct & 0x1);
+- pci_write_config32(dev_fn1, 0x10c, dword);
+- } else {
+- /* Apply offset */
+- reg += dct * 0x100;
+- }
+-
+- pci_write_config32(dev, reg, value);
+-}
+-
+ static void write_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg, uint32_t value) {
+ uint32_t dword;
+ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
+@@ -613,8 +623,7 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ 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 {
++ } else {
+ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78);
+ }
+
+@@ -1060,8 +1069,12 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ if (!persistent_data->node[node].node_present)
+ continue;
+
++ /* ECC scrub location */
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, 0, 0x5c, data->f3x5c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, 0, 0x60, data->f3x60);
++
+ /* ECC scrub rate control */
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x58, data->f3x58);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, 0, 0x58, data->f3x58);
+
+ if (is_fam15h())
+ /* Set LockDramCfg and CC6SaveEn */
+--
+1.7.9.5
+