blob: eb6b6bace68310d542a40e400bec94d73800b6a1 [file] [log] [blame]
Doug Thompson2bc65412009-05-04 20:11:14 +02001#include "amd64_edac.h"
Andreas Herrmann23ac4ae2010-09-17 18:03:43 +02002#include <asm/amd_nb.h>
Doug Thompson2bc65412009-05-04 20:11:14 +02003
4static struct edac_pci_ctl_info *amd64_ctl_pci;
5
6static int report_gart_errors;
7module_param(report_gart_errors, int, 0644);
8
9/*
10 * Set by command line parameter. If BIOS has enabled the ECC, this override is
11 * cleared to prevent re-enabling the hardware by this driver.
12 */
13static int ecc_enable_override;
14module_param(ecc_enable_override, int, 0644);
15
Tejun Heoa29d8b82010-02-02 14:39:15 +090016static struct msr __percpu *msrs;
Borislav Petkov50542252009-12-11 18:14:40 +010017
Borislav Petkov360b7f32010-10-15 19:25:38 +020018/*
19 * count successfully initialized driver instances for setup_pci_device()
20 */
21static atomic_t drv_instances = ATOMIC_INIT(0);
22
Borislav Petkovcc4d8862010-10-13 16:11:59 +020023/* Per-node driver instances */
24static struct mem_ctl_info **mcis;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +020025static struct ecc_settings **ecc_stngs;
Doug Thompson2bc65412009-05-04 20:11:14 +020026
27/*
Borislav Petkovb70ef012009-06-25 19:32:38 +020028 * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
29 * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
30 * or higher value'.
31 *
32 *FIXME: Produce a better mapping/linearisation.
33 */
Borislav Petkov39094442010-11-24 19:52:09 +010034struct scrubrate {
35 u32 scrubval; /* bit pattern for scrub rate */
36 u32 bandwidth; /* bandwidth consumed (bytes/sec) */
37} scrubrates[] = {
Borislav Petkovb70ef012009-06-25 19:32:38 +020038 { 0x01, 1600000000UL},
39 { 0x02, 800000000UL},
40 { 0x03, 400000000UL},
41 { 0x04, 200000000UL},
42 { 0x05, 100000000UL},
43 { 0x06, 50000000UL},
44 { 0x07, 25000000UL},
45 { 0x08, 12284069UL},
46 { 0x09, 6274509UL},
47 { 0x0A, 3121951UL},
48 { 0x0B, 1560975UL},
49 { 0x0C, 781440UL},
50 { 0x0D, 390720UL},
51 { 0x0E, 195300UL},
52 { 0x0F, 97650UL},
53 { 0x10, 48854UL},
54 { 0x11, 24427UL},
55 { 0x12, 12213UL},
56 { 0x13, 6101UL},
57 { 0x14, 3051UL},
58 { 0x15, 1523UL},
59 { 0x16, 761UL},
60 { 0x00, 0UL}, /* scrubbing off */
61};
62
Borislav Petkovb2b0c602010-10-08 18:32:29 +020063static int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
64 u32 *val, const char *func)
65{
66 int err = 0;
67
68 err = pci_read_config_dword(pdev, offset, val);
69 if (err)
70 amd64_warn("%s: error reading F%dx%03x.\n",
71 func, PCI_FUNC(pdev->devfn), offset);
72
73 return err;
74}
75
76int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
77 u32 val, const char *func)
78{
79 int err = 0;
80
81 err = pci_write_config_dword(pdev, offset, val);
82 if (err)
83 amd64_warn("%s: error writing to F%dx%03x.\n",
84 func, PCI_FUNC(pdev->devfn), offset);
85
86 return err;
87}
88
89/*
90 *
91 * Depending on the family, F2 DCT reads need special handling:
92 *
93 * K8: has a single DCT only
94 *
95 * F10h: each DCT has its own set of regs
96 * DCT0 -> F2x040..
97 * DCT1 -> F2x140..
98 *
99 * F15h: we select which DCT we access using F1x10C[DctCfgSel]
100 *
101 */
102static int k8_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
103 const char *func)
104{
105 if (addr >= 0x100)
106 return -EINVAL;
107
108 return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
109}
110
111static int f10_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
112 const char *func)
113{
114 return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
115}
116
117static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
118 const char *func)
119{
120 u32 reg = 0;
121 u8 dct = 0;
122
123 if (addr >= 0x140 && addr <= 0x1a0) {
124 dct = 1;
125 addr -= 0x100;
126 }
127
128 amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
129 reg &= 0xfffffffe;
130 reg |= dct;
131 amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
132
133 return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
134}
135
Borislav Petkovb70ef012009-06-25 19:32:38 +0200136/*
Doug Thompson2bc65412009-05-04 20:11:14 +0200137 * Memory scrubber control interface. For K8, memory scrubbing is handled by
138 * hardware and can involve L2 cache, dcache as well as the main memory. With
139 * F10, this is extended to L3 cache scrubbing on CPU models sporting that
140 * functionality.
141 *
142 * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
143 * (dram) over to cache lines. This is nasty, so we will use bandwidth in
144 * bytes/sec for the setting.
145 *
146 * Currently, we only do dram scrubbing. If the scrubbing is done in software on
147 * other archs, we might not have access to the caches directly.
148 */
149
150/*
151 * scan the scrub rate mapping table for a close or matching bandwidth value to
152 * issue. If requested is too big, then use last maximum value found.
153 */
Borislav Petkov395ae782010-10-01 18:38:19 +0200154static int __amd64_set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate)
Doug Thompson2bc65412009-05-04 20:11:14 +0200155{
156 u32 scrubval;
157 int i;
158
159 /*
160 * map the configured rate (new_bw) to a value specific to the AMD64
161 * memory controller and apply to register. Search for the first
162 * bandwidth entry that is greater or equal than the setting requested
163 * and program that. If at last entry, turn off DRAM scrubbing.
164 */
165 for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
166 /*
167 * skip scrub rates which aren't recommended
168 * (see F10 BKDG, F3x58)
169 */
Borislav Petkov395ae782010-10-01 18:38:19 +0200170 if (scrubrates[i].scrubval < min_rate)
Doug Thompson2bc65412009-05-04 20:11:14 +0200171 continue;
172
173 if (scrubrates[i].bandwidth <= new_bw)
174 break;
175
176 /*
177 * if no suitable bandwidth found, turn off DRAM scrubbing
178 * entirely by falling back to the last element in the
179 * scrubrates array.
180 */
181 }
182
183 scrubval = scrubrates[i].scrubval;
Doug Thompson2bc65412009-05-04 20:11:14 +0200184
Borislav Petkov5980bb92011-01-07 16:26:49 +0100185 pci_write_bits32(ctl, SCRCTRL, scrubval, 0x001F);
Doug Thompson2bc65412009-05-04 20:11:14 +0200186
Borislav Petkov39094442010-11-24 19:52:09 +0100187 if (scrubval)
188 return scrubrates[i].bandwidth;
189
Doug Thompson2bc65412009-05-04 20:11:14 +0200190 return 0;
191}
192
Borislav Petkov395ae782010-10-01 18:38:19 +0200193static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
Doug Thompson2bc65412009-05-04 20:11:14 +0200194{
195 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompson2bc65412009-05-04 20:11:14 +0200196
Borislav Petkov8d5b5d92010-10-01 20:11:07 +0200197 return __amd64_set_scrub_rate(pvt->F3, bw, pvt->min_scrubrate);
Doug Thompson2bc65412009-05-04 20:11:14 +0200198}
199
Borislav Petkov39094442010-11-24 19:52:09 +0100200static int amd64_get_scrub_rate(struct mem_ctl_info *mci)
Doug Thompson2bc65412009-05-04 20:11:14 +0200201{
202 struct amd64_pvt *pvt = mci->pvt_info;
203 u32 scrubval = 0;
Borislav Petkov39094442010-11-24 19:52:09 +0100204 int i, retval = -EINVAL;
Doug Thompson2bc65412009-05-04 20:11:14 +0200205
Borislav Petkov5980bb92011-01-07 16:26:49 +0100206 amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
Doug Thompson2bc65412009-05-04 20:11:14 +0200207
208 scrubval = scrubval & 0x001F;
209
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200210 amd64_debug("pci-read, sdram scrub control value: %d\n", scrubval);
Doug Thompson2bc65412009-05-04 20:11:14 +0200211
Roel Kluin926311f2010-01-11 20:58:21 +0100212 for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
Doug Thompson2bc65412009-05-04 20:11:14 +0200213 if (scrubrates[i].scrubval == scrubval) {
Borislav Petkov39094442010-11-24 19:52:09 +0100214 retval = scrubrates[i].bandwidth;
Doug Thompson2bc65412009-05-04 20:11:14 +0200215 break;
216 }
217 }
Borislav Petkov39094442010-11-24 19:52:09 +0100218 return retval;
Doug Thompson2bc65412009-05-04 20:11:14 +0200219}
220
Doug Thompson67757632009-04-27 15:53:22 +0200221/*
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200222 * returns true if the SysAddr given by sys_addr matches the
223 * DRAM base/limit associated with node_id
Doug Thompson67757632009-04-27 15:53:22 +0200224 */
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200225static bool amd64_base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, int nid)
Doug Thompson67757632009-04-27 15:53:22 +0200226{
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200227 u64 addr;
Doug Thompson67757632009-04-27 15:53:22 +0200228
229 /* The K8 treats this as a 40-bit value. However, bits 63-40 will be
230 * all ones if the most significant implemented address bit is 1.
231 * Here we discard bits 63-40. See section 3.4.2 of AMD publication
232 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
233 * Application Programming.
234 */
235 addr = sys_addr & 0x000000ffffffffffull;
236
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200237 return ((addr >= get_dram_base(pvt, nid)) &&
238 (addr <= get_dram_limit(pvt, nid)));
Doug Thompson67757632009-04-27 15:53:22 +0200239}
240
241/*
242 * Attempt to map a SysAddr to a node. On success, return a pointer to the
243 * mem_ctl_info structure for the node that the SysAddr maps to.
244 *
245 * On failure, return NULL.
246 */
247static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
248 u64 sys_addr)
249{
250 struct amd64_pvt *pvt;
251 int node_id;
252 u32 intlv_en, bits;
253
254 /*
255 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
256 * 3.4.4.2) registers to map the SysAddr to a node ID.
257 */
258 pvt = mci->pvt_info;
259
260 /*
261 * The value of this field should be the same for all DRAM Base
262 * registers. Therefore we arbitrarily choose to read it from the
263 * register for node 0.
264 */
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200265 intlv_en = dram_intlv_en(pvt, 0);
Doug Thompson67757632009-04-27 15:53:22 +0200266
267 if (intlv_en == 0) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200268 for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
Doug Thompson67757632009-04-27 15:53:22 +0200269 if (amd64_base_limit_match(pvt, sys_addr, node_id))
Borislav Petkov8edc5442009-09-18 12:39:19 +0200270 goto found;
Doug Thompson67757632009-04-27 15:53:22 +0200271 }
Borislav Petkov8edc5442009-09-18 12:39:19 +0200272 goto err_no_match;
Doug Thompson67757632009-04-27 15:53:22 +0200273 }
274
Borislav Petkov72f158f2009-09-18 12:27:27 +0200275 if (unlikely((intlv_en != 0x01) &&
276 (intlv_en != 0x03) &&
277 (intlv_en != 0x07))) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200278 amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
Doug Thompson67757632009-04-27 15:53:22 +0200279 return NULL;
280 }
281
282 bits = (((u32) sys_addr) >> 12) & intlv_en;
283
284 for (node_id = 0; ; ) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200285 if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
Doug Thompson67757632009-04-27 15:53:22 +0200286 break; /* intlv_sel field matches */
287
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200288 if (++node_id >= DRAM_RANGES)
Doug Thompson67757632009-04-27 15:53:22 +0200289 goto err_no_match;
290 }
291
292 /* sanity test for sys_addr */
293 if (unlikely(!amd64_base_limit_match(pvt, sys_addr, node_id))) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200294 amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
295 "range for node %d with node interleaving enabled.\n",
296 __func__, sys_addr, node_id);
Doug Thompson67757632009-04-27 15:53:22 +0200297 return NULL;
298 }
299
300found:
301 return edac_mc_find(node_id);
302
303err_no_match:
304 debugf2("sys_addr 0x%lx doesn't match any node\n",
305 (unsigned long)sys_addr);
306
307 return NULL;
308}
Doug Thompsone2ce7252009-04-27 15:57:12 +0200309
310/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100311 * compute the CS base address of the @csrow on the DRAM controller @dct.
312 * For details see F2x[5C:40] in the processor's BKDG
Doug Thompsone2ce7252009-04-27 15:57:12 +0200313 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100314static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
315 u64 *base, u64 *mask)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200316{
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100317 u64 csbase, csmask, base_bits, mask_bits;
318 u8 addr_shift;
319
320 if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
321 csbase = pvt->csels[dct].csbases[csrow];
322 csmask = pvt->csels[dct].csmasks[csrow];
323 base_bits = GENMASK(21, 31) | GENMASK(9, 15);
324 mask_bits = GENMASK(21, 29) | GENMASK(9, 15);
325 addr_shift = 4;
326 } else {
327 csbase = pvt->csels[dct].csbases[csrow];
328 csmask = pvt->csels[dct].csmasks[csrow >> 1];
329 addr_shift = 8;
330
331 if (boot_cpu_data.x86 == 0x15)
332 base_bits = mask_bits = GENMASK(19,30) | GENMASK(5,13);
333 else
334 base_bits = mask_bits = GENMASK(19,28) | GENMASK(5,13);
335 }
336
337 *base = (csbase & base_bits) << addr_shift;
338
339 *mask = ~0ULL;
340 /* poke holes for the csmask */
341 *mask &= ~(mask_bits << addr_shift);
342 /* OR them in */
343 *mask |= (csmask & mask_bits) << addr_shift;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200344}
345
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100346#define for_each_chip_select(i, dct, pvt) \
347 for (i = 0; i < pvt->csels[dct].b_cnt; i++)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200348
Borislav Petkov614ec9d2011-01-13 18:02:22 +0100349#define chip_select_base(i, dct, pvt) \
350 pvt->csels[dct].csbases[i]
351
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100352#define for_each_chip_select_mask(i, dct, pvt) \
353 for (i = 0; i < pvt->csels[dct].m_cnt; i++)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200354
355/*
356 * @input_addr is an InputAddr associated with the node given by mci. Return the
357 * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
358 */
359static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
360{
361 struct amd64_pvt *pvt;
362 int csrow;
363 u64 base, mask;
364
365 pvt = mci->pvt_info;
366
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100367 for_each_chip_select(csrow, 0, pvt) {
368 if (!csrow_enabled(csrow, 0, pvt))
Doug Thompsone2ce7252009-04-27 15:57:12 +0200369 continue;
370
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100371 get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
372
373 mask = ~mask;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200374
375 if ((input_addr & mask) == (base & mask)) {
376 debugf2("InputAddr 0x%lx matches csrow %d (node %d)\n",
377 (unsigned long)input_addr, csrow,
378 pvt->mc_node_id);
379
380 return csrow;
381 }
382 }
Doug Thompsone2ce7252009-04-27 15:57:12 +0200383 debugf2("no matching csrow for InputAddr 0x%lx (MC node %d)\n",
384 (unsigned long)input_addr, pvt->mc_node_id);
385
386 return -1;
387}
388
389/*
Doug Thompsone2ce7252009-04-27 15:57:12 +0200390 * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
391 * for the node represented by mci. Info is passed back in *hole_base,
392 * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if
393 * info is invalid. Info may be invalid for either of the following reasons:
394 *
395 * - The revision of the node is not E or greater. In this case, the DRAM Hole
396 * Address Register does not exist.
397 *
398 * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
399 * indicating that its contents are not valid.
400 *
401 * The values passed back in *hole_base, *hole_offset, and *hole_size are
402 * complete 32-bit values despite the fact that the bitfields in the DHAR
403 * only represent bits 31-24 of the base and offset values.
404 */
405int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
406 u64 *hole_offset, u64 *hole_size)
407{
408 struct amd64_pvt *pvt = mci->pvt_info;
409 u64 base;
410
411 /* only revE and later have the DRAM Hole Address Register */
Borislav Petkov1433eb92009-10-21 13:44:36 +0200412 if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) {
Doug Thompsone2ce7252009-04-27 15:57:12 +0200413 debugf1(" revision %d for node %d does not support DHAR\n",
414 pvt->ext_model, pvt->mc_node_id);
415 return 1;
416 }
417
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100418 /* valid for Fam10h and above */
Borislav Petkovc8e518d2010-12-10 19:49:19 +0100419 if (boot_cpu_data.x86 >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
Doug Thompsone2ce7252009-04-27 15:57:12 +0200420 debugf1(" Dram Memory Hoisting is DISABLED on this system\n");
421 return 1;
422 }
423
Borislav Petkovc8e518d2010-12-10 19:49:19 +0100424 if (!dhar_valid(pvt)) {
Doug Thompsone2ce7252009-04-27 15:57:12 +0200425 debugf1(" Dram Memory Hoisting is DISABLED on this node %d\n",
426 pvt->mc_node_id);
427 return 1;
428 }
429
430 /* This node has Memory Hoisting */
431
432 /* +------------------+--------------------+--------------------+-----
433 * | memory | DRAM hole | relocated |
434 * | [0, (x - 1)] | [x, 0xffffffff] | addresses from |
435 * | | | DRAM hole |
436 * | | | [0x100000000, |
437 * | | | (0x100000000+ |
438 * | | | (0xffffffff-x))] |
439 * +------------------+--------------------+--------------------+-----
440 *
441 * Above is a diagram of physical memory showing the DRAM hole and the
442 * relocated addresses from the DRAM hole. As shown, the DRAM hole
443 * starts at address x (the base address) and extends through address
444 * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the
445 * addresses in the hole so that they start at 0x100000000.
446 */
447
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100448 base = dhar_base(pvt);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200449
450 *hole_base = base;
451 *hole_size = (0x1ull << 32) - base;
452
453 if (boot_cpu_data.x86 > 0xf)
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100454 *hole_offset = f10_dhar_offset(pvt);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200455 else
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100456 *hole_offset = k8_dhar_offset(pvt);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200457
458 debugf1(" DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
459 pvt->mc_node_id, (unsigned long)*hole_base,
460 (unsigned long)*hole_offset, (unsigned long)*hole_size);
461
462 return 0;
463}
464EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
465
Doug Thompson93c2df52009-05-04 20:46:50 +0200466/*
467 * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is
468 * assumed that sys_addr maps to the node given by mci.
469 *
470 * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
471 * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
472 * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
473 * then it is also involved in translating a SysAddr to a DramAddr. Sections
474 * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
475 * These parts of the documentation are unclear. I interpret them as follows:
476 *
477 * When node n receives a SysAddr, it processes the SysAddr as follows:
478 *
479 * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
480 * Limit registers for node n. If the SysAddr is not within the range
481 * specified by the base and limit values, then node n ignores the Sysaddr
482 * (since it does not map to node n). Otherwise continue to step 2 below.
483 *
484 * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
485 * disabled so skip to step 3 below. Otherwise see if the SysAddr is within
486 * the range of relocated addresses (starting at 0x100000000) from the DRAM
487 * hole. If not, skip to step 3 below. Else get the value of the
488 * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
489 * offset defined by this value from the SysAddr.
490 *
491 * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
492 * Base register for node n. To obtain the DramAddr, subtract the base
493 * address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
494 */
495static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
496{
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200497 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompson93c2df52009-05-04 20:46:50 +0200498 u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
499 int ret = 0;
500
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200501 dram_base = get_dram_base(pvt, pvt->mc_node_id);
Doug Thompson93c2df52009-05-04 20:46:50 +0200502
503 ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
504 &hole_size);
505 if (!ret) {
506 if ((sys_addr >= (1ull << 32)) &&
507 (sys_addr < ((1ull << 32) + hole_size))) {
508 /* use DHAR to translate SysAddr to DramAddr */
509 dram_addr = sys_addr - hole_offset;
510
511 debugf2("using DHAR to translate SysAddr 0x%lx to "
512 "DramAddr 0x%lx\n",
513 (unsigned long)sys_addr,
514 (unsigned long)dram_addr);
515
516 return dram_addr;
517 }
518 }
519
520 /*
521 * Translate the SysAddr to a DramAddr as shown near the start of
522 * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8
523 * only deals with 40-bit values. Therefore we discard bits 63-40 of
524 * sys_addr below. If bit 39 of sys_addr is 1 then the bits we
525 * discard are all 1s. Otherwise the bits we discard are all 0s. See
526 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
527 * Programmer's Manual Volume 1 Application Programming.
528 */
Borislav Petkovf678b8c2010-12-13 19:21:07 +0100529 dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base;
Doug Thompson93c2df52009-05-04 20:46:50 +0200530
531 debugf2("using DRAM Base register to translate SysAddr 0x%lx to "
532 "DramAddr 0x%lx\n", (unsigned long)sys_addr,
533 (unsigned long)dram_addr);
534 return dram_addr;
535}
536
537/*
538 * @intlv_en is the value of the IntlvEn field from a DRAM Base register
539 * (section 3.4.4.1). Return the number of bits from a SysAddr that are used
540 * for node interleaving.
541 */
542static int num_node_interleave_bits(unsigned intlv_en)
543{
544 static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
545 int n;
546
547 BUG_ON(intlv_en > 7);
548 n = intlv_shift_table[intlv_en];
549 return n;
550}
551
552/* Translate the DramAddr given by @dram_addr to an InputAddr. */
553static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
554{
555 struct amd64_pvt *pvt;
556 int intlv_shift;
557 u64 input_addr;
558
559 pvt = mci->pvt_info;
560
561 /*
562 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
563 * concerning translating a DramAddr to an InputAddr.
564 */
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200565 intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
Borislav Petkovf678b8c2010-12-13 19:21:07 +0100566 input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) +
567 (dram_addr & 0xfff);
Doug Thompson93c2df52009-05-04 20:46:50 +0200568
569 debugf2(" Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
570 intlv_shift, (unsigned long)dram_addr,
571 (unsigned long)input_addr);
572
573 return input_addr;
574}
575
576/*
577 * Translate the SysAddr represented by @sys_addr to an InputAddr. It is
578 * assumed that @sys_addr maps to the node given by mci.
579 */
580static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
581{
582 u64 input_addr;
583
584 input_addr =
585 dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
586
587 debugf2("SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
588 (unsigned long)sys_addr, (unsigned long)input_addr);
589
590 return input_addr;
591}
592
593
594/*
595 * @input_addr is an InputAddr associated with the node represented by mci.
596 * Translate @input_addr to a DramAddr and return the result.
597 */
598static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
599{
600 struct amd64_pvt *pvt;
601 int node_id, intlv_shift;
602 u64 bits, dram_addr;
603 u32 intlv_sel;
604
605 /*
606 * Near the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
607 * shows how to translate a DramAddr to an InputAddr. Here we reverse
608 * this procedure. When translating from a DramAddr to an InputAddr, the
609 * bits used for node interleaving are discarded. Here we recover these
610 * bits from the IntlvSel field of the DRAM Limit register (section
611 * 3.4.4.2) for the node that input_addr is associated with.
612 */
613 pvt = mci->pvt_info;
614 node_id = pvt->mc_node_id;
615 BUG_ON((node_id < 0) || (node_id > 7));
616
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200617 intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
Doug Thompson93c2df52009-05-04 20:46:50 +0200618
619 if (intlv_shift == 0) {
620 debugf1(" InputAddr 0x%lx translates to DramAddr of "
621 "same value\n", (unsigned long)input_addr);
622
623 return input_addr;
624 }
625
Borislav Petkovf678b8c2010-12-13 19:21:07 +0100626 bits = ((input_addr & GENMASK(12, 35)) << intlv_shift) +
627 (input_addr & 0xfff);
Doug Thompson93c2df52009-05-04 20:46:50 +0200628
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200629 intlv_sel = dram_intlv_sel(pvt, node_id) & ((1 << intlv_shift) - 1);
Doug Thompson93c2df52009-05-04 20:46:50 +0200630 dram_addr = bits + (intlv_sel << 12);
631
632 debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx "
633 "(%d node interleave bits)\n", (unsigned long)input_addr,
634 (unsigned long)dram_addr, intlv_shift);
635
636 return dram_addr;
637}
638
639/*
640 * @dram_addr is a DramAddr that maps to the node represented by mci. Convert
641 * @dram_addr to a SysAddr.
642 */
643static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
644{
645 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200646 u64 hole_base, hole_offset, hole_size, base, sys_addr;
Doug Thompson93c2df52009-05-04 20:46:50 +0200647 int ret = 0;
648
649 ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
650 &hole_size);
651 if (!ret) {
652 if ((dram_addr >= hole_base) &&
653 (dram_addr < (hole_base + hole_size))) {
654 sys_addr = dram_addr + hole_offset;
655
656 debugf1("using DHAR to translate DramAddr 0x%lx to "
657 "SysAddr 0x%lx\n", (unsigned long)dram_addr,
658 (unsigned long)sys_addr);
659
660 return sys_addr;
661 }
662 }
663
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200664 base = get_dram_base(pvt, pvt->mc_node_id);
Doug Thompson93c2df52009-05-04 20:46:50 +0200665 sys_addr = dram_addr + base;
666
667 /*
668 * The sys_addr we have computed up to this point is a 40-bit value
669 * because the k8 deals with 40-bit values. However, the value we are
670 * supposed to return is a full 64-bit physical address. The AMD
671 * x86-64 architecture specifies that the most significant implemented
672 * address bit through bit 63 of a physical address must be either all
673 * 0s or all 1s. Therefore we sign-extend the 40-bit sys_addr to a
674 * 64-bit value below. See section 3.4.2 of AMD publication 24592:
675 * AMD x86-64 Architecture Programmer's Manual Volume 1 Application
676 * Programming.
677 */
678 sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
679
680 debugf1(" Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
681 pvt->mc_node_id, (unsigned long)dram_addr,
682 (unsigned long)sys_addr);
683
684 return sys_addr;
685}
686
687/*
688 * @input_addr is an InputAddr associated with the node given by mci. Translate
689 * @input_addr to a SysAddr.
690 */
691static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
692 u64 input_addr)
693{
694 return dram_addr_to_sys_addr(mci,
695 input_addr_to_dram_addr(mci, input_addr));
696}
697
698/*
699 * Find the minimum and maximum InputAddr values that map to the given @csrow.
700 * Pass back these values in *input_addr_min and *input_addr_max.
701 */
702static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
703 u64 *input_addr_min, u64 *input_addr_max)
704{
705 struct amd64_pvt *pvt;
706 u64 base, mask;
707
708 pvt = mci->pvt_info;
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100709 BUG_ON((csrow < 0) || (csrow >= pvt->csels[0].b_cnt));
Doug Thompson93c2df52009-05-04 20:46:50 +0200710
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100711 get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
Doug Thompson93c2df52009-05-04 20:46:50 +0200712
713 *input_addr_min = base & ~mask;
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100714 *input_addr_max = base | mask;
Doug Thompson93c2df52009-05-04 20:46:50 +0200715}
716
Doug Thompson93c2df52009-05-04 20:46:50 +0200717/* Map the Error address to a PAGE and PAGE OFFSET. */
718static inline void error_address_to_page_and_offset(u64 error_address,
719 u32 *page, u32 *offset)
720{
721 *page = (u32) (error_address >> PAGE_SHIFT);
722 *offset = ((u32) error_address) & ~PAGE_MASK;
723}
724
725/*
726 * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
727 * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
728 * of a node that detected an ECC memory error. mci represents the node that
729 * the error address maps to (possibly different from the node that detected
730 * the error). Return the number of the csrow that sys_addr maps to, or -1 on
731 * error.
732 */
733static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
734{
735 int csrow;
736
737 csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
738
739 if (csrow == -1)
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200740 amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
741 "address 0x%lx\n", (unsigned long)sys_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200742 return csrow;
743}
Doug Thompsone2ce7252009-04-27 15:57:12 +0200744
Borislav Petkovbfc04ae2009-11-12 19:05:07 +0100745static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
Doug Thompson2da11652009-04-27 16:09:09 +0200746
Doug Thompson2da11652009-04-27 16:09:09 +0200747/*
748 * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
749 * are ECC capable.
750 */
751static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
752{
Borislav Petkovcb328502010-12-22 14:28:24 +0100753 u8 bit;
Borislav Petkov584fcff2009-06-10 18:29:54 +0200754 enum dev_type edac_cap = EDAC_FLAG_NONE;
Doug Thompson2da11652009-04-27 16:09:09 +0200755
Borislav Petkov1433eb92009-10-21 13:44:36 +0200756 bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
Doug Thompson2da11652009-04-27 16:09:09 +0200757 ? 19
758 : 17;
759
Borislav Petkov584fcff2009-06-10 18:29:54 +0200760 if (pvt->dclr0 & BIT(bit))
Doug Thompson2da11652009-04-27 16:09:09 +0200761 edac_cap = EDAC_FLAG_SECDED;
762
763 return edac_cap;
764}
765
766
Borislav Petkov8566c4d2009-10-16 13:48:28 +0200767static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt);
Doug Thompson2da11652009-04-27 16:09:09 +0200768
Borislav Petkov68798e12009-11-03 16:18:33 +0100769static void amd64_dump_dramcfg_low(u32 dclr, int chan)
770{
771 debugf1("F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
772
773 debugf1(" DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
774 (dclr & BIT(16)) ? "un" : "",
775 (dclr & BIT(19)) ? "yes" : "no");
776
777 debugf1(" PAR/ERR parity: %s\n",
778 (dclr & BIT(8)) ? "enabled" : "disabled");
779
Borislav Petkovcb328502010-12-22 14:28:24 +0100780 if (boot_cpu_data.x86 == 0x10)
781 debugf1(" DCT 128bit mode width: %s\n",
782 (dclr & BIT(11)) ? "128b" : "64b");
Borislav Petkov68798e12009-11-03 16:18:33 +0100783
784 debugf1(" x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
785 (dclr & BIT(12)) ? "yes" : "no",
786 (dclr & BIT(13)) ? "yes" : "no",
787 (dclr & BIT(14)) ? "yes" : "no",
788 (dclr & BIT(15)) ? "yes" : "no");
789}
790
Doug Thompson2da11652009-04-27 16:09:09 +0200791/* Display and decode various NB registers for debug purposes. */
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200792static void dump_misc_regs(struct amd64_pvt *pvt)
Doug Thompson2da11652009-04-27 16:09:09 +0200793{
Borislav Petkov68798e12009-11-03 16:18:33 +0100794 debugf1("F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
Doug Thompson2da11652009-04-27 16:09:09 +0200795
Borislav Petkov68798e12009-11-03 16:18:33 +0100796 debugf1(" NB two channel DRAM capable: %s\n",
Borislav Petkov5980bb92011-01-07 16:26:49 +0100797 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100798
799 debugf1(" ECC capable: %s, ChipKill ECC capable: %s\n",
Borislav Petkov5980bb92011-01-07 16:26:49 +0100800 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
801 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100802
803 amd64_dump_dramcfg_low(pvt->dclr0, 0);
Doug Thompson2da11652009-04-27 16:09:09 +0200804
Borislav Petkov8de1d912009-10-16 13:39:30 +0200805 debugf1("F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
Doug Thompson2da11652009-04-27 16:09:09 +0200806
Borislav Petkov8de1d912009-10-16 13:39:30 +0200807 debugf1("F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, "
808 "offset: 0x%08x\n",
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100809 pvt->dhar, dhar_base(pvt),
810 (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt)
811 : f10_dhar_offset(pvt));
Doug Thompson2da11652009-04-27 16:09:09 +0200812
Borislav Petkovc8e518d2010-12-10 19:49:19 +0100813 debugf1(" DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
Doug Thompson2da11652009-04-27 16:09:09 +0200814
Borislav Petkov4d796362011-02-03 15:59:57 +0100815 amd64_debug_display_dimm_sizes(0, pvt);
816
Borislav Petkov8de1d912009-10-16 13:39:30 +0200817 /* everything below this point is Fam10h and above */
Borislav Petkov4d796362011-02-03 15:59:57 +0100818 if (boot_cpu_data.x86 == 0xf)
Doug Thompson2da11652009-04-27 16:09:09 +0200819 return;
Borislav Petkov4d796362011-02-03 15:59:57 +0100820
821 amd64_debug_display_dimm_sizes(1, pvt);
Doug Thompson2da11652009-04-27 16:09:09 +0200822
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200823 amd64_info("using %s syndromes.\n", ((pvt->syn_type == 8) ? "x8" : "x4"));
Borislav Petkovad6a32e2010-03-09 12:46:00 +0100824
Borislav Petkov8de1d912009-10-16 13:39:30 +0200825 /* Only if NOT ganged does dclr1 have valid info */
Borislav Petkov68798e12009-11-03 16:18:33 +0100826 if (!dct_ganging_enabled(pvt))
827 amd64_dump_dramcfg_low(pvt->dclr1, 1);
Doug Thompson2da11652009-04-27 16:09:09 +0200828}
829
Doug Thompson94be4bf2009-04-27 16:12:00 +0200830/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100831 * see BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
Doug Thompson94be4bf2009-04-27 16:12:00 +0200832 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100833static void prep_chip_selects(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200834{
Borislav Petkov1433eb92009-10-21 13:44:36 +0200835 if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100836 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
837 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
Borislav Petkov9d858bb2009-09-21 14:35:51 +0200838 } else {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100839 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
840 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200841 }
842}
843
844/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100845 * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
Doug Thompson94be4bf2009-04-27 16:12:00 +0200846 */
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200847static void read_dct_base_mask(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200848{
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100849 int cs;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200850
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100851 prep_chip_selects(pvt);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200852
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100853 for_each_chip_select(cs, 0, pvt) {
854 u32 reg0 = DCSB0 + (cs * 4);
855 u32 reg1 = DCSB1 + (cs * 4);
856 u32 *base0 = &pvt->csels[0].csbases[cs];
857 u32 *base1 = &pvt->csels[1].csbases[cs];
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200858
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100859 if (!amd64_read_dct_pci_cfg(pvt, reg0, base0))
Doug Thompson94be4bf2009-04-27 16:12:00 +0200860 debugf0(" DCSB0[%d]=0x%08x reg: F2x%x\n",
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100861 cs, *base0, reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200862
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100863 if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
864 continue;
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200865
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100866 if (!amd64_read_dct_pci_cfg(pvt, reg1, base1))
867 debugf0(" DCSB1[%d]=0x%08x reg: F2x%x\n",
868 cs, *base1, reg1);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200869 }
870
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100871 for_each_chip_select_mask(cs, 0, pvt) {
872 u32 reg0 = DCSM0 + (cs * 4);
873 u32 reg1 = DCSM1 + (cs * 4);
874 u32 *mask0 = &pvt->csels[0].csmasks[cs];
875 u32 *mask1 = &pvt->csels[1].csmasks[cs];
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200876
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100877 if (!amd64_read_dct_pci_cfg(pvt, reg0, mask0))
Doug Thompson94be4bf2009-04-27 16:12:00 +0200878 debugf0(" DCSM0[%d]=0x%08x reg: F2x%x\n",
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100879 cs, *mask0, reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200880
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100881 if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
882 continue;
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200883
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100884 if (!amd64_read_dct_pci_cfg(pvt, reg1, mask1))
885 debugf0(" DCSM1[%d]=0x%08x reg: F2x%x\n",
886 cs, *mask1, reg1);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200887 }
888}
889
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200890static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt, int cs)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200891{
892 enum mem_type type;
893
Borislav Petkovcb328502010-12-22 14:28:24 +0100894 /* F15h supports only DDR3 */
895 if (boot_cpu_data.x86 >= 0x15)
896 type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
897 else if (boot_cpu_data.x86 == 0x10 || pvt->ext_model >= K8_REV_F) {
Borislav Petkov6b4c0bd2009-11-12 15:37:57 +0100898 if (pvt->dchr0 & DDR3_MODE)
899 type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
900 else
901 type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200902 } else {
Doug Thompson94be4bf2009-04-27 16:12:00 +0200903 type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
904 }
905
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200906 amd64_info("CS%d: %s\n", cs, edac_mem_types[type]);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200907
908 return type;
909}
910
Borislav Petkovcb328502010-12-22 14:28:24 +0100911/* Get the number of DCT channels the memory controller is using. */
Doug Thompsonddff8762009-04-27 16:14:52 +0200912static int k8_early_channel_count(struct amd64_pvt *pvt)
913{
Borislav Petkovcb328502010-12-22 14:28:24 +0100914 int flag;
Doug Thompsonddff8762009-04-27 16:14:52 +0200915
Borislav Petkov9f56da02010-10-01 19:44:53 +0200916 if (pvt->ext_model >= K8_REV_F)
Doug Thompsonddff8762009-04-27 16:14:52 +0200917 /* RevF (NPT) and later */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +0100918 flag = pvt->dclr0 & WIDTH_128;
Borislav Petkov9f56da02010-10-01 19:44:53 +0200919 else
Doug Thompsonddff8762009-04-27 16:14:52 +0200920 /* RevE and earlier */
921 flag = pvt->dclr0 & REVE_WIDTH_128;
Doug Thompsonddff8762009-04-27 16:14:52 +0200922
923 /* not used */
924 pvt->dclr1 = 0;
925
926 return (flag) ? 2 : 1;
927}
928
Borislav Petkov70046622011-01-10 14:37:27 +0100929/* On F10h and later ErrAddr is MC4_ADDR[47:1] */
930static u64 get_error_address(struct mce *m)
Doug Thompsonddff8762009-04-27 16:14:52 +0200931{
Borislav Petkov70046622011-01-10 14:37:27 +0100932 u8 start_bit = 1;
933 u8 end_bit = 47;
934
935 if (boot_cpu_data.x86 == 0xf) {
936 start_bit = 3;
937 end_bit = 39;
938 }
939
940 return m->addr & GENMASK(start_bit, end_bit);
Doug Thompsonddff8762009-04-27 16:14:52 +0200941}
942
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200943static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
Doug Thompsonddff8762009-04-27 16:14:52 +0200944{
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200945 u32 off = range << 3;
Doug Thompsonddff8762009-04-27 16:14:52 +0200946
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200947 amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo);
948 amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
Doug Thompsonddff8762009-04-27 16:14:52 +0200949
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200950 if (boot_cpu_data.x86 == 0xf)
951 return;
Doug Thompsonddff8762009-04-27 16:14:52 +0200952
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200953 if (!dram_rw(pvt, range))
954 return;
Doug Thompsonddff8762009-04-27 16:14:52 +0200955
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200956 amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi);
957 amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
Doug Thompsonddff8762009-04-27 16:14:52 +0200958}
959
Borislav Petkovf192c7b2011-01-10 14:24:32 +0100960static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
961 u16 syndrome)
Doug Thompsonddff8762009-04-27 16:14:52 +0200962{
963 struct mem_ctl_info *src_mci;
Borislav Petkovf192c7b2011-01-10 14:24:32 +0100964 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompsonddff8762009-04-27 16:14:52 +0200965 int channel, csrow;
966 u32 page, offset;
Doug Thompsonddff8762009-04-27 16:14:52 +0200967
968 /* CHIPKILL enabled */
Borislav Petkovf192c7b2011-01-10 14:24:32 +0100969 if (pvt->nbcfg & NBCFG_CHIPKILL) {
Borislav Petkovbfc04ae2009-11-12 19:05:07 +0100970 channel = get_channel_from_ecc_syndrome(mci, syndrome);
Doug Thompsonddff8762009-04-27 16:14:52 +0200971 if (channel < 0) {
972 /*
973 * Syndrome didn't map, so we don't know which of the
974 * 2 DIMMs is in error. So we need to ID 'both' of them
975 * as suspect.
976 */
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200977 amd64_mc_warn(mci, "unknown syndrome 0x%04x - possible "
978 "error reporting race\n", syndrome);
Doug Thompsonddff8762009-04-27 16:14:52 +0200979 edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
980 return;
981 }
982 } else {
983 /*
984 * non-chipkill ecc mode
985 *
986 * The k8 documentation is unclear about how to determine the
987 * channel number when using non-chipkill memory. This method
988 * was obtained from email communication with someone at AMD.
989 * (Wish the email was placed in this comment - norsk)
990 */
Borislav Petkov44e9e2e2009-10-26 15:00:19 +0100991 channel = ((sys_addr & BIT(3)) != 0);
Doug Thompsonddff8762009-04-27 16:14:52 +0200992 }
993
994 /*
995 * Find out which node the error address belongs to. This may be
996 * different from the node that detected the error.
997 */
Borislav Petkov44e9e2e2009-10-26 15:00:19 +0100998 src_mci = find_mc_by_sys_addr(mci, sys_addr);
Keith Mannthey2cff18c2009-09-18 14:35:23 +0200999 if (!src_mci) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001000 amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001001 (unsigned long)sys_addr);
Doug Thompsonddff8762009-04-27 16:14:52 +02001002 edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1003 return;
1004 }
1005
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001006 /* Now map the sys_addr to a CSROW */
1007 csrow = sys_addr_to_csrow(src_mci, sys_addr);
Doug Thompsonddff8762009-04-27 16:14:52 +02001008 if (csrow < 0) {
1009 edac_mc_handle_ce_no_info(src_mci, EDAC_MOD_STR);
1010 } else {
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001011 error_address_to_page_and_offset(sys_addr, &page, &offset);
Doug Thompsonddff8762009-04-27 16:14:52 +02001012
1013 edac_mc_handle_ce(src_mci, page, offset, syndrome, csrow,
1014 channel, EDAC_MOD_STR);
1015 }
1016}
1017
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001018static int ddr2_cs_size(unsigned i, bool dct_width)
Doug Thompsonddff8762009-04-27 16:14:52 +02001019{
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001020 unsigned shift = 0;
Doug Thompsonddff8762009-04-27 16:14:52 +02001021
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001022 if (i <= 2)
1023 shift = i;
1024 else if (!(i & 0x1))
1025 shift = i >> 1;
Borislav Petkov1433eb92009-10-21 13:44:36 +02001026 else
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001027 shift = (i + 1) >> 1;
Doug Thompsonddff8762009-04-27 16:14:52 +02001028
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001029 return 128 << (shift + !!dct_width);
1030}
1031
1032static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1033 unsigned cs_mode)
1034{
1035 u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
1036
1037 if (pvt->ext_model >= K8_REV_F) {
1038 WARN_ON(cs_mode > 11);
1039 return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
1040 }
1041 else if (pvt->ext_model >= K8_REV_D) {
1042 WARN_ON(cs_mode > 10);
1043
1044 if (cs_mode == 3 || cs_mode == 8)
1045 return 32 << (cs_mode - 1);
1046 else
1047 return 32 << cs_mode;
1048 }
1049 else {
1050 WARN_ON(cs_mode > 6);
1051 return 32 << cs_mode;
1052 }
Doug Thompsonddff8762009-04-27 16:14:52 +02001053}
1054
Doug Thompson1afd3c92009-04-27 16:16:50 +02001055/*
1056 * Get the number of DCT channels in use.
1057 *
1058 * Return:
1059 * number of Memory Channels in operation
1060 * Pass back:
1061 * contents of the DCL0_LOW register
1062 */
Borislav Petkov7d20d142011-01-07 17:58:04 +01001063static int f1x_early_channel_count(struct amd64_pvt *pvt)
Doug Thompson1afd3c92009-04-27 16:16:50 +02001064{
Borislav Petkov6ba5dcd2009-10-13 19:26:55 +02001065 int i, j, channels = 0;
Doug Thompsonddff8762009-04-27 16:14:52 +02001066
Borislav Petkov7d20d142011-01-07 17:58:04 +01001067 /* On F10h, if we are in 128 bit mode, then we are using 2 channels */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001068 if (boot_cpu_data.x86 == 0x10 && (pvt->dclr0 & WIDTH_128))
Borislav Petkov7d20d142011-01-07 17:58:04 +01001069 return 2;
Doug Thompson1afd3c92009-04-27 16:16:50 +02001070
1071 /*
Borislav Petkovd16149e2009-10-16 19:55:49 +02001072 * Need to check if in unganged mode: In such, there are 2 channels,
1073 * but they are not in 128 bit mode and thus the above 'dclr0' status
1074 * bit will be OFF.
Doug Thompson1afd3c92009-04-27 16:16:50 +02001075 *
1076 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
1077 * their CSEnable bit on. If so, then SINGLE DIMM case.
1078 */
Borislav Petkovd16149e2009-10-16 19:55:49 +02001079 debugf0("Data width is not 128 bits - need more decoding\n");
Doug Thompson1afd3c92009-04-27 16:16:50 +02001080
1081 /*
1082 * Check DRAM Bank Address Mapping values for each DIMM to see if there
1083 * is more than just one DIMM present in unganged mode. Need to check
1084 * both controllers since DIMMs can be placed in either one.
1085 */
Borislav Petkov525a1b22010-12-21 15:53:27 +01001086 for (i = 0; i < 2; i++) {
1087 u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001088
Wan Wei57a30852009-08-07 17:04:49 +02001089 for (j = 0; j < 4; j++) {
1090 if (DBAM_DIMM(j, dbam) > 0) {
1091 channels++;
1092 break;
1093 }
1094 }
Doug Thompson1afd3c92009-04-27 16:16:50 +02001095 }
1096
Borislav Petkovd16149e2009-10-16 19:55:49 +02001097 if (channels > 2)
1098 channels = 2;
1099
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001100 amd64_info("MCT channel count: %d\n", channels);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001101
1102 return channels;
Doug Thompson1afd3c92009-04-27 16:16:50 +02001103}
1104
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001105static int ddr3_cs_size(unsigned i, bool dct_width)
Doug Thompson1afd3c92009-04-27 16:16:50 +02001106{
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001107 unsigned shift = 0;
1108 int cs_size = 0;
1109
1110 if (i == 0 || i == 3 || i == 4)
1111 cs_size = -1;
1112 else if (i <= 2)
1113 shift = i;
1114 else if (i == 12)
1115 shift = 7;
1116 else if (!(i & 0x1))
1117 shift = i >> 1;
1118 else
1119 shift = (i + 1) >> 1;
1120
1121 if (cs_size != -1)
1122 cs_size = (128 * (1 << !!dct_width)) << shift;
1123
1124 return cs_size;
1125}
1126
1127static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1128 unsigned cs_mode)
1129{
1130 u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
1131
1132 WARN_ON(cs_mode > 11);
Borislav Petkov1433eb92009-10-21 13:44:36 +02001133
1134 if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001135 return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
Borislav Petkov1433eb92009-10-21 13:44:36 +02001136 else
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001137 return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
1138}
Borislav Petkov1433eb92009-10-21 13:44:36 +02001139
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001140/*
1141 * F15h supports only 64bit DCT interfaces
1142 */
1143static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1144 unsigned cs_mode)
1145{
1146 WARN_ON(cs_mode > 12);
1147
1148 return ddr3_cs_size(cs_mode, false);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001149}
1150
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001151static void read_dram_ctl_register(struct amd64_pvt *pvt)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001152{
Doug Thompson6163b5d2009-04-27 16:20:17 +02001153
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001154 if (boot_cpu_data.x86 == 0xf)
1155 return;
1156
Borislav Petkov78da1212010-12-22 19:31:45 +01001157 if (!amd64_read_dct_pci_cfg(pvt, DCT_SEL_LO, &pvt->dct_sel_lo)) {
1158 debugf0("F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
1159 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001160
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001161 debugf0(" DCTs operate in %s mode.\n",
1162 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001163
Borislav Petkov72381bd2009-10-09 19:14:43 +02001164 if (!dct_ganging_enabled(pvt))
1165 debugf0(" Address range split per DCT: %s\n",
1166 (dct_high_range_enabled(pvt) ? "yes" : "no"));
1167
Borislav Petkov78da1212010-12-22 19:31:45 +01001168 debugf0(" data interleave for ECC: %s, "
Borislav Petkov72381bd2009-10-09 19:14:43 +02001169 "DRAM cleared since last warm reset: %s\n",
1170 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
1171 (dct_memory_cleared(pvt) ? "yes" : "no"));
1172
Borislav Petkov78da1212010-12-22 19:31:45 +01001173 debugf0(" channel interleave: %s, "
1174 "interleave bits selector: 0x%x\n",
Borislav Petkov72381bd2009-10-09 19:14:43 +02001175 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
Doug Thompson6163b5d2009-04-27 16:20:17 +02001176 dct_sel_interleave_addr(pvt));
1177 }
1178
Borislav Petkov78da1212010-12-22 19:31:45 +01001179 amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001180}
1181
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001182/*
Borislav Petkov229a7a12010-12-09 18:57:54 +01001183 * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001184 * Interleaving Modes.
1185 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001186static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
Borislav Petkov229a7a12010-12-09 18:57:54 +01001187 bool hi_range_sel, u8 intlv_en)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001188{
Borislav Petkov78da1212010-12-22 19:31:45 +01001189 u32 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001190
1191 if (dct_ganging_enabled(pvt))
Borislav Petkov229a7a12010-12-09 18:57:54 +01001192 return 0;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001193
Borislav Petkov229a7a12010-12-09 18:57:54 +01001194 if (hi_range_sel)
1195 return dct_sel_high;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001196
Borislav Petkov229a7a12010-12-09 18:57:54 +01001197 /*
1198 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1199 */
1200 if (dct_interleave_enabled(pvt)) {
1201 u8 intlv_addr = dct_sel_interleave_addr(pvt);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001202
Borislav Petkov229a7a12010-12-09 18:57:54 +01001203 /* return DCT select function: 0=DCT0, 1=DCT1 */
1204 if (!intlv_addr)
1205 return sys_addr >> 6 & 1;
1206
1207 if (intlv_addr & 0x2) {
1208 u8 shift = intlv_addr & 0x1 ? 9 : 6;
1209 u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2;
1210
1211 return ((sys_addr >> shift) & 1) ^ temp;
1212 }
1213
1214 return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1215 }
1216
1217 if (dct_high_range_enabled(pvt))
1218 return ~dct_sel_high & 1;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001219
1220 return 0;
1221}
1222
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001223/* Convert the sys_addr to the normalized DCT address */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001224static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, int range,
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001225 u64 sys_addr, bool hi_rng,
1226 u32 dct_sel_base_addr)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001227{
1228 u64 chan_off;
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001229 u64 dram_base = get_dram_base(pvt, range);
1230 u64 hole_off = f10_dhar_offset(pvt);
1231 u32 hole_valid = dhar_valid(pvt);
1232 u64 dct_sel_base_off = (pvt->dct_sel_hi & 0xFFFFFC00) << 16;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001233
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001234 if (hi_rng) {
1235 /*
1236 * if
1237 * base address of high range is below 4Gb
1238 * (bits [47:27] at [31:11])
1239 * DRAM address space on this DCT is hoisted above 4Gb &&
1240 * sys_addr > 4Gb
1241 *
1242 * remove hole offset from sys_addr
1243 * else
1244 * remove high range offset from sys_addr
1245 */
1246 if ((!(dct_sel_base_addr >> 16) ||
1247 dct_sel_base_addr < dhar_base(pvt)) &&
1248 hole_valid &&
1249 (sys_addr >= BIT_64(32)))
Borislav Petkovbc21fa52010-11-11 17:29:13 +01001250 chan_off = hole_off;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001251 else
1252 chan_off = dct_sel_base_off;
1253 } else {
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001254 /*
1255 * if
1256 * we have a valid hole &&
1257 * sys_addr > 4Gb
1258 *
1259 * remove hole
1260 * else
1261 * remove dram base to normalize to DCT address
1262 */
1263 if (hole_valid && (sys_addr >= BIT_64(32)))
Borislav Petkovbc21fa52010-11-11 17:29:13 +01001264 chan_off = hole_off;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001265 else
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001266 chan_off = dram_base;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001267 }
1268
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001269 return (sys_addr & GENMASK(6,47)) - (chan_off & GENMASK(23,47));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001270}
1271
Doug Thompson6163b5d2009-04-27 16:20:17 +02001272/*
1273 * checks if the csrow passed in is marked as SPARED, if so returns the new
1274 * spare row
1275 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001276static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001277{
Borislav Petkov614ec9d2011-01-13 18:02:22 +01001278 int tmp_cs;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001279
Borislav Petkov614ec9d2011-01-13 18:02:22 +01001280 if (online_spare_swap_done(pvt, dct) &&
1281 csrow == online_spare_bad_dramcs(pvt, dct)) {
1282
1283 for_each_chip_select(tmp_cs, dct, pvt) {
1284 if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1285 csrow = tmp_cs;
1286 break;
1287 }
1288 }
Doug Thompson6163b5d2009-04-27 16:20:17 +02001289 }
1290 return csrow;
1291}
1292
1293/*
1294 * Iterate over the DRAM DCT "base" and "mask" registers looking for a
1295 * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
1296 *
1297 * Return:
1298 * -EINVAL: NOT FOUND
1299 * 0..csrow = Chip-Select Row
1300 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001301static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001302{
1303 struct mem_ctl_info *mci;
1304 struct amd64_pvt *pvt;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001305 u64 cs_base, cs_mask;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001306 int cs_found = -EINVAL;
1307 int csrow;
1308
Borislav Petkovcc4d8862010-10-13 16:11:59 +02001309 mci = mcis[nid];
Doug Thompson6163b5d2009-04-27 16:20:17 +02001310 if (!mci)
1311 return cs_found;
1312
1313 pvt = mci->pvt_info;
1314
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001315 debugf1("input addr: 0x%llx, DCT: %d\n", in_addr, dct);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001316
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001317 for_each_chip_select(csrow, dct, pvt) {
1318 if (!csrow_enabled(csrow, dct, pvt))
Doug Thompson6163b5d2009-04-27 16:20:17 +02001319 continue;
1320
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001321 get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001322
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001323 debugf1(" CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
1324 csrow, cs_base, cs_mask);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001325
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001326 cs_mask = ~cs_mask;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001327
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001328 debugf1(" (InputAddr & ~CSMask)=0x%llx "
1329 "(CSBase & ~CSMask)=0x%llx\n",
1330 (in_addr & cs_mask), (cs_base & cs_mask));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001331
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001332 if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
1333 cs_found = f10_process_possible_spare(pvt, dct, csrow);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001334
1335 debugf1(" MATCH csrow=%d\n", cs_found);
1336 break;
1337 }
1338 }
1339 return cs_found;
1340}
1341
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001342/*
1343 * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
1344 * swapped with a region located at the bottom of memory so that the GPU can use
1345 * the interleaved region and thus two channels.
1346 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001347static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001348{
1349 u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
1350
1351 if (boot_cpu_data.x86 == 0x10) {
1352 /* only revC3 and revE have that feature */
1353 if (boot_cpu_data.x86_model < 4 ||
1354 (boot_cpu_data.x86_model < 0xa &&
1355 boot_cpu_data.x86_mask < 3))
1356 return sys_addr;
1357 }
1358
1359 amd64_read_dct_pci_cfg(pvt, SWAP_INTLV_REG, &swap_reg);
1360
1361 if (!(swap_reg & 0x1))
1362 return sys_addr;
1363
1364 swap_base = (swap_reg >> 3) & 0x7f;
1365 swap_limit = (swap_reg >> 11) & 0x7f;
1366 rgn_size = (swap_reg >> 20) & 0x7f;
1367 tmp_addr = sys_addr >> 27;
1368
1369 if (!(sys_addr >> 34) &&
1370 (((tmp_addr >= swap_base) &&
1371 (tmp_addr <= swap_limit)) ||
1372 (tmp_addr < rgn_size)))
1373 return sys_addr ^ (u64)swap_base << 27;
1374
1375 return sys_addr;
1376}
1377
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001378/* For a given @dram_range, check if @sys_addr falls within it. */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001379static int f1x_match_to_this_node(struct amd64_pvt *pvt, int range,
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001380 u64 sys_addr, int *nid, int *chan_sel)
1381{
Borislav Petkov229a7a12010-12-09 18:57:54 +01001382 int cs_found = -EINVAL;
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001383 u64 chan_addr;
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001384 u32 dct_sel_base;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001385 u8 channel;
Borislav Petkov229a7a12010-12-09 18:57:54 +01001386 bool high_range = false;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001387
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001388 u8 node_id = dram_dst_node(pvt, range);
Borislav Petkov229a7a12010-12-09 18:57:54 +01001389 u8 intlv_en = dram_intlv_en(pvt, range);
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001390 u32 intlv_sel = dram_intlv_sel(pvt, range);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001391
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001392 debugf1("(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1393 range, sys_addr, get_dram_limit(pvt, range));
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001394
Borislav Petkov355fba62011-01-17 13:03:26 +01001395 if (dhar_valid(pvt) &&
1396 dhar_base(pvt) <= sys_addr &&
1397 sys_addr < BIT_64(32)) {
1398 amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1399 sys_addr);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001400 return -EINVAL;
Borislav Petkov355fba62011-01-17 13:03:26 +01001401 }
1402
1403 if (intlv_en &&
1404 (intlv_sel != ((sys_addr >> 12) & intlv_en))) {
1405 amd64_warn("Botched intlv bits, en: 0x%x, sel: 0x%x\n",
1406 intlv_en, intlv_sel);
1407 return -EINVAL;
1408 }
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001409
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001410 sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001411
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001412 dct_sel_base = dct_sel_baseaddr(pvt);
1413
1414 /*
1415 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1416 * select between DCT0 and DCT1.
1417 */
1418 if (dct_high_range_enabled(pvt) &&
1419 !dct_ganging_enabled(pvt) &&
1420 ((sys_addr >> 27) >= (dct_sel_base >> 11)))
Borislav Petkov229a7a12010-12-09 18:57:54 +01001421 high_range = true;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001422
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001423 channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001424
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001425 chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001426 high_range, dct_sel_base);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001427
Borislav Petkove2f79db2011-01-13 14:57:34 +01001428 /* Remove node interleaving, see F1x120 */
1429 if (intlv_en)
1430 chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1431 (chan_addr & 0xfff);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001432
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001433 /* remove channel interleave */
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001434 if (dct_interleave_enabled(pvt) &&
1435 !dct_high_range_enabled(pvt) &&
1436 !dct_ganging_enabled(pvt)) {
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001437
1438 if (dct_sel_interleave_addr(pvt) != 1) {
1439 if (dct_sel_interleave_addr(pvt) == 0x3)
1440 /* hash 9 */
1441 chan_addr = ((chan_addr >> 10) << 9) |
1442 (chan_addr & 0x1ff);
1443 else
1444 /* A[6] or hash 6 */
1445 chan_addr = ((chan_addr >> 7) << 6) |
1446 (chan_addr & 0x3f);
1447 } else
1448 /* A[12] */
1449 chan_addr = ((chan_addr >> 13) << 12) |
1450 (chan_addr & 0xfff);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001451 }
1452
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001453 debugf1(" Normalized DCT addr: 0x%llx\n", chan_addr);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001454
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001455 cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001456
1457 if (cs_found >= 0) {
1458 *nid = node_id;
1459 *chan_sel = channel;
1460 }
1461 return cs_found;
1462}
1463
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001464static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001465 int *node, int *chan_sel)
1466{
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001467 int range, cs_found = -EINVAL;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001468
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001469 for (range = 0; range < DRAM_RANGES; range++) {
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001470
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001471 if (!dram_rw(pvt, range))
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001472 continue;
1473
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001474 if ((get_dram_base(pvt, range) <= sys_addr) &&
1475 (get_dram_limit(pvt, range) >= sys_addr)) {
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001476
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001477 cs_found = f1x_match_to_this_node(pvt, range,
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001478 sys_addr, node,
1479 chan_sel);
1480 if (cs_found >= 0)
1481 break;
1482 }
1483 }
1484 return cs_found;
1485}
1486
1487/*
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001488 * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1489 * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001490 *
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001491 * The @sys_addr is usually an error address received from the hardware
1492 * (MCX_ADDR).
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001493 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001494static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001495 u16 syndrome)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001496{
1497 struct amd64_pvt *pvt = mci->pvt_info;
1498 u32 page, offset;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001499 int nid, csrow, chan = 0;
1500
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001501 csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001502
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001503 if (csrow < 0) {
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001504 edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001505 return;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001506 }
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001507
1508 error_address_to_page_and_offset(sys_addr, &page, &offset);
1509
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001510 /*
1511 * We need the syndromes for channel detection only when we're
1512 * ganged. Otherwise @chan should already contain the channel at
1513 * this point.
1514 */
Borislav Petkova97fa682010-12-23 14:07:18 +01001515 if (dct_ganging_enabled(pvt))
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001516 chan = get_channel_from_ecc_syndrome(mci, syndrome);
1517
1518 if (chan >= 0)
1519 edac_mc_handle_ce(mci, page, offset, syndrome, csrow, chan,
1520 EDAC_MOD_STR);
1521 else
1522 /*
1523 * Channel unknown, report all channels on this CSROW as failed.
1524 */
1525 for (chan = 0; chan < mci->csrows[csrow].nr_channels; chan++)
1526 edac_mc_handle_ce(mci, page, offset, syndrome,
1527 csrow, chan, EDAC_MOD_STR);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001528}
1529
1530/*
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001531 * debug routine to display the memory sizes of all logical DIMMs and its
Borislav Petkovcb328502010-12-22 14:28:24 +01001532 * CSROWs
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001533 */
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001534static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001535{
Borislav Petkov603adaf2009-12-21 14:52:53 +01001536 int dimm, size0, size1, factor = 0;
Borislav Petkov525a1b22010-12-21 15:53:27 +01001537 u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
1538 u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001539
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001540 if (boot_cpu_data.x86 == 0xf) {
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001541 if (pvt->dclr0 & WIDTH_128)
Borislav Petkov603adaf2009-12-21 14:52:53 +01001542 factor = 1;
1543
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001544 /* K8 families < revF not supported yet */
Borislav Petkov1433eb92009-10-21 13:44:36 +02001545 if (pvt->ext_model < K8_REV_F)
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001546 return;
1547 else
1548 WARN_ON(ctrl != 0);
1549 }
1550
Borislav Petkov4d796362011-02-03 15:59:57 +01001551 dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 : pvt->dbam0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001552 dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->csels[1].csbases
1553 : pvt->csels[0].csbases;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001554
Borislav Petkov4d796362011-02-03 15:59:57 +01001555 debugf1("F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", ctrl, dbam);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001556
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001557 edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
1558
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001559 /* Dump memory sizes for DIMM and its CSROWs */
1560 for (dimm = 0; dimm < 4; dimm++) {
1561
1562 size0 = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001563 if (dcsb[dimm*2] & DCSB_CS_ENABLE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001564 size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
1565 DBAM_DIMM(dimm, dbam));
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001566
1567 size1 = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001568 if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001569 size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
1570 DBAM_DIMM(dimm, dbam));
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001571
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001572 amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1573 dimm * 2, size0 << factor,
1574 dimm * 2 + 1, size1 << factor);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001575 }
1576}
1577
Doug Thompson4d376072009-04-27 16:25:05 +02001578static struct amd64_family_type amd64_family_types[] = {
1579 [K8_CPUS] = {
Borislav Petkov0092b202010-10-01 19:20:05 +02001580 .ctl_name = "K8",
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001581 .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
1582 .f3_id = PCI_DEVICE_ID_AMD_K8_NB_MISC,
Doug Thompson4d376072009-04-27 16:25:05 +02001583 .ops = {
Borislav Petkov1433eb92009-10-21 13:44:36 +02001584 .early_channel_count = k8_early_channel_count,
Borislav Petkov1433eb92009-10-21 13:44:36 +02001585 .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow,
1586 .dbam_to_cs = k8_dbam_to_chip_select,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001587 .read_dct_pci_cfg = k8_read_dct_pci_cfg,
Doug Thompson4d376072009-04-27 16:25:05 +02001588 }
1589 },
1590 [F10_CPUS] = {
Borislav Petkov0092b202010-10-01 19:20:05 +02001591 .ctl_name = "F10h",
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001592 .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
1593 .f3_id = PCI_DEVICE_ID_AMD_10H_NB_MISC,
Doug Thompson4d376072009-04-27 16:25:05 +02001594 .ops = {
Borislav Petkov7d20d142011-01-07 17:58:04 +01001595 .early_channel_count = f1x_early_channel_count,
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001596 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
Borislav Petkov1433eb92009-10-21 13:44:36 +02001597 .dbam_to_cs = f10_dbam_to_chip_select,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001598 .read_dct_pci_cfg = f10_read_dct_pci_cfg,
1599 }
1600 },
1601 [F15_CPUS] = {
1602 .ctl_name = "F15h",
1603 .ops = {
Borislav Petkov7d20d142011-01-07 17:58:04 +01001604 .early_channel_count = f1x_early_channel_count,
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001605 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001606 .dbam_to_cs = f15_dbam_to_chip_select,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001607 .read_dct_pci_cfg = f15_read_dct_pci_cfg,
Doug Thompson4d376072009-04-27 16:25:05 +02001608 }
1609 },
Doug Thompson4d376072009-04-27 16:25:05 +02001610};
1611
1612static struct pci_dev *pci_get_related_function(unsigned int vendor,
1613 unsigned int device,
1614 struct pci_dev *related)
1615{
1616 struct pci_dev *dev = NULL;
1617
1618 dev = pci_get_device(vendor, device, dev);
1619 while (dev) {
1620 if ((dev->bus->number == related->bus->number) &&
1621 (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
1622 break;
1623 dev = pci_get_device(vendor, device, dev);
1624 }
1625
1626 return dev;
1627}
1628
Doug Thompsonb1289d62009-04-27 16:37:05 +02001629/*
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001630 * These are tables of eigenvectors (one per line) which can be used for the
1631 * construction of the syndrome tables. The modified syndrome search algorithm
1632 * uses those to find the symbol in error and thus the DIMM.
Doug Thompsonb1289d62009-04-27 16:37:05 +02001633 *
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001634 * Algorithm courtesy of Ross LaFetra from AMD.
Doug Thompsonb1289d62009-04-27 16:37:05 +02001635 */
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001636static u16 x4_vectors[] = {
1637 0x2f57, 0x1afe, 0x66cc, 0xdd88,
1638 0x11eb, 0x3396, 0x7f4c, 0xeac8,
1639 0x0001, 0x0002, 0x0004, 0x0008,
1640 0x1013, 0x3032, 0x4044, 0x8088,
1641 0x106b, 0x30d6, 0x70fc, 0xe0a8,
1642 0x4857, 0xc4fe, 0x13cc, 0x3288,
1643 0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
1644 0x1f39, 0x251e, 0xbd6c, 0x6bd8,
1645 0x15c1, 0x2a42, 0x89ac, 0x4758,
1646 0x2b03, 0x1602, 0x4f0c, 0xca08,
1647 0x1f07, 0x3a0e, 0x6b04, 0xbd08,
1648 0x8ba7, 0x465e, 0x244c, 0x1cc8,
1649 0x2b87, 0x164e, 0x642c, 0xdc18,
1650 0x40b9, 0x80de, 0x1094, 0x20e8,
1651 0x27db, 0x1eb6, 0x9dac, 0x7b58,
1652 0x11c1, 0x2242, 0x84ac, 0x4c58,
1653 0x1be5, 0x2d7a, 0x5e34, 0xa718,
1654 0x4b39, 0x8d1e, 0x14b4, 0x28d8,
1655 0x4c97, 0xc87e, 0x11fc, 0x33a8,
1656 0x8e97, 0x497e, 0x2ffc, 0x1aa8,
1657 0x16b3, 0x3d62, 0x4f34, 0x8518,
1658 0x1e2f, 0x391a, 0x5cac, 0xf858,
1659 0x1d9f, 0x3b7a, 0x572c, 0xfe18,
1660 0x15f5, 0x2a5a, 0x5264, 0xa3b8,
1661 0x1dbb, 0x3b66, 0x715c, 0xe3f8,
1662 0x4397, 0xc27e, 0x17fc, 0x3ea8,
1663 0x1617, 0x3d3e, 0x6464, 0xb8b8,
1664 0x23ff, 0x12aa, 0xab6c, 0x56d8,
1665 0x2dfb, 0x1ba6, 0x913c, 0x7328,
1666 0x185d, 0x2ca6, 0x7914, 0x9e28,
1667 0x171b, 0x3e36, 0x7d7c, 0xebe8,
1668 0x4199, 0x82ee, 0x19f4, 0x2e58,
1669 0x4807, 0xc40e, 0x130c, 0x3208,
1670 0x1905, 0x2e0a, 0x5804, 0xac08,
1671 0x213f, 0x132a, 0xadfc, 0x5ba8,
1672 0x19a9, 0x2efe, 0xb5cc, 0x6f88,
Doug Thompsonb1289d62009-04-27 16:37:05 +02001673};
1674
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001675static u16 x8_vectors[] = {
1676 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
1677 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
1678 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
1679 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
1680 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
1681 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
1682 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
1683 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
1684 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
1685 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
1686 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
1687 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
1688 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
1689 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
1690 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
1691 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
1692 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
1693 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
1694 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
1695};
1696
1697static int decode_syndrome(u16 syndrome, u16 *vectors, int num_vecs,
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001698 int v_dim)
Doug Thompsonb1289d62009-04-27 16:37:05 +02001699{
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001700 unsigned int i, err_sym;
Doug Thompsonb1289d62009-04-27 16:37:05 +02001701
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001702 for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
1703 u16 s = syndrome;
1704 int v_idx = err_sym * v_dim;
1705 int v_end = (err_sym + 1) * v_dim;
Doug Thompsonb1289d62009-04-27 16:37:05 +02001706
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001707 /* walk over all 16 bits of the syndrome */
1708 for (i = 1; i < (1U << 16); i <<= 1) {
1709
1710 /* if bit is set in that eigenvector... */
1711 if (v_idx < v_end && vectors[v_idx] & i) {
1712 u16 ev_comp = vectors[v_idx++];
1713
1714 /* ... and bit set in the modified syndrome, */
1715 if (s & i) {
1716 /* remove it. */
1717 s ^= ev_comp;
1718
1719 if (!s)
1720 return err_sym;
1721 }
1722
1723 } else if (s & i)
1724 /* can't get to zero, move to next symbol */
1725 break;
1726 }
Doug Thompsonb1289d62009-04-27 16:37:05 +02001727 }
1728
1729 debugf0("syndrome(%x) not found\n", syndrome);
1730 return -1;
1731}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001732
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001733static int map_err_sym_to_channel(int err_sym, int sym_size)
1734{
1735 if (sym_size == 4)
1736 switch (err_sym) {
1737 case 0x20:
1738 case 0x21:
1739 return 0;
1740 break;
1741 case 0x22:
1742 case 0x23:
1743 return 1;
1744 break;
1745 default:
1746 return err_sym >> 4;
1747 break;
1748 }
1749 /* x8 symbols */
1750 else
1751 switch (err_sym) {
1752 /* imaginary bits not in a DIMM */
1753 case 0x10:
1754 WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
1755 err_sym);
1756 return -1;
1757 break;
1758
1759 case 0x11:
1760 return 0;
1761 break;
1762 case 0x12:
1763 return 1;
1764 break;
1765 default:
1766 return err_sym >> 3;
1767 break;
1768 }
1769 return -1;
1770}
1771
1772static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
1773{
1774 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001775 int err_sym = -1;
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001776
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001777 if (pvt->syn_type == 8)
1778 err_sym = decode_syndrome(syndrome, x8_vectors,
1779 ARRAY_SIZE(x8_vectors),
1780 pvt->syn_type);
1781 else if (pvt->syn_type == 4)
1782 err_sym = decode_syndrome(syndrome, x4_vectors,
1783 ARRAY_SIZE(x4_vectors),
1784 pvt->syn_type);
1785 else {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001786 amd64_warn("Illegal syndrome type: %u\n", pvt->syn_type);
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001787 return err_sym;
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001788 }
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001789
1790 return map_err_sym_to_channel(err_sym, pvt->syn_type);
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001791}
1792
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001793/*
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001794 * Handle any Correctable Errors (CEs) that have occurred. Check for valid ERROR
1795 * ADDRESS and process.
1796 */
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001797static void amd64_handle_ce(struct mem_ctl_info *mci, struct mce *m)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001798{
1799 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001800 u64 sys_addr;
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001801 u16 syndrome;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001802
1803 /* Ensure that the Error Address is VALID */
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001804 if (!(m->status & MCI_STATUS_ADDRV)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001805 amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001806 edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1807 return;
1808 }
1809
Borislav Petkov70046622011-01-10 14:37:27 +01001810 sys_addr = get_error_address(m);
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001811 syndrome = extract_syndrome(m->status);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001812
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001813 amd64_mc_err(mci, "CE ERROR_ADDRESS= 0x%llx\n", sys_addr);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001814
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001815 pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, syndrome);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001816}
1817
1818/* Handle any Un-correctable Errors (UEs) */
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001819static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001820{
Borislav Petkov1f6bcee2009-11-13 14:02:57 +01001821 struct mem_ctl_info *log_mci, *src_mci = NULL;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001822 int csrow;
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001823 u64 sys_addr;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001824 u32 page, offset;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001825
1826 log_mci = mci;
1827
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001828 if (!(m->status & MCI_STATUS_ADDRV)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001829 amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001830 edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
1831 return;
1832 }
1833
Borislav Petkov70046622011-01-10 14:37:27 +01001834 sys_addr = get_error_address(m);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001835
1836 /*
1837 * Find out which node the error address belongs to. This may be
1838 * different from the node that detected the error.
1839 */
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001840 src_mci = find_mc_by_sys_addr(mci, sys_addr);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001841 if (!src_mci) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001842 amd64_mc_err(mci, "ERROR ADDRESS (0x%lx) NOT mapped to a MC\n",
1843 (unsigned long)sys_addr);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001844 edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
1845 return;
1846 }
1847
1848 log_mci = src_mci;
1849
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001850 csrow = sys_addr_to_csrow(log_mci, sys_addr);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001851 if (csrow < 0) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001852 amd64_mc_err(mci, "ERROR_ADDRESS (0x%lx) NOT mapped to CS\n",
1853 (unsigned long)sys_addr);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001854 edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
1855 } else {
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001856 error_address_to_page_and_offset(sys_addr, &page, &offset);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001857 edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);
1858 }
1859}
1860
Borislav Petkov549d0422009-07-24 13:51:42 +02001861static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001862 struct mce *m)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001863{
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001864 u16 ec = EC(m->status);
1865 u8 xec = XEC(m->status, 0x1f);
1866 u8 ecc_type = (m->status >> 45) & 0x3;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001867
Borislav Petkovb70ef012009-06-25 19:32:38 +02001868 /* Bail early out if this was an 'observed' error */
Borislav Petkov5980bb92011-01-07 16:26:49 +01001869 if (PP(ec) == NBSL_PP_OBS)
Borislav Petkovb70ef012009-06-25 19:32:38 +02001870 return;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001871
Borislav Petkovecaf5602009-07-23 16:32:01 +02001872 /* Do only ECC errors */
1873 if (xec && xec != F10_NBSL_EXT_ERR_ECC)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001874 return;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001875
Borislav Petkovecaf5602009-07-23 16:32:01 +02001876 if (ecc_type == 2)
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001877 amd64_handle_ce(mci, m);
Borislav Petkovecaf5602009-07-23 16:32:01 +02001878 else if (ecc_type == 1)
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001879 amd64_handle_ue(mci, m);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001880}
1881
Borislav Petkov7cfd4a82010-09-01 14:45:20 +02001882void amd64_decode_bus_error(int node_id, struct mce *m, u32 nbcfg)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001883{
Borislav Petkovcc4d8862010-10-13 16:11:59 +02001884 struct mem_ctl_info *mci = mcis[node_id];
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001885
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001886 __amd64_decode_bus_error(mci, m);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001887}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001888
Doug Thompson0ec449e2009-04-27 19:41:25 +02001889/*
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001890 * Use pvt->F2 which contains the F2 CPU PCI device to get the related
Borislav Petkovbbd0c1f2010-10-01 19:27:58 +02001891 * F1 (AddrMap) and F3 (Misc) devices. Return negative value on error.
Doug Thompson0ec449e2009-04-27 19:41:25 +02001892 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02001893static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f3_id)
Doug Thompson0ec449e2009-04-27 19:41:25 +02001894{
Doug Thompson0ec449e2009-04-27 19:41:25 +02001895 /* Reserve the ADDRESS MAP Device */
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001896 pvt->F1 = pci_get_related_function(pvt->F2->vendor, f1_id, pvt->F2);
1897 if (!pvt->F1) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001898 amd64_err("error address map device not found: "
1899 "vendor %x device 0x%x (broken BIOS?)\n",
1900 PCI_VENDOR_ID_AMD, f1_id);
Borislav Petkovbbd0c1f2010-10-01 19:27:58 +02001901 return -ENODEV;
Doug Thompson0ec449e2009-04-27 19:41:25 +02001902 }
1903
1904 /* Reserve the MISC Device */
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001905 pvt->F3 = pci_get_related_function(pvt->F2->vendor, f3_id, pvt->F2);
1906 if (!pvt->F3) {
1907 pci_dev_put(pvt->F1);
1908 pvt->F1 = NULL;
Doug Thompson0ec449e2009-04-27 19:41:25 +02001909
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001910 amd64_err("error F3 device not found: "
1911 "vendor %x device 0x%x (broken BIOS?)\n",
1912 PCI_VENDOR_ID_AMD, f3_id);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001913
Borislav Petkovbbd0c1f2010-10-01 19:27:58 +02001914 return -ENODEV;
Doug Thompson0ec449e2009-04-27 19:41:25 +02001915 }
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001916 debugf1("F1: %s\n", pci_name(pvt->F1));
1917 debugf1("F2: %s\n", pci_name(pvt->F2));
1918 debugf1("F3: %s\n", pci_name(pvt->F3));
Doug Thompson0ec449e2009-04-27 19:41:25 +02001919
1920 return 0;
1921}
1922
Borislav Petkov360b7f32010-10-15 19:25:38 +02001923static void free_mc_sibling_devs(struct amd64_pvt *pvt)
Doug Thompson0ec449e2009-04-27 19:41:25 +02001924{
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001925 pci_dev_put(pvt->F1);
1926 pci_dev_put(pvt->F3);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001927}
1928
1929/*
1930 * Retrieve the hardware registers of the memory controller (this includes the
1931 * 'Address Map' and 'Misc' device regs)
1932 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02001933static void read_mc_regs(struct amd64_pvt *pvt)
Doug Thompson0ec449e2009-04-27 19:41:25 +02001934{
1935 u64 msr_val;
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001936 u32 tmp;
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001937 int range;
Doug Thompson0ec449e2009-04-27 19:41:25 +02001938
1939 /*
1940 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
1941 * those are Read-As-Zero
1942 */
Borislav Petkove97f8bb2009-10-12 15:27:45 +02001943 rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
1944 debugf0(" TOP_MEM: 0x%016llx\n", pvt->top_mem);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001945
1946 /* check first whether TOP_MEM2 is enabled */
1947 rdmsrl(MSR_K8_SYSCFG, msr_val);
1948 if (msr_val & (1U << 21)) {
Borislav Petkove97f8bb2009-10-12 15:27:45 +02001949 rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
1950 debugf0(" TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001951 } else
1952 debugf0(" TOP_MEM2 disabled.\n");
1953
Borislav Petkov5980bb92011-01-07 16:26:49 +01001954 amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001955
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001956 read_dram_ctl_register(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001957
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001958 for (range = 0; range < DRAM_RANGES; range++) {
1959 u8 rw;
Doug Thompson0ec449e2009-04-27 19:41:25 +02001960
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001961 /* read settings for this DRAM range */
1962 read_dram_base_limit_regs(pvt, range);
Borislav Petkove97f8bb2009-10-12 15:27:45 +02001963
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001964 rw = dram_rw(pvt, range);
1965 if (!rw)
1966 continue;
1967
1968 debugf1(" DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
1969 range,
1970 get_dram_base(pvt, range),
1971 get_dram_limit(pvt, range));
1972
1973 debugf1(" IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
1974 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
1975 (rw & 0x1) ? "R" : "-",
1976 (rw & 0x2) ? "W" : "-",
1977 dram_intlv_sel(pvt, range),
1978 dram_dst_node(pvt, range));
Doug Thompson0ec449e2009-04-27 19:41:25 +02001979 }
1980
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001981 read_dct_base_mask(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001982
Borislav Petkovbc21fa52010-11-11 17:29:13 +01001983 amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
Borislav Petkov525a1b22010-12-21 15:53:27 +01001984 amd64_read_dct_pci_cfg(pvt, DBAM0, &pvt->dbam0);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001985
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001986 amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001987
Borislav Petkovcb328502010-12-22 14:28:24 +01001988 amd64_read_dct_pci_cfg(pvt, DCLR0, &pvt->dclr0);
1989 amd64_read_dct_pci_cfg(pvt, DCHR0, &pvt->dchr0);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001990
Borislav Petkov78da1212010-12-22 19:31:45 +01001991 if (!dct_ganging_enabled(pvt)) {
Borislav Petkovcb328502010-12-22 14:28:24 +01001992 amd64_read_dct_pci_cfg(pvt, DCLR1, &pvt->dclr1);
1993 amd64_read_dct_pci_cfg(pvt, DCHR1, &pvt->dchr1);
Doug Thompson0ec449e2009-04-27 19:41:25 +02001994 }
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001995
Borislav Petkov525a1b22010-12-21 15:53:27 +01001996 if (boot_cpu_data.x86 >= 0x10) {
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001997 amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
Borislav Petkov525a1b22010-12-21 15:53:27 +01001998 amd64_read_dct_pci_cfg(pvt, DBAM1, &pvt->dbam1);
1999 }
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002000
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002001 if (boot_cpu_data.x86 == 0x10 &&
2002 boot_cpu_data.x86_model > 7 &&
2003 /* F3x180[EccSymbolSize]=1 => x8 symbols */
2004 tmp & BIT(25))
2005 pvt->syn_type = 8;
2006 else
2007 pvt->syn_type = 4;
2008
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002009 dump_misc_regs(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002010}
2011
2012/*
2013 * NOTE: CPU Revision Dependent code
2014 *
2015 * Input:
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002016 * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002017 * k8 private pointer to -->
2018 * DRAM Bank Address mapping register
2019 * node_id
2020 * DCL register where dual_channel_active is
2021 *
2022 * The DBAM register consists of 4 sets of 4 bits each definitions:
2023 *
2024 * Bits: CSROWs
2025 * 0-3 CSROWs 0 and 1
2026 * 4-7 CSROWs 2 and 3
2027 * 8-11 CSROWs 4 and 5
2028 * 12-15 CSROWs 6 and 7
2029 *
2030 * Values range from: 0 to 15
2031 * The meaning of the values depends on CPU revision and dual-channel state,
2032 * see relevant BKDG more info.
2033 *
2034 * The memory controller provides for total of only 8 CSROWs in its current
2035 * architecture. Each "pair" of CSROWs normally represents just one DIMM in
2036 * single channel or two (2) DIMMs in dual channel mode.
2037 *
2038 * The following code logic collapses the various tables for CSROW based on CPU
2039 * revision.
2040 *
2041 * Returns:
2042 * The number of PAGE_SIZE pages on the specified CSROW number it
2043 * encompasses
2044 *
2045 */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01002046static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002047{
Borislav Petkov1433eb92009-10-21 13:44:36 +02002048 u32 cs_mode, nr_pages;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002049
2050 /*
2051 * The math on this doesn't look right on the surface because x/2*4 can
2052 * be simplified to x*2 but this expression makes use of the fact that
2053 * it is integral math where 1/2=0. This intermediate value becomes the
2054 * number of bits to shift the DBAM register to extract the proper CSROW
2055 * field.
2056 */
Borislav Petkov1433eb92009-10-21 13:44:36 +02002057 cs_mode = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002058
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01002059 nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002060
2061 /*
2062 * If dual channel then double the memory size of single channel.
2063 * Channel count is 1 or 2
2064 */
2065 nr_pages <<= (pvt->channel_count - 1);
2066
Borislav Petkov1433eb92009-10-21 13:44:36 +02002067 debugf0(" (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002068 debugf0(" nr_pages= %u channel-count = %d\n",
2069 nr_pages, pvt->channel_count);
2070
2071 return nr_pages;
2072}
2073
2074/*
2075 * Initialize the array of csrow attribute instances, based on the values
2076 * from pci config hardware registers.
2077 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002078static int init_csrows(struct mem_ctl_info *mci)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002079{
2080 struct csrow_info *csrow;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002081 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002082 u64 input_addr_min, input_addr_max, sys_addr, base, mask;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002083 u32 val;
Borislav Petkov6ba5dcd2009-10-13 19:26:55 +02002084 int i, empty = 1;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002085
Borislav Petkova97fa682010-12-23 14:07:18 +01002086 amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002087
Borislav Petkov2299ef72010-10-15 17:44:04 +02002088 pvt->nbcfg = val;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002089
Borislav Petkov2299ef72010-10-15 17:44:04 +02002090 debugf0("node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
2091 pvt->mc_node_id, val,
Borislav Petkova97fa682010-12-23 14:07:18 +01002092 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002093
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002094 for_each_chip_select(i, 0, pvt) {
Doug Thompson0ec449e2009-04-27 19:41:25 +02002095 csrow = &mci->csrows[i];
2096
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002097 if (!csrow_enabled(i, 0, pvt)) {
Doug Thompson0ec449e2009-04-27 19:41:25 +02002098 debugf1("----CSROW %d EMPTY for node %d\n", i,
2099 pvt->mc_node_id);
2100 continue;
2101 }
2102
2103 debugf1("----CSROW %d VALID for MC node %d\n",
2104 i, pvt->mc_node_id);
2105
2106 empty = 0;
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01002107 csrow->nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002108 find_csrow_limits(mci, i, &input_addr_min, &input_addr_max);
2109 sys_addr = input_addr_to_sys_addr(mci, input_addr_min);
2110 csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT);
2111 sys_addr = input_addr_to_sys_addr(mci, input_addr_max);
2112 csrow->last_page = (u32) (sys_addr >> PAGE_SHIFT);
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002113
2114 get_cs_base_and_mask(pvt, i, 0, &base, &mask);
2115 csrow->page_mask = ~mask;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002116 /* 8 bytes of resolution */
2117
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002118 csrow->mtype = amd64_determine_memory_type(pvt, i);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002119
2120 debugf1(" for MC node %d csrow %d:\n", pvt->mc_node_id, i);
2121 debugf1(" input_addr_min: 0x%lx input_addr_max: 0x%lx\n",
2122 (unsigned long)input_addr_min,
2123 (unsigned long)input_addr_max);
2124 debugf1(" sys_addr: 0x%lx page_mask: 0x%lx\n",
2125 (unsigned long)sys_addr, csrow->page_mask);
2126 debugf1(" nr_pages: %u first_page: 0x%lx "
2127 "last_page: 0x%lx\n",
2128 (unsigned)csrow->nr_pages,
2129 csrow->first_page, csrow->last_page);
2130
2131 /*
2132 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
2133 */
Borislav Petkova97fa682010-12-23 14:07:18 +01002134 if (pvt->nbcfg & NBCFG_ECC_ENABLE)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002135 csrow->edac_mode =
Borislav Petkova97fa682010-12-23 14:07:18 +01002136 (pvt->nbcfg & NBCFG_CHIPKILL) ?
Doug Thompson0ec449e2009-04-27 19:41:25 +02002137 EDAC_S4ECD4ED : EDAC_SECDED;
2138 else
2139 csrow->edac_mode = EDAC_NONE;
2140 }
2141
2142 return empty;
2143}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002144
Borislav Petkov06724532009-09-16 13:05:46 +02002145/* get all cores on this DCT */
Rusty Russellba578cb2009-11-03 14:56:35 +10302146static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, int nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02002147{
Borislav Petkov06724532009-09-16 13:05:46 +02002148 int cpu;
Doug Thompsonf9431992009-04-27 19:46:08 +02002149
Borislav Petkov06724532009-09-16 13:05:46 +02002150 for_each_online_cpu(cpu)
2151 if (amd_get_nb_id(cpu) == nid)
2152 cpumask_set_cpu(cpu, mask);
Doug Thompsonf9431992009-04-27 19:46:08 +02002153}
2154
2155/* check MCG_CTL on all the cpus on this node */
Borislav Petkov06724532009-09-16 13:05:46 +02002156static bool amd64_nb_mce_bank_enabled_on_node(int nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02002157{
Rusty Russellba578cb2009-11-03 14:56:35 +10302158 cpumask_var_t mask;
Borislav Petkov50542252009-12-11 18:14:40 +01002159 int cpu, nbe;
Borislav Petkov06724532009-09-16 13:05:46 +02002160 bool ret = false;
Doug Thompsonf9431992009-04-27 19:46:08 +02002161
Rusty Russellba578cb2009-11-03 14:56:35 +10302162 if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002163 amd64_warn("%s: Error allocating mask\n", __func__);
Rusty Russellba578cb2009-11-03 14:56:35 +10302164 return false;
2165 }
Borislav Petkov06724532009-09-16 13:05:46 +02002166
Rusty Russellba578cb2009-11-03 14:56:35 +10302167 get_cpus_on_this_dct_cpumask(mask, nid);
Borislav Petkov06724532009-09-16 13:05:46 +02002168
Rusty Russellba578cb2009-11-03 14:56:35 +10302169 rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
Borislav Petkov06724532009-09-16 13:05:46 +02002170
Rusty Russellba578cb2009-11-03 14:56:35 +10302171 for_each_cpu(cpu, mask) {
Borislav Petkov50542252009-12-11 18:14:40 +01002172 struct msr *reg = per_cpu_ptr(msrs, cpu);
Borislav Petkov5980bb92011-01-07 16:26:49 +01002173 nbe = reg->l & MSR_MCGCTL_NBE;
Borislav Petkov06724532009-09-16 13:05:46 +02002174
2175 debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
Borislav Petkov50542252009-12-11 18:14:40 +01002176 cpu, reg->q,
Borislav Petkov06724532009-09-16 13:05:46 +02002177 (nbe ? "enabled" : "disabled"));
2178
2179 if (!nbe)
2180 goto out;
Borislav Petkov06724532009-09-16 13:05:46 +02002181 }
2182 ret = true;
2183
2184out:
Rusty Russellba578cb2009-11-03 14:56:35 +10302185 free_cpumask_var(mask);
Doug Thompsonf9431992009-04-27 19:46:08 +02002186 return ret;
2187}
2188
Borislav Petkov2299ef72010-10-15 17:44:04 +02002189static int toggle_ecc_err_reporting(struct ecc_settings *s, u8 nid, bool on)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002190{
2191 cpumask_var_t cmask;
Borislav Petkov50542252009-12-11 18:14:40 +01002192 int cpu;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002193
2194 if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002195 amd64_warn("%s: error allocating mask\n", __func__);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002196 return false;
2197 }
2198
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002199 get_cpus_on_this_dct_cpumask(cmask, nid);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002200
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002201 rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2202
2203 for_each_cpu(cpu, cmask) {
2204
Borislav Petkov50542252009-12-11 18:14:40 +01002205 struct msr *reg = per_cpu_ptr(msrs, cpu);
2206
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002207 if (on) {
Borislav Petkov5980bb92011-01-07 16:26:49 +01002208 if (reg->l & MSR_MCGCTL_NBE)
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002209 s->flags.nb_mce_enable = 1;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002210
Borislav Petkov5980bb92011-01-07 16:26:49 +01002211 reg->l |= MSR_MCGCTL_NBE;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002212 } else {
2213 /*
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002214 * Turn off NB MCE reporting only when it was off before
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002215 */
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002216 if (!s->flags.nb_mce_enable)
Borislav Petkov5980bb92011-01-07 16:26:49 +01002217 reg->l &= ~MSR_MCGCTL_NBE;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002218 }
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002219 }
2220 wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2221
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002222 free_cpumask_var(cmask);
2223
2224 return 0;
2225}
2226
Borislav Petkov2299ef72010-10-15 17:44:04 +02002227static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid,
2228 struct pci_dev *F3)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002229{
Borislav Petkov2299ef72010-10-15 17:44:04 +02002230 bool ret = true;
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002231 u32 value, mask = 0x3; /* UECC/CECC enable */
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002232
Borislav Petkov2299ef72010-10-15 17:44:04 +02002233 if (toggle_ecc_err_reporting(s, nid, ON)) {
2234 amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
2235 return false;
2236 }
2237
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002238 amd64_read_pci_cfg(F3, NBCTL, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002239
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002240 s->old_nbctl = value & mask;
2241 s->nbctl_valid = true;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002242
2243 value |= mask;
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002244 amd64_write_pci_cfg(F3, NBCTL, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002245
Borislav Petkova97fa682010-12-23 14:07:18 +01002246 amd64_read_pci_cfg(F3, NBCFG, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002247
Borislav Petkova97fa682010-12-23 14:07:18 +01002248 debugf0("1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2249 nid, value, !!(value & NBCFG_ECC_ENABLE));
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002250
Borislav Petkova97fa682010-12-23 14:07:18 +01002251 if (!(value & NBCFG_ECC_ENABLE)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002252 amd64_warn("DRAM ECC disabled on this node, enabling...\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002253
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002254 s->flags.nb_ecc_prev = 0;
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002255
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002256 /* Attempt to turn on DRAM ECC Enable */
Borislav Petkova97fa682010-12-23 14:07:18 +01002257 value |= NBCFG_ECC_ENABLE;
2258 amd64_write_pci_cfg(F3, NBCFG, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002259
Borislav Petkova97fa682010-12-23 14:07:18 +01002260 amd64_read_pci_cfg(F3, NBCFG, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002261
Borislav Petkova97fa682010-12-23 14:07:18 +01002262 if (!(value & NBCFG_ECC_ENABLE)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002263 amd64_warn("Hardware rejected DRAM ECC enable,"
2264 "check memory DIMM configuration.\n");
Borislav Petkov2299ef72010-10-15 17:44:04 +02002265 ret = false;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002266 } else {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002267 amd64_info("Hardware accepted DRAM ECC Enable\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002268 }
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002269 } else {
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002270 s->flags.nb_ecc_prev = 1;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002271 }
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002272
Borislav Petkova97fa682010-12-23 14:07:18 +01002273 debugf0("2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2274 nid, value, !!(value & NBCFG_ECC_ENABLE));
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002275
Borislav Petkov2299ef72010-10-15 17:44:04 +02002276 return ret;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002277}
2278
Borislav Petkov360b7f32010-10-15 19:25:38 +02002279static void restore_ecc_error_reporting(struct ecc_settings *s, u8 nid,
2280 struct pci_dev *F3)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002281{
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002282 u32 value, mask = 0x3; /* UECC/CECC enable */
2283
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002284
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002285 if (!s->nbctl_valid)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002286 return;
2287
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002288 amd64_read_pci_cfg(F3, NBCTL, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002289 value &= ~mask;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002290 value |= s->old_nbctl;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002291
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002292 amd64_write_pci_cfg(F3, NBCTL, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002293
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002294 /* restore previous BIOS DRAM ECC "off" setting we force-enabled */
2295 if (!s->flags.nb_ecc_prev) {
Borislav Petkova97fa682010-12-23 14:07:18 +01002296 amd64_read_pci_cfg(F3, NBCFG, &value);
2297 value &= ~NBCFG_ECC_ENABLE;
2298 amd64_write_pci_cfg(F3, NBCFG, value);
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002299 }
2300
2301 /* restore the NB Enable MCGCTL bit */
Borislav Petkov2299ef72010-10-15 17:44:04 +02002302 if (toggle_ecc_err_reporting(s, nid, OFF))
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002303 amd64_warn("Error restoring NB MCGCTL settings!\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002304}
2305
Doug Thompsonf9431992009-04-27 19:46:08 +02002306/*
Borislav Petkov2299ef72010-10-15 17:44:04 +02002307 * EDAC requires that the BIOS have ECC enabled before
2308 * taking over the processing of ECC errors. A command line
2309 * option allows to force-enable hardware ECC later in
2310 * enable_ecc_error_reporting().
Doug Thompsonf9431992009-04-27 19:46:08 +02002311 */
Borislav Petkovcab4d272010-02-11 17:15:57 +01002312static const char *ecc_msg =
2313 "ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2314 " Either enable ECC checking or force module loading by setting "
2315 "'ecc_enable_override'.\n"
2316 " (Note that use of the override may cause unknown side effects.)\n";
Borislav Petkovbe3468e2009-08-05 15:47:22 +02002317
Borislav Petkov2299ef72010-10-15 17:44:04 +02002318static bool ecc_enabled(struct pci_dev *F3, u8 nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02002319{
2320 u32 value;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002321 u8 ecc_en = 0;
Borislav Petkov06724532009-09-16 13:05:46 +02002322 bool nb_mce_en = false;
Doug Thompsonf9431992009-04-27 19:46:08 +02002323
Borislav Petkova97fa682010-12-23 14:07:18 +01002324 amd64_read_pci_cfg(F3, NBCFG, &value);
Doug Thompsonf9431992009-04-27 19:46:08 +02002325
Borislav Petkova97fa682010-12-23 14:07:18 +01002326 ecc_en = !!(value & NBCFG_ECC_ENABLE);
Borislav Petkov2299ef72010-10-15 17:44:04 +02002327 amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
Doug Thompsonf9431992009-04-27 19:46:08 +02002328
Borislav Petkov2299ef72010-10-15 17:44:04 +02002329 nb_mce_en = amd64_nb_mce_bank_enabled_on_node(nid);
Borislav Petkov06724532009-09-16 13:05:46 +02002330 if (!nb_mce_en)
Borislav Petkov2299ef72010-10-15 17:44:04 +02002331 amd64_notice("NB MCE bank disabled, set MSR "
2332 "0x%08x[4] on node %d to enable.\n",
2333 MSR_IA32_MCG_CTL, nid);
Doug Thompsonf9431992009-04-27 19:46:08 +02002334
Borislav Petkov2299ef72010-10-15 17:44:04 +02002335 if (!ecc_en || !nb_mce_en) {
2336 amd64_notice("%s", ecc_msg);
2337 return false;
Borislav Petkov43f5e682009-12-21 18:55:18 +01002338 }
Borislav Petkov2299ef72010-10-15 17:44:04 +02002339 return true;
Doug Thompsonf9431992009-04-27 19:46:08 +02002340}
2341
Doug Thompson7d6034d2009-04-27 20:01:01 +02002342struct mcidev_sysfs_attribute sysfs_attrs[ARRAY_SIZE(amd64_dbg_attrs) +
2343 ARRAY_SIZE(amd64_inj_attrs) +
2344 1];
2345
2346struct mcidev_sysfs_attribute terminator = { .attr = { .name = NULL } };
2347
Borislav Petkov360b7f32010-10-15 19:25:38 +02002348static void set_mc_sysfs_attrs(struct mem_ctl_info *mci)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002349{
2350 unsigned int i = 0, j = 0;
2351
2352 for (; i < ARRAY_SIZE(amd64_dbg_attrs); i++)
2353 sysfs_attrs[i] = amd64_dbg_attrs[i];
2354
Borislav Petkova135cef2010-11-26 19:24:44 +01002355 if (boot_cpu_data.x86 >= 0x10)
2356 for (j = 0; j < ARRAY_SIZE(amd64_inj_attrs); j++, i++)
2357 sysfs_attrs[i] = amd64_inj_attrs[j];
Doug Thompson7d6034d2009-04-27 20:01:01 +02002358
2359 sysfs_attrs[i] = terminator;
2360
2361 mci->mc_driver_sysfs_attributes = sysfs_attrs;
2362}
2363
Borislav Petkov360b7f32010-10-15 19:25:38 +02002364static void setup_mci_misc_attrs(struct mem_ctl_info *mci)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002365{
2366 struct amd64_pvt *pvt = mci->pvt_info;
2367
2368 mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
2369 mci->edac_ctl_cap = EDAC_FLAG_NONE;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002370
Borislav Petkov5980bb92011-01-07 16:26:49 +01002371 if (pvt->nbcap & NBCAP_SECDED)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002372 mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
2373
Borislav Petkov5980bb92011-01-07 16:26:49 +01002374 if (pvt->nbcap & NBCAP_CHIPKILL)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002375 mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
2376
2377 mci->edac_cap = amd64_determine_edac_cap(pvt);
2378 mci->mod_name = EDAC_MOD_STR;
2379 mci->mod_ver = EDAC_AMD64_VERSION;
Borislav Petkov0092b202010-10-01 19:20:05 +02002380 mci->ctl_name = pvt->ctl_name;
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002381 mci->dev_name = pci_name(pvt->F2);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002382 mci->ctl_page_to_phys = NULL;
2383
Doug Thompson7d6034d2009-04-27 20:01:01 +02002384 /* memory scrubber interface */
2385 mci->set_sdram_scrub_rate = amd64_set_scrub_rate;
2386 mci->get_sdram_scrub_rate = amd64_get_scrub_rate;
2387}
2388
Borislav Petkov0092b202010-10-01 19:20:05 +02002389/*
2390 * returns a pointer to the family descriptor on success, NULL otherwise.
2391 */
2392static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
Borislav Petkov395ae782010-10-01 18:38:19 +02002393{
Borislav Petkov0092b202010-10-01 19:20:05 +02002394 u8 fam = boot_cpu_data.x86;
2395 struct amd64_family_type *fam_type = NULL;
2396
2397 switch (fam) {
Borislav Petkov395ae782010-10-01 18:38:19 +02002398 case 0xf:
Borislav Petkov0092b202010-10-01 19:20:05 +02002399 fam_type = &amd64_family_types[K8_CPUS];
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002400 pvt->ops = &amd64_family_types[K8_CPUS].ops;
Borislav Petkov0092b202010-10-01 19:20:05 +02002401 pvt->ctl_name = fam_type->ctl_name;
2402 pvt->min_scrubrate = K8_MIN_SCRUB_RATE_BITS;
Borislav Petkov395ae782010-10-01 18:38:19 +02002403 break;
2404 case 0x10:
Borislav Petkov0092b202010-10-01 19:20:05 +02002405 fam_type = &amd64_family_types[F10_CPUS];
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002406 pvt->ops = &amd64_family_types[F10_CPUS].ops;
Borislav Petkov0092b202010-10-01 19:20:05 +02002407 pvt->ctl_name = fam_type->ctl_name;
2408 pvt->min_scrubrate = F10_MIN_SCRUB_RATE_BITS;
Borislav Petkov395ae782010-10-01 18:38:19 +02002409 break;
2410
2411 default:
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002412 amd64_err("Unsupported family!\n");
Borislav Petkov0092b202010-10-01 19:20:05 +02002413 return NULL;
Borislav Petkov395ae782010-10-01 18:38:19 +02002414 }
Borislav Petkov0092b202010-10-01 19:20:05 +02002415
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002416 pvt->ext_model = boot_cpu_data.x86_model >> 4;
2417
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002418 amd64_info("%s %sdetected (node %d).\n", pvt->ctl_name,
Borislav Petkov0092b202010-10-01 19:20:05 +02002419 (fam == 0xf ?
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002420 (pvt->ext_model >= K8_REV_F ? "revF or later "
2421 : "revE or earlier ")
2422 : ""), pvt->mc_node_id);
Borislav Petkov0092b202010-10-01 19:20:05 +02002423 return fam_type;
Borislav Petkov395ae782010-10-01 18:38:19 +02002424}
2425
Borislav Petkov2299ef72010-10-15 17:44:04 +02002426static int amd64_init_one_instance(struct pci_dev *F2)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002427{
2428 struct amd64_pvt *pvt = NULL;
Borislav Petkov0092b202010-10-01 19:20:05 +02002429 struct amd64_family_type *fam_type = NULL;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002430 struct mem_ctl_info *mci = NULL;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002431 int err = 0, ret;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002432 u8 nid = get_node_id(F2);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002433
2434 ret = -ENOMEM;
2435 pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
2436 if (!pvt)
Borislav Petkov360b7f32010-10-15 19:25:38 +02002437 goto err_ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002438
Borislav Petkov360b7f32010-10-15 19:25:38 +02002439 pvt->mc_node_id = nid;
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002440 pvt->F2 = F2;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002441
Borislav Petkov395ae782010-10-01 18:38:19 +02002442 ret = -EINVAL;
Borislav Petkov0092b202010-10-01 19:20:05 +02002443 fam_type = amd64_per_family_init(pvt);
2444 if (!fam_type)
Borislav Petkov395ae782010-10-01 18:38:19 +02002445 goto err_free;
2446
Doug Thompson7d6034d2009-04-27 20:01:01 +02002447 ret = -ENODEV;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002448 err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f3_id);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002449 if (err)
2450 goto err_free;
2451
Borislav Petkov360b7f32010-10-15 19:25:38 +02002452 read_mc_regs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002453
Doug Thompson7d6034d2009-04-27 20:01:01 +02002454 /*
2455 * We need to determine how many memory channels there are. Then use
2456 * that information for calculating the size of the dynamic instance
Borislav Petkov360b7f32010-10-15 19:25:38 +02002457 * tables in the 'mci' structure.
Doug Thompson7d6034d2009-04-27 20:01:01 +02002458 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002459 ret = -EINVAL;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002460 pvt->channel_count = pvt->ops->early_channel_count(pvt);
2461 if (pvt->channel_count < 0)
Borislav Petkov360b7f32010-10-15 19:25:38 +02002462 goto err_siblings;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002463
2464 ret = -ENOMEM;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002465 mci = edac_mc_alloc(0, pvt->csels[0].b_cnt, pvt->channel_count, nid);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002466 if (!mci)
Borislav Petkov360b7f32010-10-15 19:25:38 +02002467 goto err_siblings;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002468
2469 mci->pvt_info = pvt;
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002470 mci->dev = &pvt->F2->dev;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002471
Borislav Petkov360b7f32010-10-15 19:25:38 +02002472 setup_mci_misc_attrs(mci);
2473
2474 if (init_csrows(mci))
Doug Thompson7d6034d2009-04-27 20:01:01 +02002475 mci->edac_cap = EDAC_FLAG_NONE;
2476
Borislav Petkov360b7f32010-10-15 19:25:38 +02002477 set_mc_sysfs_attrs(mci);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002478
2479 ret = -ENODEV;
2480 if (edac_mc_add_mc(mci)) {
2481 debugf1("failed edac_mc_add_mc()\n");
2482 goto err_add_mc;
2483 }
2484
Borislav Petkov549d0422009-07-24 13:51:42 +02002485 /* register stuff with EDAC MCE */
2486 if (report_gart_errors)
2487 amd_report_gart_errors(true);
2488
2489 amd_register_ecc_decoder(amd64_decode_bus_error);
2490
Borislav Petkov360b7f32010-10-15 19:25:38 +02002491 mcis[nid] = mci;
2492
2493 atomic_inc(&drv_instances);
2494
Doug Thompson7d6034d2009-04-27 20:01:01 +02002495 return 0;
2496
2497err_add_mc:
2498 edac_mc_free(mci);
2499
Borislav Petkov360b7f32010-10-15 19:25:38 +02002500err_siblings:
2501 free_mc_sibling_devs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002502
Borislav Petkov360b7f32010-10-15 19:25:38 +02002503err_free:
2504 kfree(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002505
Borislav Petkov360b7f32010-10-15 19:25:38 +02002506err_ret:
Doug Thompson7d6034d2009-04-27 20:01:01 +02002507 return ret;
2508}
2509
Borislav Petkov2299ef72010-10-15 17:44:04 +02002510static int __devinit amd64_probe_one_instance(struct pci_dev *pdev,
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002511 const struct pci_device_id *mc_type)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002512{
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002513 u8 nid = get_node_id(pdev);
Borislav Petkov2299ef72010-10-15 17:44:04 +02002514 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002515 struct ecc_settings *s;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002516 int ret = 0;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002517
Doug Thompson7d6034d2009-04-27 20:01:01 +02002518 ret = pci_enable_device(pdev);
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002519 if (ret < 0) {
Doug Thompson7d6034d2009-04-27 20:01:01 +02002520 debugf0("ret=%d\n", ret);
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002521 return -EIO;
2522 }
2523
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002524 ret = -ENOMEM;
2525 s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
2526 if (!s)
Borislav Petkov2299ef72010-10-15 17:44:04 +02002527 goto err_out;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002528
2529 ecc_stngs[nid] = s;
2530
Borislav Petkov2299ef72010-10-15 17:44:04 +02002531 if (!ecc_enabled(F3, nid)) {
2532 ret = -ENODEV;
2533
2534 if (!ecc_enable_override)
2535 goto err_enable;
2536
2537 amd64_warn("Forcing ECC on!\n");
2538
2539 if (!enable_ecc_error_reporting(s, nid, F3))
2540 goto err_enable;
2541 }
2542
2543 ret = amd64_init_one_instance(pdev);
Borislav Petkov360b7f32010-10-15 19:25:38 +02002544 if (ret < 0) {
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002545 amd64_err("Error probing instance: %d\n", nid);
Borislav Petkov360b7f32010-10-15 19:25:38 +02002546 restore_ecc_error_reporting(s, nid, F3);
2547 }
Doug Thompson7d6034d2009-04-27 20:01:01 +02002548
2549 return ret;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002550
2551err_enable:
2552 kfree(s);
2553 ecc_stngs[nid] = NULL;
2554
2555err_out:
2556 return ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002557}
2558
2559static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
2560{
2561 struct mem_ctl_info *mci;
2562 struct amd64_pvt *pvt;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002563 u8 nid = get_node_id(pdev);
2564 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2565 struct ecc_settings *s = ecc_stngs[nid];
Doug Thompson7d6034d2009-04-27 20:01:01 +02002566
2567 /* Remove from EDAC CORE tracking list */
2568 mci = edac_mc_del_mc(&pdev->dev);
2569 if (!mci)
2570 return;
2571
2572 pvt = mci->pvt_info;
2573
Borislav Petkov360b7f32010-10-15 19:25:38 +02002574 restore_ecc_error_reporting(s, nid, F3);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002575
Borislav Petkov360b7f32010-10-15 19:25:38 +02002576 free_mc_sibling_devs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002577
Borislav Petkov549d0422009-07-24 13:51:42 +02002578 /* unregister from EDAC MCE */
2579 amd_report_gart_errors(false);
2580 amd_unregister_ecc_decoder(amd64_decode_bus_error);
2581
Borislav Petkov360b7f32010-10-15 19:25:38 +02002582 kfree(ecc_stngs[nid]);
2583 ecc_stngs[nid] = NULL;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002584
Doug Thompson7d6034d2009-04-27 20:01:01 +02002585 /* Free the EDAC CORE resources */
Borislav Petkov8f68ed92009-12-21 15:15:59 +01002586 mci->pvt_info = NULL;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002587 mcis[nid] = NULL;
Borislav Petkov8f68ed92009-12-21 15:15:59 +01002588
2589 kfree(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002590 edac_mc_free(mci);
2591}
2592
2593/*
2594 * This table is part of the interface for loading drivers for PCI devices. The
2595 * PCI core identifies what devices are on a system during boot, and then
2596 * inquiry this table to see if this driver is for a given device found.
2597 */
2598static const struct pci_device_id amd64_pci_table[] __devinitdata = {
2599 {
2600 .vendor = PCI_VENDOR_ID_AMD,
2601 .device = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
2602 .subvendor = PCI_ANY_ID,
2603 .subdevice = PCI_ANY_ID,
2604 .class = 0,
2605 .class_mask = 0,
Doug Thompson7d6034d2009-04-27 20:01:01 +02002606 },
2607 {
2608 .vendor = PCI_VENDOR_ID_AMD,
2609 .device = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
2610 .subvendor = PCI_ANY_ID,
2611 .subdevice = PCI_ANY_ID,
2612 .class = 0,
2613 .class_mask = 0,
Doug Thompson7d6034d2009-04-27 20:01:01 +02002614 },
Doug Thompson7d6034d2009-04-27 20:01:01 +02002615 {0, }
2616};
2617MODULE_DEVICE_TABLE(pci, amd64_pci_table);
2618
2619static struct pci_driver amd64_pci_driver = {
2620 .name = EDAC_MOD_STR,
Borislav Petkov2299ef72010-10-15 17:44:04 +02002621 .probe = amd64_probe_one_instance,
Doug Thompson7d6034d2009-04-27 20:01:01 +02002622 .remove = __devexit_p(amd64_remove_one_instance),
2623 .id_table = amd64_pci_table,
2624};
2625
Borislav Petkov360b7f32010-10-15 19:25:38 +02002626static void setup_pci_device(void)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002627{
2628 struct mem_ctl_info *mci;
2629 struct amd64_pvt *pvt;
2630
2631 if (amd64_ctl_pci)
2632 return;
2633
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002634 mci = mcis[0];
Doug Thompson7d6034d2009-04-27 20:01:01 +02002635 if (mci) {
2636
2637 pvt = mci->pvt_info;
2638 amd64_ctl_pci =
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002639 edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002640
2641 if (!amd64_ctl_pci) {
2642 pr_warning("%s(): Unable to create PCI control\n",
2643 __func__);
2644
2645 pr_warning("%s(): PCI error report via EDAC not set\n",
2646 __func__);
2647 }
2648 }
2649}
2650
2651static int __init amd64_edac_init(void)
2652{
Borislav Petkov360b7f32010-10-15 19:25:38 +02002653 int err = -ENODEV;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002654
2655 edac_printk(KERN_INFO, EDAC_MOD_STR, EDAC_AMD64_VERSION "\n");
2656
2657 opstate_init();
2658
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +02002659 if (amd_cache_northbridges() < 0)
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002660 goto err_ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002661
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002662 err = -ENOMEM;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002663 mcis = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL);
2664 ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL);
Borislav Petkov360b7f32010-10-15 19:25:38 +02002665 if (!(mcis && ecc_stngs))
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002666 goto err_ret;
2667
Borislav Petkov50542252009-12-11 18:14:40 +01002668 msrs = msrs_alloc();
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002669 if (!msrs)
Borislav Petkov360b7f32010-10-15 19:25:38 +02002670 goto err_free;
Borislav Petkov50542252009-12-11 18:14:40 +01002671
Doug Thompson7d6034d2009-04-27 20:01:01 +02002672 err = pci_register_driver(&amd64_pci_driver);
2673 if (err)
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002674 goto err_pci;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002675
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002676 err = -ENODEV;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002677 if (!atomic_read(&drv_instances))
2678 goto err_no_instances;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002679
Borislav Petkov360b7f32010-10-15 19:25:38 +02002680 setup_pci_device();
2681 return 0;
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002682
Borislav Petkov360b7f32010-10-15 19:25:38 +02002683err_no_instances:
Doug Thompson7d6034d2009-04-27 20:01:01 +02002684 pci_unregister_driver(&amd64_pci_driver);
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002685
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002686err_pci:
2687 msrs_free(msrs);
2688 msrs = NULL;
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002689
Borislav Petkov360b7f32010-10-15 19:25:38 +02002690err_free:
2691 kfree(mcis);
2692 mcis = NULL;
2693
2694 kfree(ecc_stngs);
2695 ecc_stngs = NULL;
2696
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002697err_ret:
Doug Thompson7d6034d2009-04-27 20:01:01 +02002698 return err;
2699}
2700
2701static void __exit amd64_edac_exit(void)
2702{
2703 if (amd64_ctl_pci)
2704 edac_pci_release_generic_ctl(amd64_ctl_pci);
2705
2706 pci_unregister_driver(&amd64_pci_driver);
Borislav Petkov50542252009-12-11 18:14:40 +01002707
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002708 kfree(ecc_stngs);
2709 ecc_stngs = NULL;
2710
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002711 kfree(mcis);
2712 mcis = NULL;
2713
Borislav Petkov50542252009-12-11 18:14:40 +01002714 msrs_free(msrs);
2715 msrs = NULL;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002716}
2717
2718module_init(amd64_edac_init);
2719module_exit(amd64_edac_exit);
2720
2721MODULE_LICENSE("GPL");
2722MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
2723 "Dave Peterson, Thayne Harbaugh");
2724MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
2725 EDAC_AMD64_VERSION);
2726
2727module_param(edac_op_state, int, 0444);
2728MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");