aboutsummaryrefslogtreecommitdiff
path: root/resources/libreboot/patch/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch
diff options
context:
space:
mode:
Diffstat (limited to 'resources/libreboot/patch/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch')
-rw-r--r--resources/libreboot/patch/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/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch b/resources/libreboot/patch/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch
new file mode 100644
index 00000000..7a84e720
--- /dev/null
+++ b/resources/libreboot/patch/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
+