blob: 461da1323f2f9019996969ed0179c91d6ecd38c1 [file] [log] [blame]
Doug Thompson2bc65412009-05-04 20:11:14 +02001#include "amd64_edac.h"
Andreas Herrmann23ac4ae2010-09-17 18:03:43 +02002#include <asm/amd_nb.h>
Doug Thompson2bc65412009-05-04 20:11:14 +02003
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01004static struct edac_pci_ctl_info *pci_ctl;
Doug Thompson2bc65412009-05-04 20:11:14 +02005
6static int report_gart_errors;
7module_param(report_gart_errors, int, 0644);
8
9/*
10 * Set by command line parameter. If BIOS has enabled the ECC, this override is
11 * cleared to prevent re-enabling the hardware by this driver.
12 */
13static int ecc_enable_override;
14module_param(ecc_enable_override, int, 0644);
15
Tejun Heoa29d8b82010-02-02 14:39:15 +090016static struct msr __percpu *msrs;
Borislav Petkov50542252009-12-11 18:14:40 +010017
Borislav Petkov2ec591a2015-02-17 10:58:34 +010018/* Per-node stuff */
Borislav Petkovae7bb7c2010-10-14 16:01:30 +020019static struct ecc_settings **ecc_stngs;
Doug Thompson2bc65412009-05-04 20:11:14 +020020
21/*
Borislav Petkovb70ef012009-06-25 19:32:38 +020022 * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
23 * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
24 * or higher value'.
25 *
26 *FIXME: Produce a better mapping/linearisation.
27 */
Daniel J Bluemanc7e53012012-11-30 16:44:20 +080028static const struct scrubrate {
Borislav Petkov39094442010-11-24 19:52:09 +010029 u32 scrubval; /* bit pattern for scrub rate */
30 u32 bandwidth; /* bandwidth consumed (bytes/sec) */
31} scrubrates[] = {
Borislav Petkovb70ef012009-06-25 19:32:38 +020032 { 0x01, 1600000000UL},
33 { 0x02, 800000000UL},
34 { 0x03, 400000000UL},
35 { 0x04, 200000000UL},
36 { 0x05, 100000000UL},
37 { 0x06, 50000000UL},
38 { 0x07, 25000000UL},
39 { 0x08, 12284069UL},
40 { 0x09, 6274509UL},
41 { 0x0A, 3121951UL},
42 { 0x0B, 1560975UL},
43 { 0x0C, 781440UL},
44 { 0x0D, 390720UL},
45 { 0x0E, 195300UL},
46 { 0x0F, 97650UL},
47 { 0x10, 48854UL},
48 { 0x11, 24427UL},
49 { 0x12, 12213UL},
50 { 0x13, 6101UL},
51 { 0x14, 3051UL},
52 { 0x15, 1523UL},
53 { 0x16, 761UL},
54 { 0x00, 0UL}, /* scrubbing off */
55};
56
Borislav Petkov66fed2d2012-08-09 18:41:07 +020057int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
58 u32 *val, const char *func)
Borislav Petkovb2b0c602010-10-08 18:32:29 +020059{
60 int err = 0;
61
62 err = pci_read_config_dword(pdev, offset, val);
63 if (err)
64 amd64_warn("%s: error reading F%dx%03x.\n",
65 func, PCI_FUNC(pdev->devfn), offset);
66
67 return err;
68}
69
70int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
71 u32 val, const char *func)
72{
73 int err = 0;
74
75 err = pci_write_config_dword(pdev, offset, val);
76 if (err)
77 amd64_warn("%s: error writing to F%dx%03x.\n",
78 func, PCI_FUNC(pdev->devfn), offset);
79
80 return err;
81}
82
83/*
Borislav Petkov73ba8592011-09-19 17:34:45 +020084 * Select DCT to which PCI cfg accesses are routed
85 */
86static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
87{
88 u32 reg = 0;
89
90 amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -050091 reg &= (pvt->model == 0x30) ? ~3 : ~1;
Borislav Petkov73ba8592011-09-19 17:34:45 +020092 reg |= dct;
93 amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
94}
95
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -050096/*
97 *
98 * Depending on the family, F2 DCT reads need special handling:
99 *
100 * K8: has a single DCT only and no address offsets >= 0x100
101 *
102 * F10h: each DCT has its own set of regs
103 * DCT0 -> F2x040..
104 * DCT1 -> F2x140..
105 *
106 * F16h: has only 1 DCT
107 *
108 * F15h: we select which DCT we access using F1x10C[DctCfgSel]
109 */
110static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
111 int offset, u32 *val)
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200112{
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -0500113 switch (pvt->fam) {
114 case 0xf:
115 if (dct || offset >= 0x100)
116 return -EINVAL;
117 break;
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200118
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -0500119 case 0x10:
120 if (dct) {
121 /*
122 * Note: If ganging is enabled, barring the regs
123 * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx
124 * return 0. (cf. Section 2.8.1 F10h BKDG)
125 */
126 if (dct_ganging_enabled(pvt))
127 return 0;
128
129 offset += 0x100;
130 }
131 break;
132
133 case 0x15:
134 /*
135 * F15h: F2x1xx addresses do not map explicitly to DCT1.
136 * We should select which DCT we access using F1x10C[DctCfgSel]
137 */
138 dct = (dct && pvt->model == 0x30) ? 3 : dct;
139 f15h_select_dct(pvt, dct);
140 break;
141
142 case 0x16:
143 if (dct)
144 return -EINVAL;
145 break;
146
147 default:
148 break;
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200149 }
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -0500150 return amd64_read_pci_cfg(pvt->F2, offset, val);
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200151}
152
Borislav Petkovb70ef012009-06-25 19:32:38 +0200153/*
Doug Thompson2bc65412009-05-04 20:11:14 +0200154 * Memory scrubber control interface. For K8, memory scrubbing is handled by
155 * hardware and can involve L2 cache, dcache as well as the main memory. With
156 * F10, this is extended to L3 cache scrubbing on CPU models sporting that
157 * functionality.
158 *
159 * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
160 * (dram) over to cache lines. This is nasty, so we will use bandwidth in
161 * bytes/sec for the setting.
162 *
163 * Currently, we only do dram scrubbing. If the scrubbing is done in software on
164 * other archs, we might not have access to the caches directly.
165 */
166
167/*
168 * scan the scrub rate mapping table for a close or matching bandwidth value to
169 * issue. If requested is too big, then use last maximum value found.
170 */
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500171static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
Doug Thompson2bc65412009-05-04 20:11:14 +0200172{
173 u32 scrubval;
174 int i;
175
176 /*
177 * map the configured rate (new_bw) to a value specific to the AMD64
178 * memory controller and apply to register. Search for the first
179 * bandwidth entry that is greater or equal than the setting requested
180 * and program that. If at last entry, turn off DRAM scrubbing.
Andrew Morton168bfee2012-10-23 14:09:39 -0700181 *
182 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
183 * by falling back to the last element in scrubrates[].
Doug Thompson2bc65412009-05-04 20:11:14 +0200184 */
Andrew Morton168bfee2012-10-23 14:09:39 -0700185 for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
Doug Thompson2bc65412009-05-04 20:11:14 +0200186 /*
187 * skip scrub rates which aren't recommended
188 * (see F10 BKDG, F3x58)
189 */
Borislav Petkov395ae782010-10-01 18:38:19 +0200190 if (scrubrates[i].scrubval < min_rate)
Doug Thompson2bc65412009-05-04 20:11:14 +0200191 continue;
192
193 if (scrubrates[i].bandwidth <= new_bw)
194 break;
Doug Thompson2bc65412009-05-04 20:11:14 +0200195 }
196
197 scrubval = scrubrates[i].scrubval;
Doug Thompson2bc65412009-05-04 20:11:14 +0200198
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500199 if (pvt->fam == 0x15 && pvt->model == 0x60) {
200 f15h_select_dct(pvt, 0);
201 pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
202 f15h_select_dct(pvt, 1);
203 pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
204 } else {
205 pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F);
206 }
Doug Thompson2bc65412009-05-04 20:11:14 +0200207
Borislav Petkov39094442010-11-24 19:52:09 +0100208 if (scrubval)
209 return scrubrates[i].bandwidth;
210
Doug Thompson2bc65412009-05-04 20:11:14 +0200211 return 0;
212}
213
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100214static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
Doug Thompson2bc65412009-05-04 20:11:14 +0200215{
216 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkov87b3e0e2011-01-19 20:02:38 +0100217 u32 min_scrubrate = 0x5;
Doug Thompson2bc65412009-05-04 20:11:14 +0200218
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200219 if (pvt->fam == 0xf)
Borislav Petkov87b3e0e2011-01-19 20:02:38 +0100220 min_scrubrate = 0x0;
221
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500222 if (pvt->fam == 0x15) {
223 /* Erratum #505 */
224 if (pvt->model < 0x10)
225 f15h_select_dct(pvt, 0);
Borislav Petkov73ba8592011-09-19 17:34:45 +0200226
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500227 if (pvt->model == 0x60)
228 min_scrubrate = 0x6;
229 }
230 return __set_scrub_rate(pvt, bw, min_scrubrate);
Doug Thompson2bc65412009-05-04 20:11:14 +0200231}
232
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100233static int get_scrub_rate(struct mem_ctl_info *mci)
Doug Thompson2bc65412009-05-04 20:11:14 +0200234{
235 struct amd64_pvt *pvt = mci->pvt_info;
236 u32 scrubval = 0;
Borislav Petkov39094442010-11-24 19:52:09 +0100237 int i, retval = -EINVAL;
Doug Thompson2bc65412009-05-04 20:11:14 +0200238
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500239 if (pvt->fam == 0x15) {
240 /* Erratum #505 */
241 if (pvt->model < 0x10)
242 f15h_select_dct(pvt, 0);
Borislav Petkov73ba8592011-09-19 17:34:45 +0200243
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500244 if (pvt->model == 0x60)
245 amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
246 } else
247 amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
Doug Thompson2bc65412009-05-04 20:11:14 +0200248
249 scrubval = scrubval & 0x001F;
250
Roel Kluin926311f2010-01-11 20:58:21 +0100251 for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
Doug Thompson2bc65412009-05-04 20:11:14 +0200252 if (scrubrates[i].scrubval == scrubval) {
Borislav Petkov39094442010-11-24 19:52:09 +0100253 retval = scrubrates[i].bandwidth;
Doug Thompson2bc65412009-05-04 20:11:14 +0200254 break;
255 }
256 }
Borislav Petkov39094442010-11-24 19:52:09 +0100257 return retval;
Doug Thompson2bc65412009-05-04 20:11:14 +0200258}
259
Doug Thompson67757632009-04-27 15:53:22 +0200260/*
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200261 * returns true if the SysAddr given by sys_addr matches the
262 * DRAM base/limit associated with node_id
Doug Thompson67757632009-04-27 15:53:22 +0200263 */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100264static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid)
Doug Thompson67757632009-04-27 15:53:22 +0200265{
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200266 u64 addr;
Doug Thompson67757632009-04-27 15:53:22 +0200267
268 /* The K8 treats this as a 40-bit value. However, bits 63-40 will be
269 * all ones if the most significant implemented address bit is 1.
270 * Here we discard bits 63-40. See section 3.4.2 of AMD publication
271 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
272 * Application Programming.
273 */
274 addr = sys_addr & 0x000000ffffffffffull;
275
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200276 return ((addr >= get_dram_base(pvt, nid)) &&
277 (addr <= get_dram_limit(pvt, nid)));
Doug Thompson67757632009-04-27 15:53:22 +0200278}
279
280/*
281 * Attempt to map a SysAddr to a node. On success, return a pointer to the
282 * mem_ctl_info structure for the node that the SysAddr maps to.
283 *
284 * On failure, return NULL.
285 */
286static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
287 u64 sys_addr)
288{
289 struct amd64_pvt *pvt;
Daniel J Bluemanc7e53012012-11-30 16:44:20 +0800290 u8 node_id;
Doug Thompson67757632009-04-27 15:53:22 +0200291 u32 intlv_en, bits;
292
293 /*
294 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
295 * 3.4.4.2) registers to map the SysAddr to a node ID.
296 */
297 pvt = mci->pvt_info;
298
299 /*
300 * The value of this field should be the same for all DRAM Base
301 * registers. Therefore we arbitrarily choose to read it from the
302 * register for node 0.
303 */
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200304 intlv_en = dram_intlv_en(pvt, 0);
Doug Thompson67757632009-04-27 15:53:22 +0200305
306 if (intlv_en == 0) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200307 for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100308 if (base_limit_match(pvt, sys_addr, node_id))
Borislav Petkov8edc5442009-09-18 12:39:19 +0200309 goto found;
Doug Thompson67757632009-04-27 15:53:22 +0200310 }
Borislav Petkov8edc5442009-09-18 12:39:19 +0200311 goto err_no_match;
Doug Thompson67757632009-04-27 15:53:22 +0200312 }
313
Borislav Petkov72f158f2009-09-18 12:27:27 +0200314 if (unlikely((intlv_en != 0x01) &&
315 (intlv_en != 0x03) &&
316 (intlv_en != 0x07))) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200317 amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
Doug Thompson67757632009-04-27 15:53:22 +0200318 return NULL;
319 }
320
321 bits = (((u32) sys_addr) >> 12) & intlv_en;
322
323 for (node_id = 0; ; ) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200324 if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
Doug Thompson67757632009-04-27 15:53:22 +0200325 break; /* intlv_sel field matches */
326
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200327 if (++node_id >= DRAM_RANGES)
Doug Thompson67757632009-04-27 15:53:22 +0200328 goto err_no_match;
329 }
330
331 /* sanity test for sys_addr */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100332 if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200333 amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
334 "range for node %d with node interleaving enabled.\n",
335 __func__, sys_addr, node_id);
Doug Thompson67757632009-04-27 15:53:22 +0200336 return NULL;
337 }
338
339found:
Borislav Petkovb487c332011-02-21 18:55:00 +0100340 return edac_mc_find((int)node_id);
Doug Thompson67757632009-04-27 15:53:22 +0200341
342err_no_match:
Joe Perches956b9ba12012-04-29 17:08:39 -0300343 edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
344 (unsigned long)sys_addr);
Doug Thompson67757632009-04-27 15:53:22 +0200345
346 return NULL;
347}
Doug Thompsone2ce7252009-04-27 15:57:12 +0200348
349/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100350 * compute the CS base address of the @csrow on the DRAM controller @dct.
351 * For details see F2x[5C:40] in the processor's BKDG
Doug Thompsone2ce7252009-04-27 15:57:12 +0200352 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100353static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
354 u64 *base, u64 *mask)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200355{
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100356 u64 csbase, csmask, base_bits, mask_bits;
357 u8 addr_shift;
358
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500359 if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100360 csbase = pvt->csels[dct].csbases[csrow];
361 csmask = pvt->csels[dct].csmasks[csrow];
Chen, Gong10ef6b02013-10-18 14:29:07 -0700362 base_bits = GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9);
363 mask_bits = GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100364 addr_shift = 4;
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500365
366 /*
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500367 * F16h and F15h, models 30h and later need two addr_shift values:
368 * 8 for high and 6 for low (cf. F16h BKDG).
369 */
370 } else if (pvt->fam == 0x16 ||
371 (pvt->fam == 0x15 && pvt->model >= 0x30)) {
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500372 csbase = pvt->csels[dct].csbases[csrow];
373 csmask = pvt->csels[dct].csmasks[csrow >> 1];
374
Chen, Gong10ef6b02013-10-18 14:29:07 -0700375 *base = (csbase & GENMASK_ULL(15, 5)) << 6;
376 *base |= (csbase & GENMASK_ULL(30, 19)) << 8;
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500377
378 *mask = ~0ULL;
379 /* poke holes for the csmask */
Chen, Gong10ef6b02013-10-18 14:29:07 -0700380 *mask &= ~((GENMASK_ULL(15, 5) << 6) |
381 (GENMASK_ULL(30, 19) << 8));
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500382
Chen, Gong10ef6b02013-10-18 14:29:07 -0700383 *mask |= (csmask & GENMASK_ULL(15, 5)) << 6;
384 *mask |= (csmask & GENMASK_ULL(30, 19)) << 8;
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500385
386 return;
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100387 } else {
388 csbase = pvt->csels[dct].csbases[csrow];
389 csmask = pvt->csels[dct].csmasks[csrow >> 1];
390 addr_shift = 8;
391
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200392 if (pvt->fam == 0x15)
Chen, Gong10ef6b02013-10-18 14:29:07 -0700393 base_bits = mask_bits =
394 GENMASK_ULL(30,19) | GENMASK_ULL(13,5);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100395 else
Chen, Gong10ef6b02013-10-18 14:29:07 -0700396 base_bits = mask_bits =
397 GENMASK_ULL(28,19) | GENMASK_ULL(13,5);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100398 }
399
400 *base = (csbase & base_bits) << addr_shift;
401
402 *mask = ~0ULL;
403 /* poke holes for the csmask */
404 *mask &= ~(mask_bits << addr_shift);
405 /* OR them in */
406 *mask |= (csmask & mask_bits) << addr_shift;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200407}
408
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100409#define for_each_chip_select(i, dct, pvt) \
410 for (i = 0; i < pvt->csels[dct].b_cnt; i++)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200411
Borislav Petkov614ec9d2011-01-13 18:02:22 +0100412#define chip_select_base(i, dct, pvt) \
413 pvt->csels[dct].csbases[i]
414
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100415#define for_each_chip_select_mask(i, dct, pvt) \
416 for (i = 0; i < pvt->csels[dct].m_cnt; i++)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200417
418/*
419 * @input_addr is an InputAddr associated with the node given by mci. Return the
420 * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
421 */
422static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
423{
424 struct amd64_pvt *pvt;
425 int csrow;
426 u64 base, mask;
427
428 pvt = mci->pvt_info;
429
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100430 for_each_chip_select(csrow, 0, pvt) {
431 if (!csrow_enabled(csrow, 0, pvt))
Doug Thompsone2ce7252009-04-27 15:57:12 +0200432 continue;
433
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100434 get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
435
436 mask = ~mask;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200437
438 if ((input_addr & mask) == (base & mask)) {
Joe Perches956b9ba12012-04-29 17:08:39 -0300439 edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
440 (unsigned long)input_addr, csrow,
441 pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200442
443 return csrow;
444 }
445 }
Joe Perches956b9ba12012-04-29 17:08:39 -0300446 edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
447 (unsigned long)input_addr, pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200448
449 return -1;
450}
451
452/*
Doug Thompsone2ce7252009-04-27 15:57:12 +0200453 * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
454 * for the node represented by mci. Info is passed back in *hole_base,
455 * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if
456 * info is invalid. Info may be invalid for either of the following reasons:
457 *
458 * - The revision of the node is not E or greater. In this case, the DRAM Hole
459 * Address Register does not exist.
460 *
461 * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
462 * indicating that its contents are not valid.
463 *
464 * The values passed back in *hole_base, *hole_offset, and *hole_size are
465 * complete 32-bit values despite the fact that the bitfields in the DHAR
466 * only represent bits 31-24 of the base and offset values.
467 */
468int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
469 u64 *hole_offset, u64 *hole_size)
470{
471 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200472
473 /* only revE and later have the DRAM Hole Address Register */
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200474 if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) {
Joe Perches956b9ba12012-04-29 17:08:39 -0300475 edac_dbg(1, " revision %d for node %d does not support DHAR\n",
476 pvt->ext_model, pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200477 return 1;
478 }
479
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100480 /* valid for Fam10h and above */
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200481 if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
Joe Perches956b9ba12012-04-29 17:08:39 -0300482 edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n");
Doug Thompsone2ce7252009-04-27 15:57:12 +0200483 return 1;
484 }
485
Borislav Petkovc8e518d2010-12-10 19:49:19 +0100486 if (!dhar_valid(pvt)) {
Joe Perches956b9ba12012-04-29 17:08:39 -0300487 edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n",
488 pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200489 return 1;
490 }
491
492 /* This node has Memory Hoisting */
493
494 /* +------------------+--------------------+--------------------+-----
495 * | memory | DRAM hole | relocated |
496 * | [0, (x - 1)] | [x, 0xffffffff] | addresses from |
497 * | | | DRAM hole |
498 * | | | [0x100000000, |
499 * | | | (0x100000000+ |
500 * | | | (0xffffffff-x))] |
501 * +------------------+--------------------+--------------------+-----
502 *
503 * Above is a diagram of physical memory showing the DRAM hole and the
504 * relocated addresses from the DRAM hole. As shown, the DRAM hole
505 * starts at address x (the base address) and extends through address
506 * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the
507 * addresses in the hole so that they start at 0x100000000.
508 */
509
Borislav Petkov1f316772012-08-10 12:50:50 +0200510 *hole_base = dhar_base(pvt);
511 *hole_size = (1ULL << 32) - *hole_base;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200512
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200513 *hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt)
514 : k8_dhar_offset(pvt);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200515
Joe Perches956b9ba12012-04-29 17:08:39 -0300516 edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
517 pvt->mc_node_id, (unsigned long)*hole_base,
518 (unsigned long)*hole_offset, (unsigned long)*hole_size);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200519
520 return 0;
521}
522EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
523
Doug Thompson93c2df52009-05-04 20:46:50 +0200524/*
525 * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is
526 * assumed that sys_addr maps to the node given by mci.
527 *
528 * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
529 * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
530 * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
531 * then it is also involved in translating a SysAddr to a DramAddr. Sections
532 * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
533 * These parts of the documentation are unclear. I interpret them as follows:
534 *
535 * When node n receives a SysAddr, it processes the SysAddr as follows:
536 *
537 * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
538 * Limit registers for node n. If the SysAddr is not within the range
539 * specified by the base and limit values, then node n ignores the Sysaddr
540 * (since it does not map to node n). Otherwise continue to step 2 below.
541 *
542 * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
543 * disabled so skip to step 3 below. Otherwise see if the SysAddr is within
544 * the range of relocated addresses (starting at 0x100000000) from the DRAM
545 * hole. If not, skip to step 3 below. Else get the value of the
546 * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
547 * offset defined by this value from the SysAddr.
548 *
549 * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
550 * Base register for node n. To obtain the DramAddr, subtract the base
551 * address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
552 */
553static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
554{
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200555 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompson93c2df52009-05-04 20:46:50 +0200556 u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
Borislav Petkov1f316772012-08-10 12:50:50 +0200557 int ret;
Doug Thompson93c2df52009-05-04 20:46:50 +0200558
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200559 dram_base = get_dram_base(pvt, pvt->mc_node_id);
Doug Thompson93c2df52009-05-04 20:46:50 +0200560
561 ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
562 &hole_size);
563 if (!ret) {
Borislav Petkov1f316772012-08-10 12:50:50 +0200564 if ((sys_addr >= (1ULL << 32)) &&
565 (sys_addr < ((1ULL << 32) + hole_size))) {
Doug Thompson93c2df52009-05-04 20:46:50 +0200566 /* use DHAR to translate SysAddr to DramAddr */
567 dram_addr = sys_addr - hole_offset;
568
Joe Perches956b9ba12012-04-29 17:08:39 -0300569 edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
570 (unsigned long)sys_addr,
571 (unsigned long)dram_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200572
573 return dram_addr;
574 }
575 }
576
577 /*
578 * Translate the SysAddr to a DramAddr as shown near the start of
579 * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8
580 * only deals with 40-bit values. Therefore we discard bits 63-40 of
581 * sys_addr below. If bit 39 of sys_addr is 1 then the bits we
582 * discard are all 1s. Otherwise the bits we discard are all 0s. See
583 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
584 * Programmer's Manual Volume 1 Application Programming.
585 */
Chen, Gong10ef6b02013-10-18 14:29:07 -0700586 dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base;
Doug Thompson93c2df52009-05-04 20:46:50 +0200587
Joe Perches956b9ba12012-04-29 17:08:39 -0300588 edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
589 (unsigned long)sys_addr, (unsigned long)dram_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200590 return dram_addr;
591}
592
593/*
594 * @intlv_en is the value of the IntlvEn field from a DRAM Base register
595 * (section 3.4.4.1). Return the number of bits from a SysAddr that are used
596 * for node interleaving.
597 */
598static int num_node_interleave_bits(unsigned intlv_en)
599{
600 static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
601 int n;
602
603 BUG_ON(intlv_en > 7);
604 n = intlv_shift_table[intlv_en];
605 return n;
606}
607
608/* Translate the DramAddr given by @dram_addr to an InputAddr. */
609static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
610{
611 struct amd64_pvt *pvt;
612 int intlv_shift;
613 u64 input_addr;
614
615 pvt = mci->pvt_info;
616
617 /*
618 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
619 * concerning translating a DramAddr to an InputAddr.
620 */
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200621 intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
Chen, Gong10ef6b02013-10-18 14:29:07 -0700622 input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) +
Borislav Petkovf678b8c2010-12-13 19:21:07 +0100623 (dram_addr & 0xfff);
Doug Thompson93c2df52009-05-04 20:46:50 +0200624
Joe Perches956b9ba12012-04-29 17:08:39 -0300625 edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
626 intlv_shift, (unsigned long)dram_addr,
627 (unsigned long)input_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200628
629 return input_addr;
630}
631
632/*
633 * Translate the SysAddr represented by @sys_addr to an InputAddr. It is
634 * assumed that @sys_addr maps to the node given by mci.
635 */
636static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
637{
638 u64 input_addr;
639
640 input_addr =
641 dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
642
Masanari Iidac19ca6c2016-02-08 20:53:12 +0900643 edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n",
Joe Perches956b9ba12012-04-29 17:08:39 -0300644 (unsigned long)sys_addr, (unsigned long)input_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200645
646 return input_addr;
647}
648
Doug Thompson93c2df52009-05-04 20:46:50 +0200649/* Map the Error address to a PAGE and PAGE OFFSET. */
650static inline void error_address_to_page_and_offset(u64 error_address,
Borislav Petkov33ca0642012-08-30 18:01:36 +0200651 struct err_info *err)
Doug Thompson93c2df52009-05-04 20:46:50 +0200652{
Borislav Petkov33ca0642012-08-30 18:01:36 +0200653 err->page = (u32) (error_address >> PAGE_SHIFT);
654 err->offset = ((u32) error_address) & ~PAGE_MASK;
Doug Thompson93c2df52009-05-04 20:46:50 +0200655}
656
657/*
658 * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
659 * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
660 * of a node that detected an ECC memory error. mci represents the node that
661 * the error address maps to (possibly different from the node that detected
662 * the error). Return the number of the csrow that sys_addr maps to, or -1 on
663 * error.
664 */
665static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
666{
667 int csrow;
668
669 csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
670
671 if (csrow == -1)
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200672 amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
673 "address 0x%lx\n", (unsigned long)sys_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200674 return csrow;
675}
Doug Thompsone2ce7252009-04-27 15:57:12 +0200676
Borislav Petkovbfc04ae2009-11-12 19:05:07 +0100677static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
Doug Thompson2da11652009-04-27 16:09:09 +0200678
Doug Thompson2da11652009-04-27 16:09:09 +0200679/*
680 * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
681 * are ECC capable.
682 */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100683static unsigned long determine_edac_cap(struct amd64_pvt *pvt)
Doug Thompson2da11652009-04-27 16:09:09 +0200684{
Borislav Petkovcb328502010-12-22 14:28:24 +0100685 u8 bit;
Dan Carpenter1f6189e2011-10-06 02:30:25 -0400686 unsigned long edac_cap = EDAC_FLAG_NONE;
Doug Thompson2da11652009-04-27 16:09:09 +0200687
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200688 bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F)
Doug Thompson2da11652009-04-27 16:09:09 +0200689 ? 19
690 : 17;
691
Borislav Petkov584fcff2009-06-10 18:29:54 +0200692 if (pvt->dclr0 & BIT(bit))
Doug Thompson2da11652009-04-27 16:09:09 +0200693 edac_cap = EDAC_FLAG_SECDED;
694
695 return edac_cap;
696}
697
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100698static void debug_display_dimm_sizes(struct amd64_pvt *, u8);
Doug Thompson2da11652009-04-27 16:09:09 +0200699
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100700static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
Borislav Petkov68798e12009-11-03 16:18:33 +0100701{
Joe Perches956b9ba12012-04-29 17:08:39 -0300702 edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
Borislav Petkov68798e12009-11-03 16:18:33 +0100703
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100704 if (pvt->dram_type == MEM_LRDDR3) {
705 u32 dcsm = pvt->csels[chan].csmasks[0];
706 /*
707 * It's assumed all LRDIMMs in a DCT are going to be of
708 * same 'type' until proven otherwise. So, use a cs
709 * value of '0' here to get dcsm value.
710 */
711 edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3));
712 }
713
714 edac_dbg(1, "All DIMMs support ECC:%s\n",
715 (dclr & BIT(19)) ? "yes" : "no");
716
Borislav Petkov68798e12009-11-03 16:18:33 +0100717
Joe Perches956b9ba12012-04-29 17:08:39 -0300718 edac_dbg(1, " PAR/ERR parity: %s\n",
719 (dclr & BIT(8)) ? "enabled" : "disabled");
Borislav Petkov68798e12009-11-03 16:18:33 +0100720
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200721 if (pvt->fam == 0x10)
Joe Perches956b9ba12012-04-29 17:08:39 -0300722 edac_dbg(1, " DCT 128bit mode width: %s\n",
723 (dclr & BIT(11)) ? "128b" : "64b");
Borislav Petkov68798e12009-11-03 16:18:33 +0100724
Joe Perches956b9ba12012-04-29 17:08:39 -0300725 edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
726 (dclr & BIT(12)) ? "yes" : "no",
727 (dclr & BIT(13)) ? "yes" : "no",
728 (dclr & BIT(14)) ? "yes" : "no",
729 (dclr & BIT(15)) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100730}
731
Doug Thompson2da11652009-04-27 16:09:09 +0200732/* Display and decode various NB registers for debug purposes. */
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200733static void dump_misc_regs(struct amd64_pvt *pvt)
Doug Thompson2da11652009-04-27 16:09:09 +0200734{
Joe Perches956b9ba12012-04-29 17:08:39 -0300735 edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
Doug Thompson2da11652009-04-27 16:09:09 +0200736
Joe Perches956b9ba12012-04-29 17:08:39 -0300737 edac_dbg(1, " NB two channel DRAM capable: %s\n",
738 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100739
Joe Perches956b9ba12012-04-29 17:08:39 -0300740 edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n",
741 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
742 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100743
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100744 debug_dump_dramcfg_low(pvt, pvt->dclr0, 0);
Doug Thompson2da11652009-04-27 16:09:09 +0200745
Joe Perches956b9ba12012-04-29 17:08:39 -0300746 edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
Doug Thompson2da11652009-04-27 16:09:09 +0200747
Joe Perches956b9ba12012-04-29 17:08:39 -0300748 edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
749 pvt->dhar, dhar_base(pvt),
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200750 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
751 : f10_dhar_offset(pvt));
Doug Thompson2da11652009-04-27 16:09:09 +0200752
Joe Perches956b9ba12012-04-29 17:08:39 -0300753 edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
Doug Thompson2da11652009-04-27 16:09:09 +0200754
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100755 debug_display_dimm_sizes(pvt, 0);
Borislav Petkov4d796362011-02-03 15:59:57 +0100756
Borislav Petkov8de1d912009-10-16 13:39:30 +0200757 /* everything below this point is Fam10h and above */
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200758 if (pvt->fam == 0xf)
Doug Thompson2da11652009-04-27 16:09:09 +0200759 return;
Borislav Petkov4d796362011-02-03 15:59:57 +0100760
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100761 debug_display_dimm_sizes(pvt, 1);
Doug Thompson2da11652009-04-27 16:09:09 +0200762
Borislav Petkova3b7db02011-01-19 20:35:12 +0100763 amd64_info("using %s syndromes.\n", ((pvt->ecc_sym_sz == 8) ? "x8" : "x4"));
Borislav Petkovad6a32e2010-03-09 12:46:00 +0100764
Borislav Petkov8de1d912009-10-16 13:39:30 +0200765 /* Only if NOT ganged does dclr1 have valid info */
Borislav Petkov68798e12009-11-03 16:18:33 +0100766 if (!dct_ganging_enabled(pvt))
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100767 debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
Doug Thompson2da11652009-04-27 16:09:09 +0200768}
769
Doug Thompson94be4bf2009-04-27 16:12:00 +0200770/*
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500771 * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
Doug Thompson94be4bf2009-04-27 16:12:00 +0200772 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100773static void prep_chip_selects(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200774{
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500775 if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100776 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
777 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100778 } else if (pvt->fam == 0x15 && pvt->model == 0x30) {
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500779 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
780 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
Borislav Petkov9d858bb2009-09-21 14:35:51 +0200781 } else {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100782 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
783 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200784 }
785}
786
787/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100788 * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
Doug Thompson94be4bf2009-04-27 16:12:00 +0200789 */
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200790static void read_dct_base_mask(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200791{
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500792 int base_reg0, base_reg1, mask_reg0, mask_reg1, cs;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200793
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100794 prep_chip_selects(pvt);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200795
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500796 if (pvt->umc) {
797 base_reg0 = get_umc_base(0) + UMCCH_BASE_ADDR;
798 base_reg1 = get_umc_base(1) + UMCCH_BASE_ADDR;
799 mask_reg0 = get_umc_base(0) + UMCCH_ADDR_MASK;
800 mask_reg1 = get_umc_base(1) + UMCCH_ADDR_MASK;
801 } else {
802 base_reg0 = DCSB0;
803 base_reg1 = DCSB1;
804 mask_reg0 = DCSM0;
805 mask_reg1 = DCSM1;
806 }
807
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100808 for_each_chip_select(cs, 0, pvt) {
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500809 int reg0 = base_reg0 + (cs * 4);
810 int reg1 = base_reg1 + (cs * 4);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100811 u32 *base0 = &pvt->csels[0].csbases[cs];
812 u32 *base1 = &pvt->csels[1].csbases[cs];
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200813
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500814 if (pvt->umc) {
815 if (!amd_smn_read(pvt->mc_node_id, reg0, base0))
816 edac_dbg(0, " DCSB0[%d]=0x%08x reg: 0x%x\n",
817 cs, *base0, reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200818
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500819 if (!amd_smn_read(pvt->mc_node_id, reg1, base1))
820 edac_dbg(0, " DCSB1[%d]=0x%08x reg: 0x%x\n",
821 cs, *base1, reg1);
822 } else {
823 if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
824 edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n",
825 cs, *base0, reg0);
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200826
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500827 if (pvt->fam == 0xf)
828 continue;
829
830 if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
831 edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n",
832 cs, *base1, (pvt->fam == 0x10) ? reg1
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -0500833 : reg0);
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500834 }
Doug Thompson94be4bf2009-04-27 16:12:00 +0200835 }
836
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100837 for_each_chip_select_mask(cs, 0, pvt) {
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500838 int reg0 = mask_reg0 + (cs * 4);
839 int reg1 = mask_reg1 + (cs * 4);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100840 u32 *mask0 = &pvt->csels[0].csmasks[cs];
841 u32 *mask1 = &pvt->csels[1].csmasks[cs];
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200842
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500843 if (pvt->umc) {
844 if (!amd_smn_read(pvt->mc_node_id, reg0, mask0))
845 edac_dbg(0, " DCSM0[%d]=0x%08x reg: 0x%x\n",
846 cs, *mask0, reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200847
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500848 if (!amd_smn_read(pvt->mc_node_id, reg1, mask1))
849 edac_dbg(0, " DCSM1[%d]=0x%08x reg: 0x%x\n",
850 cs, *mask1, reg1);
851 } else {
852 if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
853 edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n",
854 cs, *mask0, reg0);
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200855
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500856 if (pvt->fam == 0xf)
857 continue;
858
859 if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
860 edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n",
861 cs, *mask1, (pvt->fam == 0x10) ? reg1
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -0500862 : reg0);
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500863 }
Doug Thompson94be4bf2009-04-27 16:12:00 +0200864 }
865}
866
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100867static void determine_memory_type(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200868{
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100869 u32 dram_ctrl, dcsm;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200870
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100871 switch (pvt->fam) {
872 case 0xf:
873 if (pvt->ext_model >= K8_REV_F)
874 goto ddr3;
875
876 pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
877 return;
878
879 case 0x10:
Borislav Petkov6b4c0bd2009-11-12 15:37:57 +0100880 if (pvt->dchr0 & DDR3_MODE)
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100881 goto ddr3;
882
883 pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
884 return;
885
886 case 0x15:
887 if (pvt->model < 0x60)
888 goto ddr3;
889
890 /*
891 * Model 0x60h needs special handling:
892 *
893 * We use a Chip Select value of '0' to obtain dcsm.
894 * Theoretically, it is possible to populate LRDIMMs of different
895 * 'Rank' value on a DCT. But this is not the common case. So,
896 * it's reasonable to assume all DIMMs are going to be of same
897 * 'type' until proven otherwise.
898 */
899 amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl);
900 dcsm = pvt->csels[0].csmasks[0];
901
902 if (((dram_ctrl >> 8) & 0x7) == 0x2)
903 pvt->dram_type = MEM_DDR4;
904 else if (pvt->dclr0 & BIT(16))
905 pvt->dram_type = MEM_DDR3;
906 else if (dcsm & 0x3)
907 pvt->dram_type = MEM_LRDDR3;
Borislav Petkov6b4c0bd2009-11-12 15:37:57 +0100908 else
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100909 pvt->dram_type = MEM_RDDR3;
910
911 return;
912
913 case 0x16:
914 goto ddr3;
915
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -0500916 case 0x17:
917 if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5))
918 pvt->dram_type = MEM_LRDDR4;
919 else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4))
920 pvt->dram_type = MEM_RDDR4;
921 else
922 pvt->dram_type = MEM_DDR4;
923 return;
924
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100925 default:
926 WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam);
927 pvt->dram_type = MEM_EMPTY;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200928 }
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100929 return;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200930
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100931ddr3:
932 pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200933}
934
Borislav Petkovcb328502010-12-22 14:28:24 +0100935/* Get the number of DCT channels the memory controller is using. */
Doug Thompsonddff8762009-04-27 16:14:52 +0200936static int k8_early_channel_count(struct amd64_pvt *pvt)
937{
Borislav Petkovcb328502010-12-22 14:28:24 +0100938 int flag;
Doug Thompsonddff8762009-04-27 16:14:52 +0200939
Borislav Petkov9f56da02010-10-01 19:44:53 +0200940 if (pvt->ext_model >= K8_REV_F)
Doug Thompsonddff8762009-04-27 16:14:52 +0200941 /* RevF (NPT) and later */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +0100942 flag = pvt->dclr0 & WIDTH_128;
Borislav Petkov9f56da02010-10-01 19:44:53 +0200943 else
Doug Thompsonddff8762009-04-27 16:14:52 +0200944 /* RevE and earlier */
945 flag = pvt->dclr0 & REVE_WIDTH_128;
Doug Thompsonddff8762009-04-27 16:14:52 +0200946
947 /* not used */
948 pvt->dclr1 = 0;
949
950 return (flag) ? 2 : 1;
951}
952
Borislav Petkov70046622011-01-10 14:37:27 +0100953/* On F10h and later ErrAddr is MC4_ADDR[47:1] */
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200954static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
Doug Thompsonddff8762009-04-27 16:14:52 +0200955{
Borislav Petkov2ec591a2015-02-17 10:58:34 +0100956 u16 mce_nid = amd_get_nb_id(m->extcpu);
957 struct mem_ctl_info *mci;
Borislav Petkov70046622011-01-10 14:37:27 +0100958 u8 start_bit = 1;
959 u8 end_bit = 47;
Borislav Petkov2ec591a2015-02-17 10:58:34 +0100960 u64 addr;
961
962 mci = edac_mc_find(mce_nid);
963 if (!mci)
964 return 0;
965
966 pvt = mci->pvt_info;
Borislav Petkov70046622011-01-10 14:37:27 +0100967
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200968 if (pvt->fam == 0xf) {
Borislav Petkov70046622011-01-10 14:37:27 +0100969 start_bit = 3;
970 end_bit = 39;
971 }
972
Chen, Gong10ef6b02013-10-18 14:29:07 -0700973 addr = m->addr & GENMASK_ULL(end_bit, start_bit);
Borislav Petkovc1ae6832011-03-30 15:42:10 +0200974
975 /*
976 * Erratum 637 workaround
977 */
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200978 if (pvt->fam == 0x15) {
Borislav Petkovc1ae6832011-03-30 15:42:10 +0200979 u64 cc6_base, tmp_addr;
980 u32 tmp;
Daniel J Blueman8b84c8d2012-11-27 14:32:10 +0800981 u8 intlv_en;
Borislav Petkovc1ae6832011-03-30 15:42:10 +0200982
Chen, Gong10ef6b02013-10-18 14:29:07 -0700983 if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7)
Borislav Petkovc1ae6832011-03-30 15:42:10 +0200984 return addr;
985
Borislav Petkovc1ae6832011-03-30 15:42:10 +0200986
987 amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
988 intlv_en = tmp >> 21 & 0x7;
989
990 /* add [47:27] + 3 trailing bits */
Chen, Gong10ef6b02013-10-18 14:29:07 -0700991 cc6_base = (tmp & GENMASK_ULL(20, 0)) << 3;
Borislav Petkovc1ae6832011-03-30 15:42:10 +0200992
993 /* reverse and add DramIntlvEn */
994 cc6_base |= intlv_en ^ 0x7;
995
996 /* pin at [47:24] */
997 cc6_base <<= 24;
998
999 if (!intlv_en)
Chen, Gong10ef6b02013-10-18 14:29:07 -07001000 return cc6_base | (addr & GENMASK_ULL(23, 0));
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001001
1002 amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
1003
1004 /* faster log2 */
Chen, Gong10ef6b02013-10-18 14:29:07 -07001005 tmp_addr = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1);
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001006
1007 /* OR DramIntlvSel into bits [14:12] */
Chen, Gong10ef6b02013-10-18 14:29:07 -07001008 tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9;
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001009
1010 /* add remaining [11:0] bits from original MC4_ADDR */
Chen, Gong10ef6b02013-10-18 14:29:07 -07001011 tmp_addr |= addr & GENMASK_ULL(11, 0);
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001012
1013 return cc6_base | tmp_addr;
1014 }
1015
1016 return addr;
Doug Thompsonddff8762009-04-27 16:14:52 +02001017}
1018
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001019static struct pci_dev *pci_get_related_function(unsigned int vendor,
1020 unsigned int device,
1021 struct pci_dev *related)
1022{
1023 struct pci_dev *dev = NULL;
1024
1025 while ((dev = pci_get_device(vendor, device, dev))) {
1026 if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) &&
1027 (dev->bus->number == related->bus->number) &&
1028 (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
1029 break;
1030 }
1031
1032 return dev;
1033}
1034
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001035static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
Doug Thompsonddff8762009-04-27 16:14:52 +02001036{
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001037 struct amd_northbridge *nb;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001038 struct pci_dev *f1 = NULL;
1039 unsigned int pci_func;
Borislav Petkov71d2a322011-02-21 19:37:24 +01001040 int off = range << 3;
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001041 u32 llim;
Doug Thompsonddff8762009-04-27 16:14:52 +02001042
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001043 amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo);
1044 amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
Doug Thompsonddff8762009-04-27 16:14:52 +02001045
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001046 if (pvt->fam == 0xf)
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001047 return;
Doug Thompsonddff8762009-04-27 16:14:52 +02001048
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001049 if (!dram_rw(pvt, range))
1050 return;
Doug Thompsonddff8762009-04-27 16:14:52 +02001051
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001052 amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi);
1053 amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
Borislav Petkovf08e4572011-03-21 20:45:06 +01001054
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001055 /* F15h: factor in CC6 save area by reading dst node's limit reg */
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001056 if (pvt->fam != 0x15)
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001057 return;
Borislav Petkovf08e4572011-03-21 20:45:06 +01001058
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001059 nb = node_to_amd_nb(dram_dst_node(pvt, range));
1060 if (WARN_ON(!nb))
1061 return;
Borislav Petkovf08e4572011-03-21 20:45:06 +01001062
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001063 if (pvt->model == 0x60)
1064 pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
1065 else if (pvt->model == 0x30)
1066 pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
1067 else
1068 pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001069
1070 f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001071 if (WARN_ON(!f1))
1072 return;
Borislav Petkovf08e4572011-03-21 20:45:06 +01001073
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001074 amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
Borislav Petkovf08e4572011-03-21 20:45:06 +01001075
Chen, Gong10ef6b02013-10-18 14:29:07 -07001076 pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0);
Borislav Petkovf08e4572011-03-21 20:45:06 +01001077
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001078 /* {[39:27],111b} */
1079 pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
Borislav Petkovf08e4572011-03-21 20:45:06 +01001080
Chen, Gong10ef6b02013-10-18 14:29:07 -07001081 pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0);
Borislav Petkovf08e4572011-03-21 20:45:06 +01001082
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001083 /* [47:40] */
1084 pvt->ranges[range].lim.hi |= llim >> 13;
1085
1086 pci_dev_put(f1);
Doug Thompsonddff8762009-04-27 16:14:52 +02001087}
1088
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001089static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
Borislav Petkov33ca0642012-08-30 18:01:36 +02001090 struct err_info *err)
Doug Thompsonddff8762009-04-27 16:14:52 +02001091{
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001092 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompsonddff8762009-04-27 16:14:52 +02001093
Borislav Petkov33ca0642012-08-30 18:01:36 +02001094 error_address_to_page_and_offset(sys_addr, err);
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001095
1096 /*
1097 * Find out which node the error address belongs to. This may be
1098 * different from the node that detected the error.
1099 */
Borislav Petkov33ca0642012-08-30 18:01:36 +02001100 err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
1101 if (!err->src_mci) {
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001102 amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1103 (unsigned long)sys_addr);
Borislav Petkov33ca0642012-08-30 18:01:36 +02001104 err->err_code = ERR_NODE;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001105 return;
1106 }
1107
1108 /* Now map the sys_addr to a CSROW */
Borislav Petkov33ca0642012-08-30 18:01:36 +02001109 err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
1110 if (err->csrow < 0) {
1111 err->err_code = ERR_CSROW;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001112 return;
1113 }
1114
Doug Thompsonddff8762009-04-27 16:14:52 +02001115 /* CHIPKILL enabled */
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001116 if (pvt->nbcfg & NBCFG_CHIPKILL) {
Borislav Petkov33ca0642012-08-30 18:01:36 +02001117 err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
1118 if (err->channel < 0) {
Doug Thompsonddff8762009-04-27 16:14:52 +02001119 /*
1120 * Syndrome didn't map, so we don't know which of the
1121 * 2 DIMMs is in error. So we need to ID 'both' of them
1122 * as suspect.
1123 */
Borislav Petkov33ca0642012-08-30 18:01:36 +02001124 amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001125 "possible error reporting race\n",
Borislav Petkov33ca0642012-08-30 18:01:36 +02001126 err->syndrome);
1127 err->err_code = ERR_CHANNEL;
Doug Thompsonddff8762009-04-27 16:14:52 +02001128 return;
1129 }
1130 } else {
1131 /*
1132 * non-chipkill ecc mode
1133 *
1134 * The k8 documentation is unclear about how to determine the
1135 * channel number when using non-chipkill memory. This method
1136 * was obtained from email communication with someone at AMD.
1137 * (Wish the email was placed in this comment - norsk)
1138 */
Borislav Petkov33ca0642012-08-30 18:01:36 +02001139 err->channel = ((sys_addr & BIT(3)) != 0);
Doug Thompsonddff8762009-04-27 16:14:52 +02001140 }
Doug Thompsonddff8762009-04-27 16:14:52 +02001141}
1142
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001143static int ddr2_cs_size(unsigned i, bool dct_width)
Doug Thompsonddff8762009-04-27 16:14:52 +02001144{
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001145 unsigned shift = 0;
Doug Thompsonddff8762009-04-27 16:14:52 +02001146
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001147 if (i <= 2)
1148 shift = i;
1149 else if (!(i & 0x1))
1150 shift = i >> 1;
Borislav Petkov1433eb92009-10-21 13:44:36 +02001151 else
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001152 shift = (i + 1) >> 1;
Doug Thompsonddff8762009-04-27 16:14:52 +02001153
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001154 return 128 << (shift + !!dct_width);
1155}
1156
1157static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001158 unsigned cs_mode, int cs_mask_nr)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001159{
1160 u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
1161
1162 if (pvt->ext_model >= K8_REV_F) {
1163 WARN_ON(cs_mode > 11);
1164 return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
1165 }
1166 else if (pvt->ext_model >= K8_REV_D) {
Borislav Petkov11b0a312011-11-09 21:28:43 +01001167 unsigned diff;
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001168 WARN_ON(cs_mode > 10);
1169
Borislav Petkov11b0a312011-11-09 21:28:43 +01001170 /*
1171 * the below calculation, besides trying to win an obfuscated C
1172 * contest, maps cs_mode values to DIMM chip select sizes. The
1173 * mappings are:
1174 *
1175 * cs_mode CS size (mb)
1176 * ======= ============
1177 * 0 32
1178 * 1 64
1179 * 2 128
1180 * 3 128
1181 * 4 256
1182 * 5 512
1183 * 6 256
1184 * 7 512
1185 * 8 1024
1186 * 9 1024
1187 * 10 2048
1188 *
1189 * Basically, it calculates a value with which to shift the
1190 * smallest CS size of 32MB.
1191 *
1192 * ddr[23]_cs_size have a similar purpose.
1193 */
1194 diff = cs_mode/3 + (unsigned)(cs_mode > 5);
1195
1196 return 32 << (cs_mode - diff);
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001197 }
1198 else {
1199 WARN_ON(cs_mode > 6);
1200 return 32 << cs_mode;
1201 }
Doug Thompsonddff8762009-04-27 16:14:52 +02001202}
1203
Doug Thompson1afd3c92009-04-27 16:16:50 +02001204/*
1205 * Get the number of DCT channels in use.
1206 *
1207 * Return:
1208 * number of Memory Channels in operation
1209 * Pass back:
1210 * contents of the DCL0_LOW register
1211 */
Borislav Petkov7d20d142011-01-07 17:58:04 +01001212static int f1x_early_channel_count(struct amd64_pvt *pvt)
Doug Thompson1afd3c92009-04-27 16:16:50 +02001213{
Borislav Petkov6ba5dcd2009-10-13 19:26:55 +02001214 int i, j, channels = 0;
Doug Thompsonddff8762009-04-27 16:14:52 +02001215
Borislav Petkov7d20d142011-01-07 17:58:04 +01001216 /* On F10h, if we are in 128 bit mode, then we are using 2 channels */
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001217 if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
Borislav Petkov7d20d142011-01-07 17:58:04 +01001218 return 2;
Doug Thompson1afd3c92009-04-27 16:16:50 +02001219
1220 /*
Borislav Petkovd16149e2009-10-16 19:55:49 +02001221 * Need to check if in unganged mode: In such, there are 2 channels,
1222 * but they are not in 128 bit mode and thus the above 'dclr0' status
1223 * bit will be OFF.
Doug Thompson1afd3c92009-04-27 16:16:50 +02001224 *
1225 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
1226 * their CSEnable bit on. If so, then SINGLE DIMM case.
1227 */
Joe Perches956b9ba12012-04-29 17:08:39 -03001228 edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
Doug Thompson1afd3c92009-04-27 16:16:50 +02001229
1230 /*
1231 * Check DRAM Bank Address Mapping values for each DIMM to see if there
1232 * is more than just one DIMM present in unganged mode. Need to check
1233 * both controllers since DIMMs can be placed in either one.
1234 */
Borislav Petkov525a1b22010-12-21 15:53:27 +01001235 for (i = 0; i < 2; i++) {
1236 u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001237
Wan Wei57a30852009-08-07 17:04:49 +02001238 for (j = 0; j < 4; j++) {
1239 if (DBAM_DIMM(j, dbam) > 0) {
1240 channels++;
1241 break;
1242 }
1243 }
Doug Thompson1afd3c92009-04-27 16:16:50 +02001244 }
1245
Borislav Petkovd16149e2009-10-16 19:55:49 +02001246 if (channels > 2)
1247 channels = 2;
1248
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001249 amd64_info("MCT channel count: %d\n", channels);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001250
1251 return channels;
Doug Thompson1afd3c92009-04-27 16:16:50 +02001252}
1253
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05001254static int f17_early_channel_count(struct amd64_pvt *pvt)
1255{
1256 int i, channels = 0;
1257
1258 /* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */
1259 for (i = 0; i < NUM_UMCS; i++)
1260 channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT);
1261
1262 amd64_info("MCT channel count: %d\n", channels);
1263
1264 return channels;
1265}
1266
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001267static int ddr3_cs_size(unsigned i, bool dct_width)
Doug Thompson1afd3c92009-04-27 16:16:50 +02001268{
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001269 unsigned shift = 0;
1270 int cs_size = 0;
1271
1272 if (i == 0 || i == 3 || i == 4)
1273 cs_size = -1;
1274 else if (i <= 2)
1275 shift = i;
1276 else if (i == 12)
1277 shift = 7;
1278 else if (!(i & 0x1))
1279 shift = i >> 1;
1280 else
1281 shift = (i + 1) >> 1;
1282
1283 if (cs_size != -1)
1284 cs_size = (128 * (1 << !!dct_width)) << shift;
1285
1286 return cs_size;
1287}
1288
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001289static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
1290{
1291 unsigned shift = 0;
1292 int cs_size = 0;
1293
1294 if (i < 4 || i == 6)
1295 cs_size = -1;
1296 else if (i == 12)
1297 shift = 7;
1298 else if (!(i & 0x1))
1299 shift = i >> 1;
1300 else
1301 shift = (i + 1) >> 1;
1302
1303 if (cs_size != -1)
1304 cs_size = rank_multiply * (128 << shift);
1305
1306 return cs_size;
1307}
1308
1309static int ddr4_cs_size(unsigned i)
1310{
1311 int cs_size = 0;
1312
1313 if (i == 0)
1314 cs_size = -1;
1315 else if (i == 1)
1316 cs_size = 1024;
1317 else
1318 /* Min cs_size = 1G */
1319 cs_size = 1024 * (1 << (i >> 1));
1320
1321 return cs_size;
1322}
1323
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001324static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001325 unsigned cs_mode, int cs_mask_nr)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001326{
1327 u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
1328
1329 WARN_ON(cs_mode > 11);
Borislav Petkov1433eb92009-10-21 13:44:36 +02001330
1331 if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001332 return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
Borislav Petkov1433eb92009-10-21 13:44:36 +02001333 else
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001334 return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
1335}
Borislav Petkov1433eb92009-10-21 13:44:36 +02001336
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001337/*
1338 * F15h supports only 64bit DCT interfaces
1339 */
1340static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001341 unsigned cs_mode, int cs_mask_nr)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001342{
1343 WARN_ON(cs_mode > 12);
1344
1345 return ddr3_cs_size(cs_mode, false);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001346}
1347
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001348/* F15h M60h supports DDR4 mapping as well.. */
1349static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1350 unsigned cs_mode, int cs_mask_nr)
1351{
1352 int cs_size;
1353 u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
1354
1355 WARN_ON(cs_mode > 12);
1356
1357 if (pvt->dram_type == MEM_DDR4) {
1358 if (cs_mode > 9)
1359 return -1;
1360
1361 cs_size = ddr4_cs_size(cs_mode);
1362 } else if (pvt->dram_type == MEM_LRDDR3) {
1363 unsigned rank_multiply = dcsm & 0xf;
1364
1365 if (rank_multiply == 3)
1366 rank_multiply = 4;
1367 cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
1368 } else {
1369 /* Minimum cs size is 512mb for F15hM60h*/
1370 if (cs_mode == 0x1)
1371 return -1;
1372
1373 cs_size = ddr3_cs_size(cs_mode, false);
1374 }
1375
1376 return cs_size;
1377}
1378
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05001379/*
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001380 * F16h and F15h model 30h have only limited cs_modes.
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05001381 */
1382static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001383 unsigned cs_mode, int cs_mask_nr)
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05001384{
1385 WARN_ON(cs_mode > 12);
1386
1387 if (cs_mode == 6 || cs_mode == 8 ||
1388 cs_mode == 9 || cs_mode == 12)
1389 return -1;
1390 else
1391 return ddr3_cs_size(cs_mode, false);
1392}
1393
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05001394static int f17_base_addr_to_cs_size(struct amd64_pvt *pvt, u8 umc,
1395 unsigned int cs_mode, int csrow_nr)
1396{
1397 u32 base_addr = pvt->csels[umc].csbases[csrow_nr];
1398
1399 /* Each mask is used for every two base addresses. */
1400 u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr >> 1];
1401
1402 /* Register [31:1] = Address [39:9]. Size is in kBs here. */
1403 u32 size = ((addr_mask >> 1) - (base_addr >> 1) + 1) >> 1;
1404
1405 edac_dbg(1, "BaseAddr: 0x%x, AddrMask: 0x%x\n", base_addr, addr_mask);
1406
1407 /* Return size in MBs. */
1408 return size >> 10;
1409}
1410
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001411static void read_dram_ctl_register(struct amd64_pvt *pvt)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001412{
Doug Thompson6163b5d2009-04-27 16:20:17 +02001413
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001414 if (pvt->fam == 0xf)
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001415 return;
1416
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05001417 if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) {
Joe Perches956b9ba12012-04-29 17:08:39 -03001418 edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
1419 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001420
Joe Perches956b9ba12012-04-29 17:08:39 -03001421 edac_dbg(0, " DCTs operate in %s mode\n",
1422 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001423
Borislav Petkov72381bd2009-10-09 19:14:43 +02001424 if (!dct_ganging_enabled(pvt))
Joe Perches956b9ba12012-04-29 17:08:39 -03001425 edac_dbg(0, " Address range split per DCT: %s\n",
1426 (dct_high_range_enabled(pvt) ? "yes" : "no"));
Borislav Petkov72381bd2009-10-09 19:14:43 +02001427
Joe Perches956b9ba12012-04-29 17:08:39 -03001428 edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
1429 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
1430 (dct_memory_cleared(pvt) ? "yes" : "no"));
Borislav Petkov72381bd2009-10-09 19:14:43 +02001431
Joe Perches956b9ba12012-04-29 17:08:39 -03001432 edac_dbg(0, " channel interleave: %s, "
1433 "interleave bits selector: 0x%x\n",
1434 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
1435 dct_sel_interleave_addr(pvt));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001436 }
1437
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05001438 amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001439}
1440
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001441/*
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001442 * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
1443 * 2.10.12 Memory Interleaving Modes).
1444 */
1445static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1446 u8 intlv_en, int num_dcts_intlv,
1447 u32 dct_sel)
1448{
1449 u8 channel = 0;
1450 u8 select;
1451
1452 if (!(intlv_en))
1453 return (u8)(dct_sel);
1454
1455 if (num_dcts_intlv == 2) {
1456 select = (sys_addr >> 8) & 0x3;
1457 channel = select ? 0x3 : 0;
Aravind Gopalakrishnan9d0e8d82014-01-21 15:03:36 -06001458 } else if (num_dcts_intlv == 4) {
1459 u8 intlv_addr = dct_sel_interleave_addr(pvt);
1460 switch (intlv_addr) {
1461 case 0x4:
1462 channel = (sys_addr >> 8) & 0x3;
1463 break;
1464 case 0x5:
1465 channel = (sys_addr >> 9) & 0x3;
1466 break;
1467 }
1468 }
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001469 return channel;
1470}
1471
1472/*
Borislav Petkov229a7a12010-12-09 18:57:54 +01001473 * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001474 * Interleaving Modes.
1475 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001476static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
Borislav Petkov229a7a12010-12-09 18:57:54 +01001477 bool hi_range_sel, u8 intlv_en)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001478{
Borislav Petkov151fa712011-02-21 19:33:10 +01001479 u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001480
1481 if (dct_ganging_enabled(pvt))
Borislav Petkov229a7a12010-12-09 18:57:54 +01001482 return 0;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001483
Borislav Petkov229a7a12010-12-09 18:57:54 +01001484 if (hi_range_sel)
1485 return dct_sel_high;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001486
Borislav Petkov229a7a12010-12-09 18:57:54 +01001487 /*
1488 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1489 */
1490 if (dct_interleave_enabled(pvt)) {
1491 u8 intlv_addr = dct_sel_interleave_addr(pvt);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001492
Borislav Petkov229a7a12010-12-09 18:57:54 +01001493 /* return DCT select function: 0=DCT0, 1=DCT1 */
1494 if (!intlv_addr)
1495 return sys_addr >> 6 & 1;
1496
1497 if (intlv_addr & 0x2) {
1498 u8 shift = intlv_addr & 0x1 ? 9 : 6;
Yazen Ghannamdc0a50a82016-08-03 10:59:15 -04001499 u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1;
Borislav Petkov229a7a12010-12-09 18:57:54 +01001500
1501 return ((sys_addr >> shift) & 1) ^ temp;
1502 }
1503
Yazen Ghannamdc0a50a82016-08-03 10:59:15 -04001504 if (intlv_addr & 0x4) {
1505 u8 shift = intlv_addr & 0x1 ? 9 : 8;
1506
1507 return (sys_addr >> shift) & 1;
1508 }
1509
Borislav Petkov229a7a12010-12-09 18:57:54 +01001510 return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1511 }
1512
1513 if (dct_high_range_enabled(pvt))
1514 return ~dct_sel_high & 1;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001515
1516 return 0;
1517}
1518
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001519/* Convert the sys_addr to the normalized DCT address */
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08001520static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001521 u64 sys_addr, bool hi_rng,
1522 u32 dct_sel_base_addr)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001523{
1524 u64 chan_off;
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001525 u64 dram_base = get_dram_base(pvt, range);
1526 u64 hole_off = f10_dhar_offset(pvt);
Dan Carpenter6f3508f2016-01-20 12:54:51 +03001527 u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001528
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001529 if (hi_rng) {
1530 /*
1531 * if
1532 * base address of high range is below 4Gb
1533 * (bits [47:27] at [31:11])
1534 * DRAM address space on this DCT is hoisted above 4Gb &&
1535 * sys_addr > 4Gb
1536 *
1537 * remove hole offset from sys_addr
1538 * else
1539 * remove high range offset from sys_addr
1540 */
1541 if ((!(dct_sel_base_addr >> 16) ||
1542 dct_sel_base_addr < dhar_base(pvt)) &&
Borislav Petkov972ea172011-02-21 19:43:02 +01001543 dhar_valid(pvt) &&
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001544 (sys_addr >= BIT_64(32)))
Borislav Petkovbc21fa52010-11-11 17:29:13 +01001545 chan_off = hole_off;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001546 else
1547 chan_off = dct_sel_base_off;
1548 } else {
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001549 /*
1550 * if
1551 * we have a valid hole &&
1552 * sys_addr > 4Gb
1553 *
1554 * remove hole
1555 * else
1556 * remove dram base to normalize to DCT address
1557 */
Borislav Petkov972ea172011-02-21 19:43:02 +01001558 if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
Borislav Petkovbc21fa52010-11-11 17:29:13 +01001559 chan_off = hole_off;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001560 else
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001561 chan_off = dram_base;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001562 }
1563
Chen, Gong10ef6b02013-10-18 14:29:07 -07001564 return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001565}
1566
Doug Thompson6163b5d2009-04-27 16:20:17 +02001567/*
1568 * checks if the csrow passed in is marked as SPARED, if so returns the new
1569 * spare row
1570 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001571static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001572{
Borislav Petkov614ec9d2011-01-13 18:02:22 +01001573 int tmp_cs;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001574
Borislav Petkov614ec9d2011-01-13 18:02:22 +01001575 if (online_spare_swap_done(pvt, dct) &&
1576 csrow == online_spare_bad_dramcs(pvt, dct)) {
1577
1578 for_each_chip_select(tmp_cs, dct, pvt) {
1579 if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1580 csrow = tmp_cs;
1581 break;
1582 }
1583 }
Doug Thompson6163b5d2009-04-27 16:20:17 +02001584 }
1585 return csrow;
1586}
1587
1588/*
1589 * Iterate over the DRAM DCT "base" and "mask" registers looking for a
1590 * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
1591 *
1592 * Return:
1593 * -EINVAL: NOT FOUND
1594 * 0..csrow = Chip-Select Row
1595 */
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08001596static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001597{
1598 struct mem_ctl_info *mci;
1599 struct amd64_pvt *pvt;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001600 u64 cs_base, cs_mask;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001601 int cs_found = -EINVAL;
1602 int csrow;
1603
Borislav Petkov2ec591a2015-02-17 10:58:34 +01001604 mci = edac_mc_find(nid);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001605 if (!mci)
1606 return cs_found;
1607
1608 pvt = mci->pvt_info;
1609
Joe Perches956b9ba12012-04-29 17:08:39 -03001610 edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001611
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001612 for_each_chip_select(csrow, dct, pvt) {
1613 if (!csrow_enabled(csrow, dct, pvt))
Doug Thompson6163b5d2009-04-27 16:20:17 +02001614 continue;
1615
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001616 get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001617
Joe Perches956b9ba12012-04-29 17:08:39 -03001618 edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
1619 csrow, cs_base, cs_mask);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001620
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001621 cs_mask = ~cs_mask;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001622
Joe Perches956b9ba12012-04-29 17:08:39 -03001623 edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
1624 (in_addr & cs_mask), (cs_base & cs_mask));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001625
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001626 if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001627 if (pvt->fam == 0x15 && pvt->model >= 0x30) {
1628 cs_found = csrow;
1629 break;
1630 }
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001631 cs_found = f10_process_possible_spare(pvt, dct, csrow);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001632
Joe Perches956b9ba12012-04-29 17:08:39 -03001633 edac_dbg(1, " MATCH csrow=%d\n", cs_found);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001634 break;
1635 }
1636 }
1637 return cs_found;
1638}
1639
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001640/*
1641 * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
1642 * swapped with a region located at the bottom of memory so that the GPU can use
1643 * the interleaved region and thus two channels.
1644 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001645static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001646{
1647 u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
1648
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001649 if (pvt->fam == 0x10) {
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001650 /* only revC3 and revE have that feature */
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001651 if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001652 return sys_addr;
1653 }
1654
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05001655 amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg);
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001656
1657 if (!(swap_reg & 0x1))
1658 return sys_addr;
1659
1660 swap_base = (swap_reg >> 3) & 0x7f;
1661 swap_limit = (swap_reg >> 11) & 0x7f;
1662 rgn_size = (swap_reg >> 20) & 0x7f;
1663 tmp_addr = sys_addr >> 27;
1664
1665 if (!(sys_addr >> 34) &&
1666 (((tmp_addr >= swap_base) &&
1667 (tmp_addr <= swap_limit)) ||
1668 (tmp_addr < rgn_size)))
1669 return sys_addr ^ (u64)swap_base << 27;
1670
1671 return sys_addr;
1672}
1673
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001674/* For a given @dram_range, check if @sys_addr falls within it. */
Borislav Petkove761359a2011-02-21 19:49:01 +01001675static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
Borislav Petkov33ca0642012-08-30 18:01:36 +02001676 u64 sys_addr, int *chan_sel)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001677{
Borislav Petkov229a7a12010-12-09 18:57:54 +01001678 int cs_found = -EINVAL;
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001679 u64 chan_addr;
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001680 u32 dct_sel_base;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001681 u8 channel;
Borislav Petkov229a7a12010-12-09 18:57:54 +01001682 bool high_range = false;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001683
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001684 u8 node_id = dram_dst_node(pvt, range);
Borislav Petkov229a7a12010-12-09 18:57:54 +01001685 u8 intlv_en = dram_intlv_en(pvt, range);
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001686 u32 intlv_sel = dram_intlv_sel(pvt, range);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001687
Joe Perches956b9ba12012-04-29 17:08:39 -03001688 edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1689 range, sys_addr, get_dram_limit(pvt, range));
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001690
Borislav Petkov355fba62011-01-17 13:03:26 +01001691 if (dhar_valid(pvt) &&
1692 dhar_base(pvt) <= sys_addr &&
1693 sys_addr < BIT_64(32)) {
1694 amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1695 sys_addr);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001696 return -EINVAL;
Borislav Petkov355fba62011-01-17 13:03:26 +01001697 }
1698
Borislav Petkovf030ddf2011-04-08 15:05:21 +02001699 if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
Borislav Petkov355fba62011-01-17 13:03:26 +01001700 return -EINVAL;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001701
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001702 sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001703
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001704 dct_sel_base = dct_sel_baseaddr(pvt);
1705
1706 /*
1707 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1708 * select between DCT0 and DCT1.
1709 */
1710 if (dct_high_range_enabled(pvt) &&
1711 !dct_ganging_enabled(pvt) &&
1712 ((sys_addr >> 27) >= (dct_sel_base >> 11)))
Borislav Petkov229a7a12010-12-09 18:57:54 +01001713 high_range = true;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001714
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001715 channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001716
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001717 chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001718 high_range, dct_sel_base);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001719
Borislav Petkove2f79db2011-01-13 14:57:34 +01001720 /* Remove node interleaving, see F1x120 */
1721 if (intlv_en)
1722 chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1723 (chan_addr & 0xfff);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001724
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001725 /* remove channel interleave */
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001726 if (dct_interleave_enabled(pvt) &&
1727 !dct_high_range_enabled(pvt) &&
1728 !dct_ganging_enabled(pvt)) {
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001729
1730 if (dct_sel_interleave_addr(pvt) != 1) {
1731 if (dct_sel_interleave_addr(pvt) == 0x3)
1732 /* hash 9 */
1733 chan_addr = ((chan_addr >> 10) << 9) |
1734 (chan_addr & 0x1ff);
1735 else
1736 /* A[6] or hash 6 */
1737 chan_addr = ((chan_addr >> 7) << 6) |
1738 (chan_addr & 0x3f);
1739 } else
1740 /* A[12] */
1741 chan_addr = ((chan_addr >> 13) << 12) |
1742 (chan_addr & 0xfff);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001743 }
1744
Joe Perches956b9ba12012-04-29 17:08:39 -03001745 edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001746
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001747 cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001748
Borislav Petkov33ca0642012-08-30 18:01:36 +02001749 if (cs_found >= 0)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001750 *chan_sel = channel;
Borislav Petkov33ca0642012-08-30 18:01:36 +02001751
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001752 return cs_found;
1753}
1754
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001755static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
1756 u64 sys_addr, int *chan_sel)
1757{
1758 int cs_found = -EINVAL;
1759 int num_dcts_intlv = 0;
1760 u64 chan_addr, chan_offset;
1761 u64 dct_base, dct_limit;
1762 u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
1763 u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
1764
1765 u64 dhar_offset = f10_dhar_offset(pvt);
1766 u8 intlv_addr = dct_sel_interleave_addr(pvt);
1767 u8 node_id = dram_dst_node(pvt, range);
1768 u8 intlv_en = dram_intlv_en(pvt, range);
1769
1770 amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
1771 amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
1772
1773 dct_offset_en = (u8) ((dct_cont_base_reg >> 3) & BIT(0));
1774 dct_sel = (u8) ((dct_cont_base_reg >> 4) & 0x7);
1775
1776 edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1777 range, sys_addr, get_dram_limit(pvt, range));
1778
1779 if (!(get_dram_base(pvt, range) <= sys_addr) &&
1780 !(get_dram_limit(pvt, range) >= sys_addr))
1781 return -EINVAL;
1782
1783 if (dhar_valid(pvt) &&
1784 dhar_base(pvt) <= sys_addr &&
1785 sys_addr < BIT_64(32)) {
1786 amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1787 sys_addr);
1788 return -EINVAL;
1789 }
1790
1791 /* Verify sys_addr is within DCT Range. */
Aravind Gopalakrishnan4fc06b32013-08-24 10:47:48 -05001792 dct_base = (u64) dct_sel_baseaddr(pvt);
1793 dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001794
1795 if (!(dct_cont_base_reg & BIT(0)) &&
Aravind Gopalakrishnan4fc06b32013-08-24 10:47:48 -05001796 !(dct_base <= (sys_addr >> 27) &&
1797 dct_limit >= (sys_addr >> 27)))
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001798 return -EINVAL;
1799
1800 /* Verify number of dct's that participate in channel interleaving. */
1801 num_dcts_intlv = (int) hweight8(intlv_en);
1802
1803 if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
1804 return -EINVAL;
1805
Yazen Ghannamdc0a50a82016-08-03 10:59:15 -04001806 if (pvt->model >= 0x60)
1807 channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en);
1808 else
1809 channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
1810 num_dcts_intlv, dct_sel);
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001811
1812 /* Verify we stay within the MAX number of channels allowed */
Aravind Gopalakrishnan7f3f5242013-12-04 11:40:11 -06001813 if (channel > 3)
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001814 return -EINVAL;
1815
1816 leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
1817
1818 /* Get normalized DCT addr */
1819 if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
1820 chan_offset = dhar_offset;
1821 else
Aravind Gopalakrishnan4fc06b32013-08-24 10:47:48 -05001822 chan_offset = dct_base << 27;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001823
1824 chan_addr = sys_addr - chan_offset;
1825
1826 /* remove channel interleave */
1827 if (num_dcts_intlv == 2) {
1828 if (intlv_addr == 0x4)
1829 chan_addr = ((chan_addr >> 9) << 8) |
1830 (chan_addr & 0xff);
1831 else if (intlv_addr == 0x5)
1832 chan_addr = ((chan_addr >> 10) << 9) |
1833 (chan_addr & 0x1ff);
1834 else
1835 return -EINVAL;
1836
1837 } else if (num_dcts_intlv == 4) {
1838 if (intlv_addr == 0x4)
1839 chan_addr = ((chan_addr >> 10) << 8) |
1840 (chan_addr & 0xff);
1841 else if (intlv_addr == 0x5)
1842 chan_addr = ((chan_addr >> 11) << 9) |
1843 (chan_addr & 0x1ff);
1844 else
1845 return -EINVAL;
1846 }
1847
1848 if (dct_offset_en) {
1849 amd64_read_pci_cfg(pvt->F1,
1850 DRAM_CONT_HIGH_OFF + (int) channel * 4,
1851 &tmp);
Aravind Gopalakrishnan4fc06b32013-08-24 10:47:48 -05001852 chan_addr += (u64) ((tmp >> 11) & 0xfff) << 27;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001853 }
1854
1855 f15h_select_dct(pvt, channel);
1856
1857 edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr);
1858
1859 /*
1860 * Find Chip select:
1861 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
1862 * there is support for 4 DCT's, but only 2 are currently functional.
1863 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
1864 * pvt->csels[1]. So we need to use '1' here to get correct info.
1865 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
1866 */
1867 alias_channel = (channel == 3) ? 1 : channel;
1868
1869 cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
1870
1871 if (cs_found >= 0)
1872 *chan_sel = alias_channel;
1873
1874 return cs_found;
1875}
1876
1877static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
1878 u64 sys_addr,
1879 int *chan_sel)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001880{
Borislav Petkove761359a2011-02-21 19:49:01 +01001881 int cs_found = -EINVAL;
1882 unsigned range;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001883
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001884 for (range = 0; range < DRAM_RANGES; range++) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001885 if (!dram_rw(pvt, range))
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001886 continue;
1887
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001888 if (pvt->fam == 0x15 && pvt->model >= 0x30)
1889 cs_found = f15_m30h_match_to_this_node(pvt, range,
1890 sys_addr,
1891 chan_sel);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001892
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001893 else if ((get_dram_base(pvt, range) <= sys_addr) &&
1894 (get_dram_limit(pvt, range) >= sys_addr)) {
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001895 cs_found = f1x_match_to_this_node(pvt, range,
Borislav Petkov33ca0642012-08-30 18:01:36 +02001896 sys_addr, chan_sel);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001897 if (cs_found >= 0)
1898 break;
1899 }
1900 }
1901 return cs_found;
1902}
1903
1904/*
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001905 * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1906 * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001907 *
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001908 * The @sys_addr is usually an error address received from the hardware
1909 * (MCX_ADDR).
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001910 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001911static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
Borislav Petkov33ca0642012-08-30 18:01:36 +02001912 struct err_info *err)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001913{
1914 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001915
Borislav Petkov33ca0642012-08-30 18:01:36 +02001916 error_address_to_page_and_offset(sys_addr, err);
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001917
Borislav Petkov33ca0642012-08-30 18:01:36 +02001918 err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
1919 if (err->csrow < 0) {
1920 err->err_code = ERR_CSROW;
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001921 return;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001922 }
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001923
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001924 /*
1925 * We need the syndromes for channel detection only when we're
1926 * ganged. Otherwise @chan should already contain the channel at
1927 * this point.
1928 */
Borislav Petkova97fa682010-12-23 14:07:18 +01001929 if (dct_ganging_enabled(pvt))
Borislav Petkov33ca0642012-08-30 18:01:36 +02001930 err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001931}
1932
1933/*
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001934 * debug routine to display the memory sizes of all logical DIMMs and its
Borislav Petkovcb328502010-12-22 14:28:24 +01001935 * CSROWs
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001936 */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01001937static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001938{
Borislav Petkovbb89f5a2012-09-12 18:06:00 +02001939 int dimm, size0, size1;
Borislav Petkov525a1b22010-12-21 15:53:27 +01001940 u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
1941 u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001942
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001943 if (pvt->fam == 0xf) {
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001944 /* K8 families < revF not supported yet */
Borislav Petkov1433eb92009-10-21 13:44:36 +02001945 if (pvt->ext_model < K8_REV_F)
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001946 return;
1947 else
1948 WARN_ON(ctrl != 0);
1949 }
1950
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05001951 if (pvt->fam == 0x10) {
1952 dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1
1953 : pvt->dbam0;
1954 dcsb = (ctrl && !dct_ganging_enabled(pvt)) ?
1955 pvt->csels[1].csbases :
1956 pvt->csels[0].csbases;
1957 } else if (ctrl) {
1958 dbam = pvt->dbam0;
1959 dcsb = pvt->csels[1].csbases;
1960 }
Joe Perches956b9ba12012-04-29 17:08:39 -03001961 edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
1962 ctrl, dbam);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001963
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001964 edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
1965
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001966 /* Dump memory sizes for DIMM and its CSROWs */
1967 for (dimm = 0; dimm < 4; dimm++) {
1968
1969 size0 = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001970 if (dcsb[dimm*2] & DCSB_CS_ENABLE)
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001971 /* For f15m60h, need multiplier for LRDIMM cs_size
1972 * calculation. We pass 'dimm' value to the dbam_to_cs
1973 * mapper so we can find the multiplier from the
1974 * corresponding DCSM.
1975 */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001976 size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001977 DBAM_DIMM(dimm, dbam),
1978 dimm);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001979
1980 size1 = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001981 if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001982 size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001983 DBAM_DIMM(dimm, dbam),
1984 dimm);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001985
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001986 amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
Borislav Petkovbb89f5a2012-09-12 18:06:00 +02001987 dimm * 2, size0,
1988 dimm * 2 + 1, size1);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001989 }
1990}
1991
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01001992static struct amd64_family_type family_types[] = {
Doug Thompson4d376072009-04-27 16:25:05 +02001993 [K8_CPUS] = {
Borislav Petkov0092b202010-10-01 19:20:05 +02001994 .ctl_name = "K8",
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001995 .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
Borislav Petkov3f37a362016-05-06 19:44:27 +02001996 .f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
Doug Thompson4d376072009-04-27 16:25:05 +02001997 .ops = {
Borislav Petkov1433eb92009-10-21 13:44:36 +02001998 .early_channel_count = k8_early_channel_count,
Borislav Petkov1433eb92009-10-21 13:44:36 +02001999 .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow,
2000 .dbam_to_cs = k8_dbam_to_chip_select,
Doug Thompson4d376072009-04-27 16:25:05 +02002001 }
2002 },
2003 [F10_CPUS] = {
Borislav Petkov0092b202010-10-01 19:20:05 +02002004 .ctl_name = "F10h",
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002005 .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002006 .f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
Doug Thompson4d376072009-04-27 16:25:05 +02002007 .ops = {
Borislav Petkov7d20d142011-01-07 17:58:04 +01002008 .early_channel_count = f1x_early_channel_count,
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01002009 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
Borislav Petkov1433eb92009-10-21 13:44:36 +02002010 .dbam_to_cs = f10_dbam_to_chip_select,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002011 }
2012 },
2013 [F15_CPUS] = {
2014 .ctl_name = "F15h",
Borislav Petkovdf71a052011-01-19 18:15:10 +01002015 .f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002016 .f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002017 .ops = {
Borislav Petkov7d20d142011-01-07 17:58:04 +01002018 .early_channel_count = f1x_early_channel_count,
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01002019 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01002020 .dbam_to_cs = f15_dbam_to_chip_select,
Doug Thompson4d376072009-04-27 16:25:05 +02002021 }
2022 },
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002023 [F15_M30H_CPUS] = {
2024 .ctl_name = "F15h_M30h",
2025 .f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002026 .f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002027 .ops = {
2028 .early_channel_count = f1x_early_channel_count,
2029 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
2030 .dbam_to_cs = f16_dbam_to_chip_select,
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002031 }
2032 },
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002033 [F15_M60H_CPUS] = {
2034 .ctl_name = "F15h_M60h",
2035 .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002036 .f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002037 .ops = {
2038 .early_channel_count = f1x_early_channel_count,
2039 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
2040 .dbam_to_cs = f15_m60h_dbam_to_chip_select,
2041 }
2042 },
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05002043 [F16_CPUS] = {
2044 .ctl_name = "F16h",
2045 .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002046 .f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05002047 .ops = {
2048 .early_channel_count = f1x_early_channel_count,
2049 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
2050 .dbam_to_cs = f16_dbam_to_chip_select,
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05002051 }
2052 },
Aravind Gopalakrishnan85a88852014-02-20 10:28:46 -06002053 [F16_M30H_CPUS] = {
2054 .ctl_name = "F16h_M30h",
2055 .f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002056 .f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
Aravind Gopalakrishnan85a88852014-02-20 10:28:46 -06002057 .ops = {
2058 .early_channel_count = f1x_early_channel_count,
2059 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
2060 .dbam_to_cs = f16_dbam_to_chip_select,
Aravind Gopalakrishnan85a88852014-02-20 10:28:46 -06002061 }
2062 },
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05002063 [F17_CPUS] = {
2064 .ctl_name = "F17h",
2065 .f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0,
2066 .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
2067 .ops = {
2068 .early_channel_count = f17_early_channel_count,
2069 .dbam_to_cs = f17_base_addr_to_cs_size,
2070 }
2071 },
Doug Thompson4d376072009-04-27 16:25:05 +02002072};
2073
Doug Thompsonb1289d62009-04-27 16:37:05 +02002074/*
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002075 * These are tables of eigenvectors (one per line) which can be used for the
2076 * construction of the syndrome tables. The modified syndrome search algorithm
2077 * uses those to find the symbol in error and thus the DIMM.
Doug Thompsonb1289d62009-04-27 16:37:05 +02002078 *
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002079 * Algorithm courtesy of Ross LaFetra from AMD.
Doug Thompsonb1289d62009-04-27 16:37:05 +02002080 */
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002081static const u16 x4_vectors[] = {
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002082 0x2f57, 0x1afe, 0x66cc, 0xdd88,
2083 0x11eb, 0x3396, 0x7f4c, 0xeac8,
2084 0x0001, 0x0002, 0x0004, 0x0008,
2085 0x1013, 0x3032, 0x4044, 0x8088,
2086 0x106b, 0x30d6, 0x70fc, 0xe0a8,
2087 0x4857, 0xc4fe, 0x13cc, 0x3288,
2088 0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
2089 0x1f39, 0x251e, 0xbd6c, 0x6bd8,
2090 0x15c1, 0x2a42, 0x89ac, 0x4758,
2091 0x2b03, 0x1602, 0x4f0c, 0xca08,
2092 0x1f07, 0x3a0e, 0x6b04, 0xbd08,
2093 0x8ba7, 0x465e, 0x244c, 0x1cc8,
2094 0x2b87, 0x164e, 0x642c, 0xdc18,
2095 0x40b9, 0x80de, 0x1094, 0x20e8,
2096 0x27db, 0x1eb6, 0x9dac, 0x7b58,
2097 0x11c1, 0x2242, 0x84ac, 0x4c58,
2098 0x1be5, 0x2d7a, 0x5e34, 0xa718,
2099 0x4b39, 0x8d1e, 0x14b4, 0x28d8,
2100 0x4c97, 0xc87e, 0x11fc, 0x33a8,
2101 0x8e97, 0x497e, 0x2ffc, 0x1aa8,
2102 0x16b3, 0x3d62, 0x4f34, 0x8518,
2103 0x1e2f, 0x391a, 0x5cac, 0xf858,
2104 0x1d9f, 0x3b7a, 0x572c, 0xfe18,
2105 0x15f5, 0x2a5a, 0x5264, 0xa3b8,
2106 0x1dbb, 0x3b66, 0x715c, 0xe3f8,
2107 0x4397, 0xc27e, 0x17fc, 0x3ea8,
2108 0x1617, 0x3d3e, 0x6464, 0xb8b8,
2109 0x23ff, 0x12aa, 0xab6c, 0x56d8,
2110 0x2dfb, 0x1ba6, 0x913c, 0x7328,
2111 0x185d, 0x2ca6, 0x7914, 0x9e28,
2112 0x171b, 0x3e36, 0x7d7c, 0xebe8,
2113 0x4199, 0x82ee, 0x19f4, 0x2e58,
2114 0x4807, 0xc40e, 0x130c, 0x3208,
2115 0x1905, 0x2e0a, 0x5804, 0xac08,
2116 0x213f, 0x132a, 0xadfc, 0x5ba8,
2117 0x19a9, 0x2efe, 0xb5cc, 0x6f88,
Doug Thompsonb1289d62009-04-27 16:37:05 +02002118};
2119
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002120static const u16 x8_vectors[] = {
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002121 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
2122 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
2123 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
2124 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
2125 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
2126 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
2127 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
2128 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
2129 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
2130 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
2131 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
2132 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
2133 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
2134 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
2135 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
2136 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
2137 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
2138 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
2139 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
2140};
2141
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002142static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
Borislav Petkovd34a6ec2011-02-23 17:41:50 +01002143 unsigned v_dim)
Doug Thompsonb1289d62009-04-27 16:37:05 +02002144{
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002145 unsigned int i, err_sym;
Doug Thompsonb1289d62009-04-27 16:37:05 +02002146
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002147 for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
2148 u16 s = syndrome;
Borislav Petkovd34a6ec2011-02-23 17:41:50 +01002149 unsigned v_idx = err_sym * v_dim;
2150 unsigned v_end = (err_sym + 1) * v_dim;
Doug Thompsonb1289d62009-04-27 16:37:05 +02002151
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002152 /* walk over all 16 bits of the syndrome */
2153 for (i = 1; i < (1U << 16); i <<= 1) {
2154
2155 /* if bit is set in that eigenvector... */
2156 if (v_idx < v_end && vectors[v_idx] & i) {
2157 u16 ev_comp = vectors[v_idx++];
2158
2159 /* ... and bit set in the modified syndrome, */
2160 if (s & i) {
2161 /* remove it. */
2162 s ^= ev_comp;
2163
2164 if (!s)
2165 return err_sym;
2166 }
2167
2168 } else if (s & i)
2169 /* can't get to zero, move to next symbol */
2170 break;
2171 }
Doug Thompsonb1289d62009-04-27 16:37:05 +02002172 }
2173
Joe Perches956b9ba12012-04-29 17:08:39 -03002174 edac_dbg(0, "syndrome(%x) not found\n", syndrome);
Doug Thompsonb1289d62009-04-27 16:37:05 +02002175 return -1;
2176}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002177
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002178static int map_err_sym_to_channel(int err_sym, int sym_size)
2179{
2180 if (sym_size == 4)
2181 switch (err_sym) {
2182 case 0x20:
2183 case 0x21:
2184 return 0;
2185 break;
2186 case 0x22:
2187 case 0x23:
2188 return 1;
2189 break;
2190 default:
2191 return err_sym >> 4;
2192 break;
2193 }
2194 /* x8 symbols */
2195 else
2196 switch (err_sym) {
2197 /* imaginary bits not in a DIMM */
2198 case 0x10:
2199 WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
2200 err_sym);
2201 return -1;
2202 break;
2203
2204 case 0x11:
2205 return 0;
2206 break;
2207 case 0x12:
2208 return 1;
2209 break;
2210 default:
2211 return err_sym >> 3;
2212 break;
2213 }
2214 return -1;
2215}
2216
2217static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
2218{
2219 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002220 int err_sym = -1;
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002221
Borislav Petkova3b7db02011-01-19 20:35:12 +01002222 if (pvt->ecc_sym_sz == 8)
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002223 err_sym = decode_syndrome(syndrome, x8_vectors,
2224 ARRAY_SIZE(x8_vectors),
Borislav Petkova3b7db02011-01-19 20:35:12 +01002225 pvt->ecc_sym_sz);
2226 else if (pvt->ecc_sym_sz == 4)
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002227 err_sym = decode_syndrome(syndrome, x4_vectors,
2228 ARRAY_SIZE(x4_vectors),
Borislav Petkova3b7db02011-01-19 20:35:12 +01002229 pvt->ecc_sym_sz);
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002230 else {
Borislav Petkova3b7db02011-01-19 20:35:12 +01002231 amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002232 return err_sym;
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002233 }
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002234
Borislav Petkova3b7db02011-01-19 20:35:12 +01002235 return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002236}
2237
Yazen Ghanname70984d2016-11-17 17:57:31 -05002238static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err,
Borislav Petkov33ca0642012-08-30 18:01:36 +02002239 u8 ecc_type)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002240{
Borislav Petkov33ca0642012-08-30 18:01:36 +02002241 enum hw_event_mc_err_type err_type;
2242 const char *string;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002243
Borislav Petkov33ca0642012-08-30 18:01:36 +02002244 if (ecc_type == 2)
2245 err_type = HW_EVENT_ERR_CORRECTED;
2246 else if (ecc_type == 1)
2247 err_type = HW_EVENT_ERR_UNCORRECTED;
Yazen Ghannamd12a9692016-11-17 17:57:32 -05002248 else if (ecc_type == 3)
2249 err_type = HW_EVENT_ERR_DEFERRED;
Borislav Petkov33ca0642012-08-30 18:01:36 +02002250 else {
2251 WARN(1, "Something is rotten in the state of Denmark.\n");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002252 return;
2253 }
2254
Borislav Petkov33ca0642012-08-30 18:01:36 +02002255 switch (err->err_code) {
2256 case DECODE_OK:
2257 string = "";
2258 break;
2259 case ERR_NODE:
2260 string = "Failed to map error addr to a node";
2261 break;
2262 case ERR_CSROW:
2263 string = "Failed to map error addr to a csrow";
2264 break;
2265 case ERR_CHANNEL:
2266 string = "unknown syndrome - possible error reporting race";
2267 break;
2268 default:
2269 string = "WTF error";
2270 break;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002271 }
Borislav Petkov33ca0642012-08-30 18:01:36 +02002272
2273 edac_mc_handle_error(err_type, mci, 1,
2274 err->page, err->offset, err->syndrome,
2275 err->csrow, err->channel, -1,
2276 string, "");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002277}
2278
Borislav Petkovdf781d02013-12-15 17:29:44 +01002279static inline void decode_bus_error(int node_id, struct mce *m)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002280{
Daniel J Blueman0c510cc2015-02-17 11:34:38 +08002281 struct mem_ctl_info *mci;
2282 struct amd64_pvt *pvt;
Borislav Petkovf192c7b2011-01-10 14:24:32 +01002283 u8 ecc_type = (m->status >> 45) & 0x3;
Borislav Petkov66fed2d2012-08-09 18:41:07 +02002284 u8 xec = XEC(m->status, 0x1f);
2285 u16 ec = EC(m->status);
Borislav Petkov33ca0642012-08-30 18:01:36 +02002286 u64 sys_addr;
2287 struct err_info err;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002288
Daniel J Blueman0c510cc2015-02-17 11:34:38 +08002289 mci = edac_mc_find(node_id);
2290 if (!mci)
2291 return;
2292
2293 pvt = mci->pvt_info;
2294
Borislav Petkov66fed2d2012-08-09 18:41:07 +02002295 /* Bail out early if this was an 'observed' error */
Borislav Petkov5980bb92011-01-07 16:26:49 +01002296 if (PP(ec) == NBSL_PP_OBS)
Borislav Petkovb70ef012009-06-25 19:32:38 +02002297 return;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002298
Borislav Petkovecaf5602009-07-23 16:32:01 +02002299 /* Do only ECC errors */
2300 if (xec && xec != F10_NBSL_EXT_ERR_ECC)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002301 return;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002302
Borislav Petkov33ca0642012-08-30 18:01:36 +02002303 memset(&err, 0, sizeof(err));
2304
Borislav Petkova4b4bed2013-08-10 13:54:48 +02002305 sys_addr = get_error_address(pvt, m);
Borislav Petkov33ca0642012-08-30 18:01:36 +02002306
Borislav Petkovecaf5602009-07-23 16:32:01 +02002307 if (ecc_type == 2)
Borislav Petkov33ca0642012-08-30 18:01:36 +02002308 err.syndrome = extract_syndrome(m->status);
2309
2310 pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
2311
Yazen Ghanname70984d2016-11-17 17:57:31 -05002312 __log_ecc_error(mci, &err, ecc_type);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002313}
2314
Doug Thompson0ec449e2009-04-27 19:41:25 +02002315/*
Borislav Petkov3f37a362016-05-06 19:44:27 +02002316 * Use pvt->F3 which contains the F3 CPU PCI device to get the related
2317 * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002318 * Reserve F0 and F6 on systems with a UMC.
Doug Thompson0ec449e2009-04-27 19:41:25 +02002319 */
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002320static int
2321reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002322{
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002323 if (pvt->umc) {
2324 pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
2325 if (!pvt->F0) {
2326 amd64_err("error F0 device not found: vendor %x device 0x%x (broken BIOS?)\n",
2327 PCI_VENDOR_ID_AMD, pci_id1);
2328 return -ENODEV;
2329 }
2330
2331 pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
2332 if (!pvt->F6) {
2333 pci_dev_put(pvt->F0);
2334 pvt->F0 = NULL;
2335
2336 amd64_err("error F6 device not found: vendor %x device 0x%x (broken BIOS?)\n",
2337 PCI_VENDOR_ID_AMD, pci_id2);
2338
2339 return -ENODEV;
2340 }
2341 edac_dbg(1, "F0: %s\n", pci_name(pvt->F0));
2342 edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
2343 edac_dbg(1, "F6: %s\n", pci_name(pvt->F6));
2344
2345 return 0;
2346 }
2347
Doug Thompson0ec449e2009-04-27 19:41:25 +02002348 /* Reserve the ADDRESS MAP Device */
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002349 pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002350 if (!pvt->F1) {
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002351 amd64_err("error address map device not found: vendor %x device 0x%x (broken BIOS?)\n",
2352 PCI_VENDOR_ID_AMD, pci_id1);
Borislav Petkovbbd0c1f62010-10-01 19:27:58 +02002353 return -ENODEV;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002354 }
2355
Borislav Petkov3f37a362016-05-06 19:44:27 +02002356 /* Reserve the DCT Device */
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002357 pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
Borislav Petkov3f37a362016-05-06 19:44:27 +02002358 if (!pvt->F2) {
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002359 pci_dev_put(pvt->F1);
2360 pvt->F1 = NULL;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002361
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002362 amd64_err("error F2 device not found: vendor %x device 0x%x (broken BIOS?)\n",
2363 PCI_VENDOR_ID_AMD, pci_id2);
2364 return -ENODEV;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002365 }
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002366
Joe Perches956b9ba12012-04-29 17:08:39 -03002367 edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2368 edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2369 edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002370
2371 return 0;
2372}
2373
Borislav Petkov360b7f32010-10-15 19:25:38 +02002374static void free_mc_sibling_devs(struct amd64_pvt *pvt)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002375{
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002376 if (pvt->umc) {
2377 pci_dev_put(pvt->F0);
2378 pci_dev_put(pvt->F6);
2379 } else {
2380 pci_dev_put(pvt->F1);
2381 pci_dev_put(pvt->F2);
2382 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02002383}
2384
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002385static void determine_ecc_sym_sz(struct amd64_pvt *pvt)
2386{
2387 pvt->ecc_sym_sz = 4;
2388
2389 if (pvt->umc) {
2390 u8 i;
2391
2392 for (i = 0; i < NUM_UMCS; i++) {
2393 /* Check enabled channels only: */
2394 if ((pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) &&
2395 (pvt->umc[i].ecc_ctrl & BIT(7))) {
2396 pvt->ecc_sym_sz = 8;
2397 break;
2398 }
2399 }
2400
2401 return;
2402 }
2403
2404 if (pvt->fam >= 0x10) {
2405 u32 tmp;
2406
2407 amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
2408 /* F16h has only DCT0, so no need to read dbam1. */
2409 if (pvt->fam != 0x16)
2410 amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1);
2411
2412 /* F10h, revD and later can do x8 ECC too. */
2413 if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
2414 pvt->ecc_sym_sz = 8;
2415 }
2416}
2417
2418/*
2419 * Retrieve the hardware registers of the memory controller.
2420 */
2421static void __read_mc_regs_df(struct amd64_pvt *pvt)
2422{
2423 u8 nid = pvt->mc_node_id;
2424 struct amd64_umc *umc;
2425 u32 i, umc_base;
2426
2427 /* Read registers from each UMC */
2428 for (i = 0; i < NUM_UMCS; i++) {
2429
2430 umc_base = get_umc_base(i);
2431 umc = &pvt->umc[i];
2432
2433 amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl);
2434 amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl);
2435 amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg);
2436 }
2437}
2438
Doug Thompson0ec449e2009-04-27 19:41:25 +02002439/*
2440 * Retrieve the hardware registers of the memory controller (this includes the
2441 * 'Address Map' and 'Misc' device regs)
2442 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002443static void read_mc_regs(struct amd64_pvt *pvt)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002444{
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002445 unsigned int range;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002446 u64 msr_val;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002447
2448 /*
2449 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002450 * those are Read-As-Zero.
Doug Thompson0ec449e2009-04-27 19:41:25 +02002451 */
Borislav Petkove97f8bb2009-10-12 15:27:45 +02002452 rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
Joe Perches956b9ba12012-04-29 17:08:39 -03002453 edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002454
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002455 /* Check first whether TOP_MEM2 is enabled: */
Doug Thompson0ec449e2009-04-27 19:41:25 +02002456 rdmsrl(MSR_K8_SYSCFG, msr_val);
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002457 if (msr_val & BIT(21)) {
Borislav Petkove97f8bb2009-10-12 15:27:45 +02002458 rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
Joe Perches956b9ba12012-04-29 17:08:39 -03002459 edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002460 } else {
Joe Perches956b9ba12012-04-29 17:08:39 -03002461 edac_dbg(0, " TOP_MEM2 disabled\n");
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002462 }
2463
2464 if (pvt->umc) {
2465 __read_mc_regs_df(pvt);
2466 amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar);
2467
2468 goto skip;
2469 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02002470
Borislav Petkov5980bb92011-01-07 16:26:49 +01002471 amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002472
Borislav Petkov5a5d2372011-01-17 17:52:57 +01002473 read_dram_ctl_register(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002474
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002475 for (range = 0; range < DRAM_RANGES; range++) {
2476 u8 rw;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002477
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002478 /* read settings for this DRAM range */
2479 read_dram_base_limit_regs(pvt, range);
Borislav Petkove97f8bb2009-10-12 15:27:45 +02002480
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002481 rw = dram_rw(pvt, range);
2482 if (!rw)
2483 continue;
2484
Joe Perches956b9ba12012-04-29 17:08:39 -03002485 edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
2486 range,
2487 get_dram_base(pvt, range),
2488 get_dram_limit(pvt, range));
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002489
Joe Perches956b9ba12012-04-29 17:08:39 -03002490 edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
2491 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
2492 (rw & 0x1) ? "R" : "-",
2493 (rw & 0x2) ? "W" : "-",
2494 dram_intlv_sel(pvt, range),
2495 dram_dst_node(pvt, range));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002496 }
2497
Borislav Petkovbc21fa52010-11-11 17:29:13 +01002498 amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05002499 amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002500
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002501 amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002502
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05002503 amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0);
2504 amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002505
Borislav Petkov78da1212010-12-22 19:31:45 +01002506 if (!dct_ganging_enabled(pvt)) {
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05002507 amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1);
2508 amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002509 }
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002510
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002511skip:
2512 read_dct_base_mask(pvt);
2513
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002514 determine_memory_type(pvt);
2515 edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
Borislav Petkova3b7db02011-01-19 20:35:12 +01002516
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002517 determine_ecc_sym_sz(pvt);
Borislav Petkova3b7db02011-01-19 20:35:12 +01002518
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002519 dump_misc_regs(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002520}
2521
2522/*
2523 * NOTE: CPU Revision Dependent code
2524 *
2525 * Input:
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002526 * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002527 * k8 private pointer to -->
2528 * DRAM Bank Address mapping register
2529 * node_id
2530 * DCL register where dual_channel_active is
2531 *
2532 * The DBAM register consists of 4 sets of 4 bits each definitions:
2533 *
2534 * Bits: CSROWs
2535 * 0-3 CSROWs 0 and 1
2536 * 4-7 CSROWs 2 and 3
2537 * 8-11 CSROWs 4 and 5
2538 * 12-15 CSROWs 6 and 7
2539 *
2540 * Values range from: 0 to 15
2541 * The meaning of the values depends on CPU revision and dual-channel state,
2542 * see relevant BKDG more info.
2543 *
2544 * The memory controller provides for total of only 8 CSROWs in its current
2545 * architecture. Each "pair" of CSROWs normally represents just one DIMM in
2546 * single channel or two (2) DIMMs in dual channel mode.
2547 *
2548 * The following code logic collapses the various tables for CSROW based on CPU
2549 * revision.
2550 *
2551 * Returns:
2552 * The number of PAGE_SIZE pages on the specified CSROW number it
2553 * encompasses
2554 *
2555 */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002556static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002557{
Borislav Petkov1433eb92009-10-21 13:44:36 +02002558 u32 cs_mode, nr_pages;
Ashish Shenoyf92cae42012-02-22 17:20:38 -08002559 u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002560
Borislav Petkov10de6492012-09-12 19:00:38 +02002561
Doug Thompson0ec449e2009-04-27 19:41:25 +02002562 /*
2563 * The math on this doesn't look right on the surface because x/2*4 can
2564 * be simplified to x*2 but this expression makes use of the fact that
2565 * it is integral math where 1/2=0. This intermediate value becomes the
2566 * number of bits to shift the DBAM register to extract the proper CSROW
2567 * field.
2568 */
Borislav Petkov0a5dfc32012-09-12 18:16:01 +02002569 cs_mode = DBAM_DIMM(csrow_nr / 2, dbam);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002570
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002571 nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, (csrow_nr / 2))
2572 << (20 - PAGE_SHIFT);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002573
Borislav Petkov10de6492012-09-12 19:00:38 +02002574 edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
2575 csrow_nr, dct, cs_mode);
2576 edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002577
2578 return nr_pages;
2579}
2580
2581/*
2582 * Initialize the array of csrow attribute instances, based on the values
2583 * from pci config hardware registers.
2584 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002585static int init_csrows(struct mem_ctl_info *mci)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002586{
Borislav Petkov10de6492012-09-12 19:00:38 +02002587 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002588 struct csrow_info *csrow;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03002589 struct dimm_info *dimm;
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03002590 enum edac_type edac_mode;
Borislav Petkov10de6492012-09-12 19:00:38 +02002591 int i, j, empty = 1;
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -03002592 int nr_pages = 0;
Borislav Petkov10de6492012-09-12 19:00:38 +02002593 u32 val;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002594
Borislav Petkova97fa682010-12-23 14:07:18 +01002595 amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002596
Borislav Petkov2299ef72010-10-15 17:44:04 +02002597 pvt->nbcfg = val;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002598
Joe Perches956b9ba12012-04-29 17:08:39 -03002599 edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
2600 pvt->mc_node_id, val,
2601 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002602
Borislav Petkov10de6492012-09-12 19:00:38 +02002603 /*
2604 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
2605 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002606 for_each_chip_select(i, 0, pvt) {
Borislav Petkov10de6492012-09-12 19:00:38 +02002607 bool row_dct0 = !!csrow_enabled(i, 0, pvt);
2608 bool row_dct1 = false;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002609
Borislav Petkova4b4bed2013-08-10 13:54:48 +02002610 if (pvt->fam != 0xf)
Borislav Petkov10de6492012-09-12 19:00:38 +02002611 row_dct1 = !!csrow_enabled(i, 1, pvt);
2612
2613 if (!row_dct0 && !row_dct1)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002614 continue;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002615
Borislav Petkov10de6492012-09-12 19:00:38 +02002616 csrow = mci->csrows[i];
Doug Thompson0ec449e2009-04-27 19:41:25 +02002617 empty = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002618
Borislav Petkov10de6492012-09-12 19:00:38 +02002619 edac_dbg(1, "MC node: %d, csrow: %d\n",
2620 pvt->mc_node_id, i);
2621
Mauro Carvalho Chehab1eef1282013-03-11 09:07:46 -03002622 if (row_dct0) {
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002623 nr_pages = get_csrow_nr_pages(pvt, 0, i);
Mauro Carvalho Chehab1eef1282013-03-11 09:07:46 -03002624 csrow->channels[0]->dimm->nr_pages = nr_pages;
2625 }
Borislav Petkov10de6492012-09-12 19:00:38 +02002626
2627 /* K8 has only one DCT */
Borislav Petkova4b4bed2013-08-10 13:54:48 +02002628 if (pvt->fam != 0xf && row_dct1) {
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002629 int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i);
Mauro Carvalho Chehab1eef1282013-03-11 09:07:46 -03002630
2631 csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
2632 nr_pages += row_dct1_pages;
2633 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02002634
Borislav Petkov10de6492012-09-12 19:00:38 +02002635 edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002636
2637 /*
2638 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
2639 */
Borislav Petkova97fa682010-12-23 14:07:18 +01002640 if (pvt->nbcfg & NBCFG_ECC_ENABLE)
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03002641 edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
2642 EDAC_S4ECD4ED : EDAC_SECDED;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002643 else
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03002644 edac_mode = EDAC_NONE;
2645
2646 for (j = 0; j < pvt->channel_count; j++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03002647 dimm = csrow->channels[j]->dimm;
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002648 dimm->mtype = pvt->dram_type;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03002649 dimm->edac_mode = edac_mode;
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03002650 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02002651 }
2652
2653 return empty;
2654}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002655
Borislav Petkov06724532009-09-16 13:05:46 +02002656/* get all cores on this DCT */
Daniel J Blueman8b84c8d2012-11-27 14:32:10 +08002657static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02002658{
Borislav Petkov06724532009-09-16 13:05:46 +02002659 int cpu;
Doug Thompsonf9431992009-04-27 19:46:08 +02002660
Borislav Petkov06724532009-09-16 13:05:46 +02002661 for_each_online_cpu(cpu)
2662 if (amd_get_nb_id(cpu) == nid)
2663 cpumask_set_cpu(cpu, mask);
Doug Thompsonf9431992009-04-27 19:46:08 +02002664}
2665
2666/* check MCG_CTL on all the cpus on this node */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002667static bool nb_mce_bank_enabled_on_node(u16 nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02002668{
Rusty Russellba578cb2009-11-03 14:56:35 +10302669 cpumask_var_t mask;
Borislav Petkov50542252009-12-11 18:14:40 +01002670 int cpu, nbe;
Borislav Petkov06724532009-09-16 13:05:46 +02002671 bool ret = false;
Doug Thompsonf9431992009-04-27 19:46:08 +02002672
Rusty Russellba578cb2009-11-03 14:56:35 +10302673 if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002674 amd64_warn("%s: Error allocating mask\n", __func__);
Rusty Russellba578cb2009-11-03 14:56:35 +10302675 return false;
2676 }
Borislav Petkov06724532009-09-16 13:05:46 +02002677
Rusty Russellba578cb2009-11-03 14:56:35 +10302678 get_cpus_on_this_dct_cpumask(mask, nid);
Borislav Petkov06724532009-09-16 13:05:46 +02002679
Rusty Russellba578cb2009-11-03 14:56:35 +10302680 rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
Borislav Petkov06724532009-09-16 13:05:46 +02002681
Rusty Russellba578cb2009-11-03 14:56:35 +10302682 for_each_cpu(cpu, mask) {
Borislav Petkov50542252009-12-11 18:14:40 +01002683 struct msr *reg = per_cpu_ptr(msrs, cpu);
Borislav Petkov5980bb92011-01-07 16:26:49 +01002684 nbe = reg->l & MSR_MCGCTL_NBE;
Borislav Petkov06724532009-09-16 13:05:46 +02002685
Joe Perches956b9ba12012-04-29 17:08:39 -03002686 edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
2687 cpu, reg->q,
2688 (nbe ? "enabled" : "disabled"));
Borislav Petkov06724532009-09-16 13:05:46 +02002689
2690 if (!nbe)
2691 goto out;
Borislav Petkov06724532009-09-16 13:05:46 +02002692 }
2693 ret = true;
2694
2695out:
Rusty Russellba578cb2009-11-03 14:56:35 +10302696 free_cpumask_var(mask);
Doug Thompsonf9431992009-04-27 19:46:08 +02002697 return ret;
2698}
2699
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002700static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002701{
2702 cpumask_var_t cmask;
Borislav Petkov50542252009-12-11 18:14:40 +01002703 int cpu;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002704
2705 if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002706 amd64_warn("%s: error allocating mask\n", __func__);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002707 return false;
2708 }
2709
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002710 get_cpus_on_this_dct_cpumask(cmask, nid);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002711
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002712 rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2713
2714 for_each_cpu(cpu, cmask) {
2715
Borislav Petkov50542252009-12-11 18:14:40 +01002716 struct msr *reg = per_cpu_ptr(msrs, cpu);
2717
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002718 if (on) {
Borislav Petkov5980bb92011-01-07 16:26:49 +01002719 if (reg->l & MSR_MCGCTL_NBE)
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002720 s->flags.nb_mce_enable = 1;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002721
Borislav Petkov5980bb92011-01-07 16:26:49 +01002722 reg->l |= MSR_MCGCTL_NBE;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002723 } else {
2724 /*
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002725 * Turn off NB MCE reporting only when it was off before
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002726 */
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002727 if (!s->flags.nb_mce_enable)
Borislav Petkov5980bb92011-01-07 16:26:49 +01002728 reg->l &= ~MSR_MCGCTL_NBE;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002729 }
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002730 }
2731 wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2732
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002733 free_cpumask_var(cmask);
2734
2735 return 0;
2736}
2737
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002738static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
Borislav Petkov2299ef72010-10-15 17:44:04 +02002739 struct pci_dev *F3)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002740{
Borislav Petkov2299ef72010-10-15 17:44:04 +02002741 bool ret = true;
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002742 u32 value, mask = 0x3; /* UECC/CECC enable */
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002743
Borislav Petkov2299ef72010-10-15 17:44:04 +02002744 if (toggle_ecc_err_reporting(s, nid, ON)) {
2745 amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
2746 return false;
2747 }
2748
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002749 amd64_read_pci_cfg(F3, NBCTL, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002750
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002751 s->old_nbctl = value & mask;
2752 s->nbctl_valid = true;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002753
2754 value |= mask;
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002755 amd64_write_pci_cfg(F3, NBCTL, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002756
Borislav Petkova97fa682010-12-23 14:07:18 +01002757 amd64_read_pci_cfg(F3, NBCFG, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002758
Joe Perches956b9ba12012-04-29 17:08:39 -03002759 edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2760 nid, value, !!(value & NBCFG_ECC_ENABLE));
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002761
Borislav Petkova97fa682010-12-23 14:07:18 +01002762 if (!(value & NBCFG_ECC_ENABLE)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002763 amd64_warn("DRAM ECC disabled on this node, enabling...\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002764
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002765 s->flags.nb_ecc_prev = 0;
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002766
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002767 /* Attempt to turn on DRAM ECC Enable */
Borislav Petkova97fa682010-12-23 14:07:18 +01002768 value |= NBCFG_ECC_ENABLE;
2769 amd64_write_pci_cfg(F3, NBCFG, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002770
Borislav Petkova97fa682010-12-23 14:07:18 +01002771 amd64_read_pci_cfg(F3, NBCFG, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002772
Borislav Petkova97fa682010-12-23 14:07:18 +01002773 if (!(value & NBCFG_ECC_ENABLE)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002774 amd64_warn("Hardware rejected DRAM ECC enable,"
2775 "check memory DIMM configuration.\n");
Borislav Petkov2299ef72010-10-15 17:44:04 +02002776 ret = false;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002777 } else {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002778 amd64_info("Hardware accepted DRAM ECC Enable\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002779 }
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002780 } else {
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002781 s->flags.nb_ecc_prev = 1;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002782 }
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002783
Joe Perches956b9ba12012-04-29 17:08:39 -03002784 edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2785 nid, value, !!(value & NBCFG_ECC_ENABLE));
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002786
Borislav Petkov2299ef72010-10-15 17:44:04 +02002787 return ret;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002788}
2789
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002790static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
Borislav Petkov360b7f32010-10-15 19:25:38 +02002791 struct pci_dev *F3)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002792{
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002793 u32 value, mask = 0x3; /* UECC/CECC enable */
2794
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002795 if (!s->nbctl_valid)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002796 return;
2797
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002798 amd64_read_pci_cfg(F3, NBCTL, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002799 value &= ~mask;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002800 value |= s->old_nbctl;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002801
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002802 amd64_write_pci_cfg(F3, NBCTL, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002803
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002804 /* restore previous BIOS DRAM ECC "off" setting we force-enabled */
2805 if (!s->flags.nb_ecc_prev) {
Borislav Petkova97fa682010-12-23 14:07:18 +01002806 amd64_read_pci_cfg(F3, NBCFG, &value);
2807 value &= ~NBCFG_ECC_ENABLE;
2808 amd64_write_pci_cfg(F3, NBCFG, value);
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002809 }
2810
2811 /* restore the NB Enable MCGCTL bit */
Borislav Petkov2299ef72010-10-15 17:44:04 +02002812 if (toggle_ecc_err_reporting(s, nid, OFF))
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002813 amd64_warn("Error restoring NB MCGCTL settings!\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002814}
2815
Doug Thompsonf9431992009-04-27 19:46:08 +02002816/*
Borislav Petkov2299ef72010-10-15 17:44:04 +02002817 * EDAC requires that the BIOS have ECC enabled before
2818 * taking over the processing of ECC errors. A command line
2819 * option allows to force-enable hardware ECC later in
2820 * enable_ecc_error_reporting().
Doug Thompsonf9431992009-04-27 19:46:08 +02002821 */
Borislav Petkovcab4d272010-02-11 17:15:57 +01002822static const char *ecc_msg =
2823 "ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2824 " Either enable ECC checking or force module loading by setting "
2825 "'ecc_enable_override'.\n"
2826 " (Note that use of the override may cause unknown side effects.)\n";
Borislav Petkovbe3468e2009-08-05 15:47:22 +02002827
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002828static bool ecc_enabled(struct pci_dev *F3, u16 nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02002829{
Borislav Petkov06724532009-09-16 13:05:46 +02002830 bool nb_mce_en = false;
Yazen Ghannam196b79f2016-11-17 17:57:34 -05002831 u8 ecc_en = 0, i;
2832 u32 value;
Doug Thompsonf9431992009-04-27 19:46:08 +02002833
Yazen Ghannam196b79f2016-11-17 17:57:34 -05002834 if (boot_cpu_data.x86 >= 0x17) {
2835 u8 umc_en_mask = 0, ecc_en_mask = 0;
Doug Thompsonf9431992009-04-27 19:46:08 +02002836
Yazen Ghannam196b79f2016-11-17 17:57:34 -05002837 for (i = 0; i < NUM_UMCS; i++) {
2838 u32 base = get_umc_base(i);
2839
2840 /* Only check enabled UMCs. */
2841 if (amd_smn_read(nid, base + UMCCH_SDP_CTRL, &value))
2842 continue;
2843
2844 if (!(value & UMC_SDP_INIT))
2845 continue;
2846
2847 umc_en_mask |= BIT(i);
2848
2849 if (amd_smn_read(nid, base + UMCCH_UMC_CAP_HI, &value))
2850 continue;
2851
2852 if (value & UMC_ECC_ENABLED)
2853 ecc_en_mask |= BIT(i);
2854 }
2855
2856 /* Check whether at least one UMC is enabled: */
2857 if (umc_en_mask)
2858 ecc_en = umc_en_mask == ecc_en_mask;
2859
2860 /* Assume UMC MCA banks are enabled. */
2861 nb_mce_en = true;
2862 } else {
2863 amd64_read_pci_cfg(F3, NBCFG, &value);
2864
2865 ecc_en = !!(value & NBCFG_ECC_ENABLE);
2866
2867 nb_mce_en = nb_mce_bank_enabled_on_node(nid);
2868 if (!nb_mce_en)
2869 amd64_notice("NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
2870 MSR_IA32_MCG_CTL, nid);
2871 }
2872
Borislav Petkov2299ef72010-10-15 17:44:04 +02002873 amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
Doug Thompsonf9431992009-04-27 19:46:08 +02002874
Borislav Petkov2299ef72010-10-15 17:44:04 +02002875 if (!ecc_en || !nb_mce_en) {
2876 amd64_notice("%s", ecc_msg);
2877 return false;
Borislav Petkov43f5e682009-12-21 18:55:18 +01002878 }
Borislav Petkov2299ef72010-10-15 17:44:04 +02002879 return true;
Doug Thompsonf9431992009-04-27 19:46:08 +02002880}
2881
Borislav Petkovdf71a052011-01-19 18:15:10 +01002882static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
2883 struct amd64_family_type *fam)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002884{
2885 struct amd64_pvt *pvt = mci->pvt_info;
2886
2887 mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
2888 mci->edac_ctl_cap = EDAC_FLAG_NONE;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002889
Borislav Petkov5980bb92011-01-07 16:26:49 +01002890 if (pvt->nbcap & NBCAP_SECDED)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002891 mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
2892
Borislav Petkov5980bb92011-01-07 16:26:49 +01002893 if (pvt->nbcap & NBCAP_CHIPKILL)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002894 mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
2895
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002896 mci->edac_cap = determine_edac_cap(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002897 mci->mod_name = EDAC_MOD_STR;
2898 mci->mod_ver = EDAC_AMD64_VERSION;
Borislav Petkovdf71a052011-01-19 18:15:10 +01002899 mci->ctl_name = fam->ctl_name;
Yazen Ghanname7934b72016-11-17 17:57:30 -05002900 mci->dev_name = pci_name(pvt->F3);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002901 mci->ctl_page_to_phys = NULL;
2902
Doug Thompson7d6034d2009-04-27 20:01:01 +02002903 /* memory scrubber interface */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002904 mci->set_sdram_scrub_rate = set_scrub_rate;
2905 mci->get_sdram_scrub_rate = get_scrub_rate;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002906}
2907
Borislav Petkov0092b202010-10-01 19:20:05 +02002908/*
2909 * returns a pointer to the family descriptor on success, NULL otherwise.
2910 */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002911static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
Borislav Petkov395ae782010-10-01 18:38:19 +02002912{
Borislav Petkov0092b202010-10-01 19:20:05 +02002913 struct amd64_family_type *fam_type = NULL;
2914
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002915 pvt->ext_model = boot_cpu_data.x86_model >> 4;
Borislav Petkova4b4bed2013-08-10 13:54:48 +02002916 pvt->stepping = boot_cpu_data.x86_mask;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002917 pvt->model = boot_cpu_data.x86_model;
2918 pvt->fam = boot_cpu_data.x86;
2919
2920 switch (pvt->fam) {
Borislav Petkov395ae782010-10-01 18:38:19 +02002921 case 0xf:
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002922 fam_type = &family_types[K8_CPUS];
2923 pvt->ops = &family_types[K8_CPUS].ops;
Borislav Petkov395ae782010-10-01 18:38:19 +02002924 break;
Borislav Petkovdf71a052011-01-19 18:15:10 +01002925
Borislav Petkov395ae782010-10-01 18:38:19 +02002926 case 0x10:
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002927 fam_type = &family_types[F10_CPUS];
2928 pvt->ops = &family_types[F10_CPUS].ops;
Borislav Petkovdf71a052011-01-19 18:15:10 +01002929 break;
2930
2931 case 0x15:
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002932 if (pvt->model == 0x30) {
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002933 fam_type = &family_types[F15_M30H_CPUS];
2934 pvt->ops = &family_types[F15_M30H_CPUS].ops;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002935 break;
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002936 } else if (pvt->model == 0x60) {
2937 fam_type = &family_types[F15_M60H_CPUS];
2938 pvt->ops = &family_types[F15_M60H_CPUS].ops;
2939 break;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002940 }
2941
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002942 fam_type = &family_types[F15_CPUS];
2943 pvt->ops = &family_types[F15_CPUS].ops;
Borislav Petkov395ae782010-10-01 18:38:19 +02002944 break;
2945
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05002946 case 0x16:
Aravind Gopalakrishnan85a88852014-02-20 10:28:46 -06002947 if (pvt->model == 0x30) {
2948 fam_type = &family_types[F16_M30H_CPUS];
2949 pvt->ops = &family_types[F16_M30H_CPUS].ops;
2950 break;
2951 }
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002952 fam_type = &family_types[F16_CPUS];
2953 pvt->ops = &family_types[F16_CPUS].ops;
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05002954 break;
2955
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05002956 case 0x17:
2957 fam_type = &family_types[F17_CPUS];
2958 pvt->ops = &family_types[F17_CPUS].ops;
2959 break;
2960
Borislav Petkov395ae782010-10-01 18:38:19 +02002961 default:
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002962 amd64_err("Unsupported family!\n");
Borislav Petkov0092b202010-10-01 19:20:05 +02002963 return NULL;
Borislav Petkov395ae782010-10-01 18:38:19 +02002964 }
Borislav Petkov0092b202010-10-01 19:20:05 +02002965
Borislav Petkovdf71a052011-01-19 18:15:10 +01002966 amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002967 (pvt->fam == 0xf ?
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002968 (pvt->ext_model >= K8_REV_F ? "revF or later "
2969 : "revE or earlier ")
2970 : ""), pvt->mc_node_id);
Borislav Petkov0092b202010-10-01 19:20:05 +02002971 return fam_type;
Borislav Petkov395ae782010-10-01 18:38:19 +02002972}
2973
Takashi Iwaie339f1e2015-02-04 11:48:53 +01002974static const struct attribute_group *amd64_edac_attr_groups[] = {
2975#ifdef CONFIG_EDAC_DEBUG
2976 &amd64_edac_dbg_group,
2977#endif
2978#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
2979 &amd64_edac_inj_group,
2980#endif
2981 NULL
2982};
2983
Borislav Petkov3f37a362016-05-06 19:44:27 +02002984static int init_one_instance(unsigned int nid)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002985{
Borislav Petkov3f37a362016-05-06 19:44:27 +02002986 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
Borislav Petkov0092b202010-10-01 19:20:05 +02002987 struct amd64_family_type *fam_type = NULL;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002988 struct mem_ctl_info *mci = NULL;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03002989 struct edac_mc_layer layers[2];
Borislav Petkov3f37a362016-05-06 19:44:27 +02002990 struct amd64_pvt *pvt = NULL;
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002991 u16 pci_id1, pci_id2;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002992 int err = 0, ret;
2993
2994 ret = -ENOMEM;
2995 pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
2996 if (!pvt)
Borislav Petkov360b7f32010-10-15 19:25:38 +02002997 goto err_ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002998
Borislav Petkov360b7f32010-10-15 19:25:38 +02002999 pvt->mc_node_id = nid;
Borislav Petkov3f37a362016-05-06 19:44:27 +02003000 pvt->F3 = F3;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003001
Borislav Petkov395ae782010-10-01 18:38:19 +02003002 ret = -EINVAL;
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003003 fam_type = per_family_init(pvt);
Borislav Petkov0092b202010-10-01 19:20:05 +02003004 if (!fam_type)
Borislav Petkov395ae782010-10-01 18:38:19 +02003005 goto err_free;
3006
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05003007 if (pvt->fam >= 0x17) {
3008 pvt->umc = kcalloc(NUM_UMCS, sizeof(struct amd64_umc), GFP_KERNEL);
3009 if (!pvt->umc) {
3010 ret = -ENOMEM;
3011 goto err_free;
3012 }
3013
3014 pci_id1 = fam_type->f0_id;
3015 pci_id2 = fam_type->f6_id;
3016 } else {
3017 pci_id1 = fam_type->f1_id;
3018 pci_id2 = fam_type->f2_id;
3019 }
3020
3021 err = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003022 if (err)
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05003023 goto err_post_init;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003024
Borislav Petkov360b7f32010-10-15 19:25:38 +02003025 read_mc_regs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003026
Doug Thompson7d6034d2009-04-27 20:01:01 +02003027 /*
3028 * We need to determine how many memory channels there are. Then use
3029 * that information for calculating the size of the dynamic instance
Borislav Petkov360b7f32010-10-15 19:25:38 +02003030 * tables in the 'mci' structure.
Doug Thompson7d6034d2009-04-27 20:01:01 +02003031 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02003032 ret = -EINVAL;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003033 pvt->channel_count = pvt->ops->early_channel_count(pvt);
3034 if (pvt->channel_count < 0)
Borislav Petkov360b7f32010-10-15 19:25:38 +02003035 goto err_siblings;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003036
3037 ret = -ENOMEM;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03003038 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
3039 layers[0].size = pvt->csels[0].b_cnt;
3040 layers[0].is_virt_csrow = true;
3041 layers[1].type = EDAC_MC_LAYER_CHANNEL;
Borislav Petkovf0a56c42013-07-23 20:01:23 +02003042
3043 /*
3044 * Always allocate two channels since we can have setups with DIMMs on
3045 * only one channel. Also, this simplifies handling later for the price
3046 * of a couple of KBs tops.
3047 */
3048 layers[1].size = 2;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03003049 layers[1].is_virt_csrow = false;
Borislav Petkovf0a56c42013-07-23 20:01:23 +02003050
Mauro Carvalho Chehabca0907b2012-05-02 14:37:00 -03003051 mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003052 if (!mci)
Borislav Petkov360b7f32010-10-15 19:25:38 +02003053 goto err_siblings;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003054
3055 mci->pvt_info = pvt;
Borislav Petkov3f37a362016-05-06 19:44:27 +02003056 mci->pdev = &pvt->F3->dev;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003057
Borislav Petkovdf71a052011-01-19 18:15:10 +01003058 setup_mci_misc_attrs(mci, fam_type);
Borislav Petkov360b7f32010-10-15 19:25:38 +02003059
3060 if (init_csrows(mci))
Doug Thompson7d6034d2009-04-27 20:01:01 +02003061 mci->edac_cap = EDAC_FLAG_NONE;
3062
Doug Thompson7d6034d2009-04-27 20:01:01 +02003063 ret = -ENODEV;
Takashi Iwaie339f1e2015-02-04 11:48:53 +01003064 if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) {
Joe Perches956b9ba12012-04-29 17:08:39 -03003065 edac_dbg(1, "failed edac_mc_add_mc()\n");
Doug Thompson7d6034d2009-04-27 20:01:01 +02003066 goto err_add_mc;
3067 }
3068
Borislav Petkov549d0422009-07-24 13:51:42 +02003069 /* register stuff with EDAC MCE */
3070 if (report_gart_errors)
3071 amd_report_gart_errors(true);
3072
Borislav Petkovdf781d02013-12-15 17:29:44 +01003073 amd_register_ecc_decoder(decode_bus_error);
Borislav Petkov549d0422009-07-24 13:51:42 +02003074
Doug Thompson7d6034d2009-04-27 20:01:01 +02003075 return 0;
3076
3077err_add_mc:
3078 edac_mc_free(mci);
3079
Borislav Petkov360b7f32010-10-15 19:25:38 +02003080err_siblings:
3081 free_mc_sibling_devs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003082
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05003083err_post_init:
3084 if (pvt->fam >= 0x17)
3085 kfree(pvt->umc);
3086
Borislav Petkov360b7f32010-10-15 19:25:38 +02003087err_free:
3088 kfree(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003089
Borislav Petkov360b7f32010-10-15 19:25:38 +02003090err_ret:
Doug Thompson7d6034d2009-04-27 20:01:01 +02003091 return ret;
3092}
3093
Borislav Petkov3f37a362016-05-06 19:44:27 +02003094static int probe_one_instance(unsigned int nid)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003095{
Borislav Petkov2299ef72010-10-15 17:44:04 +02003096 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003097 struct ecc_settings *s;
Borislav Petkov3f37a362016-05-06 19:44:27 +02003098 int ret;
Borislav Petkovb8cfa022010-10-01 19:35:38 +02003099
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003100 ret = -ENOMEM;
3101 s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
3102 if (!s)
Borislav Petkov2299ef72010-10-15 17:44:04 +02003103 goto err_out;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003104
3105 ecc_stngs[nid] = s;
3106
Borislav Petkov2299ef72010-10-15 17:44:04 +02003107 if (!ecc_enabled(F3, nid)) {
3108 ret = -ENODEV;
3109
3110 if (!ecc_enable_override)
3111 goto err_enable;
3112
Yazen Ghannam044e7a42016-11-22 15:40:16 -06003113 if (boot_cpu_data.x86 >= 0x17) {
3114 amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS.");
3115 goto err_enable;
3116 } else
3117 amd64_warn("Forcing ECC on!\n");
Borislav Petkov2299ef72010-10-15 17:44:04 +02003118
3119 if (!enable_ecc_error_reporting(s, nid, F3))
3120 goto err_enable;
3121 }
3122
Borislav Petkov3f37a362016-05-06 19:44:27 +02003123 ret = init_one_instance(nid);
Borislav Petkov360b7f32010-10-15 19:25:38 +02003124 if (ret < 0) {
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003125 amd64_err("Error probing instance: %d\n", nid);
Yazen Ghannam044e7a42016-11-22 15:40:16 -06003126
3127 if (boot_cpu_data.x86 < 0x17)
3128 restore_ecc_error_reporting(s, nid, F3);
Borislav Petkov360b7f32010-10-15 19:25:38 +02003129 }
Doug Thompson7d6034d2009-04-27 20:01:01 +02003130
3131 return ret;
Borislav Petkov2299ef72010-10-15 17:44:04 +02003132
3133err_enable:
3134 kfree(s);
3135 ecc_stngs[nid] = NULL;
3136
3137err_out:
3138 return ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003139}
3140
Borislav Petkov3f37a362016-05-06 19:44:27 +02003141static void remove_one_instance(unsigned int nid)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003142{
Borislav Petkov360b7f32010-10-15 19:25:38 +02003143 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
3144 struct ecc_settings *s = ecc_stngs[nid];
Borislav Petkov3f37a362016-05-06 19:44:27 +02003145 struct mem_ctl_info *mci;
3146 struct amd64_pvt *pvt;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003147
Borislav Petkov3f37a362016-05-06 19:44:27 +02003148 mci = find_mci_by_dev(&F3->dev);
Borislav Petkova4b4bed2013-08-10 13:54:48 +02003149 WARN_ON(!mci);
3150
Doug Thompson7d6034d2009-04-27 20:01:01 +02003151 /* Remove from EDAC CORE tracking list */
Borislav Petkov3f37a362016-05-06 19:44:27 +02003152 mci = edac_mc_del_mc(&F3->dev);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003153 if (!mci)
3154 return;
3155
3156 pvt = mci->pvt_info;
3157
Borislav Petkov360b7f32010-10-15 19:25:38 +02003158 restore_ecc_error_reporting(s, nid, F3);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003159
Borislav Petkov360b7f32010-10-15 19:25:38 +02003160 free_mc_sibling_devs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003161
Borislav Petkov549d0422009-07-24 13:51:42 +02003162 /* unregister from EDAC MCE */
3163 amd_report_gart_errors(false);
Borislav Petkovdf781d02013-12-15 17:29:44 +01003164 amd_unregister_ecc_decoder(decode_bus_error);
Borislav Petkov549d0422009-07-24 13:51:42 +02003165
Borislav Petkov360b7f32010-10-15 19:25:38 +02003166 kfree(ecc_stngs[nid]);
3167 ecc_stngs[nid] = NULL;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003168
Doug Thompson7d6034d2009-04-27 20:01:01 +02003169 /* Free the EDAC CORE resources */
Borislav Petkov8f68ed92009-12-21 15:15:59 +01003170 mci->pvt_info = NULL;
Borislav Petkov8f68ed92009-12-21 15:15:59 +01003171
3172 kfree(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003173 edac_mc_free(mci);
3174}
3175
Borislav Petkov360b7f32010-10-15 19:25:38 +02003176static void setup_pci_device(void)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003177{
3178 struct mem_ctl_info *mci;
3179 struct amd64_pvt *pvt;
3180
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003181 if (pci_ctl)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003182 return;
3183
Borislav Petkov2ec591a2015-02-17 10:58:34 +01003184 mci = edac_mc_find(0);
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003185 if (!mci)
3186 return;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003187
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003188 pvt = mci->pvt_info;
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05003189 if (pvt->umc)
3190 pci_ctl = edac_pci_create_generic_ctl(&pvt->F0->dev, EDAC_MOD_STR);
3191 else
3192 pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003193 if (!pci_ctl) {
3194 pr_warn("%s(): Unable to create PCI control\n", __func__);
3195 pr_warn("%s(): PCI error report via EDAC not set\n", __func__);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003196 }
3197}
3198
Yazen Ghannamd6efab72016-09-15 19:07:17 -05003199static const struct x86_cpu_id amd64_cpuids[] = {
3200 { X86_VENDOR_AMD, 0xF, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
3201 { X86_VENDOR_AMD, 0x10, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
3202 { X86_VENDOR_AMD, 0x15, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
3203 { X86_VENDOR_AMD, 0x16, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
3204 { }
3205};
3206MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
3207
Doug Thompson7d6034d2009-04-27 20:01:01 +02003208static int __init amd64_edac_init(void)
3209{
Borislav Petkov360b7f32010-10-15 19:25:38 +02003210 int err = -ENODEV;
Borislav Petkov3f37a362016-05-06 19:44:27 +02003211 int i;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003212
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +02003213 if (amd_cache_northbridges() < 0)
Borislav Petkov56b34b92009-12-21 18:13:01 +01003214 goto err_ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003215
Borislav Petkov6ba92fe2016-06-16 01:13:18 +02003216 opstate_init();
3217
Borislav Petkovcc4d8862010-10-13 16:11:59 +02003218 err = -ENOMEM;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003219 ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL);
Borislav Petkov2ec591a2015-02-17 10:58:34 +01003220 if (!ecc_stngs)
Borislav Petkova9f0fbe2011-03-29 18:10:53 +02003221 goto err_free;
Borislav Petkovcc4d8862010-10-13 16:11:59 +02003222
Borislav Petkov50542252009-12-11 18:14:40 +01003223 msrs = msrs_alloc();
Borislav Petkov56b34b92009-12-21 18:13:01 +01003224 if (!msrs)
Borislav Petkov360b7f32010-10-15 19:25:38 +02003225 goto err_free;
Borislav Petkov50542252009-12-11 18:14:40 +01003226
Borislav Petkov3f37a362016-05-06 19:44:27 +02003227 for (i = 0; i < amd_nb_num(); i++)
3228 if (probe_one_instance(i)) {
3229 /* unwind properly */
3230 while (--i >= 0)
3231 remove_one_instance(i);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003232
Borislav Petkov3f37a362016-05-06 19:44:27 +02003233 goto err_pci;
3234 }
Doug Thompson7d6034d2009-04-27 20:01:01 +02003235
Borislav Petkov360b7f32010-10-15 19:25:38 +02003236 setup_pci_device();
Tomasz Palaf5b10c42014-11-02 11:22:12 +01003237
3238#ifdef CONFIG_X86_32
3239 amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
3240#endif
3241
Borislav Petkovde0336b2016-04-27 12:21:21 +02003242 printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
3243
Borislav Petkov360b7f32010-10-15 19:25:38 +02003244 return 0;
Borislav Petkov56b34b92009-12-21 18:13:01 +01003245
Borislav Petkov56b34b92009-12-21 18:13:01 +01003246err_pci:
3247 msrs_free(msrs);
3248 msrs = NULL;
Borislav Petkovcc4d8862010-10-13 16:11:59 +02003249
Borislav Petkov360b7f32010-10-15 19:25:38 +02003250err_free:
Borislav Petkov360b7f32010-10-15 19:25:38 +02003251 kfree(ecc_stngs);
3252 ecc_stngs = NULL;
3253
Borislav Petkov56b34b92009-12-21 18:13:01 +01003254err_ret:
Doug Thompson7d6034d2009-04-27 20:01:01 +02003255 return err;
3256}
3257
3258static void __exit amd64_edac_exit(void)
3259{
Borislav Petkov3f37a362016-05-06 19:44:27 +02003260 int i;
3261
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003262 if (pci_ctl)
3263 edac_pci_release_generic_ctl(pci_ctl);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003264
Borislav Petkov3f37a362016-05-06 19:44:27 +02003265 for (i = 0; i < amd_nb_num(); i++)
3266 remove_one_instance(i);
Borislav Petkov50542252009-12-11 18:14:40 +01003267
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003268 kfree(ecc_stngs);
3269 ecc_stngs = NULL;
3270
Borislav Petkov50542252009-12-11 18:14:40 +01003271 msrs_free(msrs);
3272 msrs = NULL;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003273}
3274
3275module_init(amd64_edac_init);
3276module_exit(amd64_edac_exit);
3277
3278MODULE_LICENSE("GPL");
3279MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
3280 "Dave Peterson, Thayne Harbaugh");
3281MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
3282 EDAC_AMD64_VERSION);
3283
3284module_param(edac_op_state, int, 0444);
3285MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");