blob: 351496af9e8d0bf8908dd7699e661b3ab1f32aaa [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 Petkov66fed2d2012-08-09 18:41:07 +020063int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
64 u32 *val, const char *func)
Borislav Petkovb2b0c602010-10-08 18:32:29 +020065{
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
Borislav Petkov73ba8592011-09-19 17:34:45 +0200117/*
118 * Select DCT to which PCI cfg accesses are routed
119 */
120static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
121{
122 u32 reg = 0;
123
124 amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
125 reg &= 0xfffffffe;
126 reg |= dct;
127 amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
128}
129
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200130static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
131 const char *func)
132{
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200133 u8 dct = 0;
134
135 if (addr >= 0x140 && addr <= 0x1a0) {
136 dct = 1;
137 addr -= 0x100;
138 }
139
Borislav Petkov73ba8592011-09-19 17:34:45 +0200140 f15h_select_dct(pvt, dct);
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200141
142 return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
143}
144
Borislav Petkovb70ef012009-06-25 19:32:38 +0200145/*
Doug Thompson2bc65412009-05-04 20:11:14 +0200146 * Memory scrubber control interface. For K8, memory scrubbing is handled by
147 * hardware and can involve L2 cache, dcache as well as the main memory. With
148 * F10, this is extended to L3 cache scrubbing on CPU models sporting that
149 * functionality.
150 *
151 * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
152 * (dram) over to cache lines. This is nasty, so we will use bandwidth in
153 * bytes/sec for the setting.
154 *
155 * Currently, we only do dram scrubbing. If the scrubbing is done in software on
156 * other archs, we might not have access to the caches directly.
157 */
158
159/*
160 * scan the scrub rate mapping table for a close or matching bandwidth value to
161 * issue. If requested is too big, then use last maximum value found.
162 */
Borislav Petkov395ae782010-10-01 18:38:19 +0200163static int __amd64_set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate)
Doug Thompson2bc65412009-05-04 20:11:14 +0200164{
165 u32 scrubval;
166 int i;
167
168 /*
169 * map the configured rate (new_bw) to a value specific to the AMD64
170 * memory controller and apply to register. Search for the first
171 * bandwidth entry that is greater or equal than the setting requested
172 * and program that. If at last entry, turn off DRAM scrubbing.
Andrew Morton168bfee2012-10-23 14:09:39 -0700173 *
174 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
175 * by falling back to the last element in scrubrates[].
Doug Thompson2bc65412009-05-04 20:11:14 +0200176 */
Andrew Morton168bfee2012-10-23 14:09:39 -0700177 for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
Doug Thompson2bc65412009-05-04 20:11:14 +0200178 /*
179 * skip scrub rates which aren't recommended
180 * (see F10 BKDG, F3x58)
181 */
Borislav Petkov395ae782010-10-01 18:38:19 +0200182 if (scrubrates[i].scrubval < min_rate)
Doug Thompson2bc65412009-05-04 20:11:14 +0200183 continue;
184
185 if (scrubrates[i].bandwidth <= new_bw)
186 break;
Doug Thompson2bc65412009-05-04 20:11:14 +0200187 }
188
189 scrubval = scrubrates[i].scrubval;
Doug Thompson2bc65412009-05-04 20:11:14 +0200190
Borislav Petkov5980bb92011-01-07 16:26:49 +0100191 pci_write_bits32(ctl, SCRCTRL, scrubval, 0x001F);
Doug Thompson2bc65412009-05-04 20:11:14 +0200192
Borislav Petkov39094442010-11-24 19:52:09 +0100193 if (scrubval)
194 return scrubrates[i].bandwidth;
195
Doug Thompson2bc65412009-05-04 20:11:14 +0200196 return 0;
197}
198
Borislav Petkov395ae782010-10-01 18:38:19 +0200199static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
Doug Thompson2bc65412009-05-04 20:11:14 +0200200{
201 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkov87b3e0e2011-01-19 20:02:38 +0100202 u32 min_scrubrate = 0x5;
Doug Thompson2bc65412009-05-04 20:11:14 +0200203
Borislav Petkov87b3e0e2011-01-19 20:02:38 +0100204 if (boot_cpu_data.x86 == 0xf)
205 min_scrubrate = 0x0;
206
Borislav Petkov73ba8592011-09-19 17:34:45 +0200207 /* F15h Erratum #505 */
208 if (boot_cpu_data.x86 == 0x15)
209 f15h_select_dct(pvt, 0);
210
Borislav Petkov87b3e0e2011-01-19 20:02:38 +0100211 return __amd64_set_scrub_rate(pvt->F3, bw, min_scrubrate);
Doug Thompson2bc65412009-05-04 20:11:14 +0200212}
213
Borislav Petkov39094442010-11-24 19:52:09 +0100214static int amd64_get_scrub_rate(struct mem_ctl_info *mci)
Doug Thompson2bc65412009-05-04 20:11:14 +0200215{
216 struct amd64_pvt *pvt = mci->pvt_info;
217 u32 scrubval = 0;
Borislav Petkov39094442010-11-24 19:52:09 +0100218 int i, retval = -EINVAL;
Doug Thompson2bc65412009-05-04 20:11:14 +0200219
Borislav Petkov73ba8592011-09-19 17:34:45 +0200220 /* F15h Erratum #505 */
221 if (boot_cpu_data.x86 == 0x15)
222 f15h_select_dct(pvt, 0);
223
Borislav Petkov5980bb92011-01-07 16:26:49 +0100224 amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
Doug Thompson2bc65412009-05-04 20:11:14 +0200225
226 scrubval = scrubval & 0x001F;
227
Roel Kluin926311f2010-01-11 20:58:21 +0100228 for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
Doug Thompson2bc65412009-05-04 20:11:14 +0200229 if (scrubrates[i].scrubval == scrubval) {
Borislav Petkov39094442010-11-24 19:52:09 +0100230 retval = scrubrates[i].bandwidth;
Doug Thompson2bc65412009-05-04 20:11:14 +0200231 break;
232 }
233 }
Borislav Petkov39094442010-11-24 19:52:09 +0100234 return retval;
Doug Thompson2bc65412009-05-04 20:11:14 +0200235}
236
Doug Thompson67757632009-04-27 15:53:22 +0200237/*
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200238 * returns true if the SysAddr given by sys_addr matches the
239 * DRAM base/limit associated with node_id
Doug Thompson67757632009-04-27 15:53:22 +0200240 */
Borislav Petkovb487c332011-02-21 18:55:00 +0100241static bool amd64_base_limit_match(struct amd64_pvt *pvt, u64 sys_addr,
242 unsigned nid)
Doug Thompson67757632009-04-27 15:53:22 +0200243{
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200244 u64 addr;
Doug Thompson67757632009-04-27 15:53:22 +0200245
246 /* The K8 treats this as a 40-bit value. However, bits 63-40 will be
247 * all ones if the most significant implemented address bit is 1.
248 * Here we discard bits 63-40. See section 3.4.2 of AMD publication
249 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
250 * Application Programming.
251 */
252 addr = sys_addr & 0x000000ffffffffffull;
253
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200254 return ((addr >= get_dram_base(pvt, nid)) &&
255 (addr <= get_dram_limit(pvt, nid)));
Doug Thompson67757632009-04-27 15:53:22 +0200256}
257
258/*
259 * Attempt to map a SysAddr to a node. On success, return a pointer to the
260 * mem_ctl_info structure for the node that the SysAddr maps to.
261 *
262 * On failure, return NULL.
263 */
264static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
265 u64 sys_addr)
266{
267 struct amd64_pvt *pvt;
Borislav Petkovb487c332011-02-21 18:55:00 +0100268 unsigned node_id;
Doug Thompson67757632009-04-27 15:53:22 +0200269 u32 intlv_en, bits;
270
271 /*
272 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
273 * 3.4.4.2) registers to map the SysAddr to a node ID.
274 */
275 pvt = mci->pvt_info;
276
277 /*
278 * The value of this field should be the same for all DRAM Base
279 * registers. Therefore we arbitrarily choose to read it from the
280 * register for node 0.
281 */
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200282 intlv_en = dram_intlv_en(pvt, 0);
Doug Thompson67757632009-04-27 15:53:22 +0200283
284 if (intlv_en == 0) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200285 for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
Doug Thompson67757632009-04-27 15:53:22 +0200286 if (amd64_base_limit_match(pvt, sys_addr, node_id))
Borislav Petkov8edc5442009-09-18 12:39:19 +0200287 goto found;
Doug Thompson67757632009-04-27 15:53:22 +0200288 }
Borislav Petkov8edc5442009-09-18 12:39:19 +0200289 goto err_no_match;
Doug Thompson67757632009-04-27 15:53:22 +0200290 }
291
Borislav Petkov72f158f2009-09-18 12:27:27 +0200292 if (unlikely((intlv_en != 0x01) &&
293 (intlv_en != 0x03) &&
294 (intlv_en != 0x07))) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200295 amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
Doug Thompson67757632009-04-27 15:53:22 +0200296 return NULL;
297 }
298
299 bits = (((u32) sys_addr) >> 12) & intlv_en;
300
301 for (node_id = 0; ; ) {
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200302 if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
Doug Thompson67757632009-04-27 15:53:22 +0200303 break; /* intlv_sel field matches */
304
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200305 if (++node_id >= DRAM_RANGES)
Doug Thompson67757632009-04-27 15:53:22 +0200306 goto err_no_match;
307 }
308
309 /* sanity test for sys_addr */
310 if (unlikely(!amd64_base_limit_match(pvt, sys_addr, node_id))) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200311 amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
312 "range for node %d with node interleaving enabled.\n",
313 __func__, sys_addr, node_id);
Doug Thompson67757632009-04-27 15:53:22 +0200314 return NULL;
315 }
316
317found:
Borislav Petkovb487c332011-02-21 18:55:00 +0100318 return edac_mc_find((int)node_id);
Doug Thompson67757632009-04-27 15:53:22 +0200319
320err_no_match:
Joe Perches956b9ba2012-04-29 17:08:39 -0300321 edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
322 (unsigned long)sys_addr);
Doug Thompson67757632009-04-27 15:53:22 +0200323
324 return NULL;
325}
Doug Thompsone2ce7252009-04-27 15:57:12 +0200326
327/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100328 * compute the CS base address of the @csrow on the DRAM controller @dct.
329 * For details see F2x[5C:40] in the processor's BKDG
Doug Thompsone2ce7252009-04-27 15:57:12 +0200330 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100331static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
332 u64 *base, u64 *mask)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200333{
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100334 u64 csbase, csmask, base_bits, mask_bits;
335 u8 addr_shift;
336
337 if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
338 csbase = pvt->csels[dct].csbases[csrow];
339 csmask = pvt->csels[dct].csmasks[csrow];
340 base_bits = GENMASK(21, 31) | GENMASK(9, 15);
341 mask_bits = GENMASK(21, 29) | GENMASK(9, 15);
342 addr_shift = 4;
343 } else {
344 csbase = pvt->csels[dct].csbases[csrow];
345 csmask = pvt->csels[dct].csmasks[csrow >> 1];
346 addr_shift = 8;
347
348 if (boot_cpu_data.x86 == 0x15)
349 base_bits = mask_bits = GENMASK(19,30) | GENMASK(5,13);
350 else
351 base_bits = mask_bits = GENMASK(19,28) | GENMASK(5,13);
352 }
353
354 *base = (csbase & base_bits) << addr_shift;
355
356 *mask = ~0ULL;
357 /* poke holes for the csmask */
358 *mask &= ~(mask_bits << addr_shift);
359 /* OR them in */
360 *mask |= (csmask & mask_bits) << addr_shift;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200361}
362
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100363#define for_each_chip_select(i, dct, pvt) \
364 for (i = 0; i < pvt->csels[dct].b_cnt; i++)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200365
Borislav Petkov614ec9d2011-01-13 18:02:22 +0100366#define chip_select_base(i, dct, pvt) \
367 pvt->csels[dct].csbases[i]
368
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100369#define for_each_chip_select_mask(i, dct, pvt) \
370 for (i = 0; i < pvt->csels[dct].m_cnt; i++)
Doug Thompsone2ce7252009-04-27 15:57:12 +0200371
372/*
373 * @input_addr is an InputAddr associated with the node given by mci. Return the
374 * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
375 */
376static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
377{
378 struct amd64_pvt *pvt;
379 int csrow;
380 u64 base, mask;
381
382 pvt = mci->pvt_info;
383
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100384 for_each_chip_select(csrow, 0, pvt) {
385 if (!csrow_enabled(csrow, 0, pvt))
Doug Thompsone2ce7252009-04-27 15:57:12 +0200386 continue;
387
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100388 get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
389
390 mask = ~mask;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200391
392 if ((input_addr & mask) == (base & mask)) {
Joe Perches956b9ba2012-04-29 17:08:39 -0300393 edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
394 (unsigned long)input_addr, csrow,
395 pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200396
397 return csrow;
398 }
399 }
Joe Perches956b9ba2012-04-29 17:08:39 -0300400 edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
401 (unsigned long)input_addr, pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200402
403 return -1;
404}
405
406/*
Doug Thompsone2ce7252009-04-27 15:57:12 +0200407 * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
408 * for the node represented by mci. Info is passed back in *hole_base,
409 * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if
410 * info is invalid. Info may be invalid for either of the following reasons:
411 *
412 * - The revision of the node is not E or greater. In this case, the DRAM Hole
413 * Address Register does not exist.
414 *
415 * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
416 * indicating that its contents are not valid.
417 *
418 * The values passed back in *hole_base, *hole_offset, and *hole_size are
419 * complete 32-bit values despite the fact that the bitfields in the DHAR
420 * only represent bits 31-24 of the base and offset values.
421 */
422int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
423 u64 *hole_offset, u64 *hole_size)
424{
425 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200426
427 /* only revE and later have the DRAM Hole Address Register */
Borislav Petkov1433eb92009-10-21 13:44:36 +0200428 if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) {
Joe Perches956b9ba2012-04-29 17:08:39 -0300429 edac_dbg(1, " revision %d for node %d does not support DHAR\n",
430 pvt->ext_model, pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200431 return 1;
432 }
433
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100434 /* valid for Fam10h and above */
Borislav Petkovc8e518d2010-12-10 19:49:19 +0100435 if (boot_cpu_data.x86 >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
Joe Perches956b9ba2012-04-29 17:08:39 -0300436 edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n");
Doug Thompsone2ce7252009-04-27 15:57:12 +0200437 return 1;
438 }
439
Borislav Petkovc8e518d2010-12-10 19:49:19 +0100440 if (!dhar_valid(pvt)) {
Joe Perches956b9ba2012-04-29 17:08:39 -0300441 edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n",
442 pvt->mc_node_id);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200443 return 1;
444 }
445
446 /* This node has Memory Hoisting */
447
448 /* +------------------+--------------------+--------------------+-----
449 * | memory | DRAM hole | relocated |
450 * | [0, (x - 1)] | [x, 0xffffffff] | addresses from |
451 * | | | DRAM hole |
452 * | | | [0x100000000, |
453 * | | | (0x100000000+ |
454 * | | | (0xffffffff-x))] |
455 * +------------------+--------------------+--------------------+-----
456 *
457 * Above is a diagram of physical memory showing the DRAM hole and the
458 * relocated addresses from the DRAM hole. As shown, the DRAM hole
459 * starts at address x (the base address) and extends through address
460 * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the
461 * addresses in the hole so that they start at 0x100000000.
462 */
463
Borislav Petkov1f316772012-08-10 12:50:50 +0200464 *hole_base = dhar_base(pvt);
465 *hole_size = (1ULL << 32) - *hole_base;
Doug Thompsone2ce7252009-04-27 15:57:12 +0200466
467 if (boot_cpu_data.x86 > 0xf)
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100468 *hole_offset = f10_dhar_offset(pvt);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200469 else
Borislav Petkovbc21fa52010-11-11 17:29:13 +0100470 *hole_offset = k8_dhar_offset(pvt);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200471
Joe Perches956b9ba2012-04-29 17:08:39 -0300472 edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
473 pvt->mc_node_id, (unsigned long)*hole_base,
474 (unsigned long)*hole_offset, (unsigned long)*hole_size);
Doug Thompsone2ce7252009-04-27 15:57:12 +0200475
476 return 0;
477}
478EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
479
Doug Thompson93c2df52009-05-04 20:46:50 +0200480/*
481 * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is
482 * assumed that sys_addr maps to the node given by mci.
483 *
484 * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
485 * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
486 * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
487 * then it is also involved in translating a SysAddr to a DramAddr. Sections
488 * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
489 * These parts of the documentation are unclear. I interpret them as follows:
490 *
491 * When node n receives a SysAddr, it processes the SysAddr as follows:
492 *
493 * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
494 * Limit registers for node n. If the SysAddr is not within the range
495 * specified by the base and limit values, then node n ignores the Sysaddr
496 * (since it does not map to node n). Otherwise continue to step 2 below.
497 *
498 * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
499 * disabled so skip to step 3 below. Otherwise see if the SysAddr is within
500 * the range of relocated addresses (starting at 0x100000000) from the DRAM
501 * hole. If not, skip to step 3 below. Else get the value of the
502 * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
503 * offset defined by this value from the SysAddr.
504 *
505 * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
506 * Base register for node n. To obtain the DramAddr, subtract the base
507 * address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
508 */
509static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
510{
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200511 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompson93c2df52009-05-04 20:46:50 +0200512 u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
Borislav Petkov1f316772012-08-10 12:50:50 +0200513 int ret;
Doug Thompson93c2df52009-05-04 20:46:50 +0200514
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200515 dram_base = get_dram_base(pvt, pvt->mc_node_id);
Doug Thompson93c2df52009-05-04 20:46:50 +0200516
517 ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
518 &hole_size);
519 if (!ret) {
Borislav Petkov1f316772012-08-10 12:50:50 +0200520 if ((sys_addr >= (1ULL << 32)) &&
521 (sys_addr < ((1ULL << 32) + hole_size))) {
Doug Thompson93c2df52009-05-04 20:46:50 +0200522 /* use DHAR to translate SysAddr to DramAddr */
523 dram_addr = sys_addr - hole_offset;
524
Joe Perches956b9ba2012-04-29 17:08:39 -0300525 edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
526 (unsigned long)sys_addr,
527 (unsigned long)dram_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200528
529 return dram_addr;
530 }
531 }
532
533 /*
534 * Translate the SysAddr to a DramAddr as shown near the start of
535 * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8
536 * only deals with 40-bit values. Therefore we discard bits 63-40 of
537 * sys_addr below. If bit 39 of sys_addr is 1 then the bits we
538 * discard are all 1s. Otherwise the bits we discard are all 0s. See
539 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
540 * Programmer's Manual Volume 1 Application Programming.
541 */
Borislav Petkovf678b8c2010-12-13 19:21:07 +0100542 dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base;
Doug Thompson93c2df52009-05-04 20:46:50 +0200543
Joe Perches956b9ba2012-04-29 17:08:39 -0300544 edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
545 (unsigned long)sys_addr, (unsigned long)dram_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200546 return dram_addr;
547}
548
549/*
550 * @intlv_en is the value of the IntlvEn field from a DRAM Base register
551 * (section 3.4.4.1). Return the number of bits from a SysAddr that are used
552 * for node interleaving.
553 */
554static int num_node_interleave_bits(unsigned intlv_en)
555{
556 static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
557 int n;
558
559 BUG_ON(intlv_en > 7);
560 n = intlv_shift_table[intlv_en];
561 return n;
562}
563
564/* Translate the DramAddr given by @dram_addr to an InputAddr. */
565static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
566{
567 struct amd64_pvt *pvt;
568 int intlv_shift;
569 u64 input_addr;
570
571 pvt = mci->pvt_info;
572
573 /*
574 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
575 * concerning translating a DramAddr to an InputAddr.
576 */
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200577 intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
Borislav Petkovf678b8c2010-12-13 19:21:07 +0100578 input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) +
579 (dram_addr & 0xfff);
Doug Thompson93c2df52009-05-04 20:46:50 +0200580
Joe Perches956b9ba2012-04-29 17:08:39 -0300581 edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
582 intlv_shift, (unsigned long)dram_addr,
583 (unsigned long)input_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200584
585 return input_addr;
586}
587
588/*
589 * Translate the SysAddr represented by @sys_addr to an InputAddr. It is
590 * assumed that @sys_addr maps to the node given by mci.
591 */
592static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
593{
594 u64 input_addr;
595
596 input_addr =
597 dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
598
Joe Perches956b9ba2012-04-29 17:08:39 -0300599 edac_dbg(2, "SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
600 (unsigned long)sys_addr, (unsigned long)input_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200601
602 return input_addr;
603}
604
605
606/*
607 * @input_addr is an InputAddr associated with the node represented by mci.
608 * Translate @input_addr to a DramAddr and return the result.
609 */
610static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
611{
612 struct amd64_pvt *pvt;
Borislav Petkovb487c332011-02-21 18:55:00 +0100613 unsigned node_id, intlv_shift;
Doug Thompson93c2df52009-05-04 20:46:50 +0200614 u64 bits, dram_addr;
615 u32 intlv_sel;
616
617 /*
618 * Near the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
619 * shows how to translate a DramAddr to an InputAddr. Here we reverse
620 * this procedure. When translating from a DramAddr to an InputAddr, the
621 * bits used for node interleaving are discarded. Here we recover these
622 * bits from the IntlvSel field of the DRAM Limit register (section
623 * 3.4.4.2) for the node that input_addr is associated with.
624 */
625 pvt = mci->pvt_info;
626 node_id = pvt->mc_node_id;
Borislav Petkovb487c332011-02-21 18:55:00 +0100627
628 BUG_ON(node_id > 7);
Doug Thompson93c2df52009-05-04 20:46:50 +0200629
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200630 intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
Doug Thompson93c2df52009-05-04 20:46:50 +0200631 if (intlv_shift == 0) {
Joe Perches956b9ba2012-04-29 17:08:39 -0300632 edac_dbg(1, " InputAddr 0x%lx translates to DramAddr of same value\n",
633 (unsigned long)input_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200634
635 return input_addr;
636 }
637
Borislav Petkovf678b8c2010-12-13 19:21:07 +0100638 bits = ((input_addr & GENMASK(12, 35)) << intlv_shift) +
639 (input_addr & 0xfff);
Doug Thompson93c2df52009-05-04 20:46:50 +0200640
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200641 intlv_sel = dram_intlv_sel(pvt, node_id) & ((1 << intlv_shift) - 1);
Doug Thompson93c2df52009-05-04 20:46:50 +0200642 dram_addr = bits + (intlv_sel << 12);
643
Joe Perches956b9ba2012-04-29 17:08:39 -0300644 edac_dbg(1, "InputAddr 0x%lx translates to DramAddr 0x%lx (%d node interleave bits)\n",
645 (unsigned long)input_addr,
646 (unsigned long)dram_addr, intlv_shift);
Doug Thompson93c2df52009-05-04 20:46:50 +0200647
648 return dram_addr;
649}
650
651/*
652 * @dram_addr is a DramAddr that maps to the node represented by mci. Convert
653 * @dram_addr to a SysAddr.
654 */
655static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
656{
657 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200658 u64 hole_base, hole_offset, hole_size, base, sys_addr;
Doug Thompson93c2df52009-05-04 20:46:50 +0200659 int ret = 0;
660
661 ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
662 &hole_size);
663 if (!ret) {
664 if ((dram_addr >= hole_base) &&
665 (dram_addr < (hole_base + hole_size))) {
666 sys_addr = dram_addr + hole_offset;
667
Joe Perches956b9ba2012-04-29 17:08:39 -0300668 edac_dbg(1, "using DHAR to translate DramAddr 0x%lx to SysAddr 0x%lx\n",
669 (unsigned long)dram_addr,
670 (unsigned long)sys_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200671
672 return sys_addr;
673 }
674 }
675
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200676 base = get_dram_base(pvt, pvt->mc_node_id);
Doug Thompson93c2df52009-05-04 20:46:50 +0200677 sys_addr = dram_addr + base;
678
679 /*
680 * The sys_addr we have computed up to this point is a 40-bit value
681 * because the k8 deals with 40-bit values. However, the value we are
682 * supposed to return is a full 64-bit physical address. The AMD
683 * x86-64 architecture specifies that the most significant implemented
684 * address bit through bit 63 of a physical address must be either all
685 * 0s or all 1s. Therefore we sign-extend the 40-bit sys_addr to a
686 * 64-bit value below. See section 3.4.2 of AMD publication 24592:
687 * AMD x86-64 Architecture Programmer's Manual Volume 1 Application
688 * Programming.
689 */
690 sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
691
Joe Perches956b9ba2012-04-29 17:08:39 -0300692 edac_dbg(1, " Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
693 pvt->mc_node_id, (unsigned long)dram_addr,
694 (unsigned long)sys_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200695
696 return sys_addr;
697}
698
699/*
700 * @input_addr is an InputAddr associated with the node given by mci. Translate
701 * @input_addr to a SysAddr.
702 */
703static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
704 u64 input_addr)
705{
706 return dram_addr_to_sys_addr(mci,
707 input_addr_to_dram_addr(mci, input_addr));
708}
709
Doug Thompson93c2df52009-05-04 20:46:50 +0200710/* Map the Error address to a PAGE and PAGE OFFSET. */
711static inline void error_address_to_page_and_offset(u64 error_address,
712 u32 *page, u32 *offset)
713{
714 *page = (u32) (error_address >> PAGE_SHIFT);
715 *offset = ((u32) error_address) & ~PAGE_MASK;
716}
717
718/*
719 * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
720 * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
721 * of a node that detected an ECC memory error. mci represents the node that
722 * the error address maps to (possibly different from the node that detected
723 * the error). Return the number of the csrow that sys_addr maps to, or -1 on
724 * error.
725 */
726static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
727{
728 int csrow;
729
730 csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
731
732 if (csrow == -1)
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200733 amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
734 "address 0x%lx\n", (unsigned long)sys_addr);
Doug Thompson93c2df52009-05-04 20:46:50 +0200735 return csrow;
736}
Doug Thompsone2ce7252009-04-27 15:57:12 +0200737
Borislav Petkovbfc04ae2009-11-12 19:05:07 +0100738static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
Doug Thompson2da11652009-04-27 16:09:09 +0200739
Doug Thompson2da11652009-04-27 16:09:09 +0200740/*
741 * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
742 * are ECC capable.
743 */
Dan Carpenter1f6189e2011-10-06 02:30:25 -0400744static unsigned long amd64_determine_edac_cap(struct amd64_pvt *pvt)
Doug Thompson2da11652009-04-27 16:09:09 +0200745{
Borislav Petkovcb328502010-12-22 14:28:24 +0100746 u8 bit;
Dan Carpenter1f6189e2011-10-06 02:30:25 -0400747 unsigned long edac_cap = EDAC_FLAG_NONE;
Doug Thompson2da11652009-04-27 16:09:09 +0200748
Borislav Petkov1433eb92009-10-21 13:44:36 +0200749 bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
Doug Thompson2da11652009-04-27 16:09:09 +0200750 ? 19
751 : 17;
752
Borislav Petkov584fcff2009-06-10 18:29:54 +0200753 if (pvt->dclr0 & BIT(bit))
Doug Thompson2da11652009-04-27 16:09:09 +0200754 edac_cap = EDAC_FLAG_SECDED;
755
756 return edac_cap;
757}
758
Borislav Petkov8c671752011-02-23 17:25:12 +0100759static void amd64_debug_display_dimm_sizes(struct amd64_pvt *, u8);
Doug Thompson2da11652009-04-27 16:09:09 +0200760
Borislav Petkov68798e12009-11-03 16:18:33 +0100761static void amd64_dump_dramcfg_low(u32 dclr, int chan)
762{
Joe Perches956b9ba2012-04-29 17:08:39 -0300763 edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
Borislav Petkov68798e12009-11-03 16:18:33 +0100764
Joe Perches956b9ba2012-04-29 17:08:39 -0300765 edac_dbg(1, " DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
766 (dclr & BIT(16)) ? "un" : "",
767 (dclr & BIT(19)) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100768
Joe Perches956b9ba2012-04-29 17:08:39 -0300769 edac_dbg(1, " PAR/ERR parity: %s\n",
770 (dclr & BIT(8)) ? "enabled" : "disabled");
Borislav Petkov68798e12009-11-03 16:18:33 +0100771
Borislav Petkovcb328502010-12-22 14:28:24 +0100772 if (boot_cpu_data.x86 == 0x10)
Joe Perches956b9ba2012-04-29 17:08:39 -0300773 edac_dbg(1, " DCT 128bit mode width: %s\n",
774 (dclr & BIT(11)) ? "128b" : "64b");
Borislav Petkov68798e12009-11-03 16:18:33 +0100775
Joe Perches956b9ba2012-04-29 17:08:39 -0300776 edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
777 (dclr & BIT(12)) ? "yes" : "no",
778 (dclr & BIT(13)) ? "yes" : "no",
779 (dclr & BIT(14)) ? "yes" : "no",
780 (dclr & BIT(15)) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100781}
782
Doug Thompson2da11652009-04-27 16:09:09 +0200783/* Display and decode various NB registers for debug purposes. */
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200784static void dump_misc_regs(struct amd64_pvt *pvt)
Doug Thompson2da11652009-04-27 16:09:09 +0200785{
Joe Perches956b9ba2012-04-29 17:08:39 -0300786 edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
Doug Thompson2da11652009-04-27 16:09:09 +0200787
Joe Perches956b9ba2012-04-29 17:08:39 -0300788 edac_dbg(1, " NB two channel DRAM capable: %s\n",
789 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100790
Joe Perches956b9ba2012-04-29 17:08:39 -0300791 edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n",
792 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
793 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
Borislav Petkov68798e12009-11-03 16:18:33 +0100794
795 amd64_dump_dramcfg_low(pvt->dclr0, 0);
Doug Thompson2da11652009-04-27 16:09:09 +0200796
Joe Perches956b9ba2012-04-29 17:08:39 -0300797 edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
Doug Thompson2da11652009-04-27 16:09:09 +0200798
Joe Perches956b9ba2012-04-29 17:08:39 -0300799 edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
800 pvt->dhar, dhar_base(pvt),
801 (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt)
802 : f10_dhar_offset(pvt));
Doug Thompson2da11652009-04-27 16:09:09 +0200803
Joe Perches956b9ba2012-04-29 17:08:39 -0300804 edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
Doug Thompson2da11652009-04-27 16:09:09 +0200805
Borislav Petkov8c671752011-02-23 17:25:12 +0100806 amd64_debug_display_dimm_sizes(pvt, 0);
Borislav Petkov4d796362011-02-03 15:59:57 +0100807
Borislav Petkov8de1d912009-10-16 13:39:30 +0200808 /* everything below this point is Fam10h and above */
Borislav Petkov4d796362011-02-03 15:59:57 +0100809 if (boot_cpu_data.x86 == 0xf)
Doug Thompson2da11652009-04-27 16:09:09 +0200810 return;
Borislav Petkov4d796362011-02-03 15:59:57 +0100811
Borislav Petkov8c671752011-02-23 17:25:12 +0100812 amd64_debug_display_dimm_sizes(pvt, 1);
Doug Thompson2da11652009-04-27 16:09:09 +0200813
Borislav Petkova3b7db02011-01-19 20:35:12 +0100814 amd64_info("using %s syndromes.\n", ((pvt->ecc_sym_sz == 8) ? "x8" : "x4"));
Borislav Petkovad6a32e2010-03-09 12:46:00 +0100815
Borislav Petkov8de1d912009-10-16 13:39:30 +0200816 /* Only if NOT ganged does dclr1 have valid info */
Borislav Petkov68798e12009-11-03 16:18:33 +0100817 if (!dct_ganging_enabled(pvt))
818 amd64_dump_dramcfg_low(pvt->dclr1, 1);
Doug Thompson2da11652009-04-27 16:09:09 +0200819}
820
Doug Thompson94be4bf2009-04-27 16:12:00 +0200821/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100822 * see BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
Doug Thompson94be4bf2009-04-27 16:12:00 +0200823 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100824static void prep_chip_selects(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200825{
Borislav Petkov1433eb92009-10-21 13:44:36 +0200826 if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100827 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
828 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
Borislav Petkov9d858bb2009-09-21 14:35:51 +0200829 } else {
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100830 pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
831 pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200832 }
833}
834
835/*
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100836 * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
Doug Thompson94be4bf2009-04-27 16:12:00 +0200837 */
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200838static void read_dct_base_mask(struct amd64_pvt *pvt)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200839{
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100840 int cs;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200841
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100842 prep_chip_selects(pvt);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200843
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100844 for_each_chip_select(cs, 0, pvt) {
Borislav Petkov71d2a322011-02-21 19:37:24 +0100845 int reg0 = DCSB0 + (cs * 4);
846 int reg1 = DCSB1 + (cs * 4);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100847 u32 *base0 = &pvt->csels[0].csbases[cs];
848 u32 *base1 = &pvt->csels[1].csbases[cs];
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200849
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100850 if (!amd64_read_dct_pci_cfg(pvt, reg0, base0))
Joe Perches956b9ba2012-04-29 17:08:39 -0300851 edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n",
852 cs, *base0, reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200853
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100854 if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
855 continue;
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200856
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100857 if (!amd64_read_dct_pci_cfg(pvt, reg1, base1))
Joe Perches956b9ba2012-04-29 17:08:39 -0300858 edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n",
859 cs, *base1, reg1);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200860 }
861
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100862 for_each_chip_select_mask(cs, 0, pvt) {
Borislav Petkov71d2a322011-02-21 19:37:24 +0100863 int reg0 = DCSM0 + (cs * 4);
864 int reg1 = DCSM1 + (cs * 4);
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100865 u32 *mask0 = &pvt->csels[0].csmasks[cs];
866 u32 *mask1 = &pvt->csels[1].csmasks[cs];
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200867
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100868 if (!amd64_read_dct_pci_cfg(pvt, reg0, mask0))
Joe Perches956b9ba2012-04-29 17:08:39 -0300869 edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n",
870 cs, *mask0, reg0);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200871
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100872 if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
873 continue;
Borislav Petkovb2b0c602010-10-08 18:32:29 +0200874
Borislav Petkov11c75ea2010-11-29 19:49:02 +0100875 if (!amd64_read_dct_pci_cfg(pvt, reg1, mask1))
Joe Perches956b9ba2012-04-29 17:08:39 -0300876 edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n",
877 cs, *mask1, reg1);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200878 }
879}
880
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200881static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt, int cs)
Doug Thompson94be4bf2009-04-27 16:12:00 +0200882{
883 enum mem_type type;
884
Borislav Petkovcb328502010-12-22 14:28:24 +0100885 /* F15h supports only DDR3 */
886 if (boot_cpu_data.x86 >= 0x15)
887 type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
888 else if (boot_cpu_data.x86 == 0x10 || pvt->ext_model >= K8_REV_F) {
Borislav Petkov6b4c0bd2009-11-12 15:37:57 +0100889 if (pvt->dchr0 & DDR3_MODE)
890 type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
891 else
892 type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
Doug Thompson94be4bf2009-04-27 16:12:00 +0200893 } else {
Doug Thompson94be4bf2009-04-27 16:12:00 +0200894 type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
895 }
896
Borislav Petkov24f9a7f2010-10-07 18:29:15 +0200897 amd64_info("CS%d: %s\n", cs, edac_mem_types[type]);
Doug Thompson94be4bf2009-04-27 16:12:00 +0200898
899 return type;
900}
901
Borislav Petkovcb328502010-12-22 14:28:24 +0100902/* Get the number of DCT channels the memory controller is using. */
Doug Thompsonddff8762009-04-27 16:14:52 +0200903static int k8_early_channel_count(struct amd64_pvt *pvt)
904{
Borislav Petkovcb328502010-12-22 14:28:24 +0100905 int flag;
Doug Thompsonddff8762009-04-27 16:14:52 +0200906
Borislav Petkov9f56da02010-10-01 19:44:53 +0200907 if (pvt->ext_model >= K8_REV_F)
Doug Thompsonddff8762009-04-27 16:14:52 +0200908 /* RevF (NPT) and later */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +0100909 flag = pvt->dclr0 & WIDTH_128;
Borislav Petkov9f56da02010-10-01 19:44:53 +0200910 else
Doug Thompsonddff8762009-04-27 16:14:52 +0200911 /* RevE and earlier */
912 flag = pvt->dclr0 & REVE_WIDTH_128;
Doug Thompsonddff8762009-04-27 16:14:52 +0200913
914 /* not used */
915 pvt->dclr1 = 0;
916
917 return (flag) ? 2 : 1;
918}
919
Borislav Petkov70046622011-01-10 14:37:27 +0100920/* On F10h and later ErrAddr is MC4_ADDR[47:1] */
921static u64 get_error_address(struct mce *m)
Doug Thompsonddff8762009-04-27 16:14:52 +0200922{
Borislav Petkovc1ae6832011-03-30 15:42:10 +0200923 struct cpuinfo_x86 *c = &boot_cpu_data;
924 u64 addr;
Borislav Petkov70046622011-01-10 14:37:27 +0100925 u8 start_bit = 1;
926 u8 end_bit = 47;
927
Borislav Petkovc1ae6832011-03-30 15:42:10 +0200928 if (c->x86 == 0xf) {
Borislav Petkov70046622011-01-10 14:37:27 +0100929 start_bit = 3;
930 end_bit = 39;
931 }
932
Borislav Petkovc1ae6832011-03-30 15:42:10 +0200933 addr = m->addr & GENMASK(start_bit, end_bit);
934
935 /*
936 * Erratum 637 workaround
937 */
938 if (c->x86 == 0x15) {
939 struct amd64_pvt *pvt;
940 u64 cc6_base, tmp_addr;
941 u32 tmp;
942 u8 mce_nid, intlv_en;
943
944 if ((addr & GENMASK(24, 47)) >> 24 != 0x00fdf7)
945 return addr;
946
947 mce_nid = amd_get_nb_id(m->extcpu);
948 pvt = mcis[mce_nid]->pvt_info;
949
950 amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
951 intlv_en = tmp >> 21 & 0x7;
952
953 /* add [47:27] + 3 trailing bits */
954 cc6_base = (tmp & GENMASK(0, 20)) << 3;
955
956 /* reverse and add DramIntlvEn */
957 cc6_base |= intlv_en ^ 0x7;
958
959 /* pin at [47:24] */
960 cc6_base <<= 24;
961
962 if (!intlv_en)
963 return cc6_base | (addr & GENMASK(0, 23));
964
965 amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
966
967 /* faster log2 */
968 tmp_addr = (addr & GENMASK(12, 23)) << __fls(intlv_en + 1);
969
970 /* OR DramIntlvSel into bits [14:12] */
971 tmp_addr |= (tmp & GENMASK(21, 23)) >> 9;
972
973 /* add remaining [11:0] bits from original MC4_ADDR */
974 tmp_addr |= addr & GENMASK(0, 11);
975
976 return cc6_base | tmp_addr;
977 }
978
979 return addr;
Doug Thompsonddff8762009-04-27 16:14:52 +0200980}
981
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200982static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
Doug Thompsonddff8762009-04-27 16:14:52 +0200983{
Borislav Petkovf08e4572011-03-21 20:45:06 +0100984 struct cpuinfo_x86 *c = &boot_cpu_data;
Borislav Petkov71d2a322011-02-21 19:37:24 +0100985 int off = range << 3;
Doug Thompsonddff8762009-04-27 16:14:52 +0200986
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200987 amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo);
988 amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
Doug Thompsonddff8762009-04-27 16:14:52 +0200989
Borislav Petkovf08e4572011-03-21 20:45:06 +0100990 if (c->x86 == 0xf)
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200991 return;
Doug Thompsonddff8762009-04-27 16:14:52 +0200992
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200993 if (!dram_rw(pvt, range))
994 return;
Doug Thompsonddff8762009-04-27 16:14:52 +0200995
Borislav Petkov7f19bf72010-10-21 18:52:53 +0200996 amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi);
997 amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
Borislav Petkovf08e4572011-03-21 20:45:06 +0100998
999 /* Factor in CC6 save area by reading dst node's limit reg */
1000 if (c->x86 == 0x15) {
1001 struct pci_dev *f1 = NULL;
1002 u8 nid = dram_dst_node(pvt, range);
1003 u32 llim;
1004
1005 f1 = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x18 + nid, 1));
1006 if (WARN_ON(!f1))
1007 return;
1008
1009 amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
1010
1011 pvt->ranges[range].lim.lo &= GENMASK(0, 15);
1012
1013 /* {[39:27],111b} */
1014 pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
1015
1016 pvt->ranges[range].lim.hi &= GENMASK(0, 7);
1017
1018 /* [47:40] */
1019 pvt->ranges[range].lim.hi |= llim >> 13;
1020
1021 pci_dev_put(f1);
1022 }
Doug Thompsonddff8762009-04-27 16:14:52 +02001023}
1024
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001025static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
1026 u16 syndrome)
Doug Thompsonddff8762009-04-27 16:14:52 +02001027{
1028 struct mem_ctl_info *src_mci;
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001029 struct amd64_pvt *pvt = mci->pvt_info;
Doug Thompsonddff8762009-04-27 16:14:52 +02001030 int channel, csrow;
1031 u32 page, offset;
Doug Thompsonddff8762009-04-27 16:14:52 +02001032
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001033 error_address_to_page_and_offset(sys_addr, &page, &offset);
1034
1035 /*
1036 * Find out which node the error address belongs to. This may be
1037 * different from the node that detected the error.
1038 */
1039 src_mci = find_mc_by_sys_addr(mci, sys_addr);
1040 if (!src_mci) {
1041 amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1042 (unsigned long)sys_addr);
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001043 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001044 page, offset, syndrome,
1045 -1, -1, -1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001046 "failed to map error addr to a node",
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001047 "");
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001048 return;
1049 }
1050
1051 /* Now map the sys_addr to a CSROW */
1052 csrow = sys_addr_to_csrow(src_mci, sys_addr);
1053 if (csrow < 0) {
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001054 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001055 page, offset, syndrome,
1056 -1, -1, -1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001057 "failed to map error addr to a csrow",
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001058 "");
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001059 return;
1060 }
1061
Doug Thompsonddff8762009-04-27 16:14:52 +02001062 /* CHIPKILL enabled */
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001063 if (pvt->nbcfg & NBCFG_CHIPKILL) {
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001064 channel = get_channel_from_ecc_syndrome(mci, syndrome);
Doug Thompsonddff8762009-04-27 16:14:52 +02001065 if (channel < 0) {
1066 /*
1067 * Syndrome didn't map, so we don't know which of the
1068 * 2 DIMMs is in error. So we need to ID 'both' of them
1069 * as suspect.
1070 */
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001071 amd64_mc_warn(src_mci, "unknown syndrome 0x%04x - "
1072 "possible error reporting race\n",
1073 syndrome);
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001074 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001075 page, offset, syndrome,
1076 csrow, -1, -1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001077 "unknown syndrome - possible error reporting race",
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001078 "");
Doug Thompsonddff8762009-04-27 16:14:52 +02001079 return;
1080 }
1081 } else {
1082 /*
1083 * non-chipkill ecc mode
1084 *
1085 * The k8 documentation is unclear about how to determine the
1086 * channel number when using non-chipkill memory. This method
1087 * was obtained from email communication with someone at AMD.
1088 * (Wish the email was placed in this comment - norsk)
1089 */
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001090 channel = ((sys_addr & BIT(3)) != 0);
Doug Thompsonddff8762009-04-27 16:14:52 +02001091 }
1092
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001093 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, src_mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001094 page, offset, syndrome,
1095 csrow, channel, -1,
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001096 "", "");
Doug Thompsonddff8762009-04-27 16:14:52 +02001097}
1098
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001099static int ddr2_cs_size(unsigned i, bool dct_width)
Doug Thompsonddff8762009-04-27 16:14:52 +02001100{
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001101 unsigned shift = 0;
Doug Thompsonddff8762009-04-27 16:14:52 +02001102
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001103 if (i <= 2)
1104 shift = i;
1105 else if (!(i & 0x1))
1106 shift = i >> 1;
Borislav Petkov1433eb92009-10-21 13:44:36 +02001107 else
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001108 shift = (i + 1) >> 1;
Doug Thompsonddff8762009-04-27 16:14:52 +02001109
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001110 return 128 << (shift + !!dct_width);
1111}
1112
1113static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1114 unsigned cs_mode)
1115{
1116 u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
1117
1118 if (pvt->ext_model >= K8_REV_F) {
1119 WARN_ON(cs_mode > 11);
1120 return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
1121 }
1122 else if (pvt->ext_model >= K8_REV_D) {
Borislav Petkov11b0a312011-11-09 21:28:43 +01001123 unsigned diff;
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001124 WARN_ON(cs_mode > 10);
1125
Borislav Petkov11b0a312011-11-09 21:28:43 +01001126 /*
1127 * the below calculation, besides trying to win an obfuscated C
1128 * contest, maps cs_mode values to DIMM chip select sizes. The
1129 * mappings are:
1130 *
1131 * cs_mode CS size (mb)
1132 * ======= ============
1133 * 0 32
1134 * 1 64
1135 * 2 128
1136 * 3 128
1137 * 4 256
1138 * 5 512
1139 * 6 256
1140 * 7 512
1141 * 8 1024
1142 * 9 1024
1143 * 10 2048
1144 *
1145 * Basically, it calculates a value with which to shift the
1146 * smallest CS size of 32MB.
1147 *
1148 * ddr[23]_cs_size have a similar purpose.
1149 */
1150 diff = cs_mode/3 + (unsigned)(cs_mode > 5);
1151
1152 return 32 << (cs_mode - diff);
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001153 }
1154 else {
1155 WARN_ON(cs_mode > 6);
1156 return 32 << cs_mode;
1157 }
Doug Thompsonddff8762009-04-27 16:14:52 +02001158}
1159
Doug Thompson1afd3c92009-04-27 16:16:50 +02001160/*
1161 * Get the number of DCT channels in use.
1162 *
1163 * Return:
1164 * number of Memory Channels in operation
1165 * Pass back:
1166 * contents of the DCL0_LOW register
1167 */
Borislav Petkov7d20d142011-01-07 17:58:04 +01001168static int f1x_early_channel_count(struct amd64_pvt *pvt)
Doug Thompson1afd3c92009-04-27 16:16:50 +02001169{
Borislav Petkov6ba5dcd2009-10-13 19:26:55 +02001170 int i, j, channels = 0;
Doug Thompsonddff8762009-04-27 16:14:52 +02001171
Borislav Petkov7d20d142011-01-07 17:58:04 +01001172 /* On F10h, if we are in 128 bit mode, then we are using 2 channels */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001173 if (boot_cpu_data.x86 == 0x10 && (pvt->dclr0 & WIDTH_128))
Borislav Petkov7d20d142011-01-07 17:58:04 +01001174 return 2;
Doug Thompson1afd3c92009-04-27 16:16:50 +02001175
1176 /*
Borislav Petkovd16149e2009-10-16 19:55:49 +02001177 * Need to check if in unganged mode: In such, there are 2 channels,
1178 * but they are not in 128 bit mode and thus the above 'dclr0' status
1179 * bit will be OFF.
Doug Thompson1afd3c92009-04-27 16:16:50 +02001180 *
1181 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
1182 * their CSEnable bit on. If so, then SINGLE DIMM case.
1183 */
Joe Perches956b9ba2012-04-29 17:08:39 -03001184 edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
Doug Thompson1afd3c92009-04-27 16:16:50 +02001185
1186 /*
1187 * Check DRAM Bank Address Mapping values for each DIMM to see if there
1188 * is more than just one DIMM present in unganged mode. Need to check
1189 * both controllers since DIMMs can be placed in either one.
1190 */
Borislav Petkov525a1b22010-12-21 15:53:27 +01001191 for (i = 0; i < 2; i++) {
1192 u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001193
Wan Wei57a30852009-08-07 17:04:49 +02001194 for (j = 0; j < 4; j++) {
1195 if (DBAM_DIMM(j, dbam) > 0) {
1196 channels++;
1197 break;
1198 }
1199 }
Doug Thompson1afd3c92009-04-27 16:16:50 +02001200 }
1201
Borislav Petkovd16149e2009-10-16 19:55:49 +02001202 if (channels > 2)
1203 channels = 2;
1204
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001205 amd64_info("MCT channel count: %d\n", channels);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001206
1207 return channels;
Doug Thompson1afd3c92009-04-27 16:16:50 +02001208}
1209
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001210static int ddr3_cs_size(unsigned i, bool dct_width)
Doug Thompson1afd3c92009-04-27 16:16:50 +02001211{
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001212 unsigned shift = 0;
1213 int cs_size = 0;
1214
1215 if (i == 0 || i == 3 || i == 4)
1216 cs_size = -1;
1217 else if (i <= 2)
1218 shift = i;
1219 else if (i == 12)
1220 shift = 7;
1221 else if (!(i & 0x1))
1222 shift = i >> 1;
1223 else
1224 shift = (i + 1) >> 1;
1225
1226 if (cs_size != -1)
1227 cs_size = (128 * (1 << !!dct_width)) << shift;
1228
1229 return cs_size;
1230}
1231
1232static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1233 unsigned cs_mode)
1234{
1235 u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
1236
1237 WARN_ON(cs_mode > 11);
Borislav Petkov1433eb92009-10-21 13:44:36 +02001238
1239 if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001240 return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
Borislav Petkov1433eb92009-10-21 13:44:36 +02001241 else
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001242 return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
1243}
Borislav Petkov1433eb92009-10-21 13:44:36 +02001244
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001245/*
1246 * F15h supports only 64bit DCT interfaces
1247 */
1248static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1249 unsigned cs_mode)
1250{
1251 WARN_ON(cs_mode > 12);
1252
1253 return ddr3_cs_size(cs_mode, false);
Doug Thompson1afd3c92009-04-27 16:16:50 +02001254}
1255
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001256static void read_dram_ctl_register(struct amd64_pvt *pvt)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001257{
Doug Thompson6163b5d2009-04-27 16:20:17 +02001258
Borislav Petkov5a5d2372011-01-17 17:52:57 +01001259 if (boot_cpu_data.x86 == 0xf)
1260 return;
1261
Borislav Petkov78da1212010-12-22 19:31:45 +01001262 if (!amd64_read_dct_pci_cfg(pvt, DCT_SEL_LO, &pvt->dct_sel_lo)) {
Joe Perches956b9ba2012-04-29 17:08:39 -03001263 edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
1264 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001265
Joe Perches956b9ba2012-04-29 17:08:39 -03001266 edac_dbg(0, " DCTs operate in %s mode\n",
1267 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001268
Borislav Petkov72381bd2009-10-09 19:14:43 +02001269 if (!dct_ganging_enabled(pvt))
Joe Perches956b9ba2012-04-29 17:08:39 -03001270 edac_dbg(0, " Address range split per DCT: %s\n",
1271 (dct_high_range_enabled(pvt) ? "yes" : "no"));
Borislav Petkov72381bd2009-10-09 19:14:43 +02001272
Joe Perches956b9ba2012-04-29 17:08:39 -03001273 edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
1274 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
1275 (dct_memory_cleared(pvt) ? "yes" : "no"));
Borislav Petkov72381bd2009-10-09 19:14:43 +02001276
Joe Perches956b9ba2012-04-29 17:08:39 -03001277 edac_dbg(0, " channel interleave: %s, "
1278 "interleave bits selector: 0x%x\n",
1279 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
1280 dct_sel_interleave_addr(pvt));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001281 }
1282
Borislav Petkov78da1212010-12-22 19:31:45 +01001283 amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001284}
1285
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001286/*
Borislav Petkov229a7a12010-12-09 18:57:54 +01001287 * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001288 * Interleaving Modes.
1289 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001290static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
Borislav Petkov229a7a12010-12-09 18:57:54 +01001291 bool hi_range_sel, u8 intlv_en)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001292{
Borislav Petkov151fa712011-02-21 19:33:10 +01001293 u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001294
1295 if (dct_ganging_enabled(pvt))
Borislav Petkov229a7a12010-12-09 18:57:54 +01001296 return 0;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001297
Borislav Petkov229a7a12010-12-09 18:57:54 +01001298 if (hi_range_sel)
1299 return dct_sel_high;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001300
Borislav Petkov229a7a12010-12-09 18:57:54 +01001301 /*
1302 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1303 */
1304 if (dct_interleave_enabled(pvt)) {
1305 u8 intlv_addr = dct_sel_interleave_addr(pvt);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001306
Borislav Petkov229a7a12010-12-09 18:57:54 +01001307 /* return DCT select function: 0=DCT0, 1=DCT1 */
1308 if (!intlv_addr)
1309 return sys_addr >> 6 & 1;
1310
1311 if (intlv_addr & 0x2) {
1312 u8 shift = intlv_addr & 0x1 ? 9 : 6;
1313 u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2;
1314
1315 return ((sys_addr >> shift) & 1) ^ temp;
1316 }
1317
1318 return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1319 }
1320
1321 if (dct_high_range_enabled(pvt))
1322 return ~dct_sel_high & 1;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001323
1324 return 0;
1325}
1326
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001327/* Convert the sys_addr to the normalized DCT address */
Borislav Petkove7613592011-02-21 19:49:01 +01001328static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, unsigned range,
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001329 u64 sys_addr, bool hi_rng,
1330 u32 dct_sel_base_addr)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001331{
1332 u64 chan_off;
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001333 u64 dram_base = get_dram_base(pvt, range);
1334 u64 hole_off = f10_dhar_offset(pvt);
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001335 u64 dct_sel_base_off = (pvt->dct_sel_hi & 0xFFFFFC00) << 16;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001336
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001337 if (hi_rng) {
1338 /*
1339 * if
1340 * base address of high range is below 4Gb
1341 * (bits [47:27] at [31:11])
1342 * DRAM address space on this DCT is hoisted above 4Gb &&
1343 * sys_addr > 4Gb
1344 *
1345 * remove hole offset from sys_addr
1346 * else
1347 * remove high range offset from sys_addr
1348 */
1349 if ((!(dct_sel_base_addr >> 16) ||
1350 dct_sel_base_addr < dhar_base(pvt)) &&
Borislav Petkov972ea172011-02-21 19:43:02 +01001351 dhar_valid(pvt) &&
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001352 (sys_addr >= BIT_64(32)))
Borislav Petkovbc21fa52010-11-11 17:29:13 +01001353 chan_off = hole_off;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001354 else
1355 chan_off = dct_sel_base_off;
1356 } else {
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001357 /*
1358 * if
1359 * we have a valid hole &&
1360 * sys_addr > 4Gb
1361 *
1362 * remove hole
1363 * else
1364 * remove dram base to normalize to DCT address
1365 */
Borislav Petkov972ea172011-02-21 19:43:02 +01001366 if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
Borislav Petkovbc21fa52010-11-11 17:29:13 +01001367 chan_off = hole_off;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001368 else
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001369 chan_off = dram_base;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001370 }
1371
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001372 return (sys_addr & GENMASK(6,47)) - (chan_off & GENMASK(23,47));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001373}
1374
Doug Thompson6163b5d2009-04-27 16:20:17 +02001375/*
1376 * checks if the csrow passed in is marked as SPARED, if so returns the new
1377 * spare row
1378 */
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001379static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001380{
Borislav Petkov614ec9d2011-01-13 18:02:22 +01001381 int tmp_cs;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001382
Borislav Petkov614ec9d2011-01-13 18:02:22 +01001383 if (online_spare_swap_done(pvt, dct) &&
1384 csrow == online_spare_bad_dramcs(pvt, dct)) {
1385
1386 for_each_chip_select(tmp_cs, dct, pvt) {
1387 if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1388 csrow = tmp_cs;
1389 break;
1390 }
1391 }
Doug Thompson6163b5d2009-04-27 16:20:17 +02001392 }
1393 return csrow;
1394}
1395
1396/*
1397 * Iterate over the DRAM DCT "base" and "mask" registers looking for a
1398 * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
1399 *
1400 * Return:
1401 * -EINVAL: NOT FOUND
1402 * 0..csrow = Chip-Select Row
1403 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001404static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct)
Doug Thompson6163b5d2009-04-27 16:20:17 +02001405{
1406 struct mem_ctl_info *mci;
1407 struct amd64_pvt *pvt;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001408 u64 cs_base, cs_mask;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001409 int cs_found = -EINVAL;
1410 int csrow;
1411
Borislav Petkovcc4d8862010-10-13 16:11:59 +02001412 mci = mcis[nid];
Doug Thompson6163b5d2009-04-27 16:20:17 +02001413 if (!mci)
1414 return cs_found;
1415
1416 pvt = mci->pvt_info;
1417
Joe Perches956b9ba2012-04-29 17:08:39 -03001418 edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001419
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001420 for_each_chip_select(csrow, dct, pvt) {
1421 if (!csrow_enabled(csrow, dct, pvt))
Doug Thompson6163b5d2009-04-27 16:20:17 +02001422 continue;
1423
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001424 get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001425
Joe Perches956b9ba2012-04-29 17:08:39 -03001426 edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
1427 csrow, cs_base, cs_mask);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001428
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001429 cs_mask = ~cs_mask;
Doug Thompson6163b5d2009-04-27 16:20:17 +02001430
Joe Perches956b9ba2012-04-29 17:08:39 -03001431 edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
1432 (in_addr & cs_mask), (cs_base & cs_mask));
Doug Thompson6163b5d2009-04-27 16:20:17 +02001433
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001434 if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
1435 cs_found = f10_process_possible_spare(pvt, dct, csrow);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001436
Joe Perches956b9ba2012-04-29 17:08:39 -03001437 edac_dbg(1, " MATCH csrow=%d\n", cs_found);
Doug Thompson6163b5d2009-04-27 16:20:17 +02001438 break;
1439 }
1440 }
1441 return cs_found;
1442}
1443
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001444/*
1445 * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
1446 * swapped with a region located at the bottom of memory so that the GPU can use
1447 * the interleaved region and thus two channels.
1448 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001449static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001450{
1451 u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
1452
1453 if (boot_cpu_data.x86 == 0x10) {
1454 /* only revC3 and revE have that feature */
1455 if (boot_cpu_data.x86_model < 4 ||
1456 (boot_cpu_data.x86_model < 0xa &&
1457 boot_cpu_data.x86_mask < 3))
1458 return sys_addr;
1459 }
1460
1461 amd64_read_dct_pci_cfg(pvt, SWAP_INTLV_REG, &swap_reg);
1462
1463 if (!(swap_reg & 0x1))
1464 return sys_addr;
1465
1466 swap_base = (swap_reg >> 3) & 0x7f;
1467 swap_limit = (swap_reg >> 11) & 0x7f;
1468 rgn_size = (swap_reg >> 20) & 0x7f;
1469 tmp_addr = sys_addr >> 27;
1470
1471 if (!(sys_addr >> 34) &&
1472 (((tmp_addr >= swap_base) &&
1473 (tmp_addr <= swap_limit)) ||
1474 (tmp_addr < rgn_size)))
1475 return sys_addr ^ (u64)swap_base << 27;
1476
1477 return sys_addr;
1478}
1479
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001480/* For a given @dram_range, check if @sys_addr falls within it. */
Borislav Petkove7613592011-02-21 19:49:01 +01001481static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001482 u64 sys_addr, int *nid, int *chan_sel)
1483{
Borislav Petkov229a7a12010-12-09 18:57:54 +01001484 int cs_found = -EINVAL;
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001485 u64 chan_addr;
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001486 u32 dct_sel_base;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001487 u8 channel;
Borislav Petkov229a7a12010-12-09 18:57:54 +01001488 bool high_range = false;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001489
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001490 u8 node_id = dram_dst_node(pvt, range);
Borislav Petkov229a7a12010-12-09 18:57:54 +01001491 u8 intlv_en = dram_intlv_en(pvt, range);
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001492 u32 intlv_sel = dram_intlv_sel(pvt, range);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001493
Joe Perches956b9ba2012-04-29 17:08:39 -03001494 edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1495 range, sys_addr, get_dram_limit(pvt, range));
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001496
Borislav Petkov355fba62011-01-17 13:03:26 +01001497 if (dhar_valid(pvt) &&
1498 dhar_base(pvt) <= sys_addr &&
1499 sys_addr < BIT_64(32)) {
1500 amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1501 sys_addr);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001502 return -EINVAL;
Borislav Petkov355fba62011-01-17 13:03:26 +01001503 }
1504
Borislav Petkovf030ddf2011-04-08 15:05:21 +02001505 if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
Borislav Petkov355fba62011-01-17 13:03:26 +01001506 return -EINVAL;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001507
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001508 sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
Borislav Petkov95b0ef52011-01-11 22:08:07 +01001509
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001510 dct_sel_base = dct_sel_baseaddr(pvt);
1511
1512 /*
1513 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1514 * select between DCT0 and DCT1.
1515 */
1516 if (dct_high_range_enabled(pvt) &&
1517 !dct_ganging_enabled(pvt) &&
1518 ((sys_addr >> 27) >= (dct_sel_base >> 11)))
Borislav Petkov229a7a12010-12-09 18:57:54 +01001519 high_range = true;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001520
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001521 channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001522
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001523 chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
Borislav Petkovc8e518d2010-12-10 19:49:19 +01001524 high_range, dct_sel_base);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001525
Borislav Petkove2f79db2011-01-13 14:57:34 +01001526 /* Remove node interleaving, see F1x120 */
1527 if (intlv_en)
1528 chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1529 (chan_addr & 0xfff);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001530
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001531 /* remove channel interleave */
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001532 if (dct_interleave_enabled(pvt) &&
1533 !dct_high_range_enabled(pvt) &&
1534 !dct_ganging_enabled(pvt)) {
Borislav Petkov5d4b58e2011-01-13 16:01:13 +01001535
1536 if (dct_sel_interleave_addr(pvt) != 1) {
1537 if (dct_sel_interleave_addr(pvt) == 0x3)
1538 /* hash 9 */
1539 chan_addr = ((chan_addr >> 10) << 9) |
1540 (chan_addr & 0x1ff);
1541 else
1542 /* A[6] or hash 6 */
1543 chan_addr = ((chan_addr >> 7) << 6) |
1544 (chan_addr & 0x3f);
1545 } else
1546 /* A[12] */
1547 chan_addr = ((chan_addr >> 13) << 12) |
1548 (chan_addr & 0xfff);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001549 }
1550
Joe Perches956b9ba2012-04-29 17:08:39 -03001551 edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001552
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001553 cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001554
1555 if (cs_found >= 0) {
1556 *nid = node_id;
1557 *chan_sel = channel;
1558 }
1559 return cs_found;
1560}
1561
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001562static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001563 int *node, int *chan_sel)
1564{
Borislav Petkove7613592011-02-21 19:49:01 +01001565 int cs_found = -EINVAL;
1566 unsigned range;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001567
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001568 for (range = 0; range < DRAM_RANGES; range++) {
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001569
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001570 if (!dram_rw(pvt, range))
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001571 continue;
1572
Borislav Petkov7f19bf72010-10-21 18:52:53 +02001573 if ((get_dram_base(pvt, range) <= sys_addr) &&
1574 (get_dram_limit(pvt, range) >= sys_addr)) {
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001575
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001576 cs_found = f1x_match_to_this_node(pvt, range,
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001577 sys_addr, node,
1578 chan_sel);
1579 if (cs_found >= 0)
1580 break;
1581 }
1582 }
1583 return cs_found;
1584}
1585
1586/*
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001587 * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1588 * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001589 *
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001590 * The @sys_addr is usually an error address received from the hardware
1591 * (MCX_ADDR).
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001592 */
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001593static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001594 u16 syndrome)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001595{
1596 struct amd64_pvt *pvt = mci->pvt_info;
1597 u32 page, offset;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001598 int nid, csrow, chan = 0;
1599
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001600 error_address_to_page_and_offset(sys_addr, &page, &offset);
1601
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001602 csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001603
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001604 if (csrow < 0) {
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001605 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001606 page, offset, syndrome,
1607 -1, -1, -1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001608 "failed to map error addr to a csrow",
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001609 "");
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001610 return;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001611 }
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001612
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001613 /*
1614 * We need the syndromes for channel detection only when we're
1615 * ganged. Otherwise @chan should already contain the channel at
1616 * this point.
1617 */
Borislav Petkova97fa682010-12-23 14:07:18 +01001618 if (dct_ganging_enabled(pvt))
Borislav Petkovbdc30a02009-11-13 15:10:43 +01001619 chan = get_channel_from_ecc_syndrome(mci, syndrome);
1620
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001621 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001622 page, offset, syndrome,
1623 csrow, chan, -1,
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001624 "", "");
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001625}
1626
1627/*
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001628 * debug routine to display the memory sizes of all logical DIMMs and its
Borislav Petkovcb328502010-12-22 14:28:24 +01001629 * CSROWs
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001630 */
Borislav Petkov8c671752011-02-23 17:25:12 +01001631static void amd64_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001632{
Borislav Petkov603adaf2009-12-21 14:52:53 +01001633 int dimm, size0, size1, factor = 0;
Borislav Petkov525a1b22010-12-21 15:53:27 +01001634 u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
1635 u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001636
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001637 if (boot_cpu_data.x86 == 0xf) {
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001638 if (pvt->dclr0 & WIDTH_128)
Borislav Petkov603adaf2009-12-21 14:52:53 +01001639 factor = 1;
1640
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001641 /* K8 families < revF not supported yet */
Borislav Petkov1433eb92009-10-21 13:44:36 +02001642 if (pvt->ext_model < K8_REV_F)
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001643 return;
1644 else
1645 WARN_ON(ctrl != 0);
1646 }
1647
Borislav Petkov4d796362011-02-03 15:59:57 +01001648 dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 : pvt->dbam0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001649 dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->csels[1].csbases
1650 : pvt->csels[0].csbases;
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001651
Joe Perches956b9ba2012-04-29 17:08:39 -03001652 edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
1653 ctrl, dbam);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001654
Borislav Petkov8566c4d2009-10-16 13:48:28 +02001655 edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
1656
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001657 /* Dump memory sizes for DIMM and its CSROWs */
1658 for (dimm = 0; dimm < 4; dimm++) {
1659
1660 size0 = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001661 if (dcsb[dimm*2] & DCSB_CS_ENABLE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001662 size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
1663 DBAM_DIMM(dimm, dbam));
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001664
1665 size1 = 0;
Borislav Petkov11c75ea2010-11-29 19:49:02 +01001666 if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001667 size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
1668 DBAM_DIMM(dimm, dbam));
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001669
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001670 amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1671 dimm * 2, size0 << factor,
1672 dimm * 2 + 1, size1 << factor);
Doug Thompsonf71d0a02009-04-27 16:22:43 +02001673 }
1674}
1675
Doug Thompson4d376072009-04-27 16:25:05 +02001676static struct amd64_family_type amd64_family_types[] = {
1677 [K8_CPUS] = {
Borislav Petkov0092b202010-10-01 19:20:05 +02001678 .ctl_name = "K8",
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001679 .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
1680 .f3_id = PCI_DEVICE_ID_AMD_K8_NB_MISC,
Doug Thompson4d376072009-04-27 16:25:05 +02001681 .ops = {
Borislav Petkov1433eb92009-10-21 13:44:36 +02001682 .early_channel_count = k8_early_channel_count,
Borislav Petkov1433eb92009-10-21 13:44:36 +02001683 .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow,
1684 .dbam_to_cs = k8_dbam_to_chip_select,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001685 .read_dct_pci_cfg = k8_read_dct_pci_cfg,
Doug Thompson4d376072009-04-27 16:25:05 +02001686 }
1687 },
1688 [F10_CPUS] = {
Borislav Petkov0092b202010-10-01 19:20:05 +02001689 .ctl_name = "F10h",
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02001690 .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
1691 .f3_id = PCI_DEVICE_ID_AMD_10H_NB_MISC,
Doug Thompson4d376072009-04-27 16:25:05 +02001692 .ops = {
Borislav Petkov7d20d142011-01-07 17:58:04 +01001693 .early_channel_count = f1x_early_channel_count,
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001694 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
Borislav Petkov1433eb92009-10-21 13:44:36 +02001695 .dbam_to_cs = f10_dbam_to_chip_select,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001696 .read_dct_pci_cfg = f10_read_dct_pci_cfg,
1697 }
1698 },
1699 [F15_CPUS] = {
1700 .ctl_name = "F15h",
Borislav Petkovdf71a052011-01-19 18:15:10 +01001701 .f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
1702 .f3_id = PCI_DEVICE_ID_AMD_15H_NB_F3,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001703 .ops = {
Borislav Petkov7d20d142011-01-07 17:58:04 +01001704 .early_channel_count = f1x_early_channel_count,
Borislav Petkovb15f0fc2011-01-17 15:59:58 +01001705 .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01001706 .dbam_to_cs = f15_dbam_to_chip_select,
Borislav Petkovb2b0c602010-10-08 18:32:29 +02001707 .read_dct_pci_cfg = f15_read_dct_pci_cfg,
Doug Thompson4d376072009-04-27 16:25:05 +02001708 }
1709 },
Doug Thompson4d376072009-04-27 16:25:05 +02001710};
1711
1712static struct pci_dev *pci_get_related_function(unsigned int vendor,
1713 unsigned int device,
1714 struct pci_dev *related)
1715{
1716 struct pci_dev *dev = NULL;
1717
1718 dev = pci_get_device(vendor, device, dev);
1719 while (dev) {
1720 if ((dev->bus->number == related->bus->number) &&
1721 (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
1722 break;
1723 dev = pci_get_device(vendor, device, dev);
1724 }
1725
1726 return dev;
1727}
1728
Doug Thompsonb1289d62009-04-27 16:37:05 +02001729/*
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001730 * These are tables of eigenvectors (one per line) which can be used for the
1731 * construction of the syndrome tables. The modified syndrome search algorithm
1732 * uses those to find the symbol in error and thus the DIMM.
Doug Thompsonb1289d62009-04-27 16:37:05 +02001733 *
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001734 * Algorithm courtesy of Ross LaFetra from AMD.
Doug Thompsonb1289d62009-04-27 16:37:05 +02001735 */
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001736static u16 x4_vectors[] = {
1737 0x2f57, 0x1afe, 0x66cc, 0xdd88,
1738 0x11eb, 0x3396, 0x7f4c, 0xeac8,
1739 0x0001, 0x0002, 0x0004, 0x0008,
1740 0x1013, 0x3032, 0x4044, 0x8088,
1741 0x106b, 0x30d6, 0x70fc, 0xe0a8,
1742 0x4857, 0xc4fe, 0x13cc, 0x3288,
1743 0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
1744 0x1f39, 0x251e, 0xbd6c, 0x6bd8,
1745 0x15c1, 0x2a42, 0x89ac, 0x4758,
1746 0x2b03, 0x1602, 0x4f0c, 0xca08,
1747 0x1f07, 0x3a0e, 0x6b04, 0xbd08,
1748 0x8ba7, 0x465e, 0x244c, 0x1cc8,
1749 0x2b87, 0x164e, 0x642c, 0xdc18,
1750 0x40b9, 0x80de, 0x1094, 0x20e8,
1751 0x27db, 0x1eb6, 0x9dac, 0x7b58,
1752 0x11c1, 0x2242, 0x84ac, 0x4c58,
1753 0x1be5, 0x2d7a, 0x5e34, 0xa718,
1754 0x4b39, 0x8d1e, 0x14b4, 0x28d8,
1755 0x4c97, 0xc87e, 0x11fc, 0x33a8,
1756 0x8e97, 0x497e, 0x2ffc, 0x1aa8,
1757 0x16b3, 0x3d62, 0x4f34, 0x8518,
1758 0x1e2f, 0x391a, 0x5cac, 0xf858,
1759 0x1d9f, 0x3b7a, 0x572c, 0xfe18,
1760 0x15f5, 0x2a5a, 0x5264, 0xa3b8,
1761 0x1dbb, 0x3b66, 0x715c, 0xe3f8,
1762 0x4397, 0xc27e, 0x17fc, 0x3ea8,
1763 0x1617, 0x3d3e, 0x6464, 0xb8b8,
1764 0x23ff, 0x12aa, 0xab6c, 0x56d8,
1765 0x2dfb, 0x1ba6, 0x913c, 0x7328,
1766 0x185d, 0x2ca6, 0x7914, 0x9e28,
1767 0x171b, 0x3e36, 0x7d7c, 0xebe8,
1768 0x4199, 0x82ee, 0x19f4, 0x2e58,
1769 0x4807, 0xc40e, 0x130c, 0x3208,
1770 0x1905, 0x2e0a, 0x5804, 0xac08,
1771 0x213f, 0x132a, 0xadfc, 0x5ba8,
1772 0x19a9, 0x2efe, 0xb5cc, 0x6f88,
Doug Thompsonb1289d62009-04-27 16:37:05 +02001773};
1774
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001775static u16 x8_vectors[] = {
1776 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
1777 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
1778 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
1779 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
1780 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
1781 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
1782 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
1783 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
1784 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
1785 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
1786 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
1787 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
1788 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
1789 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
1790 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
1791 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
1792 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
1793 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
1794 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
1795};
1796
Borislav Petkovd34a6ec2011-02-23 17:41:50 +01001797static int decode_syndrome(u16 syndrome, u16 *vectors, unsigned num_vecs,
1798 unsigned v_dim)
Doug Thompsonb1289d62009-04-27 16:37:05 +02001799{
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001800 unsigned int i, err_sym;
Doug Thompsonb1289d62009-04-27 16:37:05 +02001801
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001802 for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
1803 u16 s = syndrome;
Borislav Petkovd34a6ec2011-02-23 17:41:50 +01001804 unsigned v_idx = err_sym * v_dim;
1805 unsigned v_end = (err_sym + 1) * v_dim;
Doug Thompsonb1289d62009-04-27 16:37:05 +02001806
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001807 /* walk over all 16 bits of the syndrome */
1808 for (i = 1; i < (1U << 16); i <<= 1) {
1809
1810 /* if bit is set in that eigenvector... */
1811 if (v_idx < v_end && vectors[v_idx] & i) {
1812 u16 ev_comp = vectors[v_idx++];
1813
1814 /* ... and bit set in the modified syndrome, */
1815 if (s & i) {
1816 /* remove it. */
1817 s ^= ev_comp;
1818
1819 if (!s)
1820 return err_sym;
1821 }
1822
1823 } else if (s & i)
1824 /* can't get to zero, move to next symbol */
1825 break;
1826 }
Doug Thompsonb1289d62009-04-27 16:37:05 +02001827 }
1828
Joe Perches956b9ba2012-04-29 17:08:39 -03001829 edac_dbg(0, "syndrome(%x) not found\n", syndrome);
Doug Thompsonb1289d62009-04-27 16:37:05 +02001830 return -1;
1831}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001832
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001833static int map_err_sym_to_channel(int err_sym, int sym_size)
1834{
1835 if (sym_size == 4)
1836 switch (err_sym) {
1837 case 0x20:
1838 case 0x21:
1839 return 0;
1840 break;
1841 case 0x22:
1842 case 0x23:
1843 return 1;
1844 break;
1845 default:
1846 return err_sym >> 4;
1847 break;
1848 }
1849 /* x8 symbols */
1850 else
1851 switch (err_sym) {
1852 /* imaginary bits not in a DIMM */
1853 case 0x10:
1854 WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
1855 err_sym);
1856 return -1;
1857 break;
1858
1859 case 0x11:
1860 return 0;
1861 break;
1862 case 0x12:
1863 return 1;
1864 break;
1865 default:
1866 return err_sym >> 3;
1867 break;
1868 }
1869 return -1;
1870}
1871
1872static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
1873{
1874 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001875 int err_sym = -1;
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001876
Borislav Petkova3b7db02011-01-19 20:35:12 +01001877 if (pvt->ecc_sym_sz == 8)
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001878 err_sym = decode_syndrome(syndrome, x8_vectors,
1879 ARRAY_SIZE(x8_vectors),
Borislav Petkova3b7db02011-01-19 20:35:12 +01001880 pvt->ecc_sym_sz);
1881 else if (pvt->ecc_sym_sz == 4)
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001882 err_sym = decode_syndrome(syndrome, x4_vectors,
1883 ARRAY_SIZE(x4_vectors),
Borislav Petkova3b7db02011-01-19 20:35:12 +01001884 pvt->ecc_sym_sz);
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001885 else {
Borislav Petkova3b7db02011-01-19 20:35:12 +01001886 amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001887 return err_sym;
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001888 }
Borislav Petkovad6a32e2010-03-09 12:46:00 +01001889
Borislav Petkova3b7db02011-01-19 20:35:12 +01001890 return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
Borislav Petkovbfc04ae2009-11-12 19:05:07 +01001891}
1892
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001893/*
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001894 * Handle any Correctable Errors (CEs) that have occurred. Check for valid ERROR
1895 * ADDRESS and process.
1896 */
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001897static void amd64_handle_ce(struct mem_ctl_info *mci, struct mce *m)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001898{
1899 struct amd64_pvt *pvt = mci->pvt_info;
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001900 u64 sys_addr;
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001901 u16 syndrome;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001902
1903 /* Ensure that the Error Address is VALID */
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001904 if (!(m->status & MCI_STATUS_ADDRV)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001905 amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001906 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001907 0, 0, 0,
1908 -1, -1, -1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001909 "HW has no ERROR_ADDRESS available",
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001910 "");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001911 return;
1912 }
1913
Borislav Petkov70046622011-01-10 14:37:27 +01001914 sys_addr = get_error_address(m);
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001915 syndrome = extract_syndrome(m->status);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001916
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001917 amd64_mc_err(mci, "CE ERROR_ADDRESS= 0x%llx\n", sys_addr);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001918
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001919 pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, syndrome);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001920}
1921
1922/* Handle any Un-correctable Errors (UEs) */
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001923static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001924{
Borislav Petkov1f6bcee2009-11-13 14:02:57 +01001925 struct mem_ctl_info *log_mci, *src_mci = NULL;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001926 int csrow;
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001927 u64 sys_addr;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001928 u32 page, offset;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001929
1930 log_mci = mci;
1931
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001932 if (!(m->status & MCI_STATUS_ADDRV)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001933 amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001934 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001935 0, 0, 0,
1936 -1, -1, -1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001937 "HW has no ERROR_ADDRESS available",
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001938 "");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001939 return;
1940 }
1941
Borislav Petkov70046622011-01-10 14:37:27 +01001942 sys_addr = get_error_address(m);
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001943 error_address_to_page_and_offset(sys_addr, &page, &offset);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001944
1945 /*
1946 * Find out which node the error address belongs to. This may be
1947 * different from the node that detected the error.
1948 */
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001949 src_mci = find_mc_by_sys_addr(mci, sys_addr);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001950 if (!src_mci) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001951 amd64_mc_err(mci, "ERROR ADDRESS (0x%lx) NOT mapped to a MC\n",
1952 (unsigned long)sys_addr);
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001953 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001954 page, offset, 0,
1955 -1, -1, -1,
Mauro Carvalho Chehab075f3092012-05-22 09:06:17 -03001956 "ERROR ADDRESS NOT mapped to a MC",
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001957 "");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001958 return;
1959 }
1960
1961 log_mci = src_mci;
1962
Borislav Petkov44e9e2e2009-10-26 15:00:19 +01001963 csrow = sys_addr_to_csrow(log_mci, sys_addr);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001964 if (csrow < 0) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02001965 amd64_mc_err(mci, "ERROR_ADDRESS (0x%lx) NOT mapped to CS\n",
1966 (unsigned long)sys_addr);
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001967 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001968 page, offset, 0,
1969 -1, -1, -1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001970 "ERROR ADDRESS NOT mapped to CS",
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001971 "");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001972 } else {
Mauro Carvalho Chehab9eb07a72012-06-04 13:27:43 -03001973 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03001974 page, offset, 0,
1975 csrow, -1, -1,
Mauro Carvalho Chehab03f7eae2012-06-04 11:29:25 -03001976 "", "");
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001977 }
1978}
1979
Borislav Petkov549d0422009-07-24 13:51:42 +02001980static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001981 struct mce *m)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001982{
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001983 u8 ecc_type = (m->status >> 45) & 0x3;
Borislav Petkov66fed2d2012-08-09 18:41:07 +02001984 u8 xec = XEC(m->status, 0x1f);
1985 u16 ec = EC(m->status);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001986
Borislav Petkov66fed2d2012-08-09 18:41:07 +02001987 /* Bail out early if this was an 'observed' error */
Borislav Petkov5980bb92011-01-07 16:26:49 +01001988 if (PP(ec) == NBSL_PP_OBS)
Borislav Petkovb70ef012009-06-25 19:32:38 +02001989 return;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001990
Borislav Petkovecaf5602009-07-23 16:32:01 +02001991 /* Do only ECC errors */
1992 if (xec && xec != F10_NBSL_EXT_ERR_ECC)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001993 return;
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001994
Borislav Petkovecaf5602009-07-23 16:32:01 +02001995 if (ecc_type == 2)
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001996 amd64_handle_ce(mci, m);
Borislav Petkovecaf5602009-07-23 16:32:01 +02001997 else if (ecc_type == 1)
Borislav Petkovf192c7b2011-01-10 14:24:32 +01001998 amd64_handle_ue(mci, m);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02001999}
2000
Borislav Petkovb0b07a22011-08-24 18:44:22 +02002001void amd64_decode_bus_error(int node_id, struct mce *m)
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002002{
Borislav Petkovb0b07a22011-08-24 18:44:22 +02002003 __amd64_decode_bus_error(mcis[node_id], m);
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002004}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002005
Doug Thompson0ec449e2009-04-27 19:41:25 +02002006/*
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002007 * Use pvt->F2 which contains the F2 CPU PCI device to get the related
Borislav Petkovbbd0c1f2010-10-01 19:27:58 +02002008 * F1 (AddrMap) and F3 (Misc) devices. Return negative value on error.
Doug Thompson0ec449e2009-04-27 19:41:25 +02002009 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002010static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f3_id)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002011{
Doug Thompson0ec449e2009-04-27 19:41:25 +02002012 /* Reserve the ADDRESS MAP Device */
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002013 pvt->F1 = pci_get_related_function(pvt->F2->vendor, f1_id, pvt->F2);
2014 if (!pvt->F1) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002015 amd64_err("error address map device not found: "
2016 "vendor %x device 0x%x (broken BIOS?)\n",
2017 PCI_VENDOR_ID_AMD, f1_id);
Borislav Petkovbbd0c1f2010-10-01 19:27:58 +02002018 return -ENODEV;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002019 }
2020
2021 /* Reserve the MISC Device */
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002022 pvt->F3 = pci_get_related_function(pvt->F2->vendor, f3_id, pvt->F2);
2023 if (!pvt->F3) {
2024 pci_dev_put(pvt->F1);
2025 pvt->F1 = NULL;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002026
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002027 amd64_err("error F3 device not found: "
2028 "vendor %x device 0x%x (broken BIOS?)\n",
2029 PCI_VENDOR_ID_AMD, f3_id);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002030
Borislav Petkovbbd0c1f2010-10-01 19:27:58 +02002031 return -ENODEV;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002032 }
Joe Perches956b9ba2012-04-29 17:08:39 -03002033 edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2034 edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2035 edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002036
2037 return 0;
2038}
2039
Borislav Petkov360b7f32010-10-15 19:25:38 +02002040static void free_mc_sibling_devs(struct amd64_pvt *pvt)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002041{
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002042 pci_dev_put(pvt->F1);
2043 pci_dev_put(pvt->F3);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002044}
2045
2046/*
2047 * Retrieve the hardware registers of the memory controller (this includes the
2048 * 'Address Map' and 'Misc' device regs)
2049 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002050static void read_mc_regs(struct amd64_pvt *pvt)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002051{
Borislav Petkova3b7db02011-01-19 20:35:12 +01002052 struct cpuinfo_x86 *c = &boot_cpu_data;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002053 u64 msr_val;
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002054 u32 tmp;
Borislav Petkove7613592011-02-21 19:49:01 +01002055 unsigned range;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002056
2057 /*
2058 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
2059 * those are Read-As-Zero
2060 */
Borislav Petkove97f8bb2009-10-12 15:27:45 +02002061 rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
Joe Perches956b9ba2012-04-29 17:08:39 -03002062 edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002063
2064 /* check first whether TOP_MEM2 is enabled */
2065 rdmsrl(MSR_K8_SYSCFG, msr_val);
2066 if (msr_val & (1U << 21)) {
Borislav Petkove97f8bb2009-10-12 15:27:45 +02002067 rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
Joe Perches956b9ba2012-04-29 17:08:39 -03002068 edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002069 } else
Joe Perches956b9ba2012-04-29 17:08:39 -03002070 edac_dbg(0, " TOP_MEM2 disabled\n");
Doug Thompson0ec449e2009-04-27 19:41:25 +02002071
Borislav Petkov5980bb92011-01-07 16:26:49 +01002072 amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002073
Borislav Petkov5a5d2372011-01-17 17:52:57 +01002074 read_dram_ctl_register(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002075
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002076 for (range = 0; range < DRAM_RANGES; range++) {
2077 u8 rw;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002078
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002079 /* read settings for this DRAM range */
2080 read_dram_base_limit_regs(pvt, range);
Borislav Petkove97f8bb2009-10-12 15:27:45 +02002081
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002082 rw = dram_rw(pvt, range);
2083 if (!rw)
2084 continue;
2085
Joe Perches956b9ba2012-04-29 17:08:39 -03002086 edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
2087 range,
2088 get_dram_base(pvt, range),
2089 get_dram_limit(pvt, range));
Borislav Petkov7f19bf72010-10-21 18:52:53 +02002090
Joe Perches956b9ba2012-04-29 17:08:39 -03002091 edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
2092 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
2093 (rw & 0x1) ? "R" : "-",
2094 (rw & 0x2) ? "W" : "-",
2095 dram_intlv_sel(pvt, range),
2096 dram_dst_node(pvt, range));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002097 }
2098
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002099 read_dct_base_mask(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002100
Borislav Petkovbc21fa52010-11-11 17:29:13 +01002101 amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
Borislav Petkov525a1b22010-12-21 15:53:27 +01002102 amd64_read_dct_pci_cfg(pvt, DBAM0, &pvt->dbam0);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002103
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002104 amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002105
Borislav Petkovcb328502010-12-22 14:28:24 +01002106 amd64_read_dct_pci_cfg(pvt, DCLR0, &pvt->dclr0);
2107 amd64_read_dct_pci_cfg(pvt, DCHR0, &pvt->dchr0);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002108
Borislav Petkov78da1212010-12-22 19:31:45 +01002109 if (!dct_ganging_enabled(pvt)) {
Borislav Petkovcb328502010-12-22 14:28:24 +01002110 amd64_read_dct_pci_cfg(pvt, DCLR1, &pvt->dclr1);
2111 amd64_read_dct_pci_cfg(pvt, DCHR1, &pvt->dchr1);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002112 }
Borislav Petkovad6a32e2010-03-09 12:46:00 +01002113
Borislav Petkova3b7db02011-01-19 20:35:12 +01002114 pvt->ecc_sym_sz = 4;
2115
2116 if (c->x86 >= 0x10) {
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002117 amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
Borislav Petkov525a1b22010-12-21 15:53:27 +01002118 amd64_read_dct_pci_cfg(pvt, DBAM1, &pvt->dbam1);
Borislav Petkova3b7db02011-01-19 20:35:12 +01002119
2120 /* F10h, revD and later can do x8 ECC too */
2121 if ((c->x86 > 0x10 || c->x86_model > 7) && tmp & BIT(25))
2122 pvt->ecc_sym_sz = 8;
Borislav Petkov525a1b22010-12-21 15:53:27 +01002123 }
Borislav Petkovb2b0c602010-10-08 18:32:29 +02002124 dump_misc_regs(pvt);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002125}
2126
2127/*
2128 * NOTE: CPU Revision Dependent code
2129 *
2130 * Input:
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002131 * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002132 * k8 private pointer to -->
2133 * DRAM Bank Address mapping register
2134 * node_id
2135 * DCL register where dual_channel_active is
2136 *
2137 * The DBAM register consists of 4 sets of 4 bits each definitions:
2138 *
2139 * Bits: CSROWs
2140 * 0-3 CSROWs 0 and 1
2141 * 4-7 CSROWs 2 and 3
2142 * 8-11 CSROWs 4 and 5
2143 * 12-15 CSROWs 6 and 7
2144 *
2145 * Values range from: 0 to 15
2146 * The meaning of the values depends on CPU revision and dual-channel state,
2147 * see relevant BKDG more info.
2148 *
2149 * The memory controller provides for total of only 8 CSROWs in its current
2150 * architecture. Each "pair" of CSROWs normally represents just one DIMM in
2151 * single channel or two (2) DIMMs in dual channel mode.
2152 *
2153 * The following code logic collapses the various tables for CSROW based on CPU
2154 * revision.
2155 *
2156 * Returns:
2157 * The number of PAGE_SIZE pages on the specified CSROW number it
2158 * encompasses
2159 *
2160 */
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01002161static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002162{
Borislav Petkov1433eb92009-10-21 13:44:36 +02002163 u32 cs_mode, nr_pages;
Ashish Shenoyf92cae42012-02-22 17:20:38 -08002164 u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002165
2166 /*
2167 * The math on this doesn't look right on the surface because x/2*4 can
2168 * be simplified to x*2 but this expression makes use of the fact that
2169 * it is integral math where 1/2=0. This intermediate value becomes the
2170 * number of bits to shift the DBAM register to extract the proper CSROW
2171 * field.
2172 */
Ashish Shenoyf92cae42012-02-22 17:20:38 -08002173 cs_mode = (dbam >> ((csrow_nr / 2) * 4)) & 0xF;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002174
Borislav Petkov41d8bfa2011-01-18 19:16:08 +01002175 nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002176
Joe Perches956b9ba2012-04-29 17:08:39 -03002177 edac_dbg(0, " (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
2178 edac_dbg(0, " nr_pages/channel= %u channel-count = %d\n",
2179 nr_pages, pvt->channel_count);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002180
2181 return nr_pages;
2182}
2183
2184/*
2185 * Initialize the array of csrow attribute instances, based on the values
2186 * from pci config hardware registers.
2187 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002188static int init_csrows(struct mem_ctl_info *mci)
Doug Thompson0ec449e2009-04-27 19:41:25 +02002189{
2190 struct csrow_info *csrow;
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03002191 struct dimm_info *dimm;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002192 struct amd64_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab5e2af0c2012-01-27 21:20:32 -03002193 u64 base, mask;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002194 u32 val;
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03002195 int i, j, empty = 1;
2196 enum mem_type mtype;
2197 enum edac_type edac_mode;
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -03002198 int nr_pages = 0;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002199
Borislav Petkova97fa682010-12-23 14:07:18 +01002200 amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002201
Borislav Petkov2299ef72010-10-15 17:44:04 +02002202 pvt->nbcfg = val;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002203
Joe Perches956b9ba2012-04-29 17:08:39 -03002204 edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
2205 pvt->mc_node_id, val,
2206 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
Doug Thompson0ec449e2009-04-27 19:41:25 +02002207
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002208 for_each_chip_select(i, 0, pvt) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03002209 csrow = mci->csrows[i];
Doug Thompson0ec449e2009-04-27 19:41:25 +02002210
Ashish Shenoyf92cae42012-02-22 17:20:38 -08002211 if (!csrow_enabled(i, 0, pvt) && !csrow_enabled(i, 1, pvt)) {
Joe Perches956b9ba2012-04-29 17:08:39 -03002212 edac_dbg(1, "----CSROW %d VALID for MC node %d\n",
2213 i, pvt->mc_node_id);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002214 continue;
2215 }
2216
Doug Thompson0ec449e2009-04-27 19:41:25 +02002217 empty = 0;
Ashish Shenoyf92cae42012-02-22 17:20:38 -08002218 if (csrow_enabled(i, 0, pvt))
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -03002219 nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
Ashish Shenoyf92cae42012-02-22 17:20:38 -08002220 if (csrow_enabled(i, 1, pvt))
Mauro Carvalho Chehaba895bf82012-01-28 09:09:38 -03002221 nr_pages += amd64_csrow_nr_pages(pvt, 1, i);
Borislav Petkov11c75ea2010-11-29 19:49:02 +01002222
2223 get_cs_base_and_mask(pvt, i, 0, &base, &mask);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002224 /* 8 bytes of resolution */
2225
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03002226 mtype = amd64_determine_memory_type(pvt, i);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002227
Joe Perches956b9ba2012-04-29 17:08:39 -03002228 edac_dbg(1, " for MC node %d csrow %d:\n", pvt->mc_node_id, i);
2229 edac_dbg(1, " nr_pages: %u\n",
2230 nr_pages * pvt->channel_count);
Doug Thompson0ec449e2009-04-27 19:41:25 +02002231
2232 /*
2233 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
2234 */
Borislav Petkova97fa682010-12-23 14:07:18 +01002235 if (pvt->nbcfg & NBCFG_ECC_ENABLE)
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03002236 edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
2237 EDAC_S4ECD4ED : EDAC_SECDED;
Doug Thompson0ec449e2009-04-27 19:41:25 +02002238 else
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03002239 edac_mode = EDAC_NONE;
2240
2241 for (j = 0; j < pvt->channel_count; j++) {
Mauro Carvalho Chehabde3910eb2012-04-24 15:05:43 -03002242 dimm = csrow->channels[j]->dimm;
2243 dimm->mtype = mtype;
2244 dimm->edac_mode = edac_mode;
2245 dimm->nr_pages = nr_pages;
Mauro Carvalho Chehab084a4fc2012-01-27 18:38:08 -03002246 }
Doug Thompson0ec449e2009-04-27 19:41:25 +02002247 }
2248
2249 return empty;
2250}
Doug Thompsond27bf6f2009-05-06 17:55:27 +02002251
Borislav Petkov06724532009-09-16 13:05:46 +02002252/* get all cores on this DCT */
Borislav Petkovb487c332011-02-21 18:55:00 +01002253static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, unsigned nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02002254{
Borislav Petkov06724532009-09-16 13:05:46 +02002255 int cpu;
Doug Thompsonf9431992009-04-27 19:46:08 +02002256
Borislav Petkov06724532009-09-16 13:05:46 +02002257 for_each_online_cpu(cpu)
2258 if (amd_get_nb_id(cpu) == nid)
2259 cpumask_set_cpu(cpu, mask);
Doug Thompsonf9431992009-04-27 19:46:08 +02002260}
2261
2262/* check MCG_CTL on all the cpus on this node */
Borislav Petkovb487c332011-02-21 18:55:00 +01002263static bool amd64_nb_mce_bank_enabled_on_node(unsigned nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02002264{
Rusty Russellba578cb2009-11-03 14:56:35 +10302265 cpumask_var_t mask;
Borislav Petkov50542252009-12-11 18:14:40 +01002266 int cpu, nbe;
Borislav Petkov06724532009-09-16 13:05:46 +02002267 bool ret = false;
Doug Thompsonf9431992009-04-27 19:46:08 +02002268
Rusty Russellba578cb2009-11-03 14:56:35 +10302269 if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002270 amd64_warn("%s: Error allocating mask\n", __func__);
Rusty Russellba578cb2009-11-03 14:56:35 +10302271 return false;
2272 }
Borislav Petkov06724532009-09-16 13:05:46 +02002273
Rusty Russellba578cb2009-11-03 14:56:35 +10302274 get_cpus_on_this_dct_cpumask(mask, nid);
Borislav Petkov06724532009-09-16 13:05:46 +02002275
Rusty Russellba578cb2009-11-03 14:56:35 +10302276 rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
Borislav Petkov06724532009-09-16 13:05:46 +02002277
Rusty Russellba578cb2009-11-03 14:56:35 +10302278 for_each_cpu(cpu, mask) {
Borislav Petkov50542252009-12-11 18:14:40 +01002279 struct msr *reg = per_cpu_ptr(msrs, cpu);
Borislav Petkov5980bb92011-01-07 16:26:49 +01002280 nbe = reg->l & MSR_MCGCTL_NBE;
Borislav Petkov06724532009-09-16 13:05:46 +02002281
Joe Perches956b9ba2012-04-29 17:08:39 -03002282 edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
2283 cpu, reg->q,
2284 (nbe ? "enabled" : "disabled"));
Borislav Petkov06724532009-09-16 13:05:46 +02002285
2286 if (!nbe)
2287 goto out;
Borislav Petkov06724532009-09-16 13:05:46 +02002288 }
2289 ret = true;
2290
2291out:
Rusty Russellba578cb2009-11-03 14:56:35 +10302292 free_cpumask_var(mask);
Doug Thompsonf9431992009-04-27 19:46:08 +02002293 return ret;
2294}
2295
Borislav Petkov2299ef72010-10-15 17:44:04 +02002296static int toggle_ecc_err_reporting(struct ecc_settings *s, u8 nid, bool on)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002297{
2298 cpumask_var_t cmask;
Borislav Petkov50542252009-12-11 18:14:40 +01002299 int cpu;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002300
2301 if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002302 amd64_warn("%s: error allocating mask\n", __func__);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002303 return false;
2304 }
2305
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002306 get_cpus_on_this_dct_cpumask(cmask, nid);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002307
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002308 rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2309
2310 for_each_cpu(cpu, cmask) {
2311
Borislav Petkov50542252009-12-11 18:14:40 +01002312 struct msr *reg = per_cpu_ptr(msrs, cpu);
2313
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002314 if (on) {
Borislav Petkov5980bb92011-01-07 16:26:49 +01002315 if (reg->l & MSR_MCGCTL_NBE)
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002316 s->flags.nb_mce_enable = 1;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002317
Borislav Petkov5980bb92011-01-07 16:26:49 +01002318 reg->l |= MSR_MCGCTL_NBE;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002319 } else {
2320 /*
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002321 * Turn off NB MCE reporting only when it was off before
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002322 */
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002323 if (!s->flags.nb_mce_enable)
Borislav Petkov5980bb92011-01-07 16:26:49 +01002324 reg->l &= ~MSR_MCGCTL_NBE;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002325 }
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002326 }
2327 wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2328
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002329 free_cpumask_var(cmask);
2330
2331 return 0;
2332}
2333
Borislav Petkov2299ef72010-10-15 17:44:04 +02002334static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid,
2335 struct pci_dev *F3)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002336{
Borislav Petkov2299ef72010-10-15 17:44:04 +02002337 bool ret = true;
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002338 u32 value, mask = 0x3; /* UECC/CECC enable */
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002339
Borislav Petkov2299ef72010-10-15 17:44:04 +02002340 if (toggle_ecc_err_reporting(s, nid, ON)) {
2341 amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
2342 return false;
2343 }
2344
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002345 amd64_read_pci_cfg(F3, NBCTL, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002346
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002347 s->old_nbctl = value & mask;
2348 s->nbctl_valid = true;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002349
2350 value |= mask;
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002351 amd64_write_pci_cfg(F3, NBCTL, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002352
Borislav Petkova97fa682010-12-23 14:07:18 +01002353 amd64_read_pci_cfg(F3, NBCFG, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002354
Joe Perches956b9ba2012-04-29 17:08:39 -03002355 edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2356 nid, value, !!(value & NBCFG_ECC_ENABLE));
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002357
Borislav Petkova97fa682010-12-23 14:07:18 +01002358 if (!(value & NBCFG_ECC_ENABLE)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002359 amd64_warn("DRAM ECC disabled on this node, enabling...\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002360
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002361 s->flags.nb_ecc_prev = 0;
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002362
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002363 /* Attempt to turn on DRAM ECC Enable */
Borislav Petkova97fa682010-12-23 14:07:18 +01002364 value |= NBCFG_ECC_ENABLE;
2365 amd64_write_pci_cfg(F3, NBCFG, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002366
Borislav Petkova97fa682010-12-23 14:07:18 +01002367 amd64_read_pci_cfg(F3, NBCFG, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002368
Borislav Petkova97fa682010-12-23 14:07:18 +01002369 if (!(value & NBCFG_ECC_ENABLE)) {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002370 amd64_warn("Hardware rejected DRAM ECC enable,"
2371 "check memory DIMM configuration.\n");
Borislav Petkov2299ef72010-10-15 17:44:04 +02002372 ret = false;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002373 } else {
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002374 amd64_info("Hardware accepted DRAM ECC Enable\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002375 }
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002376 } else {
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002377 s->flags.nb_ecc_prev = 1;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002378 }
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002379
Joe Perches956b9ba2012-04-29 17:08:39 -03002380 edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2381 nid, value, !!(value & NBCFG_ECC_ENABLE));
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002382
Borislav Petkov2299ef72010-10-15 17:44:04 +02002383 return ret;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002384}
2385
Borislav Petkov360b7f32010-10-15 19:25:38 +02002386static void restore_ecc_error_reporting(struct ecc_settings *s, u8 nid,
2387 struct pci_dev *F3)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002388{
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002389 u32 value, mask = 0x3; /* UECC/CECC enable */
2390
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002391
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002392 if (!s->nbctl_valid)
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002393 return;
2394
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002395 amd64_read_pci_cfg(F3, NBCTL, &value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002396 value &= ~mask;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002397 value |= s->old_nbctl;
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002398
Borislav Petkovc9f4f262010-12-22 19:48:20 +01002399 amd64_write_pci_cfg(F3, NBCTL, value);
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002400
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002401 /* restore previous BIOS DRAM ECC "off" setting we force-enabled */
2402 if (!s->flags.nb_ecc_prev) {
Borislav Petkova97fa682010-12-23 14:07:18 +01002403 amd64_read_pci_cfg(F3, NBCFG, &value);
2404 value &= ~NBCFG_ECC_ENABLE;
2405 amd64_write_pci_cfg(F3, NBCFG, value);
Borislav Petkovd95cf4d2010-02-24 14:49:47 +01002406 }
2407
2408 /* restore the NB Enable MCGCTL bit */
Borislav Petkov2299ef72010-10-15 17:44:04 +02002409 if (toggle_ecc_err_reporting(s, nid, OFF))
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002410 amd64_warn("Error restoring NB MCGCTL settings!\n");
Borislav Petkovf6d6ae92009-11-03 15:29:26 +01002411}
2412
Doug Thompsonf9431992009-04-27 19:46:08 +02002413/*
Borislav Petkov2299ef72010-10-15 17:44:04 +02002414 * EDAC requires that the BIOS have ECC enabled before
2415 * taking over the processing of ECC errors. A command line
2416 * option allows to force-enable hardware ECC later in
2417 * enable_ecc_error_reporting().
Doug Thompsonf9431992009-04-27 19:46:08 +02002418 */
Borislav Petkovcab4d272010-02-11 17:15:57 +01002419static const char *ecc_msg =
2420 "ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2421 " Either enable ECC checking or force module loading by setting "
2422 "'ecc_enable_override'.\n"
2423 " (Note that use of the override may cause unknown side effects.)\n";
Borislav Petkovbe3468e2009-08-05 15:47:22 +02002424
Borislav Petkov2299ef72010-10-15 17:44:04 +02002425static bool ecc_enabled(struct pci_dev *F3, u8 nid)
Doug Thompsonf9431992009-04-27 19:46:08 +02002426{
2427 u32 value;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002428 u8 ecc_en = 0;
Borislav Petkov06724532009-09-16 13:05:46 +02002429 bool nb_mce_en = false;
Doug Thompsonf9431992009-04-27 19:46:08 +02002430
Borislav Petkova97fa682010-12-23 14:07:18 +01002431 amd64_read_pci_cfg(F3, NBCFG, &value);
Doug Thompsonf9431992009-04-27 19:46:08 +02002432
Borislav Petkova97fa682010-12-23 14:07:18 +01002433 ecc_en = !!(value & NBCFG_ECC_ENABLE);
Borislav Petkov2299ef72010-10-15 17:44:04 +02002434 amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
Doug Thompsonf9431992009-04-27 19:46:08 +02002435
Borislav Petkov2299ef72010-10-15 17:44:04 +02002436 nb_mce_en = amd64_nb_mce_bank_enabled_on_node(nid);
Borislav Petkov06724532009-09-16 13:05:46 +02002437 if (!nb_mce_en)
Borislav Petkov2299ef72010-10-15 17:44:04 +02002438 amd64_notice("NB MCE bank disabled, set MSR "
2439 "0x%08x[4] on node %d to enable.\n",
2440 MSR_IA32_MCG_CTL, nid);
Doug Thompsonf9431992009-04-27 19:46:08 +02002441
Borislav Petkov2299ef72010-10-15 17:44:04 +02002442 if (!ecc_en || !nb_mce_en) {
2443 amd64_notice("%s", ecc_msg);
2444 return false;
Borislav Petkov43f5e682009-12-21 18:55:18 +01002445 }
Borislav Petkov2299ef72010-10-15 17:44:04 +02002446 return true;
Doug Thompsonf9431992009-04-27 19:46:08 +02002447}
2448
Mauro Carvalho Chehabc5608752012-03-21 14:00:44 -03002449static int set_mc_sysfs_attrs(struct mem_ctl_info *mci)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002450{
Mauro Carvalho Chehabc5608752012-03-21 14:00:44 -03002451 int rc;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002452
Mauro Carvalho Chehabc5608752012-03-21 14:00:44 -03002453 rc = amd64_create_sysfs_dbg_files(mci);
2454 if (rc < 0)
2455 return rc;
2456
2457 if (boot_cpu_data.x86 >= 0x10) {
2458 rc = amd64_create_sysfs_inject_files(mci);
2459 if (rc < 0)
2460 return rc;
2461 }
2462
2463 return 0;
2464}
2465
2466static void del_mc_sysfs_attrs(struct mem_ctl_info *mci)
2467{
2468 amd64_remove_sysfs_dbg_files(mci);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002469
Borislav Petkova135cef2010-11-26 19:24:44 +01002470 if (boot_cpu_data.x86 >= 0x10)
Mauro Carvalho Chehabc5608752012-03-21 14:00:44 -03002471 amd64_remove_sysfs_inject_files(mci);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002472}
2473
Borislav Petkovdf71a052011-01-19 18:15:10 +01002474static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
2475 struct amd64_family_type *fam)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002476{
2477 struct amd64_pvt *pvt = mci->pvt_info;
2478
2479 mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
2480 mci->edac_ctl_cap = EDAC_FLAG_NONE;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002481
Borislav Petkov5980bb92011-01-07 16:26:49 +01002482 if (pvt->nbcap & NBCAP_SECDED)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002483 mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
2484
Borislav Petkov5980bb92011-01-07 16:26:49 +01002485 if (pvt->nbcap & NBCAP_CHIPKILL)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002486 mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
2487
2488 mci->edac_cap = amd64_determine_edac_cap(pvt);
2489 mci->mod_name = EDAC_MOD_STR;
2490 mci->mod_ver = EDAC_AMD64_VERSION;
Borislav Petkovdf71a052011-01-19 18:15:10 +01002491 mci->ctl_name = fam->ctl_name;
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002492 mci->dev_name = pci_name(pvt->F2);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002493 mci->ctl_page_to_phys = NULL;
2494
Doug Thompson7d6034d2009-04-27 20:01:01 +02002495 /* memory scrubber interface */
2496 mci->set_sdram_scrub_rate = amd64_set_scrub_rate;
2497 mci->get_sdram_scrub_rate = amd64_get_scrub_rate;
2498}
2499
Borislav Petkov0092b202010-10-01 19:20:05 +02002500/*
2501 * returns a pointer to the family descriptor on success, NULL otherwise.
2502 */
2503static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
Borislav Petkov395ae782010-10-01 18:38:19 +02002504{
Borislav Petkov0092b202010-10-01 19:20:05 +02002505 u8 fam = boot_cpu_data.x86;
2506 struct amd64_family_type *fam_type = NULL;
2507
2508 switch (fam) {
Borislav Petkov395ae782010-10-01 18:38:19 +02002509 case 0xf:
Borislav Petkov0092b202010-10-01 19:20:05 +02002510 fam_type = &amd64_family_types[K8_CPUS];
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002511 pvt->ops = &amd64_family_types[K8_CPUS].ops;
Borislav Petkov395ae782010-10-01 18:38:19 +02002512 break;
Borislav Petkovdf71a052011-01-19 18:15:10 +01002513
Borislav Petkov395ae782010-10-01 18:38:19 +02002514 case 0x10:
Borislav Petkov0092b202010-10-01 19:20:05 +02002515 fam_type = &amd64_family_types[F10_CPUS];
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002516 pvt->ops = &amd64_family_types[F10_CPUS].ops;
Borislav Petkovdf71a052011-01-19 18:15:10 +01002517 break;
2518
2519 case 0x15:
2520 fam_type = &amd64_family_types[F15_CPUS];
2521 pvt->ops = &amd64_family_types[F15_CPUS].ops;
Borislav Petkov395ae782010-10-01 18:38:19 +02002522 break;
2523
2524 default:
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002525 amd64_err("Unsupported family!\n");
Borislav Petkov0092b202010-10-01 19:20:05 +02002526 return NULL;
Borislav Petkov395ae782010-10-01 18:38:19 +02002527 }
Borislav Petkov0092b202010-10-01 19:20:05 +02002528
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002529 pvt->ext_model = boot_cpu_data.x86_model >> 4;
2530
Borislav Petkovdf71a052011-01-19 18:15:10 +01002531 amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
Borislav Petkov0092b202010-10-01 19:20:05 +02002532 (fam == 0xf ?
Borislav Petkov24f9a7f2010-10-07 18:29:15 +02002533 (pvt->ext_model >= K8_REV_F ? "revF or later "
2534 : "revE or earlier ")
2535 : ""), pvt->mc_node_id);
Borislav Petkov0092b202010-10-01 19:20:05 +02002536 return fam_type;
Borislav Petkov395ae782010-10-01 18:38:19 +02002537}
2538
Borislav Petkov2299ef72010-10-15 17:44:04 +02002539static int amd64_init_one_instance(struct pci_dev *F2)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002540{
2541 struct amd64_pvt *pvt = NULL;
Borislav Petkov0092b202010-10-01 19:20:05 +02002542 struct amd64_family_type *fam_type = NULL;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002543 struct mem_ctl_info *mci = NULL;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03002544 struct edac_mc_layer layers[2];
Doug Thompson7d6034d2009-04-27 20:01:01 +02002545 int err = 0, ret;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002546 u8 nid = get_node_id(F2);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002547
2548 ret = -ENOMEM;
2549 pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
2550 if (!pvt)
Borislav Petkov360b7f32010-10-15 19:25:38 +02002551 goto err_ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002552
Borislav Petkov360b7f32010-10-15 19:25:38 +02002553 pvt->mc_node_id = nid;
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002554 pvt->F2 = F2;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002555
Borislav Petkov395ae782010-10-01 18:38:19 +02002556 ret = -EINVAL;
Borislav Petkov0092b202010-10-01 19:20:05 +02002557 fam_type = amd64_per_family_init(pvt);
2558 if (!fam_type)
Borislav Petkov395ae782010-10-01 18:38:19 +02002559 goto err_free;
2560
Doug Thompson7d6034d2009-04-27 20:01:01 +02002561 ret = -ENODEV;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002562 err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f3_id);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002563 if (err)
2564 goto err_free;
2565
Borislav Petkov360b7f32010-10-15 19:25:38 +02002566 read_mc_regs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002567
Doug Thompson7d6034d2009-04-27 20:01:01 +02002568 /*
2569 * We need to determine how many memory channels there are. Then use
2570 * that information for calculating the size of the dynamic instance
Borislav Petkov360b7f32010-10-15 19:25:38 +02002571 * tables in the 'mci' structure.
Doug Thompson7d6034d2009-04-27 20:01:01 +02002572 */
Borislav Petkov360b7f32010-10-15 19:25:38 +02002573 ret = -EINVAL;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002574 pvt->channel_count = pvt->ops->early_channel_count(pvt);
2575 if (pvt->channel_count < 0)
Borislav Petkov360b7f32010-10-15 19:25:38 +02002576 goto err_siblings;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002577
2578 ret = -ENOMEM;
Mauro Carvalho Chehabab5a5032012-04-16 15:03:50 -03002579 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
2580 layers[0].size = pvt->csels[0].b_cnt;
2581 layers[0].is_virt_csrow = true;
2582 layers[1].type = EDAC_MC_LAYER_CHANNEL;
2583 layers[1].size = pvt->channel_count;
2584 layers[1].is_virt_csrow = false;
Mauro Carvalho Chehabca0907b2012-05-02 14:37:00 -03002585 mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002586 if (!mci)
Borislav Petkov360b7f32010-10-15 19:25:38 +02002587 goto err_siblings;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002588
2589 mci->pvt_info = pvt;
Mauro Carvalho Chehabfd687502012-03-16 07:44:18 -03002590 mci->pdev = &pvt->F2->dev;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002591
Borislav Petkovdf71a052011-01-19 18:15:10 +01002592 setup_mci_misc_attrs(mci, fam_type);
Borislav Petkov360b7f32010-10-15 19:25:38 +02002593
2594 if (init_csrows(mci))
Doug Thompson7d6034d2009-04-27 20:01:01 +02002595 mci->edac_cap = EDAC_FLAG_NONE;
2596
Doug Thompson7d6034d2009-04-27 20:01:01 +02002597 ret = -ENODEV;
2598 if (edac_mc_add_mc(mci)) {
Joe Perches956b9ba2012-04-29 17:08:39 -03002599 edac_dbg(1, "failed edac_mc_add_mc()\n");
Doug Thompson7d6034d2009-04-27 20:01:01 +02002600 goto err_add_mc;
2601 }
Mauro Carvalho Chehabc5608752012-03-21 14:00:44 -03002602 if (set_mc_sysfs_attrs(mci)) {
Joe Perches956b9ba2012-04-29 17:08:39 -03002603 edac_dbg(1, "failed edac_mc_add_mc()\n");
Mauro Carvalho Chehabc5608752012-03-21 14:00:44 -03002604 goto err_add_sysfs;
2605 }
Doug Thompson7d6034d2009-04-27 20:01:01 +02002606
Borislav Petkov549d0422009-07-24 13:51:42 +02002607 /* register stuff with EDAC MCE */
2608 if (report_gart_errors)
2609 amd_report_gart_errors(true);
2610
2611 amd_register_ecc_decoder(amd64_decode_bus_error);
2612
Borislav Petkov360b7f32010-10-15 19:25:38 +02002613 mcis[nid] = mci;
2614
2615 atomic_inc(&drv_instances);
2616
Doug Thompson7d6034d2009-04-27 20:01:01 +02002617 return 0;
2618
Mauro Carvalho Chehabc5608752012-03-21 14:00:44 -03002619err_add_sysfs:
2620 edac_mc_del_mc(mci->pdev);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002621err_add_mc:
2622 edac_mc_free(mci);
2623
Borislav Petkov360b7f32010-10-15 19:25:38 +02002624err_siblings:
2625 free_mc_sibling_devs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002626
Borislav Petkov360b7f32010-10-15 19:25:38 +02002627err_free:
2628 kfree(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002629
Borislav Petkov360b7f32010-10-15 19:25:38 +02002630err_ret:
Doug Thompson7d6034d2009-04-27 20:01:01 +02002631 return ret;
2632}
2633
Borislav Petkov2299ef72010-10-15 17:44:04 +02002634static int __devinit amd64_probe_one_instance(struct pci_dev *pdev,
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002635 const struct pci_device_id *mc_type)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002636{
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002637 u8 nid = get_node_id(pdev);
Borislav Petkov2299ef72010-10-15 17:44:04 +02002638 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002639 struct ecc_settings *s;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002640 int ret = 0;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002641
Doug Thompson7d6034d2009-04-27 20:01:01 +02002642 ret = pci_enable_device(pdev);
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002643 if (ret < 0) {
Joe Perches956b9ba2012-04-29 17:08:39 -03002644 edac_dbg(0, "ret=%d\n", ret);
Borislav Petkovb8cfa022010-10-01 19:35:38 +02002645 return -EIO;
2646 }
2647
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002648 ret = -ENOMEM;
2649 s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
2650 if (!s)
Borislav Petkov2299ef72010-10-15 17:44:04 +02002651 goto err_out;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002652
2653 ecc_stngs[nid] = s;
2654
Borislav Petkov2299ef72010-10-15 17:44:04 +02002655 if (!ecc_enabled(F3, nid)) {
2656 ret = -ENODEV;
2657
2658 if (!ecc_enable_override)
2659 goto err_enable;
2660
2661 amd64_warn("Forcing ECC on!\n");
2662
2663 if (!enable_ecc_error_reporting(s, nid, F3))
2664 goto err_enable;
2665 }
2666
2667 ret = amd64_init_one_instance(pdev);
Borislav Petkov360b7f32010-10-15 19:25:38 +02002668 if (ret < 0) {
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002669 amd64_err("Error probing instance: %d\n", nid);
Borislav Petkov360b7f32010-10-15 19:25:38 +02002670 restore_ecc_error_reporting(s, nid, F3);
2671 }
Doug Thompson7d6034d2009-04-27 20:01:01 +02002672
2673 return ret;
Borislav Petkov2299ef72010-10-15 17:44:04 +02002674
2675err_enable:
2676 kfree(s);
2677 ecc_stngs[nid] = NULL;
2678
2679err_out:
2680 return ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002681}
2682
2683static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
2684{
2685 struct mem_ctl_info *mci;
2686 struct amd64_pvt *pvt;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002687 u8 nid = get_node_id(pdev);
2688 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2689 struct ecc_settings *s = ecc_stngs[nid];
Doug Thompson7d6034d2009-04-27 20:01:01 +02002690
Mauro Carvalho Chehabc5608752012-03-21 14:00:44 -03002691 mci = find_mci_by_dev(&pdev->dev);
2692 del_mc_sysfs_attrs(mci);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002693 /* Remove from EDAC CORE tracking list */
2694 mci = edac_mc_del_mc(&pdev->dev);
2695 if (!mci)
2696 return;
2697
2698 pvt = mci->pvt_info;
2699
Borislav Petkov360b7f32010-10-15 19:25:38 +02002700 restore_ecc_error_reporting(s, nid, F3);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002701
Borislav Petkov360b7f32010-10-15 19:25:38 +02002702 free_mc_sibling_devs(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002703
Borislav Petkov549d0422009-07-24 13:51:42 +02002704 /* unregister from EDAC MCE */
2705 amd_report_gart_errors(false);
2706 amd_unregister_ecc_decoder(amd64_decode_bus_error);
2707
Borislav Petkov360b7f32010-10-15 19:25:38 +02002708 kfree(ecc_stngs[nid]);
2709 ecc_stngs[nid] = NULL;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002710
Doug Thompson7d6034d2009-04-27 20:01:01 +02002711 /* Free the EDAC CORE resources */
Borislav Petkov8f68ed92009-12-21 15:15:59 +01002712 mci->pvt_info = NULL;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002713 mcis[nid] = NULL;
Borislav Petkov8f68ed92009-12-21 15:15:59 +01002714
2715 kfree(pvt);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002716 edac_mc_free(mci);
2717}
2718
2719/*
2720 * This table is part of the interface for loading drivers for PCI devices. The
2721 * PCI core identifies what devices are on a system during boot, and then
2722 * inquiry this table to see if this driver is for a given device found.
2723 */
Lionel Debroux36c46f32012-02-27 07:41:47 +01002724static DEFINE_PCI_DEVICE_TABLE(amd64_pci_table) = {
Doug Thompson7d6034d2009-04-27 20:01:01 +02002725 {
2726 .vendor = PCI_VENDOR_ID_AMD,
2727 .device = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
2728 .subvendor = PCI_ANY_ID,
2729 .subdevice = PCI_ANY_ID,
2730 .class = 0,
2731 .class_mask = 0,
Doug Thompson7d6034d2009-04-27 20:01:01 +02002732 },
2733 {
2734 .vendor = PCI_VENDOR_ID_AMD,
2735 .device = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
2736 .subvendor = PCI_ANY_ID,
2737 .subdevice = PCI_ANY_ID,
2738 .class = 0,
2739 .class_mask = 0,
Doug Thompson7d6034d2009-04-27 20:01:01 +02002740 },
Borislav Petkovdf71a052011-01-19 18:15:10 +01002741 {
2742 .vendor = PCI_VENDOR_ID_AMD,
2743 .device = PCI_DEVICE_ID_AMD_15H_NB_F2,
2744 .subvendor = PCI_ANY_ID,
2745 .subdevice = PCI_ANY_ID,
2746 .class = 0,
2747 .class_mask = 0,
2748 },
2749
Doug Thompson7d6034d2009-04-27 20:01:01 +02002750 {0, }
2751};
2752MODULE_DEVICE_TABLE(pci, amd64_pci_table);
2753
2754static struct pci_driver amd64_pci_driver = {
2755 .name = EDAC_MOD_STR,
Borislav Petkov2299ef72010-10-15 17:44:04 +02002756 .probe = amd64_probe_one_instance,
Doug Thompson7d6034d2009-04-27 20:01:01 +02002757 .remove = __devexit_p(amd64_remove_one_instance),
2758 .id_table = amd64_pci_table,
2759};
2760
Borislav Petkov360b7f32010-10-15 19:25:38 +02002761static void setup_pci_device(void)
Doug Thompson7d6034d2009-04-27 20:01:01 +02002762{
2763 struct mem_ctl_info *mci;
2764 struct amd64_pvt *pvt;
2765
2766 if (amd64_ctl_pci)
2767 return;
2768
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002769 mci = mcis[0];
Doug Thompson7d6034d2009-04-27 20:01:01 +02002770 if (mci) {
2771
2772 pvt = mci->pvt_info;
2773 amd64_ctl_pci =
Borislav Petkov8d5b5d92010-10-01 20:11:07 +02002774 edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002775
2776 if (!amd64_ctl_pci) {
2777 pr_warning("%s(): Unable to create PCI control\n",
2778 __func__);
2779
2780 pr_warning("%s(): PCI error report via EDAC not set\n",
2781 __func__);
2782 }
2783 }
2784}
2785
2786static int __init amd64_edac_init(void)
2787{
Borislav Petkov360b7f32010-10-15 19:25:38 +02002788 int err = -ENODEV;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002789
Borislav Petkovdf71a052011-01-19 18:15:10 +01002790 printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
Doug Thompson7d6034d2009-04-27 20:01:01 +02002791
2792 opstate_init();
2793
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +02002794 if (amd_cache_northbridges() < 0)
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002795 goto err_ret;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002796
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002797 err = -ENOMEM;
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002798 mcis = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL);
2799 ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL);
Borislav Petkov360b7f32010-10-15 19:25:38 +02002800 if (!(mcis && ecc_stngs))
Borislav Petkova9f0fbe2011-03-29 18:10:53 +02002801 goto err_free;
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002802
Borislav Petkov50542252009-12-11 18:14:40 +01002803 msrs = msrs_alloc();
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002804 if (!msrs)
Borislav Petkov360b7f32010-10-15 19:25:38 +02002805 goto err_free;
Borislav Petkov50542252009-12-11 18:14:40 +01002806
Doug Thompson7d6034d2009-04-27 20:01:01 +02002807 err = pci_register_driver(&amd64_pci_driver);
2808 if (err)
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002809 goto err_pci;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002810
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002811 err = -ENODEV;
Borislav Petkov360b7f32010-10-15 19:25:38 +02002812 if (!atomic_read(&drv_instances))
2813 goto err_no_instances;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002814
Borislav Petkov360b7f32010-10-15 19:25:38 +02002815 setup_pci_device();
2816 return 0;
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002817
Borislav Petkov360b7f32010-10-15 19:25:38 +02002818err_no_instances:
Doug Thompson7d6034d2009-04-27 20:01:01 +02002819 pci_unregister_driver(&amd64_pci_driver);
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002820
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002821err_pci:
2822 msrs_free(msrs);
2823 msrs = NULL;
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002824
Borislav Petkov360b7f32010-10-15 19:25:38 +02002825err_free:
2826 kfree(mcis);
2827 mcis = NULL;
2828
2829 kfree(ecc_stngs);
2830 ecc_stngs = NULL;
2831
Borislav Petkov56b34b91e2009-12-21 18:13:01 +01002832err_ret:
Doug Thompson7d6034d2009-04-27 20:01:01 +02002833 return err;
2834}
2835
2836static void __exit amd64_edac_exit(void)
2837{
2838 if (amd64_ctl_pci)
2839 edac_pci_release_generic_ctl(amd64_ctl_pci);
2840
2841 pci_unregister_driver(&amd64_pci_driver);
Borislav Petkov50542252009-12-11 18:14:40 +01002842
Borislav Petkovae7bb7c2010-10-14 16:01:30 +02002843 kfree(ecc_stngs);
2844 ecc_stngs = NULL;
2845
Borislav Petkovcc4d8862010-10-13 16:11:59 +02002846 kfree(mcis);
2847 mcis = NULL;
2848
Borislav Petkov50542252009-12-11 18:14:40 +01002849 msrs_free(msrs);
2850 msrs = NULL;
Doug Thompson7d6034d2009-04-27 20:01:01 +02002851}
2852
2853module_init(amd64_edac_init);
2854module_exit(amd64_edac_exit);
2855
2856MODULE_LICENSE("GPL");
2857MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
2858 "Dave Peterson, Thayne Harbaugh");
2859MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
2860 EDAC_AMD64_VERSION);
2861
2862module_param(edac_op_state, int, 0444);
2863MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");