aboutsummaryrefslogtreecommitdiff
path: root/resources/libreboot/patch/kgpe-d16/0071-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch
blob: 7a84e7206fa961ef91b2ddc6fdc8b38aa8b0e0be (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
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