blob: 9fbad908a854810ab716bc98a00da83ba6e041bb [file] [log] [blame]
Thomas Gleixner09c434b2019-05-19 13:08:20 +01001// SPDX-License-Identifier: GPL-2.0-only
Doug Thompson2bc65412009-05-04 20:11:14 +02002#include "amd64_edac.h"
Andreas Herrmann23ac4ae2010-09-17 18:03:43 +02003#include <asm/amd_nb.h>
Doug Thompson2bc65412009-05-04 20:11:14 +02004
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01005static struct edac_pci_ctl_info *pci_ctl;
Doug Thompson2bc65412009-05-04 20:11:14 +02006
7static int report_gart_errors;
8module_param(report_gart_errors, int, 0644);
9
10/*
11 * Set by command line parameter. If BIOS has enabled the ECC, this override is
12 * cleared to prevent re-enabling the hardware by this driver.
13 */
14static int ecc_enable_override;
15module_param(ecc_enable_override, int, 0644);
16
Tejun Heoa29d8b82010-02-02 14:39:15 +090017static struct msr __percpu *msrs;
Borislav Petkov50542252009-12-11 18:14:40 +010018
Yazen Ghannam38ddd4d2019-10-22 20:35:09 +000019static struct amd64_family_type *fam_type;
20
Borislav Petkov2ec591a2015-02-17 10:58:34 +010021/* Per-node stuff */
Borislav Petkovae7bb7c2010-10-14 16:01:30 +020022static struct ecc_settings **ecc_stngs;
Doug Thompson2bc65412009-05-04 20:11:14 +020023
24/*
Borislav Petkovb70ef012009-06-25 19:32:38 +020025 * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
26 * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
27 * or higher value'.
28 *
29 *FIXME: Produce a better mapping/linearisation.
30 */
Daniel J Bluemanc7e53012012-11-30 16:44:20 +080031static const struct scrubrate {
Borislav Petkov39094442010-11-24 19:52:09 +010032 u32 scrubval; /* bit pattern for scrub rate */
33 u32 bandwidth; /* bandwidth consumed (bytes/sec) */
34} scrubrates[] = {
Borislav Petkovb70ef012009-06-25 19:32:38 +020035 { 0x01, 1600000000UL},
36 { 0x02, 800000000UL},
37 { 0x03, 400000000UL},
38 { 0x04, 200000000UL},
39 { 0x05, 100000000UL},
40 { 0x06, 50000000UL},
41 { 0x07, 25000000UL},
42 { 0x08, 12284069UL},
43 { 0x09, 6274509UL},
44 { 0x0A, 3121951UL},
45 { 0x0B, 1560975UL},
46 { 0x0C, 781440UL},
47 { 0x0D, 390720UL},
48 { 0x0E, 195300UL},
49 { 0x0F, 97650UL},
50 { 0x10, 48854UL},
51 { 0x11, 24427UL},
52 { 0x12, 12213UL},
53 { 0x13, 6101UL},
54 { 0x14, 3051UL},
55 { 0x15, 1523UL},
56 { 0x16, 761UL},
57 { 0x00, 0UL}, /* scrubbing off */
58};
59
Borislav Petkov66fed2d2012-08-09 18:41:07 +020060int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
61 u32 *val, const char *func)
Borislav Petkovb2b0c602010-10-08 18:32:29 +020062{
63 int err = 0;
64
65 err = pci_read_config_dword(pdev, offset, val);
66 if (err)
67 amd64_warn("%s: error reading F%dx%03x.\n",
68 func, PCI_FUNC(pdev->devfn), offset);
69
70 return err;
71}
72
73int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
74 u32 val, const char *func)
75{
76 int err = 0;
77
78 err = pci_write_config_dword(pdev, offset, val);
79 if (err)
80 amd64_warn("%s: error writing to F%dx%03x.\n",
81 func, PCI_FUNC(pdev->devfn), offset);
82
83 return err;
84}
85
86/*
Borislav Petkov73ba8592011-09-19 17:34:45 +020087 * Select DCT to which PCI cfg accesses are routed
88 */
89static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
90{
91 u32 reg = 0;
92
93 amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -050094 reg &= (pvt->model == 0x30) ? ~3 : ~1;
Borislav Petkov73ba8592011-09-19 17:34:45 +020095 reg |= dct;
96 amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
97}
98
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -050099/*
100 *
101 * Depending on the family, F2 DCT reads need special handling:
102 *
103 * K8: has a single DCT only and no address offsets >= 0x100
104 *
105 * F10h: each DCT has its own set of regs
106 * DCT0 -> F2x040..
107 * DCT1 -> F2x140..
108 *
109 * F16h: has only 1 DCT
110 *
111 * F15h: we select which DCT we access using F1x10C[DctCfgSel]
112 */
113static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
114 int offset, u32 *val)
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200115{
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -0500116 switch (pvt->fam) {
117 case 0xf:
118 if (dct || offset >= 0x100)
119 return -EINVAL;
120 break;
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200121
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -0500122 case 0x10:
123 if (dct) {
124 /*
125 * Note: If ganging is enabled, barring the regs
126 * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx
127 * return 0. (cf. Section 2.8.1 F10h BKDG)
128 */
129 if (dct_ganging_enabled(pvt))
130 return 0;
131
132 offset += 0x100;
133 }
134 break;
135
136 case 0x15:
137 /*
138 * F15h: F2x1xx addresses do not map explicitly to DCT1.
139 * We should select which DCT we access using F1x10C[DctCfgSel]
140 */
141 dct = (dct && pvt->model == 0x30) ? 3 : dct;
142 f15h_select_dct(pvt, dct);
143 break;
144
145 case 0x16:
146 if (dct)
147 return -EINVAL;
148 break;
149
150 default:
151 break;
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200152 }
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -0500153 return amd64_read_pci_cfg(pvt->F2, offset, val);
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200154}
155
Borislav Petkovb70ef012009-06-25 19:32:38 +0200156/*
Doug Thompson2bc65412009-05-04 20:11:14 +0200157 * Memory scrubber control interface. For K8, memory scrubbing is handled by
158 * hardware and can involve L2 cache, dcache as well as the main memory. With
159 * F10, this is extended to L3 cache scrubbing on CPU models sporting that
160 * functionality.
161 *
162 * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
163 * (dram) over to cache lines. This is nasty, so we will use bandwidth in
164 * bytes/sec for the setting.
165 *
166 * Currently, we only do dram scrubbing. If the scrubbing is done in software on
167 * other archs, we might not have access to the caches directly.
168 */
169
Yazen Ghannam8051c0a2016-11-17 17:57:42 -0500170static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval)
171{
172 /*
173 * Fam17h supports scrub values between 0x5 and 0x14. Also, the values
174 * are shifted down by 0x5, so scrubval 0x5 is written to the register
175 * as 0x0, scrubval 0x6 as 0x1, etc.
176 */
177 if (scrubval >= 0x5 && scrubval <= 0x14) {
178 scrubval -= 0x5;
179 pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF);
180 pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1);
181 } else {
182 pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1);
183 }
184}
Doug Thompson2bc65412009-05-04 20:11:14 +0200185/*
Yazen Ghannam8051c0a2016-11-17 17:57:42 -0500186 * Scan the scrub rate mapping table for a close or matching bandwidth value to
Doug Thompson2bc65412009-05-04 20:11:14 +0200187 * issue. If requested is too big, then use last maximum value found.
188 */
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500189static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
Doug Thompson2bc65412009-05-04 20:11:14 +0200190{
191 u32 scrubval;
192 int i;
193
194 /*
195 * map the configured rate (new_bw) to a value specific to the AMD64
196 * memory controller and apply to register. Search for the first
197 * bandwidth entry that is greater or equal than the setting requested
198 * and program that. If at last entry, turn off DRAM scrubbing.
Andrew Morton168bfee2012-10-23 14:09:39 -0700199 *
200 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
201 * by falling back to the last element in scrubrates[].
Doug Thompson2bc65412009-05-04 20:11:14 +0200202 */
Andrew Morton168bfee2012-10-23 14:09:39 -0700203 for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
Doug Thompson2bc65412009-05-04 20:11:14 +0200204 /*
205 * skip scrub rates which aren't recommended
206 * (see F10 BKDG, F3x58)
207 */
Borislav Petkov395ae782010-10-01 18:38:19 +0200208 if (scrubrates[i].scrubval < min_rate)
Doug Thompson2bc65412009-05-04 20:11:14 +0200209 continue;
210
211 if (scrubrates[i].bandwidth <= new_bw)
212 break;
Doug Thompson2bc65412009-05-04 20:11:14 +0200213 }
214
215 scrubval = scrubrates[i].scrubval;
Doug Thompson2bc65412009-05-04 20:11:14 +0200216
Yazen Ghannamdcd01392020-01-10 01:56:51 +0000217 if (pvt->umc) {
Yazen Ghannam8051c0a2016-11-17 17:57:42 -0500218 __f17h_set_scrubval(pvt, scrubval);
219 } else if (pvt->fam == 0x15 && pvt->model == 0x60) {
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500220 f15h_select_dct(pvt, 0);
221 pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
222 f15h_select_dct(pvt, 1);
223 pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
224 } else {
225 pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F);
226 }
Doug Thompson2bc65412009-05-04 20:11:14 +0200227
Borislav Petkov39094442010-11-24 19:52:09 +0100228 if (scrubval)
229 return scrubrates[i].bandwidth;
230
Doug Thompson2bc65412009-05-04 20:11:14 +0200231 return 0;
232}
233
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100234static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
Doug Thompson2bc65412009-05-04 20:11:14 +0200235{
236 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkov87b3e0e2011-01-19 20:02:38 +0100237 u32 min_scrubrate = 0x5;
Doug Thompson2bc65412009-05-04 20:11:14 +0200238
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200239 if (pvt->fam == 0xf)
Borislav Petkov87b3e0e2011-01-19 20:02:38 +0100240 min_scrubrate = 0x0;
241
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500242 if (pvt->fam == 0x15) {
243 /* Erratum #505 */
244 if (pvt->model < 0x10)
245 f15h_select_dct(pvt, 0);
Borislav Petkov73ba8592011-09-19 17:34:45 +0200246
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500247 if (pvt->model == 0x60)
248 min_scrubrate = 0x6;
249 }
250 return __set_scrub_rate(pvt, bw, min_scrubrate);
Doug Thompson2bc65412009-05-04 20:11:14 +0200251}
252
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100253static int get_scrub_rate(struct mem_ctl_info *mci)
Doug Thompson2bc65412009-05-04 20:11:14 +0200254{
255 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkov39094442010-11-24 19:52:09 +0100256 int i, retval = -EINVAL;
Yazen Ghannam8051c0a2016-11-17 17:57:42 -0500257 u32 scrubval = 0;
Doug Thompson2bc65412009-05-04 20:11:14 +0200258
Yazen Ghannamdcd01392020-01-10 01:56:51 +0000259 if (pvt->umc) {
Yazen Ghannam8051c0a2016-11-17 17:57:42 -0500260 amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval);
261 if (scrubval & BIT(0)) {
262 amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval);
263 scrubval &= 0xF;
264 scrubval += 0x5;
265 } else {
266 scrubval = 0;
267 }
Yazen Ghannamdcd01392020-01-10 01:56:51 +0000268 } else if (pvt->fam == 0x15) {
269 /* Erratum #505 */
270 if (pvt->model < 0x10)
271 f15h_select_dct(pvt, 0);
Yazen Ghannam8051c0a2016-11-17 17:57:42 -0500272
Yazen Ghannamdcd01392020-01-10 01:56:51 +0000273 if (pvt->model == 0x60)
274 amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
275 } else {
Aravind Gopalakrishnanda921102015-09-28 06:43:12 -0500276 amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
Yazen Ghannam8051c0a2016-11-17 17:57:42 -0500277 }
Doug Thompson2bc65412009-05-04 20:11:14 +0200278
279 scrubval = scrubval & 0x001F;
280
Roel Kluin926311f2010-01-11 20:58:21 +0100281 for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
Doug Thompson2bc65412009-05-04 20:11:14 +0200282 if (scrubrates[i].scrubval == scrubval) {
Borislav Petkov39094442010-11-24 19:52:09 +0100283 retval = scrubrates[i].bandwidth;
Doug Thompson2bc65412009-05-04 20:11:14 +0200284 break;
285 }
286 }
Borislav Petkov39094442010-11-24 19:52:09 +0100287 return retval;
Doug Thompson2bc65412009-05-04 20:11:14 +0200288}
289
Doug Thompson67757632009-04-27 15:53:22 +0200290/*
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200291 * returns true if the SysAddr given by sys_addr matches the
292 * DRAM base/limit associated with node_id
Doug Thompson67757632009-04-27 15:53:22 +0200293 */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100294static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid)
Doug Thompson67757632009-04-27 15:53:22 +0200295{
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200296 u64 addr;
Doug Thompson67757632009-04-27 15:53:22 +0200297
298 /* The K8 treats this as a 40-bit value. However, bits 63-40 will be
299 * all ones if the most significant implemented address bit is 1.
300 * Here we discard bits 63-40. See section 3.4.2 of AMD publication
301 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
302 * Application Programming.
303 */
304 addr = sys_addr & 0x000000ffffffffffull;
305
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200306 return ((addr >= get_dram_base(pvt, nid)) &&
307 (addr <= get_dram_limit(pvt, nid)));
Doug Thompson67757632009-04-27 15:53:22 +0200308}
309
310/*
311 * Attempt to map a SysAddr to a node. On success, return a pointer to the
312 * mem_ctl_info structure for the node that the SysAddr maps to.
313 *
314 * On failure, return NULL.
315 */
316static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
317 u64 sys_addr)
318{
319 struct amd64_pvt *pvt;
Daniel J Bluemanc7e53012012-11-30 16:44:20 +0800320 u8 node_id;
Doug Thompson67757632009-04-27 15:53:22 +0200321 u32 intlv_en, bits;
322
323 /*
324 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
325 * 3.4.4.2) registers to map the SysAddr to a node ID.
326 */
327 pvt = mci->pvt_info;
328
329 /*
330 * The value of this field should be the same for all DRAM Base
331 * registers. Therefore we arbitrarily choose to read it from the
332 * register for node 0.
333 */
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200334 intlv_en = dram_intlv_en(pvt, 0);
Doug Thompson67757632009-04-27 15:53:22 +0200335
336 if (intlv_en == 0) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200337 for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100338 if (base_limit_match(pvt, sys_addr, node_id))
Borislav Petkov8edc5442009-09-18 12:39:19 +0200339 goto found;
Doug Thompson67757632009-04-27 15:53:22 +0200340 }
Borislav Petkov8edc5442009-09-18 12:39:19 +0200341 goto err_no_match;
Doug Thompson67757632009-04-27 15:53:22 +0200342 }
343
Borislav Petkov72f158f2009-09-18 12:27:27 +0200344 if (unlikely((intlv_en != 0x01) &&
345 (intlv_en != 0x03) &&
346 (intlv_en != 0x07))) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200347 amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
Doug Thompson67757632009-04-27 15:53:22 +0200348 return NULL;
349 }
350
351 bits = (((u32) sys_addr) >> 12) & intlv_en;
352
353 for (node_id = 0; ; ) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200354 if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
Doug Thompson67757632009-04-27 15:53:22 +0200355 break; /* intlv_sel field matches */
356
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200357 if (++node_id >= DRAM_RANGES)
Doug Thompson67757632009-04-27 15:53:22 +0200358 goto err_no_match;
359 }
360
361 /* sanity test for sys_addr */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100362 if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200363 amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
364 "range for node %d with node interleaving enabled.\n",
365 __func__, sys_addr, node_id);
Doug Thompson67757632009-04-27 15:53:22 +0200366 return NULL;
367 }
368
369found:
Borislav Petkovb487c332011-02-21 18:55:00 +0100370 return edac_mc_find((int)node_id);
Doug Thompson67757632009-04-27 15:53:22 +0200371
372err_no_match:
Joe Perches956b9ba12012-04-29 17:08:39 -0300373 edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
374 (unsigned long)sys_addr);
Doug Thompson67757632009-04-27 15:53:22 +0200375
376 return NULL;
377}
Doug Thompsone2ce7252009-04-27 15:57:12 +0200378
379/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100380 * compute the CS base address of the @csrow on the DRAM controller @dct.
381 * For details see F2x[5C:40] in the processor's BKDG
Doug Thompsone2ce7252009-04-27 15:57:12 +0200382 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100383static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
384 u64 *base, u64 *mask)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200385{
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100386 u64 csbase, csmask, base_bits, mask_bits;
387 u8 addr_shift;
388
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500389 if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100390 csbase = pvt->csels[dct].csbases[csrow];
391 csmask = pvt->csels[dct].csmasks[csrow];
Chen, Gong10ef6b02013-10-18 14:29:07 -0700392 base_bits = GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9);
393 mask_bits = GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100394 addr_shift = 4;
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500395
396 /*
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500397 * F16h and F15h, models 30h and later need two addr_shift values:
398 * 8 for high and 6 for low (cf. F16h BKDG).
399 */
400 } else if (pvt->fam == 0x16 ||
401 (pvt->fam == 0x15 && pvt->model >= 0x30)) {
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500402 csbase = pvt->csels[dct].csbases[csrow];
403 csmask = pvt->csels[dct].csmasks[csrow >> 1];
404
Chen, Gong10ef6b02013-10-18 14:29:07 -0700405 *base = (csbase & GENMASK_ULL(15, 5)) << 6;
406 *base |= (csbase & GENMASK_ULL(30, 19)) << 8;
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500407
408 *mask = ~0ULL;
409 /* poke holes for the csmask */
Chen, Gong10ef6b02013-10-18 14:29:07 -0700410 *mask &= ~((GENMASK_ULL(15, 5) << 6) |
411 (GENMASK_ULL(30, 19) << 8));
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500412
Chen, Gong10ef6b02013-10-18 14:29:07 -0700413 *mask |= (csmask & GENMASK_ULL(15, 5)) << 6;
414 *mask |= (csmask & GENMASK_ULL(30, 19)) << 8;
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -0500415
416 return;
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100417 } else {
418 csbase = pvt->csels[dct].csbases[csrow];
419 csmask = pvt->csels[dct].csmasks[csrow >> 1];
420 addr_shift = 8;
421
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200422 if (pvt->fam == 0x15)
Chen, Gong10ef6b02013-10-18 14:29:07 -0700423 base_bits = mask_bits =
424 GENMASK_ULL(30,19) | GENMASK_ULL(13,5);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100425 else
Chen, Gong10ef6b02013-10-18 14:29:07 -0700426 base_bits = mask_bits =
427 GENMASK_ULL(28,19) | GENMASK_ULL(13,5);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100428 }
429
430 *base = (csbase & base_bits) << addr_shift;
431
432 *mask = ~0ULL;
433 /* poke holes for the csmask */
434 *mask &= ~(mask_bits << addr_shift);
435 /* OR them in */
436 *mask |= (csmask & mask_bits) << addr_shift;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200437}
438
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100439#define for_each_chip_select(i, dct, pvt) \
440 for (i = 0; i < pvt->csels[dct].b_cnt; i++)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200441
Borislav Petkov614ec9d2011-01-13 18:02:22 +0100442#define chip_select_base(i, dct, pvt) \
443 pvt->csels[dct].csbases[i]
444
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100445#define for_each_chip_select_mask(i, dct, pvt) \
446 for (i = 0; i < pvt->csels[dct].m_cnt; i++)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200447
Yazen Ghannam4d30d2b2019-02-28 15:36:10 +0000448#define for_each_umc(i) \
Yazen Ghannam5e4c5522019-10-22 20:35:11 +0000449 for (i = 0; i < fam_type->max_mcs; i++)
Yazen Ghannam4d30d2b2019-02-28 15:36:10 +0000450
Doug Thompsone2ce7252009-04-27 15:57:12 +0200451/*
452 * @input_addr is an InputAddr associated with the node given by mci. Return the
453 * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
454 */
455static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
456{
457 struct amd64_pvt *pvt;
458 int csrow;
459 u64 base, mask;
460
461 pvt = mci->pvt_info;
462
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100463 for_each_chip_select(csrow, 0, pvt) {
464 if (!csrow_enabled(csrow, 0, pvt))
Doug Thompsone2ce7252009-04-27 15:57:12 +0200465 continue;
466
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100467 get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
468
469 mask = ~mask;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200470
471 if ((input_addr & mask) == (base & mask)) {
Joe Perches956b9ba12012-04-29 17:08:39 -0300472 edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
473 (unsigned long)input_addr, csrow,
474 pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200475
476 return csrow;
477 }
478 }
Joe Perches956b9ba12012-04-29 17:08:39 -0300479 edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
480 (unsigned long)input_addr, pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200481
482 return -1;
483}
484
485/*
Doug Thompsone2ce7252009-04-27 15:57:12 +0200486 * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
487 * for the node represented by mci. Info is passed back in *hole_base,
488 * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if
489 * info is invalid. Info may be invalid for either of the following reasons:
490 *
491 * - The revision of the node is not E or greater. In this case, the DRAM Hole
492 * Address Register does not exist.
493 *
494 * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
495 * indicating that its contents are not valid.
496 *
497 * The values passed back in *hole_base, *hole_offset, and *hole_size are
498 * complete 32-bit values despite the fact that the bitfields in the DHAR
499 * only represent bits 31-24 of the base and offset values.
500 */
501int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
502 u64 *hole_offset, u64 *hole_size)
503{
504 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200505
506 /* only revE and later have the DRAM Hole Address Register */
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200507 if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) {
Joe Perches956b9ba12012-04-29 17:08:39 -0300508 edac_dbg(1, " revision %d for node %d does not support DHAR\n",
509 pvt->ext_model, pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200510 return 1;
511 }
512
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100513 /* valid for Fam10h and above */
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200514 if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
Joe Perches956b9ba12012-04-29 17:08:39 -0300515 edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n");
Doug Thompsone2ce7252009-04-27 15:57:12 +0200516 return 1;
517 }
518
Borislav Petkovc8e518d2010-12-10 19:49:19 +0100519 if (!dhar_valid(pvt)) {
Joe Perches956b9ba12012-04-29 17:08:39 -0300520 edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n",
521 pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200522 return 1;
523 }
524
525 /* This node has Memory Hoisting */
526
527 /* +------------------+--------------------+--------------------+-----
528 * | memory | DRAM hole | relocated |
529 * | [0, (x - 1)] | [x, 0xffffffff] | addresses from |
530 * | | | DRAM hole |
531 * | | | [0x100000000, |
532 * | | | (0x100000000+ |
533 * | | | (0xffffffff-x))] |
534 * +------------------+--------------------+--------------------+-----
535 *
536 * Above is a diagram of physical memory showing the DRAM hole and the
537 * relocated addresses from the DRAM hole. As shown, the DRAM hole
538 * starts at address x (the base address) and extends through address
539 * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the
540 * addresses in the hole so that they start at 0x100000000.
541 */
542
Borislav Petkov1f316772012-08-10 12:50:50 +0200543 *hole_base = dhar_base(pvt);
544 *hole_size = (1ULL << 32) - *hole_base;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200545
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200546 *hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt)
547 : k8_dhar_offset(pvt);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200548
Joe Perches956b9ba12012-04-29 17:08:39 -0300549 edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
550 pvt->mc_node_id, (unsigned long)*hole_base,
551 (unsigned long)*hole_offset, (unsigned long)*hole_size);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200552
553 return 0;
554}
555EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
556
Doug Thompson93c2df52009-05-04 20:46:50 +0200557/*
558 * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is
559 * assumed that sys_addr maps to the node given by mci.
560 *
561 * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
562 * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
563 * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
564 * then it is also involved in translating a SysAddr to a DramAddr. Sections
565 * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
566 * These parts of the documentation are unclear. I interpret them as follows:
567 *
568 * When node n receives a SysAddr, it processes the SysAddr as follows:
569 *
570 * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
571 * Limit registers for node n. If the SysAddr is not within the range
572 * specified by the base and limit values, then node n ignores the Sysaddr
573 * (since it does not map to node n). Otherwise continue to step 2 below.
574 *
575 * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
576 * disabled so skip to step 3 below. Otherwise see if the SysAddr is within
577 * the range of relocated addresses (starting at 0x100000000) from the DRAM
578 * hole. If not, skip to step 3 below. Else get the value of the
579 * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
580 * offset defined by this value from the SysAddr.
581 *
582 * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
583 * Base register for node n. To obtain the DramAddr, subtract the base
584 * address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
585 */
586static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
587{
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200588 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompson93c2df52009-05-04 20:46:50 +0200589 u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
Borislav Petkov1f316772012-08-10 12:50:50 +0200590 int ret;
Doug Thompson93c2df52009-05-04 20:46:50 +0200591
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200592 dram_base = get_dram_base(pvt, pvt->mc_node_id);
Doug Thompson93c2df52009-05-04 20:46:50 +0200593
594 ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
595 &hole_size);
596 if (!ret) {
Borislav Petkov1f316772012-08-10 12:50:50 +0200597 if ((sys_addr >= (1ULL << 32)) &&
598 (sys_addr < ((1ULL << 32) + hole_size))) {
Doug Thompson93c2df52009-05-04 20:46:50 +0200599 /* use DHAR to translate SysAddr to DramAddr */
600 dram_addr = sys_addr - hole_offset;
601
Joe Perches956b9ba12012-04-29 17:08:39 -0300602 edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
603 (unsigned long)sys_addr,
604 (unsigned long)dram_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200605
606 return dram_addr;
607 }
608 }
609
610 /*
611 * Translate the SysAddr to a DramAddr as shown near the start of
612 * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8
613 * only deals with 40-bit values. Therefore we discard bits 63-40 of
614 * sys_addr below. If bit 39 of sys_addr is 1 then the bits we
615 * discard are all 1s. Otherwise the bits we discard are all 0s. See
616 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
617 * Programmer's Manual Volume 1 Application Programming.
618 */
Chen, Gong10ef6b02013-10-18 14:29:07 -0700619 dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base;
Doug Thompson93c2df52009-05-04 20:46:50 +0200620
Joe Perches956b9ba12012-04-29 17:08:39 -0300621 edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
622 (unsigned long)sys_addr, (unsigned long)dram_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200623 return dram_addr;
624}
625
626/*
627 * @intlv_en is the value of the IntlvEn field from a DRAM Base register
628 * (section 3.4.4.1). Return the number of bits from a SysAddr that are used
629 * for node interleaving.
630 */
631static int num_node_interleave_bits(unsigned intlv_en)
632{
633 static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
634 int n;
635
636 BUG_ON(intlv_en > 7);
637 n = intlv_shift_table[intlv_en];
638 return n;
639}
640
641/* Translate the DramAddr given by @dram_addr to an InputAddr. */
642static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
643{
644 struct amd64_pvt *pvt;
645 int intlv_shift;
646 u64 input_addr;
647
648 pvt = mci->pvt_info;
649
650 /*
651 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
652 * concerning translating a DramAddr to an InputAddr.
653 */
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200654 intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
Chen, Gong10ef6b02013-10-18 14:29:07 -0700655 input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) +
Borislav Petkovf678b8c2010-12-13 19:21:07 +0100656 (dram_addr & 0xfff);
Doug Thompson93c2df52009-05-04 20:46:50 +0200657
Joe Perches956b9ba12012-04-29 17:08:39 -0300658 edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
659 intlv_shift, (unsigned long)dram_addr,
660 (unsigned long)input_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200661
662 return input_addr;
663}
664
665/*
666 * Translate the SysAddr represented by @sys_addr to an InputAddr. It is
667 * assumed that @sys_addr maps to the node given by mci.
668 */
669static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
670{
671 u64 input_addr;
672
673 input_addr =
674 dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
675
Masanari Iidac19ca6c2016-02-08 20:53:12 +0900676 edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n",
Joe Perches956b9ba12012-04-29 17:08:39 -0300677 (unsigned long)sys_addr, (unsigned long)input_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200678
679 return input_addr;
680}
681
Doug Thompson93c2df52009-05-04 20:46:50 +0200682/* Map the Error address to a PAGE and PAGE OFFSET. */
683static inline void error_address_to_page_and_offset(u64 error_address,
Borislav Petkov33ca0642012-08-30 18:01:36 +0200684 struct err_info *err)
Doug Thompson93c2df52009-05-04 20:46:50 +0200685{
Borislav Petkov33ca0642012-08-30 18:01:36 +0200686 err->page = (u32) (error_address >> PAGE_SHIFT);
687 err->offset = ((u32) error_address) & ~PAGE_MASK;
Doug Thompson93c2df52009-05-04 20:46:50 +0200688}
689
690/*
691 * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
692 * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
693 * of a node that detected an ECC memory error. mci represents the node that
694 * the error address maps to (possibly different from the node that detected
695 * the error). Return the number of the csrow that sys_addr maps to, or -1 on
696 * error.
697 */
698static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
699{
700 int csrow;
701
702 csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
703
704 if (csrow == -1)
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200705 amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
706 "address 0x%lx\n", (unsigned long)sys_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200707 return csrow;
708}
Doug Thompsone2ce7252009-04-27 15:57:12 +0200709
Borislav Petkovbfc04ae2009-11-12 19:05:07 +0100710static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
Doug Thompson2da11652009-04-27 16:09:09 +0200711
Doug Thompson2da11652009-04-27 16:09:09 +0200712/*
713 * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
714 * are ECC capable.
715 */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100716static unsigned long determine_edac_cap(struct amd64_pvt *pvt)
Doug Thompson2da11652009-04-27 16:09:09 +0200717{
Dan Carpenter1f6189e2011-10-06 02:30:25 -0400718 unsigned long edac_cap = EDAC_FLAG_NONE;
Yazen Ghannamd27f3a32016-11-17 17:57:40 -0500719 u8 bit;
Doug Thompson2da11652009-04-27 16:09:09 +0200720
Yazen Ghannamd27f3a32016-11-17 17:57:40 -0500721 if (pvt->umc) {
722 u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0;
Doug Thompson2da11652009-04-27 16:09:09 +0200723
Yazen Ghannam4d30d2b2019-02-28 15:36:10 +0000724 for_each_umc(i) {
Yazen Ghannamd27f3a32016-11-17 17:57:40 -0500725 if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT))
726 continue;
727
728 umc_en_mask |= BIT(i);
729
730 /* UMC Configuration bit 12 (DimmEccEn) */
731 if (pvt->umc[i].umc_cfg & BIT(12))
732 dimm_ecc_en_mask |= BIT(i);
733 }
734
735 if (umc_en_mask == dimm_ecc_en_mask)
736 edac_cap = EDAC_FLAG_SECDED;
737 } else {
738 bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F)
739 ? 19
740 : 17;
741
742 if (pvt->dclr0 & BIT(bit))
743 edac_cap = EDAC_FLAG_SECDED;
744 }
Doug Thompson2da11652009-04-27 16:09:09 +0200745
746 return edac_cap;
747}
748
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100749static void debug_display_dimm_sizes(struct amd64_pvt *, u8);
Doug Thompson2da11652009-04-27 16:09:09 +0200750
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100751static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
Borislav Petkov68798e12009-11-03 16:18:33 +0100752{
Joe Perches956b9ba12012-04-29 17:08:39 -0300753 edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
Borislav Petkov68798e12009-11-03 16:18:33 +0100754
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100755 if (pvt->dram_type == MEM_LRDDR3) {
756 u32 dcsm = pvt->csels[chan].csmasks[0];
757 /*
758 * It's assumed all LRDIMMs in a DCT are going to be of
759 * same 'type' until proven otherwise. So, use a cs
760 * value of '0' here to get dcsm value.
761 */
762 edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3));
763 }
764
765 edac_dbg(1, "All DIMMs support ECC:%s\n",
766 (dclr & BIT(19)) ? "yes" : "no");
767
Borislav Petkov68798e12009-11-03 16:18:33 +0100768
Joe Perches956b9ba12012-04-29 17:08:39 -0300769 edac_dbg(1, " PAR/ERR parity: %s\n",
770 (dclr & BIT(8)) ? "enabled" : "disabled");
Borislav Petkov68798e12009-11-03 16:18:33 +0100771
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200772 if (pvt->fam == 0x10)
Joe Perches956b9ba12012-04-29 17:08:39 -0300773 edac_dbg(1, " DCT 128bit mode width: %s\n",
774 (dclr & BIT(11)) ? "128b" : "64b");
Borislav Petkov68798e12009-11-03 16:18:33 +0100775
Joe Perches956b9ba12012-04-29 17:08:39 -0300776 edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
777 (dclr & BIT(12)) ? "yes" : "no",
778 (dclr & BIT(13)) ? "yes" : "no",
779 (dclr & BIT(14)) ? "yes" : "no",
780 (dclr & BIT(15)) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100781}
782
Yazen Ghanname53a3b22019-08-21 23:59:59 +0000783#define CS_EVEN_PRIMARY BIT(0)
784#define CS_ODD_PRIMARY BIT(1)
Yazen Ghannam81f50902019-08-22 00:00:02 +0000785#define CS_EVEN_SECONDARY BIT(2)
786#define CS_ODD_SECONDARY BIT(3)
Yazen Ghanname53a3b22019-08-21 23:59:59 +0000787
Yazen Ghannam81f50902019-08-22 00:00:02 +0000788#define CS_EVEN (CS_EVEN_PRIMARY | CS_EVEN_SECONDARY)
789#define CS_ODD (CS_ODD_PRIMARY | CS_ODD_SECONDARY)
Yazen Ghanname53a3b22019-08-21 23:59:59 +0000790
791static int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
Yazen Ghannamfc00c6a2019-02-28 15:36:12 +0000792{
Yazen Ghanname53a3b22019-08-21 23:59:59 +0000793 int cs_mode = 0;
Yazen Ghannamfc00c6a2019-02-28 15:36:12 +0000794
Yazen Ghanname53a3b22019-08-21 23:59:59 +0000795 if (csrow_enabled(2 * dimm, ctrl, pvt))
796 cs_mode |= CS_EVEN_PRIMARY;
Yazen Ghannamfc00c6a2019-02-28 15:36:12 +0000797
Yazen Ghanname53a3b22019-08-21 23:59:59 +0000798 if (csrow_enabled(2 * dimm + 1, ctrl, pvt))
799 cs_mode |= CS_ODD_PRIMARY;
800
Yazen Ghannam81f50902019-08-22 00:00:02 +0000801 /* Asymmetric dual-rank DIMM support. */
802 if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt))
803 cs_mode |= CS_ODD_SECONDARY;
804
Yazen Ghanname53a3b22019-08-21 23:59:59 +0000805 return cs_mode;
Yazen Ghannamfc00c6a2019-02-28 15:36:12 +0000806}
807
Yazen Ghannam07ed82e2016-11-28 08:50:21 -0600808static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl)
809{
Yazen Ghanname53a3b22019-08-21 23:59:59 +0000810 int dimm, size0, size1, cs0, cs1, cs_mode;
Yazen Ghannam07ed82e2016-11-28 08:50:21 -0600811
812 edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl);
813
Yazen Ghannamd971e282019-08-21 23:59:55 +0000814 for (dimm = 0; dimm < 2; dimm++) {
Yazen Ghannameb77e6b2017-04-27 12:11:54 -0500815 cs0 = dimm * 2;
Yazen Ghannameb77e6b2017-04-27 12:11:54 -0500816 cs1 = dimm * 2 + 1;
817
Yazen Ghanname53a3b22019-08-21 23:59:59 +0000818 cs_mode = f17_get_cs_mode(dimm, ctrl, pvt);
819
820 size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0);
821 size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1);
Yazen Ghannam07ed82e2016-11-28 08:50:21 -0600822
823 amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
Yazen Ghannameb77e6b2017-04-27 12:11:54 -0500824 cs0, size0,
825 cs1, size1);
Yazen Ghannam07ed82e2016-11-28 08:50:21 -0600826 }
827}
828
829static void __dump_misc_regs_df(struct amd64_pvt *pvt)
830{
831 struct amd64_umc *umc;
832 u32 i, tmp, umc_base;
833
Yazen Ghannam4d30d2b2019-02-28 15:36:10 +0000834 for_each_umc(i) {
Yazen Ghannam07ed82e2016-11-28 08:50:21 -0600835 umc_base = get_umc_base(i);
836 umc = &pvt->umc[i];
837
838 edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg);
839 edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg);
840 edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl);
841 edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl);
842
843 amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp);
844 edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp);
845
846 amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp);
847 edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp);
848 edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi);
849
850 edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n",
851 i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no",
852 (umc->umc_cap_hi & BIT(31)) ? "yes" : "no");
853 edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n",
854 i, (umc->umc_cfg & BIT(12)) ? "yes" : "no");
855 edac_dbg(1, "UMC%d x4 DIMMs present: %s\n",
856 i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no");
857 edac_dbg(1, "UMC%d x16 DIMMs present: %s\n",
858 i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no");
859
860 if (pvt->dram_type == MEM_LRDDR4) {
861 amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp);
862 edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n",
863 i, 1 << ((tmp >> 4) & 0x3));
864 }
865
866 debug_display_dimm_sizes_df(pvt, i);
867 }
868
869 edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n",
870 pvt->dhar, dhar_base(pvt));
871}
872
Doug Thompson2da11652009-04-27 16:09:09 +0200873/* Display and decode various NB registers for debug purposes. */
Yazen Ghannam07ed82e2016-11-28 08:50:21 -0600874static void __dump_misc_regs(struct amd64_pvt *pvt)
Doug Thompson2da11652009-04-27 16:09:09 +0200875{
Joe Perches956b9ba12012-04-29 17:08:39 -0300876 edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
Doug Thompson2da11652009-04-27 16:09:09 +0200877
Joe Perches956b9ba12012-04-29 17:08:39 -0300878 edac_dbg(1, " NB two channel DRAM capable: %s\n",
879 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100880
Joe Perches956b9ba12012-04-29 17:08:39 -0300881 edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n",
882 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
883 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100884
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100885 debug_dump_dramcfg_low(pvt, pvt->dclr0, 0);
Doug Thompson2da11652009-04-27 16:09:09 +0200886
Joe Perches956b9ba12012-04-29 17:08:39 -0300887 edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
Doug Thompson2da11652009-04-27 16:09:09 +0200888
Joe Perches956b9ba12012-04-29 17:08:39 -0300889 edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
890 pvt->dhar, dhar_base(pvt),
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200891 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
892 : f10_dhar_offset(pvt));
Doug Thompson2da11652009-04-27 16:09:09 +0200893
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100894 debug_display_dimm_sizes(pvt, 0);
Borislav Petkov4d796362011-02-03 15:59:57 +0100895
Borislav Petkov8de1d912009-10-16 13:39:30 +0200896 /* everything below this point is Fam10h and above */
Borislav Petkova4b4bed2013-08-10 13:54:48 +0200897 if (pvt->fam == 0xf)
Doug Thompson2da11652009-04-27 16:09:09 +0200898 return;
Borislav Petkov4d796362011-02-03 15:59:57 +0100899
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100900 debug_display_dimm_sizes(pvt, 1);
Doug Thompson2da11652009-04-27 16:09:09 +0200901
Borislav Petkov8de1d912009-10-16 13:39:30 +0200902 /* Only if NOT ganged does dclr1 have valid info */
Borislav Petkov68798e12009-11-03 16:18:33 +0100903 if (!dct_ganging_enabled(pvt))
Borislav Petkovd1ea71c2013-12-15 17:54:27 +0100904 debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
Doug Thompson2da11652009-04-27 16:09:09 +0200905}
906
Yazen Ghannam07ed82e2016-11-28 08:50:21 -0600907/* Display and decode various NB registers for debug purposes. */
908static void dump_misc_regs(struct amd64_pvt *pvt)
909{
910 if (pvt->umc)
911 __dump_misc_regs_df(pvt);
912 else
913 __dump_misc_regs(pvt);
914
915 edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
916
Yazen Ghannam78359612019-02-28 15:36:11 +0000917 amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz);
Yazen Ghannam07ed82e2016-11-28 08:50:21 -0600918}
919
Doug Thompson94be4bf2009-04-27 16:12:00 +0200920/*
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500921 * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
Doug Thompson94be4bf2009-04-27 16:12:00 +0200922 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100923static void prep_chip_selects(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200924{
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500925 if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100926 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
927 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +0100928 } else if (pvt->fam == 0x15 && pvt->model == 0x30) {
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -0500929 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
930 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
Yazen Ghannamd971e282019-08-21 23:59:55 +0000931 } else if (pvt->fam >= 0x17) {
932 int umc;
933
934 for_each_umc(umc) {
935 pvt->csels[umc].b_cnt = 4;
936 pvt->csels[umc].m_cnt = 2;
937 }
938
Borislav Petkov9d858bb2009-09-21 14:35:51 +0200939 } else {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100940 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
941 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200942 }
943}
944
Yazen Ghannamd971e282019-08-21 23:59:55 +0000945static void read_umc_base_mask(struct amd64_pvt *pvt)
946{
Yazen Ghannam75747292019-08-22 00:00:01 +0000947 u32 umc_base_reg, umc_base_reg_sec;
948 u32 umc_mask_reg, umc_mask_reg_sec;
949 u32 base_reg, base_reg_sec;
950 u32 mask_reg, mask_reg_sec;
951 u32 *base, *base_sec;
952 u32 *mask, *mask_sec;
Yazen Ghannamd971e282019-08-21 23:59:55 +0000953 int cs, umc;
954
955 for_each_umc(umc) {
956 umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR;
Yazen Ghannam75747292019-08-22 00:00:01 +0000957 umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC;
Yazen Ghannamd971e282019-08-21 23:59:55 +0000958
959 for_each_chip_select(cs, umc, pvt) {
960 base = &pvt->csels[umc].csbases[cs];
Yazen Ghannam75747292019-08-22 00:00:01 +0000961 base_sec = &pvt->csels[umc].csbases_sec[cs];
Yazen Ghannamd971e282019-08-21 23:59:55 +0000962
963 base_reg = umc_base_reg + (cs * 4);
Yazen Ghannam75747292019-08-22 00:00:01 +0000964 base_reg_sec = umc_base_reg_sec + (cs * 4);
Yazen Ghannamd971e282019-08-21 23:59:55 +0000965
966 if (!amd_smn_read(pvt->mc_node_id, base_reg, base))
967 edac_dbg(0, " DCSB%d[%d]=0x%08x reg: 0x%x\n",
968 umc, cs, *base, base_reg);
Yazen Ghannam75747292019-08-22 00:00:01 +0000969
970 if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec))
971 edac_dbg(0, " DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n",
972 umc, cs, *base_sec, base_reg_sec);
Yazen Ghannamd971e282019-08-21 23:59:55 +0000973 }
974
975 umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK;
Yazen Ghannam75747292019-08-22 00:00:01 +0000976 umc_mask_reg_sec = get_umc_base(umc) + UMCCH_ADDR_MASK_SEC;
Yazen Ghannamd971e282019-08-21 23:59:55 +0000977
978 for_each_chip_select_mask(cs, umc, pvt) {
979 mask = &pvt->csels[umc].csmasks[cs];
Yazen Ghannam75747292019-08-22 00:00:01 +0000980 mask_sec = &pvt->csels[umc].csmasks_sec[cs];
Yazen Ghannamd971e282019-08-21 23:59:55 +0000981
982 mask_reg = umc_mask_reg + (cs * 4);
Yazen Ghannam75747292019-08-22 00:00:01 +0000983 mask_reg_sec = umc_mask_reg_sec + (cs * 4);
Yazen Ghannamd971e282019-08-21 23:59:55 +0000984
985 if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask))
986 edac_dbg(0, " DCSM%d[%d]=0x%08x reg: 0x%x\n",
987 umc, cs, *mask, mask_reg);
Yazen Ghannam75747292019-08-22 00:00:01 +0000988
989 if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec))
990 edac_dbg(0, " DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n",
991 umc, cs, *mask_sec, mask_reg_sec);
Yazen Ghannamd971e282019-08-21 23:59:55 +0000992 }
993 }
994}
995
Doug Thompson94be4bf2009-04-27 16:12:00 +0200996/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100997 * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
Doug Thompson94be4bf2009-04-27 16:12:00 +0200998 */
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200999static void read_dct_base_mask(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +02001000{
Yazen Ghannamd971e282019-08-21 23:59:55 +00001001 int cs;
Doug Thompson94be4bf2009-04-27 16:12:00 +02001002
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001003 prep_chip_selects(pvt);
Doug Thompson94be4bf2009-04-27 16:12:00 +02001004
Yazen Ghannamd971e282019-08-21 23:59:55 +00001005 if (pvt->umc)
1006 return read_umc_base_mask(pvt);
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05001007
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001008 for_each_chip_select(cs, 0, pvt) {
Yazen Ghannamd971e282019-08-21 23:59:55 +00001009 int reg0 = DCSB0 + (cs * 4);
1010 int reg1 = DCSB1 + (cs * 4);
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001011 u32 *base0 = &pvt->csels[0].csbases[cs];
1012 u32 *base1 = &pvt->csels[1].csbases[cs];
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001013
Yazen Ghannamd971e282019-08-21 23:59:55 +00001014 if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
1015 edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n",
1016 cs, *base0, reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +02001017
Yazen Ghannamd971e282019-08-21 23:59:55 +00001018 if (pvt->fam == 0xf)
1019 continue;
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001020
Yazen Ghannamd971e282019-08-21 23:59:55 +00001021 if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
1022 edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n",
1023 cs, *base1, (pvt->fam == 0x10) ? reg1
1024 : reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +02001025 }
1026
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001027 for_each_chip_select_mask(cs, 0, pvt) {
Yazen Ghannamd971e282019-08-21 23:59:55 +00001028 int reg0 = DCSM0 + (cs * 4);
1029 int reg1 = DCSM1 + (cs * 4);
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001030 u32 *mask0 = &pvt->csels[0].csmasks[cs];
1031 u32 *mask1 = &pvt->csels[1].csmasks[cs];
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001032
Yazen Ghannamd971e282019-08-21 23:59:55 +00001033 if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
1034 edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n",
1035 cs, *mask0, reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +02001036
Yazen Ghannamd971e282019-08-21 23:59:55 +00001037 if (pvt->fam == 0xf)
1038 continue;
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001039
Yazen Ghannamd971e282019-08-21 23:59:55 +00001040 if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
1041 edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n",
1042 cs, *mask1, (pvt->fam == 0x10) ? reg1
1043 : reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +02001044 }
1045}
1046
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001047static void determine_memory_type(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +02001048{
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001049 u32 dram_ctrl, dcsm;
Doug Thompson94be4bf2009-04-27 16:12:00 +02001050
Yazen Ghannamdcd01392020-01-10 01:56:51 +00001051 if (pvt->umc) {
1052 if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5))
1053 pvt->dram_type = MEM_LRDDR4;
1054 else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4))
1055 pvt->dram_type = MEM_RDDR4;
1056 else
1057 pvt->dram_type = MEM_DDR4;
1058 return;
1059 }
1060
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001061 switch (pvt->fam) {
1062 case 0xf:
1063 if (pvt->ext_model >= K8_REV_F)
1064 goto ddr3;
1065
1066 pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
1067 return;
1068
1069 case 0x10:
Borislav Petkov6b4c0bd2009-11-12 15:37:57 +01001070 if (pvt->dchr0 & DDR3_MODE)
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001071 goto ddr3;
1072
1073 pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
1074 return;
1075
1076 case 0x15:
1077 if (pvt->model < 0x60)
1078 goto ddr3;
1079
1080 /*
1081 * Model 0x60h needs special handling:
1082 *
1083 * We use a Chip Select value of '0' to obtain dcsm.
1084 * Theoretically, it is possible to populate LRDIMMs of different
1085 * 'Rank' value on a DCT. But this is not the common case. So,
1086 * it's reasonable to assume all DIMMs are going to be of same
1087 * 'type' until proven otherwise.
1088 */
1089 amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl);
1090 dcsm = pvt->csels[0].csmasks[0];
1091
1092 if (((dram_ctrl >> 8) & 0x7) == 0x2)
1093 pvt->dram_type = MEM_DDR4;
1094 else if (pvt->dclr0 & BIT(16))
1095 pvt->dram_type = MEM_DDR3;
1096 else if (dcsm & 0x3)
1097 pvt->dram_type = MEM_LRDDR3;
Borislav Petkov6b4c0bd2009-11-12 15:37:57 +01001098 else
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001099 pvt->dram_type = MEM_RDDR3;
1100
1101 return;
1102
1103 case 0x16:
1104 goto ddr3;
1105
1106 default:
1107 WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam);
1108 pvt->dram_type = MEM_EMPTY;
Doug Thompson94be4bf2009-04-27 16:12:00 +02001109 }
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001110 return;
Doug Thompson94be4bf2009-04-27 16:12:00 +02001111
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001112ddr3:
1113 pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
Doug Thompson94be4bf2009-04-27 16:12:00 +02001114}
1115
Borislav Petkovcb328502010-12-22 14:28:24 +01001116/* Get the number of DCT channels the memory controller is using. */
Doug Thompsonddff8762009-04-27 16:14:52 +02001117static int k8_early_channel_count(struct amd64_pvt *pvt)
1118{
Borislav Petkovcb328502010-12-22 14:28:24 +01001119 int flag;
Doug Thompsonddff8762009-04-27 16:14:52 +02001120
Borislav Petkov9f56da02010-10-01 19:44:53 +02001121 if (pvt->ext_model >= K8_REV_F)
Doug Thompsonddff8762009-04-27 16:14:52 +02001122 /* RevF (NPT) and later */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001123 flag = pvt->dclr0 & WIDTH_128;
Borislav Petkov9f56da02010-10-01 19:44:53 +02001124 else
Doug Thompsonddff8762009-04-27 16:14:52 +02001125 /* RevE and earlier */
1126 flag = pvt->dclr0 & REVE_WIDTH_128;
Doug Thompsonddff8762009-04-27 16:14:52 +02001127
1128 /* not used */
1129 pvt->dclr1 = 0;
1130
1131 return (flag) ? 2 : 1;
1132}
1133
Borislav Petkov70046622011-01-10 14:37:27 +01001134/* On F10h and later ErrAddr is MC4_ADDR[47:1] */
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001135static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
Doug Thompsonddff8762009-04-27 16:14:52 +02001136{
Borislav Petkov2ec591a2015-02-17 10:58:34 +01001137 u16 mce_nid = amd_get_nb_id(m->extcpu);
1138 struct mem_ctl_info *mci;
Borislav Petkov70046622011-01-10 14:37:27 +01001139 u8 start_bit = 1;
1140 u8 end_bit = 47;
Borislav Petkov2ec591a2015-02-17 10:58:34 +01001141 u64 addr;
1142
1143 mci = edac_mc_find(mce_nid);
1144 if (!mci)
1145 return 0;
1146
1147 pvt = mci->pvt_info;
Borislav Petkov70046622011-01-10 14:37:27 +01001148
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001149 if (pvt->fam == 0xf) {
Borislav Petkov70046622011-01-10 14:37:27 +01001150 start_bit = 3;
1151 end_bit = 39;
1152 }
1153
Chen, Gong10ef6b02013-10-18 14:29:07 -07001154 addr = m->addr & GENMASK_ULL(end_bit, start_bit);
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001155
1156 /*
1157 * Erratum 637 workaround
1158 */
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001159 if (pvt->fam == 0x15) {
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001160 u64 cc6_base, tmp_addr;
1161 u32 tmp;
Daniel J Blueman8b84c8d2012-11-27 14:32:10 +08001162 u8 intlv_en;
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001163
Chen, Gong10ef6b02013-10-18 14:29:07 -07001164 if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7)
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001165 return addr;
1166
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001167
1168 amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
1169 intlv_en = tmp >> 21 & 0x7;
1170
1171 /* add [47:27] + 3 trailing bits */
Chen, Gong10ef6b02013-10-18 14:29:07 -07001172 cc6_base = (tmp & GENMASK_ULL(20, 0)) << 3;
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001173
1174 /* reverse and add DramIntlvEn */
1175 cc6_base |= intlv_en ^ 0x7;
1176
1177 /* pin at [47:24] */
1178 cc6_base <<= 24;
1179
1180 if (!intlv_en)
Chen, Gong10ef6b02013-10-18 14:29:07 -07001181 return cc6_base | (addr & GENMASK_ULL(23, 0));
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001182
1183 amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
1184
1185 /* faster log2 */
Chen, Gong10ef6b02013-10-18 14:29:07 -07001186 tmp_addr = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1);
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001187
1188 /* OR DramIntlvSel into bits [14:12] */
Chen, Gong10ef6b02013-10-18 14:29:07 -07001189 tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9;
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001190
1191 /* add remaining [11:0] bits from original MC4_ADDR */
Chen, Gong10ef6b02013-10-18 14:29:07 -07001192 tmp_addr |= addr & GENMASK_ULL(11, 0);
Borislav Petkovc1ae6832011-03-30 15:42:10 +02001193
1194 return cc6_base | tmp_addr;
1195 }
1196
1197 return addr;
Doug Thompsonddff8762009-04-27 16:14:52 +02001198}
1199
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001200static struct pci_dev *pci_get_related_function(unsigned int vendor,
1201 unsigned int device,
1202 struct pci_dev *related)
1203{
1204 struct pci_dev *dev = NULL;
1205
1206 while ((dev = pci_get_device(vendor, device, dev))) {
1207 if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) &&
1208 (dev->bus->number == related->bus->number) &&
1209 (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
1210 break;
1211 }
1212
1213 return dev;
1214}
1215
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001216static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
Doug Thompsonddff8762009-04-27 16:14:52 +02001217{
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001218 struct amd_northbridge *nb;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001219 struct pci_dev *f1 = NULL;
1220 unsigned int pci_func;
Borislav Petkov71d2a322011-02-21 19:37:24 +01001221 int off = range << 3;
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001222 u32 llim;
Doug Thompsonddff8762009-04-27 16:14:52 +02001223
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001224 amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo);
1225 amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
Doug Thompsonddff8762009-04-27 16:14:52 +02001226
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001227 if (pvt->fam == 0xf)
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001228 return;
Doug Thompsonddff8762009-04-27 16:14:52 +02001229
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001230 if (!dram_rw(pvt, range))
1231 return;
Doug Thompsonddff8762009-04-27 16:14:52 +02001232
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001233 amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi);
1234 amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
Borislav Petkovf08e4572011-03-21 20:45:06 +01001235
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001236 /* F15h: factor in CC6 save area by reading dst node's limit reg */
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001237 if (pvt->fam != 0x15)
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001238 return;
Borislav Petkovf08e4572011-03-21 20:45:06 +01001239
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001240 nb = node_to_amd_nb(dram_dst_node(pvt, range));
1241 if (WARN_ON(!nb))
1242 return;
Borislav Petkovf08e4572011-03-21 20:45:06 +01001243
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001244 if (pvt->model == 0x60)
1245 pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
1246 else if (pvt->model == 0x30)
1247 pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
1248 else
1249 pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001250
1251 f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001252 if (WARN_ON(!f1))
1253 return;
Borislav Petkovf08e4572011-03-21 20:45:06 +01001254
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001255 amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
Borislav Petkovf08e4572011-03-21 20:45:06 +01001256
Chen, Gong10ef6b02013-10-18 14:29:07 -07001257 pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0);
Borislav Petkovf08e4572011-03-21 20:45:06 +01001258
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001259 /* {[39:27],111b} */
1260 pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
Borislav Petkovf08e4572011-03-21 20:45:06 +01001261
Chen, Gong10ef6b02013-10-18 14:29:07 -07001262 pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0);
Borislav Petkovf08e4572011-03-21 20:45:06 +01001263
Daniel J Bluemane2c0bff2012-11-30 16:44:19 +08001264 /* [47:40] */
1265 pvt->ranges[range].lim.hi |= llim >> 13;
1266
1267 pci_dev_put(f1);
Doug Thompsonddff8762009-04-27 16:14:52 +02001268}
1269
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001270static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
Borislav Petkov33ca0642012-08-30 18:01:36 +02001271 struct err_info *err)
Doug Thompsonddff8762009-04-27 16:14:52 +02001272{
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001273 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompsonddff8762009-04-27 16:14:52 +02001274
Borislav Petkov33ca0642012-08-30 18:01:36 +02001275 error_address_to_page_and_offset(sys_addr, err);
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001276
1277 /*
1278 * Find out which node the error address belongs to. This may be
1279 * different from the node that detected the error.
1280 */
Borislav Petkov33ca0642012-08-30 18:01:36 +02001281 err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
1282 if (!err->src_mci) {
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001283 amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1284 (unsigned long)sys_addr);
Borislav Petkov33ca0642012-08-30 18:01:36 +02001285 err->err_code = ERR_NODE;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001286 return;
1287 }
1288
1289 /* Now map the sys_addr to a CSROW */
Borislav Petkov33ca0642012-08-30 18:01:36 +02001290 err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
1291 if (err->csrow < 0) {
1292 err->err_code = ERR_CSROW;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001293 return;
1294 }
1295
Doug Thompsonddff8762009-04-27 16:14:52 +02001296 /* CHIPKILL enabled */
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001297 if (pvt->nbcfg & NBCFG_CHIPKILL) {
Borislav Petkov33ca0642012-08-30 18:01:36 +02001298 err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
1299 if (err->channel < 0) {
Doug Thompsonddff8762009-04-27 16:14:52 +02001300 /*
1301 * Syndrome didn't map, so we don't know which of the
1302 * 2 DIMMs is in error. So we need to ID 'both' of them
1303 * as suspect.
1304 */
Borislav Petkov33ca0642012-08-30 18:01:36 +02001305 amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001306 "possible error reporting race\n",
Borislav Petkov33ca0642012-08-30 18:01:36 +02001307 err->syndrome);
1308 err->err_code = ERR_CHANNEL;
Doug Thompsonddff8762009-04-27 16:14:52 +02001309 return;
1310 }
1311 } else {
1312 /*
1313 * non-chipkill ecc mode
1314 *
1315 * The k8 documentation is unclear about how to determine the
1316 * channel number when using non-chipkill memory. This method
1317 * was obtained from email communication with someone at AMD.
1318 * (Wish the email was placed in this comment - norsk)
1319 */
Borislav Petkov33ca0642012-08-30 18:01:36 +02001320 err->channel = ((sys_addr & BIT(3)) != 0);
Doug Thompsonddff8762009-04-27 16:14:52 +02001321 }
Doug Thompsonddff8762009-04-27 16:14:52 +02001322}
1323
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001324static int ddr2_cs_size(unsigned i, bool dct_width)
Doug Thompsonddff8762009-04-27 16:14:52 +02001325{
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001326 unsigned shift = 0;
Doug Thompsonddff8762009-04-27 16:14:52 +02001327
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001328 if (i <= 2)
1329 shift = i;
1330 else if (!(i & 0x1))
1331 shift = i >> 1;
Borislav Petkov1433eb92009-10-21 13:44:36 +02001332 else
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001333 shift = (i + 1) >> 1;
Doug Thompsonddff8762009-04-27 16:14:52 +02001334
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001335 return 128 << (shift + !!dct_width);
1336}
1337
1338static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001339 unsigned cs_mode, int cs_mask_nr)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001340{
1341 u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
1342
1343 if (pvt->ext_model >= K8_REV_F) {
1344 WARN_ON(cs_mode > 11);
1345 return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
1346 }
1347 else if (pvt->ext_model >= K8_REV_D) {
Borislav Petkov11b0a312011-11-09 21:28:43 +01001348 unsigned diff;
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001349 WARN_ON(cs_mode > 10);
1350
Borislav Petkov11b0a312011-11-09 21:28:43 +01001351 /*
1352 * the below calculation, besides trying to win an obfuscated C
1353 * contest, maps cs_mode values to DIMM chip select sizes. The
1354 * mappings are:
1355 *
1356 * cs_mode CS size (mb)
1357 * ======= ============
1358 * 0 32
1359 * 1 64
1360 * 2 128
1361 * 3 128
1362 * 4 256
1363 * 5 512
1364 * 6 256
1365 * 7 512
1366 * 8 1024
1367 * 9 1024
1368 * 10 2048
1369 *
1370 * Basically, it calculates a value with which to shift the
1371 * smallest CS size of 32MB.
1372 *
1373 * ddr[23]_cs_size have a similar purpose.
1374 */
1375 diff = cs_mode/3 + (unsigned)(cs_mode > 5);
1376
1377 return 32 << (cs_mode - diff);
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001378 }
1379 else {
1380 WARN_ON(cs_mode > 6);
1381 return 32 << cs_mode;
1382 }
Doug Thompsonddff8762009-04-27 16:14:52 +02001383}
1384
Doug Thompson1afd3c92009-04-27 16:16:50 +02001385/*
1386 * Get the number of DCT channels in use.
1387 *
1388 * Return:
1389 * number of Memory Channels in operation
1390 * Pass back:
1391 * contents of the DCL0_LOW register
1392 */
Borislav Petkov7d20d142011-01-07 17:58:04 +01001393static int f1x_early_channel_count(struct amd64_pvt *pvt)
Doug Thompson1afd3c92009-04-27 16:16:50 +02001394{
Borislav Petkov6ba5dcd2009-10-13 19:26:55 +02001395 int i, j, channels = 0;
Doug Thompsonddff8762009-04-27 16:14:52 +02001396
Borislav Petkov7d20d142011-01-07 17:58:04 +01001397 /* On F10h, if we are in 128 bit mode, then we are using 2 channels */
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001398 if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
Borislav Petkov7d20d142011-01-07 17:58:04 +01001399 return 2;
Doug Thompson1afd3c92009-04-27 16:16:50 +02001400
1401 /*
Borislav Petkovd16149e2009-10-16 19:55:49 +02001402 * Need to check if in unganged mode: In such, there are 2 channels,
1403 * but they are not in 128 bit mode and thus the above 'dclr0' status
1404 * bit will be OFF.
Doug Thompson1afd3c92009-04-27 16:16:50 +02001405 *
1406 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
1407 * their CSEnable bit on. If so, then SINGLE DIMM case.
1408 */
Joe Perches956b9ba12012-04-29 17:08:39 -03001409 edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
Doug Thompson1afd3c92009-04-27 16:16:50 +02001410
1411 /*
1412 * Check DRAM Bank Address Mapping values for each DIMM to see if there
1413 * is more than just one DIMM present in unganged mode. Need to check
1414 * both controllers since DIMMs can be placed in either one.
1415 */
Borislav Petkov525a1b22010-12-21 15:53:27 +01001416 for (i = 0; i < 2; i++) {
1417 u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001418
Wan Wei57a30852009-08-07 17:04:49 +02001419 for (j = 0; j < 4; j++) {
1420 if (DBAM_DIMM(j, dbam) > 0) {
1421 channels++;
1422 break;
1423 }
1424 }
Doug Thompson1afd3c92009-04-27 16:16:50 +02001425 }
1426
Borislav Petkovd16149e2009-10-16 19:55:49 +02001427 if (channels > 2)
1428 channels = 2;
1429
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001430 amd64_info("MCT channel count: %d\n", channels);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001431
1432 return channels;
Doug Thompson1afd3c92009-04-27 16:16:50 +02001433}
1434
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05001435static int f17_early_channel_count(struct amd64_pvt *pvt)
1436{
1437 int i, channels = 0;
1438
1439 /* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */
Yazen Ghannam4d30d2b2019-02-28 15:36:10 +00001440 for_each_umc(i)
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05001441 channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT);
1442
1443 amd64_info("MCT channel count: %d\n", channels);
1444
1445 return channels;
1446}
1447
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001448static int ddr3_cs_size(unsigned i, bool dct_width)
Doug Thompson1afd3c92009-04-27 16:16:50 +02001449{
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001450 unsigned shift = 0;
1451 int cs_size = 0;
1452
1453 if (i == 0 || i == 3 || i == 4)
1454 cs_size = -1;
1455 else if (i <= 2)
1456 shift = i;
1457 else if (i == 12)
1458 shift = 7;
1459 else if (!(i & 0x1))
1460 shift = i >> 1;
1461 else
1462 shift = (i + 1) >> 1;
1463
1464 if (cs_size != -1)
1465 cs_size = (128 * (1 << !!dct_width)) << shift;
1466
1467 return cs_size;
1468}
1469
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001470static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
1471{
1472 unsigned shift = 0;
1473 int cs_size = 0;
1474
1475 if (i < 4 || i == 6)
1476 cs_size = -1;
1477 else if (i == 12)
1478 shift = 7;
1479 else if (!(i & 0x1))
1480 shift = i >> 1;
1481 else
1482 shift = (i + 1) >> 1;
1483
1484 if (cs_size != -1)
1485 cs_size = rank_multiply * (128 << shift);
1486
1487 return cs_size;
1488}
1489
1490static int ddr4_cs_size(unsigned i)
1491{
1492 int cs_size = 0;
1493
1494 if (i == 0)
1495 cs_size = -1;
1496 else if (i == 1)
1497 cs_size = 1024;
1498 else
1499 /* Min cs_size = 1G */
1500 cs_size = 1024 * (1 << (i >> 1));
1501
1502 return cs_size;
1503}
1504
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001505static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001506 unsigned cs_mode, int cs_mask_nr)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001507{
1508 u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
1509
1510 WARN_ON(cs_mode > 11);
Borislav Petkov1433eb92009-10-21 13:44:36 +02001511
1512 if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001513 return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
Borislav Petkov1433eb92009-10-21 13:44:36 +02001514 else
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001515 return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
1516}
Borislav Petkov1433eb92009-10-21 13:44:36 +02001517
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001518/*
1519 * F15h supports only 64bit DCT interfaces
1520 */
1521static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001522 unsigned cs_mode, int cs_mask_nr)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001523{
1524 WARN_ON(cs_mode > 12);
1525
1526 return ddr3_cs_size(cs_mode, false);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001527}
1528
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001529/* F15h M60h supports DDR4 mapping as well.. */
1530static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1531 unsigned cs_mode, int cs_mask_nr)
1532{
1533 int cs_size;
1534 u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
1535
1536 WARN_ON(cs_mode > 12);
1537
1538 if (pvt->dram_type == MEM_DDR4) {
1539 if (cs_mode > 9)
1540 return -1;
1541
1542 cs_size = ddr4_cs_size(cs_mode);
1543 } else if (pvt->dram_type == MEM_LRDDR3) {
1544 unsigned rank_multiply = dcsm & 0xf;
1545
1546 if (rank_multiply == 3)
1547 rank_multiply = 4;
1548 cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
1549 } else {
1550 /* Minimum cs size is 512mb for F15hM60h*/
1551 if (cs_mode == 0x1)
1552 return -1;
1553
1554 cs_size = ddr3_cs_size(cs_mode, false);
1555 }
1556
1557 return cs_size;
1558}
1559
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05001560/*
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001561 * F16h and F15h model 30h have only limited cs_modes.
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05001562 */
1563static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01001564 unsigned cs_mode, int cs_mask_nr)
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05001565{
1566 WARN_ON(cs_mode > 12);
1567
1568 if (cs_mode == 6 || cs_mode == 8 ||
1569 cs_mode == 9 || cs_mode == 12)
1570 return -1;
1571 else
1572 return ddr3_cs_size(cs_mode, false);
1573}
1574
Yazen Ghanname53a3b22019-08-21 23:59:59 +00001575static int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05001576 unsigned int cs_mode, int csrow_nr)
1577{
Yazen Ghanname53a3b22019-08-21 23:59:59 +00001578 u32 addr_mask_orig, addr_mask_deinterleaved;
1579 u32 msb, weight, num_zero_bits;
1580 int dimm, size = 0;
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05001581
Yazen Ghanname53a3b22019-08-21 23:59:59 +00001582 /* No Chip Selects are enabled. */
1583 if (!cs_mode)
1584 return size;
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05001585
Yazen Ghanname53a3b22019-08-21 23:59:59 +00001586 /* Requested size of an even CS but none are enabled. */
1587 if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1))
1588 return size;
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05001589
Yazen Ghanname53a3b22019-08-21 23:59:59 +00001590 /* Requested size of an odd CS but none are enabled. */
1591 if (!(cs_mode & CS_ODD) && (csrow_nr & 1))
1592 return size;
1593
1594 /*
1595 * There is one mask per DIMM, and two Chip Selects per DIMM.
1596 * CS0 and CS1 -> DIMM0
1597 * CS2 and CS3 -> DIMM1
1598 */
1599 dimm = csrow_nr >> 1;
1600
Yazen Ghannam81f50902019-08-22 00:00:02 +00001601 /* Asymmetric dual-rank DIMM support. */
1602 if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY))
1603 addr_mask_orig = pvt->csels[umc].csmasks_sec[dimm];
1604 else
1605 addr_mask_orig = pvt->csels[umc].csmasks[dimm];
Yazen Ghanname53a3b22019-08-21 23:59:59 +00001606
1607 /*
1608 * The number of zero bits in the mask is equal to the number of bits
1609 * in a full mask minus the number of bits in the current mask.
1610 *
1611 * The MSB is the number of bits in the full mask because BIT[0] is
1612 * always 0.
1613 */
1614 msb = fls(addr_mask_orig) - 1;
1615 weight = hweight_long(addr_mask_orig);
1616 num_zero_bits = msb - weight;
1617
1618 /* Take the number of zero bits off from the top of the mask. */
1619 addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1);
1620
1621 edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm);
1622 edac_dbg(1, " Original AddrMask: 0x%x\n", addr_mask_orig);
1623 edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved);
1624
1625 /* Register [31:1] = Address [39:9]. Size is in kBs here. */
1626 size = (addr_mask_deinterleaved >> 2) + 1;
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05001627
1628 /* Return size in MBs. */
1629 return size >> 10;
1630}
1631
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001632static void read_dram_ctl_register(struct amd64_pvt *pvt)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001633{
Doug Thompson6163b5d2009-04-27 16:20:17 +02001634
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001635 if (pvt->fam == 0xf)
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001636 return;
1637
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05001638 if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) {
Joe Perches956b9ba12012-04-29 17:08:39 -03001639 edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
1640 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001641
Joe Perches956b9ba12012-04-29 17:08:39 -03001642 edac_dbg(0, " DCTs operate in %s mode\n",
1643 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001644
Borislav Petkov72381bd2009-10-09 19:14:43 +02001645 if (!dct_ganging_enabled(pvt))
Joe Perches956b9ba12012-04-29 17:08:39 -03001646 edac_dbg(0, " Address range split per DCT: %s\n",
1647 (dct_high_range_enabled(pvt) ? "yes" : "no"));
Borislav Petkov72381bd2009-10-09 19:14:43 +02001648
Joe Perches956b9ba12012-04-29 17:08:39 -03001649 edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
1650 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
1651 (dct_memory_cleared(pvt) ? "yes" : "no"));
Borislav Petkov72381bd2009-10-09 19:14:43 +02001652
Joe Perches956b9ba12012-04-29 17:08:39 -03001653 edac_dbg(0, " channel interleave: %s, "
1654 "interleave bits selector: 0x%x\n",
1655 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
1656 dct_sel_interleave_addr(pvt));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001657 }
1658
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05001659 amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001660}
1661
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001662/*
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001663 * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
1664 * 2.10.12 Memory Interleaving Modes).
1665 */
1666static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1667 u8 intlv_en, int num_dcts_intlv,
1668 u32 dct_sel)
1669{
1670 u8 channel = 0;
1671 u8 select;
1672
1673 if (!(intlv_en))
1674 return (u8)(dct_sel);
1675
1676 if (num_dcts_intlv == 2) {
1677 select = (sys_addr >> 8) & 0x3;
1678 channel = select ? 0x3 : 0;
Aravind Gopalakrishnan9d0e8d82014-01-21 15:03:36 -06001679 } else if (num_dcts_intlv == 4) {
1680 u8 intlv_addr = dct_sel_interleave_addr(pvt);
1681 switch (intlv_addr) {
1682 case 0x4:
1683 channel = (sys_addr >> 8) & 0x3;
1684 break;
1685 case 0x5:
1686 channel = (sys_addr >> 9) & 0x3;
1687 break;
1688 }
1689 }
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001690 return channel;
1691}
1692
1693/*
Borislav Petkov229a7a12010-12-09 18:57:54 +01001694 * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001695 * Interleaving Modes.
1696 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001697static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
Borislav Petkov229a7a12010-12-09 18:57:54 +01001698 bool hi_range_sel, u8 intlv_en)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001699{
Borislav Petkov151fa712011-02-21 19:33:10 +01001700 u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001701
1702 if (dct_ganging_enabled(pvt))
Borislav Petkov229a7a12010-12-09 18:57:54 +01001703 return 0;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001704
Borislav Petkov229a7a12010-12-09 18:57:54 +01001705 if (hi_range_sel)
1706 return dct_sel_high;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001707
Borislav Petkov229a7a12010-12-09 18:57:54 +01001708 /*
1709 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1710 */
1711 if (dct_interleave_enabled(pvt)) {
1712 u8 intlv_addr = dct_sel_interleave_addr(pvt);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001713
Borislav Petkov229a7a12010-12-09 18:57:54 +01001714 /* return DCT select function: 0=DCT0, 1=DCT1 */
1715 if (!intlv_addr)
1716 return sys_addr >> 6 & 1;
1717
1718 if (intlv_addr & 0x2) {
1719 u8 shift = intlv_addr & 0x1 ? 9 : 6;
Yazen Ghannamdc0a50a82016-08-03 10:59:15 -04001720 u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1;
Borislav Petkov229a7a12010-12-09 18:57:54 +01001721
1722 return ((sys_addr >> shift) & 1) ^ temp;
1723 }
1724
Yazen Ghannamdc0a50a82016-08-03 10:59:15 -04001725 if (intlv_addr & 0x4) {
1726 u8 shift = intlv_addr & 0x1 ? 9 : 8;
1727
1728 return (sys_addr >> shift) & 1;
1729 }
1730
Borislav Petkov229a7a12010-12-09 18:57:54 +01001731 return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1732 }
1733
1734 if (dct_high_range_enabled(pvt))
1735 return ~dct_sel_high & 1;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001736
1737 return 0;
1738}
1739
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001740/* Convert the sys_addr to the normalized DCT address */
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08001741static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001742 u64 sys_addr, bool hi_rng,
1743 u32 dct_sel_base_addr)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001744{
1745 u64 chan_off;
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001746 u64 dram_base = get_dram_base(pvt, range);
1747 u64 hole_off = f10_dhar_offset(pvt);
Dan Carpenter6f3508f2016-01-20 12:54:51 +03001748 u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001749
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001750 if (hi_rng) {
1751 /*
1752 * if
1753 * base address of high range is below 4Gb
1754 * (bits [47:27] at [31:11])
1755 * DRAM address space on this DCT is hoisted above 4Gb &&
1756 * sys_addr > 4Gb
1757 *
1758 * remove hole offset from sys_addr
1759 * else
1760 * remove high range offset from sys_addr
1761 */
1762 if ((!(dct_sel_base_addr >> 16) ||
1763 dct_sel_base_addr < dhar_base(pvt)) &&
Borislav Petkov972ea172011-02-21 19:43:02 +01001764 dhar_valid(pvt) &&
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001765 (sys_addr >= BIT_64(32)))
Borislav Petkovbc21fa52010-11-11 17:29:13 +01001766 chan_off = hole_off;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001767 else
1768 chan_off = dct_sel_base_off;
1769 } else {
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001770 /*
1771 * if
1772 * we have a valid hole &&
1773 * sys_addr > 4Gb
1774 *
1775 * remove hole
1776 * else
1777 * remove dram base to normalize to DCT address
1778 */
Borislav Petkov972ea172011-02-21 19:43:02 +01001779 if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
Borislav Petkovbc21fa52010-11-11 17:29:13 +01001780 chan_off = hole_off;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001781 else
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001782 chan_off = dram_base;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001783 }
1784
Chen, Gong10ef6b02013-10-18 14:29:07 -07001785 return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001786}
1787
Doug Thompson6163b5d2009-04-27 16:20:17 +02001788/*
1789 * checks if the csrow passed in is marked as SPARED, if so returns the new
1790 * spare row
1791 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001792static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001793{
Borislav Petkov614ec9d2011-01-13 18:02:22 +01001794 int tmp_cs;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001795
Borislav Petkov614ec9d2011-01-13 18:02:22 +01001796 if (online_spare_swap_done(pvt, dct) &&
1797 csrow == online_spare_bad_dramcs(pvt, dct)) {
1798
1799 for_each_chip_select(tmp_cs, dct, pvt) {
1800 if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1801 csrow = tmp_cs;
1802 break;
1803 }
1804 }
Doug Thompson6163b5d2009-04-27 16:20:17 +02001805 }
1806 return csrow;
1807}
1808
1809/*
1810 * Iterate over the DRAM DCT "base" and "mask" registers looking for a
1811 * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
1812 *
1813 * Return:
1814 * -EINVAL: NOT FOUND
1815 * 0..csrow = Chip-Select Row
1816 */
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08001817static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001818{
1819 struct mem_ctl_info *mci;
1820 struct amd64_pvt *pvt;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001821 u64 cs_base, cs_mask;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001822 int cs_found = -EINVAL;
1823 int csrow;
1824
Borislav Petkov2ec591a2015-02-17 10:58:34 +01001825 mci = edac_mc_find(nid);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001826 if (!mci)
1827 return cs_found;
1828
1829 pvt = mci->pvt_info;
1830
Joe Perches956b9ba12012-04-29 17:08:39 -03001831 edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001832
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001833 for_each_chip_select(csrow, dct, pvt) {
1834 if (!csrow_enabled(csrow, dct, pvt))
Doug Thompson6163b5d2009-04-27 16:20:17 +02001835 continue;
1836
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001837 get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001838
Joe Perches956b9ba12012-04-29 17:08:39 -03001839 edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
1840 csrow, cs_base, cs_mask);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001841
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001842 cs_mask = ~cs_mask;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001843
Joe Perches956b9ba12012-04-29 17:08:39 -03001844 edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
1845 (in_addr & cs_mask), (cs_base & cs_mask));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001846
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001847 if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001848 if (pvt->fam == 0x15 && pvt->model >= 0x30) {
1849 cs_found = csrow;
1850 break;
1851 }
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001852 cs_found = f10_process_possible_spare(pvt, dct, csrow);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001853
Joe Perches956b9ba12012-04-29 17:08:39 -03001854 edac_dbg(1, " MATCH csrow=%d\n", cs_found);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001855 break;
1856 }
1857 }
1858 return cs_found;
1859}
1860
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001861/*
1862 * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
1863 * swapped with a region located at the bottom of memory so that the GPU can use
1864 * the interleaved region and thus two channels.
1865 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001866static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001867{
1868 u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
1869
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001870 if (pvt->fam == 0x10) {
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001871 /* only revC3 and revE have that feature */
Borislav Petkova4b4bed2013-08-10 13:54:48 +02001872 if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001873 return sys_addr;
1874 }
1875
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05001876 amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg);
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001877
1878 if (!(swap_reg & 0x1))
1879 return sys_addr;
1880
1881 swap_base = (swap_reg >> 3) & 0x7f;
1882 swap_limit = (swap_reg >> 11) & 0x7f;
1883 rgn_size = (swap_reg >> 20) & 0x7f;
1884 tmp_addr = sys_addr >> 27;
1885
1886 if (!(sys_addr >> 34) &&
1887 (((tmp_addr >= swap_base) &&
1888 (tmp_addr <= swap_limit)) ||
1889 (tmp_addr < rgn_size)))
1890 return sys_addr ^ (u64)swap_base << 27;
1891
1892 return sys_addr;
1893}
1894
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001895/* For a given @dram_range, check if @sys_addr falls within it. */
Borislav Petkove761359a2011-02-21 19:49:01 +01001896static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
Borislav Petkov33ca0642012-08-30 18:01:36 +02001897 u64 sys_addr, int *chan_sel)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001898{
Borislav Petkov229a7a12010-12-09 18:57:54 +01001899 int cs_found = -EINVAL;
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001900 u64 chan_addr;
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001901 u32 dct_sel_base;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001902 u8 channel;
Borislav Petkov229a7a12010-12-09 18:57:54 +01001903 bool high_range = false;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001904
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001905 u8 node_id = dram_dst_node(pvt, range);
Borislav Petkov229a7a12010-12-09 18:57:54 +01001906 u8 intlv_en = dram_intlv_en(pvt, range);
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001907 u32 intlv_sel = dram_intlv_sel(pvt, range);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001908
Joe Perches956b9ba12012-04-29 17:08:39 -03001909 edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1910 range, sys_addr, get_dram_limit(pvt, range));
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001911
Borislav Petkov355fba62011-01-17 13:03:26 +01001912 if (dhar_valid(pvt) &&
1913 dhar_base(pvt) <= sys_addr &&
1914 sys_addr < BIT_64(32)) {
1915 amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1916 sys_addr);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001917 return -EINVAL;
Borislav Petkov355fba62011-01-17 13:03:26 +01001918 }
1919
Borislav Petkovf030ddf2011-04-08 15:05:21 +02001920 if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
Borislav Petkov355fba62011-01-17 13:03:26 +01001921 return -EINVAL;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001922
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001923 sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001924
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001925 dct_sel_base = dct_sel_baseaddr(pvt);
1926
1927 /*
1928 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1929 * select between DCT0 and DCT1.
1930 */
1931 if (dct_high_range_enabled(pvt) &&
1932 !dct_ganging_enabled(pvt) &&
1933 ((sys_addr >> 27) >= (dct_sel_base >> 11)))
Borislav Petkov229a7a12010-12-09 18:57:54 +01001934 high_range = true;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001935
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001936 channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001937
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001938 chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001939 high_range, dct_sel_base);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001940
Borislav Petkove2f79db2011-01-13 14:57:34 +01001941 /* Remove node interleaving, see F1x120 */
1942 if (intlv_en)
1943 chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1944 (chan_addr & 0xfff);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001945
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001946 /* remove channel interleave */
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001947 if (dct_interleave_enabled(pvt) &&
1948 !dct_high_range_enabled(pvt) &&
1949 !dct_ganging_enabled(pvt)) {
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001950
1951 if (dct_sel_interleave_addr(pvt) != 1) {
1952 if (dct_sel_interleave_addr(pvt) == 0x3)
1953 /* hash 9 */
1954 chan_addr = ((chan_addr >> 10) << 9) |
1955 (chan_addr & 0x1ff);
1956 else
1957 /* A[6] or hash 6 */
1958 chan_addr = ((chan_addr >> 7) << 6) |
1959 (chan_addr & 0x3f);
1960 } else
1961 /* A[12] */
1962 chan_addr = ((chan_addr >> 13) << 12) |
1963 (chan_addr & 0xfff);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001964 }
1965
Joe Perches956b9ba12012-04-29 17:08:39 -03001966 edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001967
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001968 cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001969
Borislav Petkov33ca0642012-08-30 18:01:36 +02001970 if (cs_found >= 0)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001971 *chan_sel = channel;
Borislav Petkov33ca0642012-08-30 18:01:36 +02001972
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001973 return cs_found;
1974}
1975
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05001976static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
1977 u64 sys_addr, int *chan_sel)
1978{
1979 int cs_found = -EINVAL;
1980 int num_dcts_intlv = 0;
1981 u64 chan_addr, chan_offset;
1982 u64 dct_base, dct_limit;
1983 u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
1984 u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
1985
1986 u64 dhar_offset = f10_dhar_offset(pvt);
1987 u8 intlv_addr = dct_sel_interleave_addr(pvt);
1988 u8 node_id = dram_dst_node(pvt, range);
1989 u8 intlv_en = dram_intlv_en(pvt, range);
1990
1991 amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
1992 amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
1993
1994 dct_offset_en = (u8) ((dct_cont_base_reg >> 3) & BIT(0));
1995 dct_sel = (u8) ((dct_cont_base_reg >> 4) & 0x7);
1996
1997 edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1998 range, sys_addr, get_dram_limit(pvt, range));
1999
2000 if (!(get_dram_base(pvt, range) <= sys_addr) &&
2001 !(get_dram_limit(pvt, range) >= sys_addr))
2002 return -EINVAL;
2003
2004 if (dhar_valid(pvt) &&
2005 dhar_base(pvt) <= sys_addr &&
2006 sys_addr < BIT_64(32)) {
2007 amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
2008 sys_addr);
2009 return -EINVAL;
2010 }
2011
2012 /* Verify sys_addr is within DCT Range. */
Aravind Gopalakrishnan4fc06b32013-08-24 10:47:48 -05002013 dct_base = (u64) dct_sel_baseaddr(pvt);
2014 dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002015
2016 if (!(dct_cont_base_reg & BIT(0)) &&
Aravind Gopalakrishnan4fc06b32013-08-24 10:47:48 -05002017 !(dct_base <= (sys_addr >> 27) &&
2018 dct_limit >= (sys_addr >> 27)))
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002019 return -EINVAL;
2020
2021 /* Verify number of dct's that participate in channel interleaving. */
2022 num_dcts_intlv = (int) hweight8(intlv_en);
2023
2024 if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
2025 return -EINVAL;
2026
Yazen Ghannamdc0a50a82016-08-03 10:59:15 -04002027 if (pvt->model >= 0x60)
2028 channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en);
2029 else
2030 channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
2031 num_dcts_intlv, dct_sel);
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002032
2033 /* Verify we stay within the MAX number of channels allowed */
Aravind Gopalakrishnan7f3f5242013-12-04 11:40:11 -06002034 if (channel > 3)
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002035 return -EINVAL;
2036
2037 leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
2038
2039 /* Get normalized DCT addr */
2040 if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
2041 chan_offset = dhar_offset;
2042 else
Aravind Gopalakrishnan4fc06b32013-08-24 10:47:48 -05002043 chan_offset = dct_base << 27;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002044
2045 chan_addr = sys_addr - chan_offset;
2046
2047 /* remove channel interleave */
2048 if (num_dcts_intlv == 2) {
2049 if (intlv_addr == 0x4)
2050 chan_addr = ((chan_addr >> 9) << 8) |
2051 (chan_addr & 0xff);
2052 else if (intlv_addr == 0x5)
2053 chan_addr = ((chan_addr >> 10) << 9) |
2054 (chan_addr & 0x1ff);
2055 else
2056 return -EINVAL;
2057
2058 } else if (num_dcts_intlv == 4) {
2059 if (intlv_addr == 0x4)
2060 chan_addr = ((chan_addr >> 10) << 8) |
2061 (chan_addr & 0xff);
2062 else if (intlv_addr == 0x5)
2063 chan_addr = ((chan_addr >> 11) << 9) |
2064 (chan_addr & 0x1ff);
2065 else
2066 return -EINVAL;
2067 }
2068
2069 if (dct_offset_en) {
2070 amd64_read_pci_cfg(pvt->F1,
2071 DRAM_CONT_HIGH_OFF + (int) channel * 4,
2072 &tmp);
Aravind Gopalakrishnan4fc06b32013-08-24 10:47:48 -05002073 chan_addr += (u64) ((tmp >> 11) & 0xfff) << 27;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002074 }
2075
2076 f15h_select_dct(pvt, channel);
2077
2078 edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr);
2079
2080 /*
2081 * Find Chip select:
2082 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
2083 * there is support for 4 DCT's, but only 2 are currently functional.
2084 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
2085 * pvt->csels[1]. So we need to use '1' here to get correct info.
2086 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
2087 */
2088 alias_channel = (channel == 3) ? 1 : channel;
2089
2090 cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
2091
2092 if (cs_found >= 0)
2093 *chan_sel = alias_channel;
2094
2095 return cs_found;
2096}
2097
2098static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
2099 u64 sys_addr,
2100 int *chan_sel)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002101{
Borislav Petkove761359a2011-02-21 19:49:01 +01002102 int cs_found = -EINVAL;
2103 unsigned range;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002104
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002105 for (range = 0; range < DRAM_RANGES; range++) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002106 if (!dram_rw(pvt, range))
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002107 continue;
2108
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002109 if (pvt->fam == 0x15 && pvt->model >= 0x30)
2110 cs_found = f15_m30h_match_to_this_node(pvt, range,
2111 sys_addr,
2112 chan_sel);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002113
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002114 else if ((get_dram_base(pvt, range) <= sys_addr) &&
2115 (get_dram_limit(pvt, range) >= sys_addr)) {
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01002116 cs_found = f1x_match_to_this_node(pvt, range,
Borislav Petkov33ca0642012-08-30 18:01:36 +02002117 sys_addr, chan_sel);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002118 if (cs_found >= 0)
2119 break;
2120 }
2121 }
2122 return cs_found;
2123}
2124
2125/*
Borislav Petkovbdc30a02009-11-13 15:10:43 +01002126 * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
2127 * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002128 *
Borislav Petkovbdc30a02009-11-13 15:10:43 +01002129 * The @sys_addr is usually an error address received from the hardware
2130 * (MCX_ADDR).
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002131 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01002132static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
Borislav Petkov33ca0642012-08-30 18:01:36 +02002133 struct err_info *err)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002134{
2135 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002136
Borislav Petkov33ca0642012-08-30 18:01:36 +02002137 error_address_to_page_and_offset(sys_addr, err);
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03002138
Borislav Petkov33ca0642012-08-30 18:01:36 +02002139 err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
2140 if (err->csrow < 0) {
2141 err->err_code = ERR_CSROW;
Borislav Petkovbdc30a02009-11-13 15:10:43 +01002142 return;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002143 }
Borislav Petkovbdc30a02009-11-13 15:10:43 +01002144
Borislav Petkovbdc30a02009-11-13 15:10:43 +01002145 /*
2146 * We need the syndromes for channel detection only when we're
2147 * ganged. Otherwise @chan should already contain the channel at
2148 * this point.
2149 */
Borislav Petkova97fa682010-12-23 14:07:18 +01002150 if (dct_ganging_enabled(pvt))
Borislav Petkov33ca0642012-08-30 18:01:36 +02002151 err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002152}
2153
2154/*
Borislav Petkov8566c4d2009-10-16 13:48:28 +02002155 * debug routine to display the memory sizes of all logical DIMMs and its
Borislav Petkovcb328502010-12-22 14:28:24 +01002156 * CSROWs
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002157 */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002158static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002159{
Borislav Petkovbb89f5a2012-09-12 18:06:00 +02002160 int dimm, size0, size1;
Borislav Petkov525a1b22010-12-21 15:53:27 +01002161 u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
2162 u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002163
Borislav Petkova4b4bed2013-08-10 13:54:48 +02002164 if (pvt->fam == 0xf) {
Borislav Petkov8566c4d2009-10-16 13:48:28 +02002165 /* K8 families < revF not supported yet */
Borislav Petkov1433eb92009-10-21 13:44:36 +02002166 if (pvt->ext_model < K8_REV_F)
Borislav Petkov8566c4d2009-10-16 13:48:28 +02002167 return;
2168 else
2169 WARN_ON(ctrl != 0);
2170 }
2171
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05002172 if (pvt->fam == 0x10) {
2173 dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1
2174 : pvt->dbam0;
2175 dcsb = (ctrl && !dct_ganging_enabled(pvt)) ?
2176 pvt->csels[1].csbases :
2177 pvt->csels[0].csbases;
2178 } else if (ctrl) {
2179 dbam = pvt->dbam0;
2180 dcsb = pvt->csels[1].csbases;
2181 }
Joe Perches956b9ba12012-04-29 17:08:39 -03002182 edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
2183 ctrl, dbam);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002184
Borislav Petkov8566c4d2009-10-16 13:48:28 +02002185 edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
2186
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002187 /* Dump memory sizes for DIMM and its CSROWs */
2188 for (dimm = 0; dimm < 4; dimm++) {
2189
2190 size0 = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002191 if (dcsb[dimm*2] & DCSB_CS_ENABLE)
Yazen Ghannam07ed82e2016-11-28 08:50:21 -06002192 /*
2193 * For F15m60h, we need multiplier for LRDIMM cs_size
2194 * calculation. We pass dimm value to the dbam_to_cs
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002195 * mapper so we can find the multiplier from the
2196 * corresponding DCSM.
2197 */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01002198 size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002199 DBAM_DIMM(dimm, dbam),
2200 dimm);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002201
2202 size1 = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002203 if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01002204 size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002205 DBAM_DIMM(dimm, dbam),
2206 dimm);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002207
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002208 amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
Borislav Petkovbb89f5a2012-09-12 18:06:00 +02002209 dimm * 2, size0,
2210 dimm * 2 + 1, size1);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02002211 }
2212}
2213
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01002214static struct amd64_family_type family_types[] = {
Doug Thompson4d376072009-04-27 16:25:05 +02002215 [K8_CPUS] = {
Borislav Petkov0092b202010-10-01 19:20:05 +02002216 .ctl_name = "K8",
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002217 .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002218 .f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002219 .max_mcs = 2,
Doug Thompson4d376072009-04-27 16:25:05 +02002220 .ops = {
Borislav Petkov1433eb92009-10-21 13:44:36 +02002221 .early_channel_count = k8_early_channel_count,
Borislav Petkov1433eb92009-10-21 13:44:36 +02002222 .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow,
2223 .dbam_to_cs = k8_dbam_to_chip_select,
Doug Thompson4d376072009-04-27 16:25:05 +02002224 }
2225 },
2226 [F10_CPUS] = {
Borislav Petkov0092b202010-10-01 19:20:05 +02002227 .ctl_name = "F10h",
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002228 .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002229 .f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002230 .max_mcs = 2,
Doug Thompson4d376072009-04-27 16:25:05 +02002231 .ops = {
Borislav Petkov7d20d142011-01-07 17:58:04 +01002232 .early_channel_count = f1x_early_channel_count,
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01002233 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
Borislav Petkov1433eb92009-10-21 13:44:36 +02002234 .dbam_to_cs = f10_dbam_to_chip_select,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002235 }
2236 },
2237 [F15_CPUS] = {
2238 .ctl_name = "F15h",
Borislav Petkovdf71a052011-01-19 18:15:10 +01002239 .f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002240 .f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002241 .max_mcs = 2,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002242 .ops = {
Borislav Petkov7d20d142011-01-07 17:58:04 +01002243 .early_channel_count = f1x_early_channel_count,
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01002244 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01002245 .dbam_to_cs = f15_dbam_to_chip_select,
Doug Thompson4d376072009-04-27 16:25:05 +02002246 }
2247 },
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002248 [F15_M30H_CPUS] = {
2249 .ctl_name = "F15h_M30h",
2250 .f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002251 .f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002252 .max_mcs = 2,
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002253 .ops = {
2254 .early_channel_count = f1x_early_channel_count,
2255 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
2256 .dbam_to_cs = f16_dbam_to_chip_select,
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05002257 }
2258 },
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002259 [F15_M60H_CPUS] = {
2260 .ctl_name = "F15h_M60h",
2261 .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002262 .f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002263 .max_mcs = 2,
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002264 .ops = {
2265 .early_channel_count = f1x_early_channel_count,
2266 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
2267 .dbam_to_cs = f15_m60h_dbam_to_chip_select,
2268 }
2269 },
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05002270 [F16_CPUS] = {
2271 .ctl_name = "F16h",
2272 .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002273 .f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002274 .max_mcs = 2,
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05002275 .ops = {
2276 .early_channel_count = f1x_early_channel_count,
2277 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
2278 .dbam_to_cs = f16_dbam_to_chip_select,
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05002279 }
2280 },
Aravind Gopalakrishnan85a88852014-02-20 10:28:46 -06002281 [F16_M30H_CPUS] = {
2282 .ctl_name = "F16h_M30h",
2283 .f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1,
Borislav Petkov3f37a362016-05-06 19:44:27 +02002284 .f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002285 .max_mcs = 2,
Aravind Gopalakrishnan85a88852014-02-20 10:28:46 -06002286 .ops = {
2287 .early_channel_count = f1x_early_channel_count,
2288 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
2289 .dbam_to_cs = f16_dbam_to_chip_select,
Aravind Gopalakrishnan85a88852014-02-20 10:28:46 -06002290 }
2291 },
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05002292 [F17_CPUS] = {
2293 .ctl_name = "F17h",
2294 .f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0,
2295 .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002296 .max_mcs = 2,
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05002297 .ops = {
2298 .early_channel_count = f17_early_channel_count,
Yazen Ghanname53a3b22019-08-21 23:59:59 +00002299 .dbam_to_cs = f17_addr_mask_to_cs_size,
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05002300 }
2301 },
Michael Jin8960de42018-08-16 15:28:40 -04002302 [F17_M10H_CPUS] = {
2303 .ctl_name = "F17h_M10h",
2304 .f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0,
2305 .f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002306 .max_mcs = 2,
Michael Jin8960de42018-08-16 15:28:40 -04002307 .ops = {
2308 .early_channel_count = f17_early_channel_count,
Yazen Ghanname53a3b22019-08-21 23:59:59 +00002309 .dbam_to_cs = f17_addr_mask_to_cs_size,
Michael Jin8960de42018-08-16 15:28:40 -04002310 }
2311 },
Yazen Ghannam6e8462392019-02-28 15:36:09 +00002312 [F17_M30H_CPUS] = {
2313 .ctl_name = "F17h_M30h",
2314 .f0_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F0,
2315 .f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002316 .max_mcs = 8,
Yazen Ghannam6e8462392019-02-28 15:36:09 +00002317 .ops = {
2318 .early_channel_count = f17_early_channel_count,
Yazen Ghanname53a3b22019-08-21 23:59:59 +00002319 .dbam_to_cs = f17_addr_mask_to_cs_size,
Yazen Ghannam6e8462392019-02-28 15:36:09 +00002320 }
2321 },
Isaac Vaughn3e443eb2019-09-06 23:21:38 +00002322 [F17_M70H_CPUS] = {
2323 .ctl_name = "F17h_M70h",
2324 .f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0,
2325 .f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6,
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00002326 .max_mcs = 2,
Isaac Vaughn3e443eb2019-09-06 23:21:38 +00002327 .ops = {
2328 .early_channel_count = f17_early_channel_count,
2329 .dbam_to_cs = f17_addr_mask_to_cs_size,
2330 }
2331 },
Yazen Ghannam2eb61c92020-01-10 01:56:50 +00002332 [F19_CPUS] = {
2333 .ctl_name = "F19h",
2334 .f0_id = PCI_DEVICE_ID_AMD_19H_DF_F0,
2335 .f6_id = PCI_DEVICE_ID_AMD_19H_DF_F6,
2336 .max_mcs = 8,
2337 .ops = {
2338 .early_channel_count = f17_early_channel_count,
2339 .dbam_to_cs = f17_addr_mask_to_cs_size,
2340 }
2341 },
Doug Thompson4d376072009-04-27 16:25:05 +02002342};
2343
Doug Thompsonb1289d62009-04-27 16:37:05 +02002344/*
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002345 * These are tables of eigenvectors (one per line) which can be used for the
2346 * construction of the syndrome tables. The modified syndrome search algorithm
2347 * uses those to find the symbol in error and thus the DIMM.
Doug Thompsonb1289d62009-04-27 16:37:05 +02002348 *
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002349 * Algorithm courtesy of Ross LaFetra from AMD.
Doug Thompsonb1289d62009-04-27 16:37:05 +02002350 */
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002351static const u16 x4_vectors[] = {
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002352 0x2f57, 0x1afe, 0x66cc, 0xdd88,
2353 0x11eb, 0x3396, 0x7f4c, 0xeac8,
2354 0x0001, 0x0002, 0x0004, 0x0008,
2355 0x1013, 0x3032, 0x4044, 0x8088,
2356 0x106b, 0x30d6, 0x70fc, 0xe0a8,
2357 0x4857, 0xc4fe, 0x13cc, 0x3288,
2358 0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
2359 0x1f39, 0x251e, 0xbd6c, 0x6bd8,
2360 0x15c1, 0x2a42, 0x89ac, 0x4758,
2361 0x2b03, 0x1602, 0x4f0c, 0xca08,
2362 0x1f07, 0x3a0e, 0x6b04, 0xbd08,
2363 0x8ba7, 0x465e, 0x244c, 0x1cc8,
2364 0x2b87, 0x164e, 0x642c, 0xdc18,
2365 0x40b9, 0x80de, 0x1094, 0x20e8,
2366 0x27db, 0x1eb6, 0x9dac, 0x7b58,
2367 0x11c1, 0x2242, 0x84ac, 0x4c58,
2368 0x1be5, 0x2d7a, 0x5e34, 0xa718,
2369 0x4b39, 0x8d1e, 0x14b4, 0x28d8,
2370 0x4c97, 0xc87e, 0x11fc, 0x33a8,
2371 0x8e97, 0x497e, 0x2ffc, 0x1aa8,
2372 0x16b3, 0x3d62, 0x4f34, 0x8518,
2373 0x1e2f, 0x391a, 0x5cac, 0xf858,
2374 0x1d9f, 0x3b7a, 0x572c, 0xfe18,
2375 0x15f5, 0x2a5a, 0x5264, 0xa3b8,
2376 0x1dbb, 0x3b66, 0x715c, 0xe3f8,
2377 0x4397, 0xc27e, 0x17fc, 0x3ea8,
2378 0x1617, 0x3d3e, 0x6464, 0xb8b8,
2379 0x23ff, 0x12aa, 0xab6c, 0x56d8,
2380 0x2dfb, 0x1ba6, 0x913c, 0x7328,
2381 0x185d, 0x2ca6, 0x7914, 0x9e28,
2382 0x171b, 0x3e36, 0x7d7c, 0xebe8,
2383 0x4199, 0x82ee, 0x19f4, 0x2e58,
2384 0x4807, 0xc40e, 0x130c, 0x3208,
2385 0x1905, 0x2e0a, 0x5804, 0xac08,
2386 0x213f, 0x132a, 0xadfc, 0x5ba8,
2387 0x19a9, 0x2efe, 0xb5cc, 0x6f88,
Doug Thompsonb1289d62009-04-27 16:37:05 +02002388};
2389
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002390static const u16 x8_vectors[] = {
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002391 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
2392 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
2393 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
2394 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
2395 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
2396 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
2397 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
2398 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
2399 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
2400 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
2401 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
2402 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
2403 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
2404 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
2405 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
2406 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
2407 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
2408 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
2409 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
2410};
2411
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08002412static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
Borislav Petkovd34a6ec2011-02-23 17:41:50 +01002413 unsigned v_dim)
Doug Thompsonb1289d62009-04-27 16:37:05 +02002414{
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002415 unsigned int i, err_sym;
Doug Thompsonb1289d62009-04-27 16:37:05 +02002416
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002417 for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
2418 u16 s = syndrome;
Borislav Petkovd34a6ec2011-02-23 17:41:50 +01002419 unsigned v_idx = err_sym * v_dim;
2420 unsigned v_end = (err_sym + 1) * v_dim;
Doug Thompsonb1289d62009-04-27 16:37:05 +02002421
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002422 /* walk over all 16 bits of the syndrome */
2423 for (i = 1; i < (1U << 16); i <<= 1) {
2424
2425 /* if bit is set in that eigenvector... */
2426 if (v_idx < v_end && vectors[v_idx] & i) {
2427 u16 ev_comp = vectors[v_idx++];
2428
2429 /* ... and bit set in the modified syndrome, */
2430 if (s & i) {
2431 /* remove it. */
2432 s ^= ev_comp;
2433
2434 if (!s)
2435 return err_sym;
2436 }
2437
2438 } else if (s & i)
2439 /* can't get to zero, move to next symbol */
2440 break;
2441 }
Doug Thompsonb1289d62009-04-27 16:37:05 +02002442 }
2443
Joe Perches956b9ba12012-04-29 17:08:39 -03002444 edac_dbg(0, "syndrome(%x) not found\n", syndrome);
Doug Thompsonb1289d62009-04-27 16:37:05 +02002445 return -1;
2446}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002447
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002448static int map_err_sym_to_channel(int err_sym, int sym_size)
2449{
2450 if (sym_size == 4)
2451 switch (err_sym) {
2452 case 0x20:
2453 case 0x21:
2454 return 0;
2455 break;
2456 case 0x22:
2457 case 0x23:
2458 return 1;
2459 break;
2460 default:
2461 return err_sym >> 4;
2462 break;
2463 }
2464 /* x8 symbols */
2465 else
2466 switch (err_sym) {
2467 /* imaginary bits not in a DIMM */
2468 case 0x10:
2469 WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
2470 err_sym);
2471 return -1;
2472 break;
2473
2474 case 0x11:
2475 return 0;
2476 break;
2477 case 0x12:
2478 return 1;
2479 break;
2480 default:
2481 return err_sym >> 3;
2482 break;
2483 }
2484 return -1;
2485}
2486
2487static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
2488{
2489 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002490 int err_sym = -1;
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002491
Borislav Petkova3b7db02011-01-19 20:35:12 +01002492 if (pvt->ecc_sym_sz == 8)
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002493 err_sym = decode_syndrome(syndrome, x8_vectors,
2494 ARRAY_SIZE(x8_vectors),
Borislav Petkova3b7db02011-01-19 20:35:12 +01002495 pvt->ecc_sym_sz);
2496 else if (pvt->ecc_sym_sz == 4)
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002497 err_sym = decode_syndrome(syndrome, x4_vectors,
2498 ARRAY_SIZE(x4_vectors),
Borislav Petkova3b7db02011-01-19 20:35:12 +01002499 pvt->ecc_sym_sz);
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002500 else {
Borislav Petkova3b7db02011-01-19 20:35:12 +01002501 amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002502 return err_sym;
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002503 }
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002504
Borislav Petkova3b7db02011-01-19 20:35:12 +01002505 return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01002506}
2507
Yazen Ghanname70984d2016-11-17 17:57:31 -05002508static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err,
Borislav Petkov33ca0642012-08-30 18:01:36 +02002509 u8 ecc_type)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002510{
Borislav Petkov33ca0642012-08-30 18:01:36 +02002511 enum hw_event_mc_err_type err_type;
2512 const char *string;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002513
Borislav Petkov33ca0642012-08-30 18:01:36 +02002514 if (ecc_type == 2)
2515 err_type = HW_EVENT_ERR_CORRECTED;
2516 else if (ecc_type == 1)
2517 err_type = HW_EVENT_ERR_UNCORRECTED;
Yazen Ghannamd12a9692016-11-17 17:57:32 -05002518 else if (ecc_type == 3)
2519 err_type = HW_EVENT_ERR_DEFERRED;
Borislav Petkov33ca0642012-08-30 18:01:36 +02002520 else {
2521 WARN(1, "Something is rotten in the state of Denmark.\n");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002522 return;
2523 }
2524
Borislav Petkov33ca0642012-08-30 18:01:36 +02002525 switch (err->err_code) {
2526 case DECODE_OK:
2527 string = "";
2528 break;
2529 case ERR_NODE:
2530 string = "Failed to map error addr to a node";
2531 break;
2532 case ERR_CSROW:
2533 string = "Failed to map error addr to a csrow";
2534 break;
2535 case ERR_CHANNEL:
Yazen Ghannam713ad542016-11-28 12:59:53 -06002536 string = "Unknown syndrome - possible error reporting race";
2537 break;
2538 case ERR_SYND:
2539 string = "MCA_SYND not valid - unknown syndrome and csrow";
2540 break;
2541 case ERR_NORM_ADDR:
2542 string = "Cannot decode normalized address";
Borislav Petkov33ca0642012-08-30 18:01:36 +02002543 break;
2544 default:
2545 string = "WTF error";
2546 break;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002547 }
Borislav Petkov33ca0642012-08-30 18:01:36 +02002548
2549 edac_mc_handle_error(err_type, mci, 1,
2550 err->page, err->offset, err->syndrome,
2551 err->csrow, err->channel, -1,
2552 string, "");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002553}
2554
Borislav Petkovdf781d02013-12-15 17:29:44 +01002555static inline void decode_bus_error(int node_id, struct mce *m)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002556{
Daniel J Blueman0c510cc2015-02-17 11:34:38 +08002557 struct mem_ctl_info *mci;
2558 struct amd64_pvt *pvt;
Borislav Petkovf192c7b2011-01-10 14:24:32 +01002559 u8 ecc_type = (m->status >> 45) & 0x3;
Borislav Petkov66fed2d2012-08-09 18:41:07 +02002560 u8 xec = XEC(m->status, 0x1f);
2561 u16 ec = EC(m->status);
Borislav Petkov33ca0642012-08-30 18:01:36 +02002562 u64 sys_addr;
2563 struct err_info err;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002564
Daniel J Blueman0c510cc2015-02-17 11:34:38 +08002565 mci = edac_mc_find(node_id);
2566 if (!mci)
2567 return;
2568
2569 pvt = mci->pvt_info;
2570
Borislav Petkov66fed2d2012-08-09 18:41:07 +02002571 /* Bail out early if this was an 'observed' error */
Borislav Petkov5980bb92011-01-07 16:26:49 +01002572 if (PP(ec) == NBSL_PP_OBS)
Borislav Petkovb70ef012009-06-25 19:32:38 +02002573 return;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002574
Borislav Petkovecaf5602009-07-23 16:32:01 +02002575 /* Do only ECC errors */
2576 if (xec && xec != F10_NBSL_EXT_ERR_ECC)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002577 return;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002578
Borislav Petkov33ca0642012-08-30 18:01:36 +02002579 memset(&err, 0, sizeof(err));
2580
Borislav Petkova4b4bed2013-08-10 13:54:48 +02002581 sys_addr = get_error_address(pvt, m);
Borislav Petkov33ca0642012-08-30 18:01:36 +02002582
Borislav Petkovecaf5602009-07-23 16:32:01 +02002583 if (ecc_type == 2)
Borislav Petkov33ca0642012-08-30 18:01:36 +02002584 err.syndrome = extract_syndrome(m->status);
2585
2586 pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
2587
Yazen Ghanname70984d2016-11-17 17:57:31 -05002588 __log_ecc_error(mci, &err, ecc_type);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002589}
2590
Doug Thompson0ec449e2009-04-27 19:41:25 +02002591/*
Yazen Ghannam713ad542016-11-28 12:59:53 -06002592 * To find the UMC channel represented by this bank we need to match on its
2593 * instance_id. The instance_id of a bank is held in the lower 32 bits of its
2594 * IPID.
Yazen Ghannambdcee772019-02-28 15:36:10 +00002595 *
2596 * Currently, we can derive the channel number by looking at the 6th nibble in
2597 * the instance_id. For example, instance_id=0xYXXXXX where Y is the channel
2598 * number.
Yazen Ghannam713ad542016-11-28 12:59:53 -06002599 */
Yazen Ghannambdcee772019-02-28 15:36:10 +00002600static int find_umc_channel(struct mce *m)
Yazen Ghannam713ad542016-11-28 12:59:53 -06002601{
Yazen Ghannambdcee772019-02-28 15:36:10 +00002602 return (m->ipid & GENMASK(31, 0)) >> 20;
Yazen Ghannam713ad542016-11-28 12:59:53 -06002603}
2604
2605static void decode_umc_error(int node_id, struct mce *m)
2606{
2607 u8 ecc_type = (m->status >> 45) & 0x3;
2608 struct mem_ctl_info *mci;
2609 struct amd64_pvt *pvt;
2610 struct err_info err;
2611 u64 sys_addr;
2612
2613 mci = edac_mc_find(node_id);
2614 if (!mci)
2615 return;
2616
2617 pvt = mci->pvt_info;
2618
2619 memset(&err, 0, sizeof(err));
2620
2621 if (m->status & MCI_STATUS_DEFERRED)
2622 ecc_type = 3;
2623
Yazen Ghannambdcee772019-02-28 15:36:10 +00002624 err.channel = find_umc_channel(m);
Yazen Ghannam713ad542016-11-28 12:59:53 -06002625
Yazen Ghannam713ad542016-11-28 12:59:53 -06002626 if (!(m->status & MCI_STATUS_SYNDV)) {
2627 err.err_code = ERR_SYND;
2628 goto log_error;
2629 }
2630
2631 if (ecc_type == 2) {
2632 u8 length = (m->synd >> 18) & 0x3f;
2633
2634 if (length)
2635 err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0);
2636 else
2637 err.err_code = ERR_CHANNEL;
2638 }
2639
2640 err.csrow = m->synd & 0x7;
2641
Yazen Ghannam8a2eaab2019-08-22 00:00:00 +00002642 if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
2643 err.err_code = ERR_NORM_ADDR;
2644 goto log_error;
2645 }
2646
2647 error_address_to_page_and_offset(sys_addr, &err);
2648
Yazen Ghannam713ad542016-11-28 12:59:53 -06002649log_error:
2650 __log_ecc_error(mci, &err, ecc_type);
2651}
2652
2653/*
Borislav Petkov3f37a362016-05-06 19:44:27 +02002654 * Use pvt->F3 which contains the F3 CPU PCI device to get the related
2655 * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002656 * Reserve F0 and F6 on systems with a UMC.
Doug Thompson0ec449e2009-04-27 19:41:25 +02002657 */
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002658static int
2659reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002660{
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002661 if (pvt->umc) {
2662 pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
2663 if (!pvt->F0) {
Borislav Petkov5246c542016-12-01 11:35:07 +01002664 amd64_err("F0 not found, device 0x%x (broken BIOS?)\n", pci_id1);
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002665 return -ENODEV;
2666 }
2667
2668 pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
2669 if (!pvt->F6) {
2670 pci_dev_put(pvt->F0);
2671 pvt->F0 = NULL;
2672
Borislav Petkov5246c542016-12-01 11:35:07 +01002673 amd64_err("F6 not found: device 0x%x (broken BIOS?)\n", pci_id2);
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002674 return -ENODEV;
2675 }
Borislav Petkov5246c542016-12-01 11:35:07 +01002676
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002677 edac_dbg(1, "F0: %s\n", pci_name(pvt->F0));
2678 edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
2679 edac_dbg(1, "F6: %s\n", pci_name(pvt->F6));
2680
2681 return 0;
2682 }
2683
Doug Thompson0ec449e2009-04-27 19:41:25 +02002684 /* Reserve the ADDRESS MAP Device */
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002685 pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002686 if (!pvt->F1) {
Borislav Petkov5246c542016-12-01 11:35:07 +01002687 amd64_err("F1 not found: device 0x%x (broken BIOS?)\n", pci_id1);
Borislav Petkovbbd0c1f62010-10-01 19:27:58 +02002688 return -ENODEV;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002689 }
2690
Borislav Petkov3f37a362016-05-06 19:44:27 +02002691 /* Reserve the DCT Device */
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002692 pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
Borislav Petkov3f37a362016-05-06 19:44:27 +02002693 if (!pvt->F2) {
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002694 pci_dev_put(pvt->F1);
2695 pvt->F1 = NULL;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002696
Borislav Petkov5246c542016-12-01 11:35:07 +01002697 amd64_err("F2 not found: device 0x%x (broken BIOS?)\n", pci_id2);
2698 return -ENODEV;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002699 }
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002700
Joe Perches956b9ba12012-04-29 17:08:39 -03002701 edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2702 edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2703 edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002704
2705 return 0;
2706}
2707
Borislav Petkov360b7f32010-10-15 19:25:38 +02002708static void free_mc_sibling_devs(struct amd64_pvt *pvt)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002709{
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05002710 if (pvt->umc) {
2711 pci_dev_put(pvt->F0);
2712 pci_dev_put(pvt->F6);
2713 } else {
2714 pci_dev_put(pvt->F1);
2715 pci_dev_put(pvt->F2);
2716 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02002717}
2718
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002719static void determine_ecc_sym_sz(struct amd64_pvt *pvt)
2720{
2721 pvt->ecc_sym_sz = 4;
2722
2723 if (pvt->umc) {
2724 u8 i;
2725
Yazen Ghannam4d30d2b2019-02-28 15:36:10 +00002726 for_each_umc(i) {
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002727 /* Check enabled channels only: */
Yazen Ghannam78359612019-02-28 15:36:11 +00002728 if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
2729 if (pvt->umc[i].ecc_ctrl & BIT(9)) {
2730 pvt->ecc_sym_sz = 16;
2731 return;
2732 } else if (pvt->umc[i].ecc_ctrl & BIT(7)) {
2733 pvt->ecc_sym_sz = 8;
2734 return;
2735 }
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002736 }
2737 }
Yazen Ghannam78359612019-02-28 15:36:11 +00002738 } else if (pvt->fam >= 0x10) {
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002739 u32 tmp;
2740
2741 amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
2742 /* F16h has only DCT0, so no need to read dbam1. */
2743 if (pvt->fam != 0x16)
2744 amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1);
2745
2746 /* F10h, revD and later can do x8 ECC too. */
2747 if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
2748 pvt->ecc_sym_sz = 8;
2749 }
2750}
2751
2752/*
2753 * Retrieve the hardware registers of the memory controller.
2754 */
2755static void __read_mc_regs_df(struct amd64_pvt *pvt)
2756{
2757 u8 nid = pvt->mc_node_id;
2758 struct amd64_umc *umc;
2759 u32 i, umc_base;
2760
2761 /* Read registers from each UMC */
Yazen Ghannam4d30d2b2019-02-28 15:36:10 +00002762 for_each_umc(i) {
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002763
2764 umc_base = get_umc_base(i);
2765 umc = &pvt->umc[i];
2766
Yazen Ghannam07ed82e2016-11-28 08:50:21 -06002767 amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg);
2768 amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg);
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002769 amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl);
2770 amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl);
Yazen Ghannam07ed82e2016-11-28 08:50:21 -06002771 amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi);
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002772 }
2773}
2774
Doug Thompson0ec449e2009-04-27 19:41:25 +02002775/*
2776 * Retrieve the hardware registers of the memory controller (this includes the
2777 * 'Address Map' and 'Misc' device regs)
2778 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002779static void read_mc_regs(struct amd64_pvt *pvt)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002780{
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002781 unsigned int range;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002782 u64 msr_val;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002783
2784 /*
2785 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002786 * those are Read-As-Zero.
Doug Thompson0ec449e2009-04-27 19:41:25 +02002787 */
Borislav Petkove97f8bb2009-10-12 15:27:45 +02002788 rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
Joe Perches956b9ba12012-04-29 17:08:39 -03002789 edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002790
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002791 /* Check first whether TOP_MEM2 is enabled: */
Doug Thompson0ec449e2009-04-27 19:41:25 +02002792 rdmsrl(MSR_K8_SYSCFG, msr_val);
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002793 if (msr_val & BIT(21)) {
Borislav Petkove97f8bb2009-10-12 15:27:45 +02002794 rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
Joe Perches956b9ba12012-04-29 17:08:39 -03002795 edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002796 } else {
Joe Perches956b9ba12012-04-29 17:08:39 -03002797 edac_dbg(0, " TOP_MEM2 disabled\n");
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002798 }
2799
2800 if (pvt->umc) {
2801 __read_mc_regs_df(pvt);
2802 amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar);
2803
2804 goto skip;
2805 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02002806
Borislav Petkov5980bb92011-01-07 16:26:49 +01002807 amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002808
Borislav Petkov5a5d2372011-01-17 17:52:57 +01002809 read_dram_ctl_register(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002810
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002811 for (range = 0; range < DRAM_RANGES; range++) {
2812 u8 rw;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002813
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002814 /* read settings for this DRAM range */
2815 read_dram_base_limit_regs(pvt, range);
Borislav Petkove97f8bb2009-10-12 15:27:45 +02002816
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002817 rw = dram_rw(pvt, range);
2818 if (!rw)
2819 continue;
2820
Joe Perches956b9ba12012-04-29 17:08:39 -03002821 edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
2822 range,
2823 get_dram_base(pvt, range),
2824 get_dram_limit(pvt, range));
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002825
Joe Perches956b9ba12012-04-29 17:08:39 -03002826 edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
2827 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
2828 (rw & 0x1) ? "R" : "-",
2829 (rw & 0x2) ? "W" : "-",
2830 dram_intlv_sel(pvt, range),
2831 dram_dst_node(pvt, range));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002832 }
2833
Borislav Petkovbc21fa52010-11-11 17:29:13 +01002834 amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05002835 amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002836
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002837 amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002838
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05002839 amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0);
2840 amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002841
Borislav Petkov78da1212010-12-22 19:31:45 +01002842 if (!dct_ganging_enabled(pvt)) {
Aravind Gopalakrishnan7981a282014-09-15 11:37:38 -05002843 amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1);
2844 amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002845 }
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002846
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002847skip:
2848 read_dct_base_mask(pvt);
2849
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01002850 determine_memory_type(pvt);
2851 edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
Borislav Petkova3b7db02011-01-19 20:35:12 +01002852
Yazen Ghannamb64ce7c2016-11-17 17:57:37 -05002853 determine_ecc_sym_sz(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002854}
2855
2856/*
2857 * NOTE: CPU Revision Dependent code
2858 *
2859 * Input:
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002860 * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002861 * k8 private pointer to -->
2862 * DRAM Bank Address mapping register
2863 * node_id
2864 * DCL register where dual_channel_active is
2865 *
2866 * The DBAM register consists of 4 sets of 4 bits each definitions:
2867 *
2868 * Bits: CSROWs
2869 * 0-3 CSROWs 0 and 1
2870 * 4-7 CSROWs 2 and 3
2871 * 8-11 CSROWs 4 and 5
2872 * 12-15 CSROWs 6 and 7
2873 *
2874 * Values range from: 0 to 15
2875 * The meaning of the values depends on CPU revision and dual-channel state,
2876 * see relevant BKDG more info.
2877 *
2878 * The memory controller provides for total of only 8 CSROWs in its current
2879 * architecture. Each "pair" of CSROWs normally represents just one DIMM in
2880 * single channel or two (2) DIMMs in dual channel mode.
2881 *
2882 * The following code logic collapses the various tables for CSROW based on CPU
2883 * revision.
2884 *
2885 * Returns:
2886 * The number of PAGE_SIZE pages on the specified CSROW number it
2887 * encompasses
2888 *
2889 */
Yazen Ghannameb77e6b2017-04-27 12:11:54 -05002890static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002891{
Ashish Shenoyf92cae42012-02-22 17:20:38 -08002892 u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
Yazen Ghannameb77e6b2017-04-27 12:11:54 -05002893 int csrow_nr = csrow_nr_orig;
2894 u32 cs_mode, nr_pages;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002895
Yazen Ghanname53a3b22019-08-21 23:59:59 +00002896 if (!pvt->umc) {
Yazen Ghannameb77e6b2017-04-27 12:11:54 -05002897 csrow_nr >>= 1;
Yazen Ghanname53a3b22019-08-21 23:59:59 +00002898 cs_mode = DBAM_DIMM(csrow_nr, dbam);
2899 } else {
2900 cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt);
2901 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02002902
Yazen Ghannameb77e6b2017-04-27 12:11:54 -05002903 nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr);
2904 nr_pages <<= 20 - PAGE_SHIFT;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002905
Borislav Petkov10de6492012-09-12 19:00:38 +02002906 edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
Yazen Ghannameb77e6b2017-04-27 12:11:54 -05002907 csrow_nr_orig, dct, cs_mode);
Borislav Petkov10de6492012-09-12 19:00:38 +02002908 edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002909
2910 return nr_pages;
2911}
2912
Yazen Ghannam353a1fc2019-08-21 23:59:57 +00002913static int init_csrows_df(struct mem_ctl_info *mci)
2914{
2915 struct amd64_pvt *pvt = mci->pvt_info;
2916 enum edac_type edac_mode = EDAC_NONE;
2917 enum dev_type dev_type = DEV_UNKNOWN;
2918 struct dimm_info *dimm;
2919 int empty = 1;
2920 u8 umc, cs;
2921
2922 if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) {
2923 edac_mode = EDAC_S16ECD16ED;
2924 dev_type = DEV_X16;
2925 } else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) {
2926 edac_mode = EDAC_S8ECD8ED;
2927 dev_type = DEV_X8;
2928 } else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) {
2929 edac_mode = EDAC_S4ECD4ED;
2930 dev_type = DEV_X4;
2931 } else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) {
2932 edac_mode = EDAC_SECDED;
2933 }
2934
2935 for_each_umc(umc) {
2936 for_each_chip_select(cs, umc, pvt) {
2937 if (!csrow_enabled(cs, umc, pvt))
2938 continue;
2939
2940 empty = 0;
2941 dimm = mci->csrows[cs]->channels[umc]->dimm;
2942
2943 edac_dbg(1, "MC node: %d, csrow: %d\n",
2944 pvt->mc_node_id, cs);
2945
2946 dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs);
2947 dimm->mtype = pvt->dram_type;
2948 dimm->edac_mode = edac_mode;
2949 dimm->dtype = dev_type;
Yazen Ghannam466503d2019-10-22 20:35:14 +00002950 dimm->grain = 64;
Yazen Ghannam353a1fc2019-08-21 23:59:57 +00002951 }
2952 }
2953
2954 return empty;
2955}
2956
Doug Thompson0ec449e2009-04-27 19:41:25 +02002957/*
2958 * Initialize the array of csrow attribute instances, based on the values
2959 * from pci config hardware registers.
2960 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002961static int init_csrows(struct mem_ctl_info *mci)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002962{
Borislav Petkov10de6492012-09-12 19:00:38 +02002963 struct amd64_pvt *pvt = mci->pvt_info;
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06002964 enum edac_type edac_mode = EDAC_NONE;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002965 struct csrow_info *csrow;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03002966 struct dimm_info *dimm;
Borislav Petkov10de6492012-09-12 19:00:38 +02002967 int i, j, empty = 1;
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -03002968 int nr_pages = 0;
Borislav Petkov10de6492012-09-12 19:00:38 +02002969 u32 val;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002970
Yazen Ghannam353a1fc2019-08-21 23:59:57 +00002971 if (pvt->umc)
2972 return init_csrows_df(mci);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002973
Yazen Ghannam353a1fc2019-08-21 23:59:57 +00002974 amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002975
Yazen Ghannam353a1fc2019-08-21 23:59:57 +00002976 pvt->nbcfg = val;
2977
2978 edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
2979 pvt->mc_node_id, val,
2980 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002981
Borislav Petkov10de6492012-09-12 19:00:38 +02002982 /*
2983 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
2984 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002985 for_each_chip_select(i, 0, pvt) {
Borislav Petkov10de6492012-09-12 19:00:38 +02002986 bool row_dct0 = !!csrow_enabled(i, 0, pvt);
2987 bool row_dct1 = false;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002988
Borislav Petkova4b4bed2013-08-10 13:54:48 +02002989 if (pvt->fam != 0xf)
Borislav Petkov10de6492012-09-12 19:00:38 +02002990 row_dct1 = !!csrow_enabled(i, 1, pvt);
2991
2992 if (!row_dct0 && !row_dct1)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002993 continue;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002994
Borislav Petkov10de6492012-09-12 19:00:38 +02002995 csrow = mci->csrows[i];
Doug Thompson0ec449e2009-04-27 19:41:25 +02002996 empty = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002997
Borislav Petkov10de6492012-09-12 19:00:38 +02002998 edac_dbg(1, "MC node: %d, csrow: %d\n",
2999 pvt->mc_node_id, i);
3000
Mauro Carvalho Chehab1eef1282013-03-11 09:07:46 -03003001 if (row_dct0) {
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003002 nr_pages = get_csrow_nr_pages(pvt, 0, i);
Mauro Carvalho Chehab1eef1282013-03-11 09:07:46 -03003003 csrow->channels[0]->dimm->nr_pages = nr_pages;
3004 }
Borislav Petkov10de6492012-09-12 19:00:38 +02003005
3006 /* K8 has only one DCT */
Borislav Petkova4b4bed2013-08-10 13:54:48 +02003007 if (pvt->fam != 0xf && row_dct1) {
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003008 int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i);
Mauro Carvalho Chehab1eef1282013-03-11 09:07:46 -03003009
3010 csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
3011 nr_pages += row_dct1_pages;
3012 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02003013
Borislav Petkov10de6492012-09-12 19:00:38 +02003014 edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
Doug Thompson0ec449e2009-04-27 19:41:25 +02003015
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003016 /* Determine DIMM ECC mode: */
Yazen Ghannam353a1fc2019-08-21 23:59:57 +00003017 if (pvt->nbcfg & NBCFG_ECC_ENABLE) {
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003018 edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL)
3019 ? EDAC_S4ECD4ED
3020 : EDAC_SECDED;
3021 }
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03003022
3023 for (j = 0; j < pvt->channel_count; j++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03003024 dimm = csrow->channels[j]->dimm;
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01003025 dimm->mtype = pvt->dram_type;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03003026 dimm->edac_mode = edac_mode;
Yazen Ghannam466503d2019-10-22 20:35:14 +00003027 dimm->grain = 64;
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03003028 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02003029 }
3030
3031 return empty;
3032}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02003033
Borislav Petkov06724532009-09-16 13:05:46 +02003034/* get all cores on this DCT */
Daniel J Blueman8b84c8d2012-11-27 14:32:10 +08003035static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02003036{
Borislav Petkov06724532009-09-16 13:05:46 +02003037 int cpu;
Doug Thompsonf9431992009-04-27 19:46:08 +02003038
Borislav Petkov06724532009-09-16 13:05:46 +02003039 for_each_online_cpu(cpu)
3040 if (amd_get_nb_id(cpu) == nid)
3041 cpumask_set_cpu(cpu, mask);
Doug Thompsonf9431992009-04-27 19:46:08 +02003042}
3043
3044/* check MCG_CTL on all the cpus on this node */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003045static bool nb_mce_bank_enabled_on_node(u16 nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02003046{
Rusty Russellba578cb2009-11-03 14:56:35 +10303047 cpumask_var_t mask;
Borislav Petkov50542252009-12-11 18:14:40 +01003048 int cpu, nbe;
Borislav Petkov06724532009-09-16 13:05:46 +02003049 bool ret = false;
Doug Thompsonf9431992009-04-27 19:46:08 +02003050
Rusty Russellba578cb2009-11-03 14:56:35 +10303051 if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02003052 amd64_warn("%s: Error allocating mask\n", __func__);
Rusty Russellba578cb2009-11-03 14:56:35 +10303053 return false;
3054 }
Borislav Petkov06724532009-09-16 13:05:46 +02003055
Rusty Russellba578cb2009-11-03 14:56:35 +10303056 get_cpus_on_this_dct_cpumask(mask, nid);
Borislav Petkov06724532009-09-16 13:05:46 +02003057
Rusty Russellba578cb2009-11-03 14:56:35 +10303058 rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
Borislav Petkov06724532009-09-16 13:05:46 +02003059
Rusty Russellba578cb2009-11-03 14:56:35 +10303060 for_each_cpu(cpu, mask) {
Borislav Petkov50542252009-12-11 18:14:40 +01003061 struct msr *reg = per_cpu_ptr(msrs, cpu);
Borislav Petkov5980bb92011-01-07 16:26:49 +01003062 nbe = reg->l & MSR_MCGCTL_NBE;
Borislav Petkov06724532009-09-16 13:05:46 +02003063
Joe Perches956b9ba12012-04-29 17:08:39 -03003064 edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
3065 cpu, reg->q,
3066 (nbe ? "enabled" : "disabled"));
Borislav Petkov06724532009-09-16 13:05:46 +02003067
3068 if (!nbe)
3069 goto out;
Borislav Petkov06724532009-09-16 13:05:46 +02003070 }
3071 ret = true;
3072
3073out:
Rusty Russellba578cb2009-11-03 14:56:35 +10303074 free_cpumask_var(mask);
Doug Thompsonf9431992009-04-27 19:46:08 +02003075 return ret;
3076}
3077
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08003078static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003079{
3080 cpumask_var_t cmask;
Borislav Petkov50542252009-12-11 18:14:40 +01003081 int cpu;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003082
3083 if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02003084 amd64_warn("%s: error allocating mask\n", __func__);
Pan Bian0de278842016-12-04 14:07:18 +08003085 return -ENOMEM;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003086 }
3087
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003088 get_cpus_on_this_dct_cpumask(cmask, nid);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003089
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003090 rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
3091
3092 for_each_cpu(cpu, cmask) {
3093
Borislav Petkov50542252009-12-11 18:14:40 +01003094 struct msr *reg = per_cpu_ptr(msrs, cpu);
3095
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003096 if (on) {
Borislav Petkov5980bb92011-01-07 16:26:49 +01003097 if (reg->l & MSR_MCGCTL_NBE)
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003098 s->flags.nb_mce_enable = 1;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003099
Borislav Petkov5980bb92011-01-07 16:26:49 +01003100 reg->l |= MSR_MCGCTL_NBE;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003101 } else {
3102 /*
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01003103 * Turn off NB MCE reporting only when it was off before
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003104 */
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003105 if (!s->flags.nb_mce_enable)
Borislav Petkov5980bb92011-01-07 16:26:49 +01003106 reg->l &= ~MSR_MCGCTL_NBE;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003107 }
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003108 }
3109 wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
3110
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003111 free_cpumask_var(cmask);
3112
3113 return 0;
3114}
3115
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08003116static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
Borislav Petkov2299ef72010-10-15 17:44:04 +02003117 struct pci_dev *F3)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003118{
Borislav Petkov2299ef72010-10-15 17:44:04 +02003119 bool ret = true;
Borislav Petkovc9f4f262010-12-22 19:48:20 +01003120 u32 value, mask = 0x3; /* UECC/CECC enable */
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003121
Borislav Petkov2299ef72010-10-15 17:44:04 +02003122 if (toggle_ecc_err_reporting(s, nid, ON)) {
3123 amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
3124 return false;
3125 }
3126
Borislav Petkovc9f4f262010-12-22 19:48:20 +01003127 amd64_read_pci_cfg(F3, NBCTL, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003128
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003129 s->old_nbctl = value & mask;
3130 s->nbctl_valid = true;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003131
3132 value |= mask;
Borislav Petkovc9f4f262010-12-22 19:48:20 +01003133 amd64_write_pci_cfg(F3, NBCTL, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003134
Borislav Petkova97fa682010-12-23 14:07:18 +01003135 amd64_read_pci_cfg(F3, NBCFG, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003136
Joe Perches956b9ba12012-04-29 17:08:39 -03003137 edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
3138 nid, value, !!(value & NBCFG_ECC_ENABLE));
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003139
Borislav Petkova97fa682010-12-23 14:07:18 +01003140 if (!(value & NBCFG_ECC_ENABLE)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02003141 amd64_warn("DRAM ECC disabled on this node, enabling...\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003142
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003143 s->flags.nb_ecc_prev = 0;
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01003144
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003145 /* Attempt to turn on DRAM ECC Enable */
Borislav Petkova97fa682010-12-23 14:07:18 +01003146 value |= NBCFG_ECC_ENABLE;
3147 amd64_write_pci_cfg(F3, NBCFG, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003148
Borislav Petkova97fa682010-12-23 14:07:18 +01003149 amd64_read_pci_cfg(F3, NBCFG, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003150
Borislav Petkova97fa682010-12-23 14:07:18 +01003151 if (!(value & NBCFG_ECC_ENABLE)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02003152 amd64_warn("Hardware rejected DRAM ECC enable,"
3153 "check memory DIMM configuration.\n");
Borislav Petkov2299ef72010-10-15 17:44:04 +02003154 ret = false;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003155 } else {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02003156 amd64_info("Hardware accepted DRAM ECC Enable\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003157 }
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01003158 } else {
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003159 s->flags.nb_ecc_prev = 1;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003160 }
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01003161
Joe Perches956b9ba12012-04-29 17:08:39 -03003162 edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
3163 nid, value, !!(value & NBCFG_ECC_ENABLE));
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003164
Borislav Petkov2299ef72010-10-15 17:44:04 +02003165 return ret;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003166}
3167
Daniel J Bluemanc7e53012012-11-30 16:44:20 +08003168static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
Borislav Petkov360b7f32010-10-15 19:25:38 +02003169 struct pci_dev *F3)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003170{
Borislav Petkovc9f4f262010-12-22 19:48:20 +01003171 u32 value, mask = 0x3; /* UECC/CECC enable */
3172
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003173 if (!s->nbctl_valid)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003174 return;
3175
Borislav Petkovc9f4f262010-12-22 19:48:20 +01003176 amd64_read_pci_cfg(F3, NBCTL, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003177 value &= ~mask;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003178 value |= s->old_nbctl;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003179
Borislav Petkovc9f4f262010-12-22 19:48:20 +01003180 amd64_write_pci_cfg(F3, NBCTL, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003181
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003182 /* restore previous BIOS DRAM ECC "off" setting we force-enabled */
3183 if (!s->flags.nb_ecc_prev) {
Borislav Petkova97fa682010-12-23 14:07:18 +01003184 amd64_read_pci_cfg(F3, NBCFG, &value);
3185 value &= ~NBCFG_ECC_ENABLE;
3186 amd64_write_pci_cfg(F3, NBCFG, value);
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01003187 }
3188
3189 /* restore the NB Enable MCGCTL bit */
Borislav Petkov2299ef72010-10-15 17:44:04 +02003190 if (toggle_ecc_err_reporting(s, nid, OFF))
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02003191 amd64_warn("Error restoring NB MCGCTL settings!\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01003192}
3193
Yazen Ghannam1c9b08b2019-10-22 20:35:12 +00003194static bool ecc_enabled(struct amd64_pvt *pvt)
Doug Thompsonf9431992009-04-27 19:46:08 +02003195{
Yazen Ghannam1c9b08b2019-10-22 20:35:12 +00003196 u16 nid = pvt->mc_node_id;
Borislav Petkov06724532009-09-16 13:05:46 +02003197 bool nb_mce_en = false;
Yazen Ghannam196b79f2016-11-17 17:57:34 -05003198 u8 ecc_en = 0, i;
3199 u32 value;
Doug Thompsonf9431992009-04-27 19:46:08 +02003200
Yazen Ghannam196b79f2016-11-17 17:57:34 -05003201 if (boot_cpu_data.x86 >= 0x17) {
3202 u8 umc_en_mask = 0, ecc_en_mask = 0;
Yazen Ghannam1c9b08b2019-10-22 20:35:12 +00003203 struct amd64_umc *umc;
Doug Thompsonf9431992009-04-27 19:46:08 +02003204
Yazen Ghannam4d30d2b2019-02-28 15:36:10 +00003205 for_each_umc(i) {
Yazen Ghannam1c9b08b2019-10-22 20:35:12 +00003206 umc = &pvt->umc[i];
Yazen Ghannam196b79f2016-11-17 17:57:34 -05003207
3208 /* Only check enabled UMCs. */
Yazen Ghannam1c9b08b2019-10-22 20:35:12 +00003209 if (!(umc->sdp_ctrl & UMC_SDP_INIT))
Yazen Ghannam196b79f2016-11-17 17:57:34 -05003210 continue;
3211
3212 umc_en_mask |= BIT(i);
3213
Yazen Ghannam1c9b08b2019-10-22 20:35:12 +00003214 if (umc->umc_cap_hi & UMC_ECC_ENABLED)
Yazen Ghannam196b79f2016-11-17 17:57:34 -05003215 ecc_en_mask |= BIT(i);
3216 }
3217
3218 /* Check whether at least one UMC is enabled: */
3219 if (umc_en_mask)
3220 ecc_en = umc_en_mask == ecc_en_mask;
Yazen Ghannam11ab1ca2017-01-27 11:24:19 -06003221 else
3222 edac_dbg(0, "Node %d: No enabled UMCs.\n", nid);
Yazen Ghannam196b79f2016-11-17 17:57:34 -05003223
3224 /* Assume UMC MCA banks are enabled. */
3225 nb_mce_en = true;
3226 } else {
Yazen Ghannam1c9b08b2019-10-22 20:35:12 +00003227 amd64_read_pci_cfg(pvt->F3, NBCFG, &value);
Yazen Ghannam196b79f2016-11-17 17:57:34 -05003228
3229 ecc_en = !!(value & NBCFG_ECC_ENABLE);
3230
3231 nb_mce_en = nb_mce_bank_enabled_on_node(nid);
3232 if (!nb_mce_en)
Yazen Ghannam11ab1ca2017-01-27 11:24:19 -06003233 edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
Yazen Ghannam196b79f2016-11-17 17:57:34 -05003234 MSR_IA32_MCG_CTL, nid);
3235 }
3236
Yazen Ghannam11ab1ca2017-01-27 11:24:19 -06003237 amd64_info("Node %d: DRAM ECC %s.\n",
3238 nid, (ecc_en ? "enabled" : "disabled"));
Doug Thompsonf9431992009-04-27 19:46:08 +02003239
Borislav Petkov7fdfee92019-11-09 10:00:54 +01003240 if (!ecc_en || !nb_mce_en)
Borislav Petkov2299ef72010-10-15 17:44:04 +02003241 return false;
Borislav Petkov7fdfee92019-11-09 10:00:54 +01003242 else
3243 return true;
Doug Thompsonf9431992009-04-27 19:46:08 +02003244}
3245
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003246static inline void
3247f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt)
3248{
Yazen Ghannamf8be8e52019-08-21 23:59:56 +00003249 u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1;
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003250
Yazen Ghannam4d30d2b2019-02-28 15:36:10 +00003251 for_each_umc(i) {
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003252 if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
3253 ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED);
3254 cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP);
Yazen Ghannamf8be8e52019-08-21 23:59:56 +00003255
3256 dev_x4 &= !!(pvt->umc[i].dimm_cfg & BIT(6));
3257 dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7));
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003258 }
3259 }
3260
3261 /* Set chipkill only if ECC is enabled: */
3262 if (ecc_en) {
3263 mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
3264
Yazen Ghannamf8be8e52019-08-21 23:59:56 +00003265 if (!cpk_en)
3266 return;
3267
3268 if (dev_x4)
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003269 mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
Yazen Ghannamf8be8e52019-08-21 23:59:56 +00003270 else if (dev_x16)
3271 mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED;
3272 else
3273 mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED;
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003274 }
3275}
3276
Yazen Ghannam38ddd4d2019-10-22 20:35:09 +00003277static void setup_mci_misc_attrs(struct mem_ctl_info *mci)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003278{
3279 struct amd64_pvt *pvt = mci->pvt_info;
3280
3281 mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
3282 mci->edac_ctl_cap = EDAC_FLAG_NONE;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003283
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003284 if (pvt->umc) {
3285 f17h_determine_edac_ctl_cap(mci, pvt);
3286 } else {
3287 if (pvt->nbcap & NBCAP_SECDED)
3288 mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003289
Yazen Ghannam2d09d8f2016-11-29 08:51:56 -06003290 if (pvt->nbcap & NBCAP_CHIPKILL)
3291 mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
3292 }
Doug Thompson7d6034d2009-04-27 20:01:01 +02003293
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003294 mci->edac_cap = determine_edac_cap(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003295 mci->mod_name = EDAC_MOD_STR;
Yazen Ghannam38ddd4d2019-10-22 20:35:09 +00003296 mci->ctl_name = fam_type->ctl_name;
Yazen Ghanname7934b72016-11-17 17:57:30 -05003297 mci->dev_name = pci_name(pvt->F3);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003298 mci->ctl_page_to_phys = NULL;
3299
Doug Thompson7d6034d2009-04-27 20:01:01 +02003300 /* memory scrubber interface */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003301 mci->set_sdram_scrub_rate = set_scrub_rate;
3302 mci->get_sdram_scrub_rate = get_scrub_rate;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003303}
3304
Borislav Petkov0092b202010-10-01 19:20:05 +02003305/*
3306 * returns a pointer to the family descriptor on success, NULL otherwise.
3307 */
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003308static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
Borislav Petkov395ae782010-10-01 18:38:19 +02003309{
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05003310 pvt->ext_model = boot_cpu_data.x86_model >> 4;
Jia Zhangb3991512018-01-01 09:52:10 +08003311 pvt->stepping = boot_cpu_data.x86_stepping;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05003312 pvt->model = boot_cpu_data.x86_model;
3313 pvt->fam = boot_cpu_data.x86;
3314
3315 switch (pvt->fam) {
Borislav Petkov395ae782010-10-01 18:38:19 +02003316 case 0xf:
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003317 fam_type = &family_types[K8_CPUS];
3318 pvt->ops = &family_types[K8_CPUS].ops;
Borislav Petkov395ae782010-10-01 18:38:19 +02003319 break;
Borislav Petkovdf71a052011-01-19 18:15:10 +01003320
Borislav Petkov395ae782010-10-01 18:38:19 +02003321 case 0x10:
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003322 fam_type = &family_types[F10_CPUS];
3323 pvt->ops = &family_types[F10_CPUS].ops;
Borislav Petkovdf71a052011-01-19 18:15:10 +01003324 break;
3325
3326 case 0x15:
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05003327 if (pvt->model == 0x30) {
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003328 fam_type = &family_types[F15_M30H_CPUS];
3329 pvt->ops = &family_types[F15_M30H_CPUS].ops;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05003330 break;
Aravind Gopalakrishnana597d2a2014-10-30 12:16:09 +01003331 } else if (pvt->model == 0x60) {
3332 fam_type = &family_types[F15_M60H_CPUS];
3333 pvt->ops = &family_types[F15_M60H_CPUS].ops;
3334 break;
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05003335 }
3336
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003337 fam_type = &family_types[F15_CPUS];
3338 pvt->ops = &family_types[F15_CPUS].ops;
Borislav Petkov395ae782010-10-01 18:38:19 +02003339 break;
3340
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05003341 case 0x16:
Aravind Gopalakrishnan85a88852014-02-20 10:28:46 -06003342 if (pvt->model == 0x30) {
3343 fam_type = &family_types[F16_M30H_CPUS];
3344 pvt->ops = &family_types[F16_M30H_CPUS].ops;
3345 break;
3346 }
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003347 fam_type = &family_types[F16_CPUS];
3348 pvt->ops = &family_types[F16_CPUS].ops;
Aravind Gopalakrishnan94c1acf2013-04-17 14:57:13 -05003349 break;
3350
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05003351 case 0x17:
Michael Jin8960de42018-08-16 15:28:40 -04003352 if (pvt->model >= 0x10 && pvt->model <= 0x2f) {
3353 fam_type = &family_types[F17_M10H_CPUS];
3354 pvt->ops = &family_types[F17_M10H_CPUS].ops;
3355 break;
Yazen Ghannam6e8462392019-02-28 15:36:09 +00003356 } else if (pvt->model >= 0x30 && pvt->model <= 0x3f) {
3357 fam_type = &family_types[F17_M30H_CPUS];
3358 pvt->ops = &family_types[F17_M30H_CPUS].ops;
3359 break;
Isaac Vaughn3e443eb2019-09-06 23:21:38 +00003360 } else if (pvt->model >= 0x70 && pvt->model <= 0x7f) {
3361 fam_type = &family_types[F17_M70H_CPUS];
3362 pvt->ops = &family_types[F17_M70H_CPUS].ops;
3363 break;
Michael Jin8960de42018-08-16 15:28:40 -04003364 }
Pu Wenc4a3e942018-09-27 16:31:28 +02003365 /* fall through */
3366 case 0x18:
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05003367 fam_type = &family_types[F17_CPUS];
3368 pvt->ops = &family_types[F17_CPUS].ops;
Pu Wenc4a3e942018-09-27 16:31:28 +02003369
3370 if (pvt->fam == 0x18)
3371 family_types[F17_CPUS].ctl_name = "F18h";
Yazen Ghannamf1cbbec2016-11-17 17:57:35 -05003372 break;
3373
Yazen Ghannam2eb61c92020-01-10 01:56:50 +00003374 case 0x19:
3375 fam_type = &family_types[F19_CPUS];
3376 pvt->ops = &family_types[F19_CPUS].ops;
3377 family_types[F19_CPUS].ctl_name = "F19h";
3378 break;
3379
Borislav Petkov395ae782010-10-01 18:38:19 +02003380 default:
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02003381 amd64_err("Unsupported family!\n");
Borislav Petkov0092b202010-10-01 19:20:05 +02003382 return NULL;
Borislav Petkov395ae782010-10-01 18:38:19 +02003383 }
Borislav Petkov0092b202010-10-01 19:20:05 +02003384
Borislav Petkovdf71a052011-01-19 18:15:10 +01003385 amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
Aravind Gopalakrishnan18b94f62013-08-09 11:54:49 -05003386 (pvt->fam == 0xf ?
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02003387 (pvt->ext_model >= K8_REV_F ? "revF or later "
3388 : "revE or earlier ")
3389 : ""), pvt->mc_node_id);
Borislav Petkov0092b202010-10-01 19:20:05 +02003390 return fam_type;
Borislav Petkov395ae782010-10-01 18:38:19 +02003391}
3392
Takashi Iwaie339f1e2015-02-04 11:48:53 +01003393static const struct attribute_group *amd64_edac_attr_groups[] = {
3394#ifdef CONFIG_EDAC_DEBUG
3395 &amd64_edac_dbg_group,
3396#endif
3397#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
3398 &amd64_edac_inj_group,
3399#endif
3400 NULL
3401};
3402
Yazen Ghannam80355a32019-10-22 20:35:10 +00003403static int hw_info_get(struct amd64_pvt *pvt)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003404{
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05003405 u16 pci_id1, pci_id2;
Yazen Ghannam80355a32019-10-22 20:35:10 +00003406 int ret = -EINVAL;
Borislav Petkov395ae782010-10-01 18:38:19 +02003407
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05003408 if (pvt->fam >= 0x17) {
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00003409 pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL);
Yazen Ghannam80355a32019-10-22 20:35:10 +00003410 if (!pvt->umc)
3411 return -ENOMEM;
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05003412
3413 pci_id1 = fam_type->f0_id;
3414 pci_id2 = fam_type->f6_id;
3415 } else {
3416 pci_id1 = fam_type->f1_id;
3417 pci_id2 = fam_type->f2_id;
3418 }
3419
Yazen Ghannam80355a32019-10-22 20:35:10 +00003420 ret = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2);
3421 if (ret)
3422 return ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003423
Borislav Petkov360b7f32010-10-15 19:25:38 +02003424 read_mc_regs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003425
Yazen Ghannam80355a32019-10-22 20:35:10 +00003426 return 0;
3427}
3428
3429static void hw_info_put(struct amd64_pvt *pvt)
3430{
3431 if (pvt->F0 || pvt->F1)
3432 free_mc_sibling_devs(pvt);
3433
3434 kfree(pvt->umc);
3435}
3436
3437static int init_one_instance(struct amd64_pvt *pvt)
3438{
3439 struct mem_ctl_info *mci = NULL;
3440 struct edac_mc_layer layers[2];
3441 int ret = -EINVAL;
3442
Doug Thompson7d6034d2009-04-27 20:01:01 +02003443 /*
3444 * We need to determine how many memory channels there are. Then use
3445 * that information for calculating the size of the dynamic instance
Borislav Petkov360b7f32010-10-15 19:25:38 +02003446 * tables in the 'mci' structure.
Doug Thompson7d6034d2009-04-27 20:01:01 +02003447 */
3448 pvt->channel_count = pvt->ops->early_channel_count(pvt);
3449 if (pvt->channel_count < 0)
Yazen Ghannam80355a32019-10-22 20:35:10 +00003450 return ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003451
3452 ret = -ENOMEM;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03003453 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
3454 layers[0].size = pvt->csels[0].b_cnt;
3455 layers[0].is_virt_csrow = true;
3456 layers[1].type = EDAC_MC_LAYER_CHANNEL;
Borislav Petkovf0a56c42013-07-23 20:01:23 +02003457
3458 /*
3459 * Always allocate two channels since we can have setups with DIMMs on
3460 * only one channel. Also, this simplifies handling later for the price
3461 * of a couple of KBs tops.
3462 */
Yazen Ghannam5e4c5522019-10-22 20:35:11 +00003463 layers[1].size = fam_type->max_mcs;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03003464 layers[1].is_virt_csrow = false;
Borislav Petkovf0a56c42013-07-23 20:01:23 +02003465
Yazen Ghannam80355a32019-10-22 20:35:10 +00003466 mci = edac_mc_alloc(pvt->mc_node_id, ARRAY_SIZE(layers), layers, 0);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003467 if (!mci)
Yazen Ghannam80355a32019-10-22 20:35:10 +00003468 return ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003469
3470 mci->pvt_info = pvt;
Borislav Petkov3f37a362016-05-06 19:44:27 +02003471 mci->pdev = &pvt->F3->dev;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003472
Yazen Ghannam38ddd4d2019-10-22 20:35:09 +00003473 setup_mci_misc_attrs(mci);
Borislav Petkov360b7f32010-10-15 19:25:38 +02003474
3475 if (init_csrows(mci))
Doug Thompson7d6034d2009-04-27 20:01:01 +02003476 mci->edac_cap = EDAC_FLAG_NONE;
3477
Doug Thompson7d6034d2009-04-27 20:01:01 +02003478 ret = -ENODEV;
Takashi Iwaie339f1e2015-02-04 11:48:53 +01003479 if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) {
Joe Perches956b9ba12012-04-29 17:08:39 -03003480 edac_dbg(1, "failed edac_mc_add_mc()\n");
Yazen Ghannam80355a32019-10-22 20:35:10 +00003481 edac_mc_free(mci);
3482 return ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003483 }
3484
Doug Thompson7d6034d2009-04-27 20:01:01 +02003485 return 0;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003486}
3487
Yazen Ghannam582f94b2019-11-06 01:25:01 +00003488static bool instance_has_memory(struct amd64_pvt *pvt)
3489{
3490 bool cs_enabled = false;
3491 int cs = 0, dct = 0;
3492
3493 for (dct = 0; dct < fam_type->max_mcs; dct++) {
3494 for_each_chip_select(cs, dct, pvt)
3495 cs_enabled |= csrow_enabled(cs, dct, pvt);
3496 }
3497
3498 return cs_enabled;
3499}
3500
Borislav Petkov3f37a362016-05-06 19:44:27 +02003501static int probe_one_instance(unsigned int nid)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003502{
Borislav Petkov2299ef72010-10-15 17:44:04 +02003503 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
Yazen Ghannam80355a32019-10-22 20:35:10 +00003504 struct amd64_pvt *pvt = NULL;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003505 struct ecc_settings *s;
Borislav Petkov3f37a362016-05-06 19:44:27 +02003506 int ret;
Borislav Petkovb8cfa022010-10-01 19:35:38 +02003507
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003508 ret = -ENOMEM;
3509 s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
3510 if (!s)
Borislav Petkov2299ef72010-10-15 17:44:04 +02003511 goto err_out;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003512
3513 ecc_stngs[nid] = s;
3514
Yazen Ghannam80355a32019-10-22 20:35:10 +00003515 pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
3516 if (!pvt)
3517 goto err_settings;
3518
3519 pvt->mc_node_id = nid;
3520 pvt->F3 = F3;
3521
3522 fam_type = per_family_init(pvt);
3523 if (!fam_type)
3524 goto err_enable;
3525
3526 ret = hw_info_get(pvt);
3527 if (ret < 0)
3528 goto err_enable;
3529
Yazen Ghannam582f94b2019-11-06 01:25:01 +00003530 ret = 0;
3531 if (!instance_has_memory(pvt)) {
3532 amd64_info("Node %d: No DIMMs detected.\n", nid);
3533 goto err_enable;
3534 }
3535
Yazen Ghannam1c9b08b2019-10-22 20:35:12 +00003536 if (!ecc_enabled(pvt)) {
Yazen Ghannam582f94b2019-11-06 01:25:01 +00003537 ret = -ENODEV;
Borislav Petkov2299ef72010-10-15 17:44:04 +02003538
3539 if (!ecc_enable_override)
3540 goto err_enable;
3541
Yazen Ghannam044e7a42016-11-22 15:40:16 -06003542 if (boot_cpu_data.x86 >= 0x17) {
3543 amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS.");
3544 goto err_enable;
3545 } else
3546 amd64_warn("Forcing ECC on!\n");
Borislav Petkov2299ef72010-10-15 17:44:04 +02003547
3548 if (!enable_ecc_error_reporting(s, nid, F3))
3549 goto err_enable;
3550 }
3551
Yazen Ghannam80355a32019-10-22 20:35:10 +00003552 ret = init_one_instance(pvt);
Borislav Petkov360b7f32010-10-15 19:25:38 +02003553 if (ret < 0) {
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003554 amd64_err("Error probing instance: %d\n", nid);
Yazen Ghannam044e7a42016-11-22 15:40:16 -06003555
3556 if (boot_cpu_data.x86 < 0x17)
3557 restore_ecc_error_reporting(s, nid, F3);
Yazen Ghannam2b9b2c42017-01-24 16:32:24 -06003558
3559 goto err_enable;
Borislav Petkov360b7f32010-10-15 19:25:38 +02003560 }
Doug Thompson7d6034d2009-04-27 20:01:01 +02003561
Yazen Ghannam582f94b2019-11-06 01:25:01 +00003562 dump_misc_regs(pvt);
3563
Doug Thompson7d6034d2009-04-27 20:01:01 +02003564 return ret;
Borislav Petkov2299ef72010-10-15 17:44:04 +02003565
3566err_enable:
Yazen Ghannam80355a32019-10-22 20:35:10 +00003567 hw_info_put(pvt);
3568 kfree(pvt);
3569
3570err_settings:
Borislav Petkov2299ef72010-10-15 17:44:04 +02003571 kfree(s);
3572 ecc_stngs[nid] = NULL;
3573
3574err_out:
3575 return ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003576}
3577
Borislav Petkov3f37a362016-05-06 19:44:27 +02003578static void remove_one_instance(unsigned int nid)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003579{
Borislav Petkov360b7f32010-10-15 19:25:38 +02003580 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
3581 struct ecc_settings *s = ecc_stngs[nid];
Borislav Petkov3f37a362016-05-06 19:44:27 +02003582 struct mem_ctl_info *mci;
3583 struct amd64_pvt *pvt;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003584
3585 /* Remove from EDAC CORE tracking list */
Borislav Petkov3f37a362016-05-06 19:44:27 +02003586 mci = edac_mc_del_mc(&F3->dev);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003587 if (!mci)
3588 return;
3589
3590 pvt = mci->pvt_info;
3591
Borislav Petkov360b7f32010-10-15 19:25:38 +02003592 restore_ecc_error_reporting(s, nid, F3);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003593
Borislav Petkov360b7f32010-10-15 19:25:38 +02003594 kfree(ecc_stngs[nid]);
3595 ecc_stngs[nid] = NULL;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003596
Doug Thompson7d6034d2009-04-27 20:01:01 +02003597 /* Free the EDAC CORE resources */
Borislav Petkov8f68ed92009-12-21 15:15:59 +01003598 mci->pvt_info = NULL;
Borislav Petkov8f68ed92009-12-21 15:15:59 +01003599
Yazen Ghannam80355a32019-10-22 20:35:10 +00003600 hw_info_put(pvt);
Borislav Petkov8f68ed92009-12-21 15:15:59 +01003601 kfree(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003602 edac_mc_free(mci);
3603}
3604
Borislav Petkov360b7f32010-10-15 19:25:38 +02003605static void setup_pci_device(void)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003606{
3607 struct mem_ctl_info *mci;
3608 struct amd64_pvt *pvt;
3609
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003610 if (pci_ctl)
Doug Thompson7d6034d2009-04-27 20:01:01 +02003611 return;
3612
Borislav Petkov2ec591a2015-02-17 10:58:34 +01003613 mci = edac_mc_find(0);
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003614 if (!mci)
3615 return;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003616
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003617 pvt = mci->pvt_info;
Yazen Ghannam936fc3a2016-11-17 17:57:36 -05003618 if (pvt->umc)
3619 pci_ctl = edac_pci_create_generic_ctl(&pvt->F0->dev, EDAC_MOD_STR);
3620 else
3621 pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003622 if (!pci_ctl) {
3623 pr_warn("%s(): Unable to create PCI control\n", __func__);
3624 pr_warn("%s(): PCI error report via EDAC not set\n", __func__);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003625 }
3626}
3627
Yazen Ghannamd6efab72016-09-15 19:07:17 -05003628static const struct x86_cpu_id amd64_cpuids[] = {
3629 { X86_VENDOR_AMD, 0xF, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
3630 { X86_VENDOR_AMD, 0x10, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
3631 { X86_VENDOR_AMD, 0x15, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
3632 { X86_VENDOR_AMD, 0x16, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
Yazen Ghannam95d3af62016-11-17 17:57:43 -05003633 { X86_VENDOR_AMD, 0x17, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
Pu Wenc4a3e942018-09-27 16:31:28 +02003634 { X86_VENDOR_HYGON, 0x18, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
Yazen Ghannam2eb61c92020-01-10 01:56:50 +00003635 { X86_VENDOR_AMD, 0x19, X86_MODEL_ANY, X86_FEATURE_ANY, 0 },
Yazen Ghannamd6efab72016-09-15 19:07:17 -05003636 { }
3637};
3638MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
3639
Doug Thompson7d6034d2009-04-27 20:01:01 +02003640static int __init amd64_edac_init(void)
3641{
Toshi Kani301375e2017-08-23 16:54:47 -06003642 const char *owner;
Borislav Petkov360b7f32010-10-15 19:25:38 +02003643 int err = -ENODEV;
Borislav Petkov3f37a362016-05-06 19:44:27 +02003644 int i;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003645
Toshi Kani301375e2017-08-23 16:54:47 -06003646 owner = edac_get_owner();
3647 if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
3648 return -EBUSY;
3649
Yazen Ghannam1bd99002017-01-27 11:24:23 -06003650 if (!x86_match_cpu(amd64_cpuids))
3651 return -ENODEV;
3652
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +02003653 if (amd_cache_northbridges() < 0)
Yazen Ghannam1bd99002017-01-27 11:24:23 -06003654 return -ENODEV;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003655
Borislav Petkov6ba92fe2016-06-16 01:13:18 +02003656 opstate_init();
3657
Borislav Petkovcc4d8862010-10-13 16:11:59 +02003658 err = -ENOMEM;
Kees Cook6396bb22018-06-12 14:03:40 -07003659 ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL);
Borislav Petkov2ec591a2015-02-17 10:58:34 +01003660 if (!ecc_stngs)
Borislav Petkova9f0fbe2011-03-29 18:10:53 +02003661 goto err_free;
Borislav Petkovcc4d8862010-10-13 16:11:59 +02003662
Borislav Petkov50542252009-12-11 18:14:40 +01003663 msrs = msrs_alloc();
Borislav Petkov56b34b92009-12-21 18:13:01 +01003664 if (!msrs)
Borislav Petkov360b7f32010-10-15 19:25:38 +02003665 goto err_free;
Borislav Petkov50542252009-12-11 18:14:40 +01003666
Yazen Ghannam2287c632017-01-13 09:52:19 -06003667 for (i = 0; i < amd_nb_num(); i++) {
3668 err = probe_one_instance(i);
3669 if (err) {
Borislav Petkov3f37a362016-05-06 19:44:27 +02003670 /* unwind properly */
3671 while (--i >= 0)
3672 remove_one_instance(i);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003673
Borislav Petkov3f37a362016-05-06 19:44:27 +02003674 goto err_pci;
3675 }
Yazen Ghannam2287c632017-01-13 09:52:19 -06003676 }
Doug Thompson7d6034d2009-04-27 20:01:01 +02003677
Yazen Ghannam4688c9b2017-01-27 11:24:22 -06003678 if (!edac_has_mcs()) {
3679 err = -ENODEV;
3680 goto err_pci;
3681 }
3682
Yazen Ghannam234365f2017-01-24 16:32:25 -06003683 /* register stuff with EDAC MCE */
3684 if (report_gart_errors)
3685 amd_report_gart_errors(true);
3686
3687 if (boot_cpu_data.x86 >= 0x17)
3688 amd_register_ecc_decoder(decode_umc_error);
3689 else
3690 amd_register_ecc_decoder(decode_bus_error);
3691
Borislav Petkov360b7f32010-10-15 19:25:38 +02003692 setup_pci_device();
Tomasz Palaf5b10c42014-11-02 11:22:12 +01003693
3694#ifdef CONFIG_X86_32
3695 amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
3696#endif
3697
Borislav Petkovde0336b2016-04-27 12:21:21 +02003698 printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
3699
Borislav Petkov360b7f32010-10-15 19:25:38 +02003700 return 0;
Borislav Petkov56b34b92009-12-21 18:13:01 +01003701
Borislav Petkov56b34b92009-12-21 18:13:01 +01003702err_pci:
3703 msrs_free(msrs);
3704 msrs = NULL;
Borislav Petkovcc4d8862010-10-13 16:11:59 +02003705
Borislav Petkov360b7f32010-10-15 19:25:38 +02003706err_free:
Borislav Petkov360b7f32010-10-15 19:25:38 +02003707 kfree(ecc_stngs);
3708 ecc_stngs = NULL;
3709
Doug Thompson7d6034d2009-04-27 20:01:01 +02003710 return err;
3711}
3712
3713static void __exit amd64_edac_exit(void)
3714{
Borislav Petkov3f37a362016-05-06 19:44:27 +02003715 int i;
3716
Borislav Petkovd1ea71c2013-12-15 17:54:27 +01003717 if (pci_ctl)
3718 edac_pci_release_generic_ctl(pci_ctl);
Doug Thompson7d6034d2009-04-27 20:01:01 +02003719
Yazen Ghannam234365f2017-01-24 16:32:25 -06003720 /* unregister from EDAC MCE */
3721 amd_report_gart_errors(false);
3722
3723 if (boot_cpu_data.x86 >= 0x17)
3724 amd_unregister_ecc_decoder(decode_umc_error);
3725 else
3726 amd_unregister_ecc_decoder(decode_bus_error);
3727
Borislav Petkov3f37a362016-05-06 19:44:27 +02003728 for (i = 0; i < amd_nb_num(); i++)
3729 remove_one_instance(i);
Borislav Petkov50542252009-12-11 18:14:40 +01003730
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02003731 kfree(ecc_stngs);
3732 ecc_stngs = NULL;
3733
Borislav Petkov50542252009-12-11 18:14:40 +01003734 msrs_free(msrs);
3735 msrs = NULL;
Doug Thompson7d6034d2009-04-27 20:01:01 +02003736}
3737
3738module_init(amd64_edac_init);
3739module_exit(amd64_edac_exit);
3740
3741MODULE_LICENSE("GPL");
3742MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
3743 "Dave Peterson, Thayne Harbaugh");
3744MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
3745 EDAC_AMD64_VERSION);
3746
3747module_param(edac_op_state, int, 0444);
3748MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");