blob: 79aa84eaa12dbe3138c24363385a2f2bda06a3e9 [file] [log] [blame]
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001/* Intel 7 core Memory Controller kernel module (Nehalem)
2 *
3 * This file may be distributed under the terms of the
4 * GNU General Public License version 2 only.
5 *
6 * Copyright (c) 2009 by:
7 * Mauro Carvalho Chehab <mchehab@redhat.com>
8 *
9 * Red Hat Inc. http://www.redhat.com
10 *
11 * Forked and adapted from the i5400_edac driver
12 *
13 * Based on the following public Intel datasheets:
14 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
15 * Datasheet, Volume 2:
16 * http://download.intel.com/design/processor/datashts/320835.pdf
17 * Intel Xeon Processor 5500 Series Datasheet Volume 2
18 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
19 * also available at:
20 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
21 */
22
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030023#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/pci.h>
26#include <linux/pci_ids.h>
27#include <linux/slab.h>
28#include <linux/edac.h>
29#include <linux/mmzone.h>
30
31#include "edac_core.h"
32
33
34/*
35 * Alter this version for the module when modifications are made
36 */
37#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
38#define EDAC_MOD_STR "i7core_edac"
39
40/* HACK: temporary, just to enable all logs, for now */
41#undef debugf0
42#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
43
44/*
45 * Debug macros
46 */
47#define i7core_printk(level, fmt, arg...) \
48 edac_printk(level, "i7core", fmt, ##arg)
49
50#define i7core_mc_printk(mci, level, fmt, arg...) \
51 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
52
53/*
54 * i7core Memory Controller Registers
55 */
56
57 /* OFFSETS for Device 3 Function 0 */
58
59#define MC_CONTROL 0x48
60#define MC_STATUS 0x4c
61#define MC_MAX_DOD 0x64
62
63 /* OFFSETS for Devices 4,5 and 6 Function 0 */
64
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030065#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
66 #define THREE_DIMMS_PRESENT (1 << 24)
67 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
68 #define QUAD_RANK_PRESENT (1 << 22)
69 #define REGISTERED_DIMM (1 << 15)
70
71#define MC_CHANNEL_RANK_PRESENT 0x7c
72 #define RANK_PRESENT_MASK 0xffff
73
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030074#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030075#define MC_CHANNEL_ERROR_MASK 0xf8
76#define MC_CHANNEL_ERROR_INJECT 0xfc
77 #define INJECT_ADDR_PARITY 0x10
78 #define INJECT_ECC 0x08
79 #define MASK_CACHELINE 0x06
80 #define MASK_FULL_CACHELINE 0x06
81 #define MASK_MSB32_CACHELINE 0x04
82 #define MASK_LSB32_CACHELINE 0x02
83 #define NO_MASK_CACHELINE 0x00
84 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030085
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030086 /* OFFSETS for Devices 4,5 and 6 Function 1 */
87#define MC_DOD_CH_DIMM0 0x48
88#define MC_DOD_CH_DIMM1 0x4c
89#define MC_DOD_CH_DIMM2 0x50
90 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
91 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
92 #define DIMM_PRESENT_MASK (1 << 9)
93 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
94 #define NUMBANK_MASK ((1 << 8) | (1 << 7))
95 #define NUMBANK(x) (((x) & NUMBANK_MASK) >> 7)
96 #define NUMRANK_MASK ((1 << 6) | (1 << 5))
97 #define NUMRANK(x) (((x) & NUMRANK_MASK) >> 5)
98 #define NUMROW_MASK ((1 << 4) | (1 << 3))
99 #define NUMROW(x) (((x) & NUMROW_MASK) >> 3)
100 #define NUMCOL_MASK 3
101 #define NUMCOL(x) ((x) & NUMCOL_MASK)
102
103#define MC_SAG_CH_0 0x80
104#define MC_SAG_CH_1 0x84
105#define MC_SAG_CH_2 0x88
106#define MC_SAG_CH_3 0x8c
107#define MC_SAG_CH_4 0x90
108#define MC_SAG_CH_5 0x94
109#define MC_SAG_CH_6 0x98
110#define MC_SAG_CH_7 0x9c
111
112#define MC_RIR_LIMIT_CH_0 0x40
113#define MC_RIR_LIMIT_CH_1 0x44
114#define MC_RIR_LIMIT_CH_2 0x48
115#define MC_RIR_LIMIT_CH_3 0x4C
116#define MC_RIR_LIMIT_CH_4 0x50
117#define MC_RIR_LIMIT_CH_5 0x54
118#define MC_RIR_LIMIT_CH_6 0x58
119#define MC_RIR_LIMIT_CH_7 0x5C
120#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
121
122#define MC_RIR_WAY_CH 0x80
123 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
124 #define MC_RIR_WAY_RANK_MASK 0x7
125
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300126/*
127 * i7core structs
128 */
129
130#define NUM_CHANS 3
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300131#define NUM_MCR_FUNCS 4
132#define NUM_CHAN_FUNCS 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300133
134struct i7core_info {
135 u32 mc_control;
136 u32 mc_status;
137 u32 max_dod;
138};
139
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300140
141struct i7core_inject {
142 int enable;
143
144 u32 section;
145 u32 type;
146 u32 eccmask;
147
148 /* Error address mask */
149 int channel, dimm, rank, bank, page, col;
150};
151
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300152struct i7core_channel {
153 u32 ranks;
154 u32 dimms;
155};
156
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300157struct pci_id_descr {
158 int dev;
159 int func;
160 int dev_id;
161 struct pci_dev *pdev;
162};
163
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300164struct i7core_pvt {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300165 struct pci_dev *pci_mcr[NUM_MCR_FUNCS];
166 struct pci_dev *pci_ch[NUM_CHANS][NUM_CHAN_FUNCS];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300167 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300168 struct i7core_inject inject;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300169 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300170};
171
172/* Device name and register DID (Device ID) */
173struct i7core_dev_info {
174 const char *ctl_name; /* name for this device */
175 u16 fsb_mapping_errors; /* DID for the branchmap,control */
176};
177
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300178#define PCI_DESCR(device, function, device_id) \
179 .dev = (device), \
180 .func = (function), \
181 .dev_id = (device_id)
182
183struct pci_id_descr pci_devs[] = {
184 /* Memory controller */
185 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
186 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
187 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
188 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
189
190 /* Channel 0 */
191 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
192 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
193 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
194 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
195
196 /* Channel 1 */
197 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
198 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
199 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
200 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
201
202 /* Channel 2 */
203 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
204 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
205 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
206 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300207};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300208#define N_DEVS ARRAY_SIZE(pci_devs)
209
210/*
211 * pci_device_id table for which devices we are looking for
212 * This should match the first device at pci_devs table
213 */
214static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
215 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)},
216 {0,} /* 0 terminated list. */
217};
218
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300219
220/* Table of devices attributes supported by this driver */
221static const struct i7core_dev_info i7core_devs[] = {
222 {
223 .ctl_name = "i7 Core",
224 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
225 },
226};
227
228static struct edac_pci_ctl_info *i7core_pci;
229
230/****************************************************************************
231 Anciliary status routines
232 ****************************************************************************/
233
234 /* MC_CONTROL bits */
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300235#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & 1 << (8 + ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300236#define ECCx8(pvt) ((pvt)->info.mc_control & 1 << 1)
237
238 /* MC_STATUS bits */
239#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & 1 << 3)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300240#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & 1 << ch)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300241
242 /* MC_MAX_DOD read functions */
243static inline int maxnumdimms(struct i7core_pvt *pvt)
244{
245 return (pvt->info.max_dod & 0x3) + 1;
246}
247
248static inline int maxnumrank(struct i7core_pvt *pvt)
249{
250 static int ranks[4] = { 1, 2, 4, -EINVAL };
251
252 return ranks[(pvt->info.max_dod >> 2) & 0x3];
253}
254
255static inline int maxnumbank(struct i7core_pvt *pvt)
256{
257 static int banks[4] = { 4, 8, 16, -EINVAL };
258
259 return banks[(pvt->info.max_dod >> 4) & 0x3];
260}
261
262static inline int maxnumrow(struct i7core_pvt *pvt)
263{
264 static int rows[8] = {
265 1 << 12, 1 << 13, 1 << 14, 1 << 15,
266 1 << 16, -EINVAL, -EINVAL, -EINVAL,
267 };
268
269 return rows[((pvt->info.max_dod >> 6) & 0x7)];
270}
271
272static inline int maxnumcol(struct i7core_pvt *pvt)
273{
274 static int cols[8] = {
275 1 << 10, 1 << 11, 1 << 12, -EINVAL,
276 };
277 return cols[((pvt->info.max_dod >> 9) & 0x3) << 12];
278}
279
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300280
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300281/****************************************************************************
282 Memory check routines
283 ****************************************************************************/
284static int get_dimm_config(struct mem_ctl_info *mci)
285{
286 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300287 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300288
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300289 if (!pvt->pci_mcr[0])
290 return -ENODEV;
291
292 pci_read_config_dword(pvt->pci_mcr[0], MC_CONTROL, &pvt->info.mc_control);
293 pci_read_config_dword(pvt->pci_mcr[0], MC_STATUS, &pvt->info.mc_status);
294 pci_read_config_dword(pvt->pci_mcr[0], MC_MAX_DOD, &pvt->info.max_dod);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300295
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300296 if (ECC_ENABLED(pvt))
297 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4);
298 else
299 debugf0("ECC disabled\n");
300
301 /* FIXME: need to handle the error codes */
302 debugf0("DOD Maximum limits: DIMMS: %d, %d-ranked, %d-banked\n",
303 maxnumdimms(pvt), maxnumrank(pvt), maxnumbank(pvt));
304 debugf0("DOD Maximum rows x colums = 0x%x x 0x%x\n",
305 maxnumrow(pvt), maxnumcol(pvt));
306
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300307 debugf0("Memory channel configuration:\n");
308
309 for (i = 0; i < NUM_CHANS; i++) {
310 u32 data;
311
312 if (!CH_ACTIVE(pvt, i)) {
313 debugf0("Channel %i is not active\n", i);
314 continue;
315 }
316 if (CH_DISABLED(pvt, i)) {
317 debugf0("Channel %i is disabled\n", i);
318 continue;
319 }
320
321 pci_read_config_dword(pvt->pci_ch[i][0],
322 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
323
324 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT)? 4 : 2;
325
326 if (data & THREE_DIMMS_PRESENT)
327 pvt->channel[i].dimms = 3;
328 else if (data & SINGLE_QUAD_RANK_PRESENT)
329 pvt->channel[i].dimms = 1;
330 else
331 pvt->channel[i].dimms = 2;
332
333 debugf0("Channel %d (0x%08x): %d ranks, %d dimms "
334 "(%sregistered)\n", i, data,
335 pvt->channel[i].ranks, pvt->channel[i].dimms,
336 (data & REGISTERED_DIMM)? "" : "un" );
337 }
338
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300339 return 0;
340}
341
342/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300343 Error insertion routines
344 ****************************************************************************/
345
346/* The i7core has independent error injection features per channel.
347 However, to have a simpler code, we don't allow enabling error injection
348 on more than one channel.
349 Also, since a change at an inject parameter will be applied only at enable,
350 we're disabling error injection on all write calls to the sysfs nodes that
351 controls the error code injection.
352 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300353static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300354{
355 struct i7core_pvt *pvt = mci->pvt_info;
356
357 pvt->inject.enable = 0;
358
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300359 if (!pvt->pci_ch[pvt->inject.channel][0])
360 return -ENODEV;
361
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300362 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
363 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300364
365 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300366}
367
368/*
369 * i7core inject inject.section
370 *
371 * accept and store error injection inject.section value
372 * bit 0 - refers to the lower 32-byte half cacheline
373 * bit 1 - refers to the upper 32-byte half cacheline
374 */
375static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
376 const char *data, size_t count)
377{
378 struct i7core_pvt *pvt = mci->pvt_info;
379 unsigned long value;
380 int rc;
381
382 if (pvt->inject.enable)
383 disable_inject(mci);
384
385 rc = strict_strtoul(data, 10, &value);
386 if ((rc < 0) || (value > 3))
387 return 0;
388
389 pvt->inject.section = (u32) value;
390 return count;
391}
392
393static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
394 char *data)
395{
396 struct i7core_pvt *pvt = mci->pvt_info;
397 return sprintf(data, "0x%08x\n", pvt->inject.section);
398}
399
400/*
401 * i7core inject.type
402 *
403 * accept and store error injection inject.section value
404 * bit 0 - repeat enable - Enable error repetition
405 * bit 1 - inject ECC error
406 * bit 2 - inject parity error
407 */
408static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
409 const char *data, size_t count)
410{
411 struct i7core_pvt *pvt = mci->pvt_info;
412 unsigned long value;
413 int rc;
414
415 if (pvt->inject.enable)
416 disable_inject(mci);
417
418 rc = strict_strtoul(data, 10, &value);
419 if ((rc < 0) || (value > 7))
420 return 0;
421
422 pvt->inject.type = (u32) value;
423 return count;
424}
425
426static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
427 char *data)
428{
429 struct i7core_pvt *pvt = mci->pvt_info;
430 return sprintf(data, "0x%08x\n", pvt->inject.type);
431}
432
433/*
434 * i7core_inject_inject.eccmask_store
435 *
436 * The type of error (UE/CE) will depend on the inject.eccmask value:
437 * Any bits set to a 1 will flip the corresponding ECC bit
438 * Correctable errors can be injected by flipping 1 bit or the bits within
439 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
440 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
441 * uncorrectable error to be injected.
442 */
443static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
444 const char *data, size_t count)
445{
446 struct i7core_pvt *pvt = mci->pvt_info;
447 unsigned long value;
448 int rc;
449
450 if (pvt->inject.enable)
451 disable_inject(mci);
452
453 rc = strict_strtoul(data, 10, &value);
454 if (rc < 0)
455 return 0;
456
457 pvt->inject.eccmask = (u32) value;
458 return count;
459}
460
461static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
462 char *data)
463{
464 struct i7core_pvt *pvt = mci->pvt_info;
465 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
466}
467
468/*
469 * i7core_addrmatch
470 *
471 * The type of error (UE/CE) will depend on the inject.eccmask value:
472 * Any bits set to a 1 will flip the corresponding ECC bit
473 * Correctable errors can be injected by flipping 1 bit or the bits within
474 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
475 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
476 * uncorrectable error to be injected.
477 */
478static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
479 const char *data, size_t count)
480{
481 struct i7core_pvt *pvt = mci->pvt_info;
482 char *cmd, *val;
483 long value;
484 int rc;
485
486 if (pvt->inject.enable)
487 disable_inject(mci);
488
489 do {
490 cmd = strsep((char **) &data, ":");
491 if (!cmd)
492 break;
493 val = strsep((char **) &data, " \n\t");
494 if (!val)
495 return cmd - data;
496
497 if (!strcasecmp(val,"any"))
498 value = -1;
499 else {
500 rc = strict_strtol(val, 10, &value);
501 if ((rc < 0) || (value < 0))
502 return cmd - data;
503 }
504
505 if (!strcasecmp(cmd,"channel")) {
506 if (value < 3)
507 pvt->inject.channel = value;
508 else
509 return cmd - data;
510 } else if (!strcasecmp(cmd,"dimm")) {
511 if (value < 4)
512 pvt->inject.dimm = value;
513 else
514 return cmd - data;
515 } else if (!strcasecmp(cmd,"rank")) {
516 if (value < 4)
517 pvt->inject.rank = value;
518 else
519 return cmd - data;
520 } else if (!strcasecmp(cmd,"bank")) {
521 if (value < 4)
522 pvt->inject.bank = value;
523 else
524 return cmd - data;
525 } else if (!strcasecmp(cmd,"page")) {
526 if (value <= 0xffff)
527 pvt->inject.page = value;
528 else
529 return cmd - data;
530 } else if (!strcasecmp(cmd,"col") ||
531 !strcasecmp(cmd,"column")) {
532 if (value <= 0x3fff)
533 pvt->inject.col = value;
534 else
535 return cmd - data;
536 }
537 } while (1);
538
539 return count;
540}
541
542static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
543 char *data)
544{
545 struct i7core_pvt *pvt = mci->pvt_info;
546 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
547
548 if (pvt->inject.channel < 0)
549 sprintf(channel, "any");
550 else
551 sprintf(channel, "%d", pvt->inject.channel);
552 if (pvt->inject.dimm < 0)
553 sprintf(dimm, "any");
554 else
555 sprintf(dimm, "%d", pvt->inject.dimm);
556 if (pvt->inject.bank < 0)
557 sprintf(bank, "any");
558 else
559 sprintf(bank, "%d", pvt->inject.bank);
560 if (pvt->inject.rank < 0)
561 sprintf(rank, "any");
562 else
563 sprintf(rank, "%d", pvt->inject.rank);
564 if (pvt->inject.page < 0)
565 sprintf(page, "any");
566 else
567 sprintf(page, "0x%04x", pvt->inject.page);
568 if (pvt->inject.col < 0)
569 sprintf(col, "any");
570 else
571 sprintf(col, "0x%04x", pvt->inject.col);
572
573 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
574 "rank: %s\npage: %s\ncolumn: %s\n",
575 channel, dimm, bank, rank, page, col);
576}
577
578/*
579 * This routine prepares the Memory Controller for error injection.
580 * The error will be injected when some process tries to write to the
581 * memory that matches the given criteria.
582 * The criteria can be set in terms of a mask where dimm, rank, bank, page
583 * and col can be specified.
584 * A -1 value for any of the mask items will make the MCU to ignore
585 * that matching criteria for error injection.
586 *
587 * It should be noticed that the error will only happen after a write operation
588 * on a memory that matches the condition. if REPEAT_EN is not enabled at
589 * inject mask, then it will produce just one error. Otherwise, it will repeat
590 * until the injectmask would be cleaned.
591 *
592 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
593 * is reliable enough to check if the MC is using the
594 * three channels. However, this is not clear at the datasheet.
595 */
596static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
597 const char *data, size_t count)
598{
599 struct i7core_pvt *pvt = mci->pvt_info;
600 u32 injectmask;
601 u64 mask = 0;
602 int rc;
603 long enable;
604
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300605 if (!pvt->pci_ch[pvt->inject.channel][0])
606 return 0;
607
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300608 rc = strict_strtoul(data, 10, &enable);
609 if ((rc < 0))
610 return 0;
611
612 if (enable) {
613 pvt->inject.enable = 1;
614 } else {
615 disable_inject(mci);
616 return count;
617 }
618
619 /* Sets pvt->inject.dimm mask */
620 if (pvt->inject.dimm < 0)
621 mask |= 1l << 41;
622 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300623 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300624 mask |= (pvt->inject.dimm & 0x3l) << 35;
625 else
626 mask |= (pvt->inject.dimm & 0x1l) << 36;
627 }
628
629 /* Sets pvt->inject.rank mask */
630 if (pvt->inject.rank < 0)
631 mask |= 1l << 40;
632 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300633 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300634 mask |= (pvt->inject.rank & 0x1l) << 34;
635 else
636 mask |= (pvt->inject.rank & 0x3l) << 34;
637 }
638
639 /* Sets pvt->inject.bank mask */
640 if (pvt->inject.bank < 0)
641 mask |= 1l << 39;
642 else
643 mask |= (pvt->inject.bank & 0x15l) << 30;
644
645 /* Sets pvt->inject.page mask */
646 if (pvt->inject.page < 0)
647 mask |= 1l << 38;
648 else
649 mask |= (pvt->inject.page & 0xffffl) << 14;
650
651 /* Sets pvt->inject.column mask */
652 if (pvt->inject.col < 0)
653 mask |= 1l << 37;
654 else
655 mask |= (pvt->inject.col & 0x3fffl);
656
657 pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0],
658 MC_CHANNEL_ADDR_MATCH, mask);
659
660 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
661 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
662
663 /*
664 * bit 0: REPEAT_EN
665 * bits 1-2: MASK_HALF_CACHELINE
666 * bit 3: INJECT_ECC
667 * bit 4: INJECT_ADDR_PARITY
668 */
669
670 injectmask = (pvt->inject.type & 1) &&
671 (pvt->inject.section & 0x3) << 1 &&
672 (pvt->inject.type & 0x6) << (3 - 1);
673
674 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
675 MC_CHANNEL_ERROR_MASK, injectmask);
676
677
678 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
679 mask, pvt->inject.eccmask, injectmask);
680
681 return count;
682}
683
684static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
685 char *data)
686{
687 struct i7core_pvt *pvt = mci->pvt_info;
688 return sprintf(data, "%d\n", pvt->inject.enable);
689}
690
691/*
692 * Sysfs struct
693 */
694static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
695
696 {
697 .attr = {
698 .name = "inject_section",
699 .mode = (S_IRUGO | S_IWUSR)
700 },
701 .show = i7core_inject_section_show,
702 .store = i7core_inject_section_store,
703 }, {
704 .attr = {
705 .name = "inject_type",
706 .mode = (S_IRUGO | S_IWUSR)
707 },
708 .show = i7core_inject_type_show,
709 .store = i7core_inject_type_store,
710 }, {
711 .attr = {
712 .name = "inject_eccmask",
713 .mode = (S_IRUGO | S_IWUSR)
714 },
715 .show = i7core_inject_eccmask_show,
716 .store = i7core_inject_eccmask_store,
717 }, {
718 .attr = {
719 .name = "inject_addrmatch",
720 .mode = (S_IRUGO | S_IWUSR)
721 },
722 .show = i7core_inject_addrmatch_show,
723 .store = i7core_inject_addrmatch_store,
724 }, {
725 .attr = {
726 .name = "inject_enable",
727 .mode = (S_IRUGO | S_IWUSR)
728 },
729 .show = i7core_inject_enable_show,
730 .store = i7core_inject_enable_store,
731 },
732};
733
734/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300735 Device initialization routines: put/get, init/exit
736 ****************************************************************************/
737
738/*
739 * i7core_put_devices 'put' all the devices that we have
740 * reserved via 'get'
741 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300742static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300743{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300744 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300745
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300746 for (i = 0; i < N_DEVS; i++)
747 pci_dev_put(pci_devs[i].pdev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300748}
749
750/*
751 * i7core_get_devices Find and perform 'get' operation on the MCH's
752 * device/functions we want to reference for this driver
753 *
754 * Need to 'get' device 16 func 1 and func 2
755 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300756static int i7core_get_devices(struct mem_ctl_info *mci, struct pci_dev *mcidev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300757{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300758 struct i7core_pvt *pvt = mci->pvt_info;
759 int rc, i,func;
760 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300761
762 pvt = mci->pvt_info;
763 memset(pvt, 0, sizeof(*pvt));
764
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300765 for (i = 0; i < N_DEVS; i++) {
766 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
767 pci_devs[i].dev_id, NULL);
768 if (!pdev) {
769 /* End of list, leave */
770 i7core_printk(KERN_ERR,
771 "Device not found: PCI ID %04x:%04x "
772 "(dev %d, func %d)\n",
773 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
774 pci_devs[i].dev,pci_devs[i].func);
775 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
776 continue; /* Only on chips with RDIMMs */
777 else
778 i7core_put_devices();
779 }
780 pci_devs[i].pdev = pdev;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300781
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300782 rc = pci_enable_device(pdev);
783 if (rc < 0) {
784 i7core_printk(KERN_ERR,
785 "Couldn't enable PCI ID %04x:%04x "
786 "(dev %d, func %d)\n",
787 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
788 pci_devs[i].dev, pci_devs[i].func);
789 i7core_put_devices();
790 return rc;
791 }
792 /* Sanity check */
793 if (PCI_FUNC(pdev->devfn) != pci_devs[i].func) {
794 i7core_printk(KERN_ERR,
795 "Device PCI ID %04x:%04x "
796 "has function %d instead of %d\n",
797 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
798 PCI_FUNC(pdev->devfn), pci_devs[i].func);
799 i7core_put_devices();
800 return -EINVAL;
801 }
802
803 i7core_printk(KERN_INFO,
804 "Registered device %0x:%0x fn=%0x %0x\n",
805 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
806 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
807
808 func = PCI_FUNC(pdev->devfn);
809 if (pci_devs[i].dev < 4) {
810 pvt->pci_mcr[func] = pdev;
811 } else {
812 pvt->pci_ch[pci_devs[i].dev - 4][func] = pdev;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300813 }
814 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300815
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300816 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300817
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300818 return 0;
819}
820
821/*
822 * i7core_probe Probe for ONE instance of device to see if it is
823 * present.
824 * return:
825 * 0 for FOUND a device
826 * < 0 for error code
827 */
828static int __devinit i7core_probe(struct pci_dev *pdev,
829 const struct pci_device_id *id)
830{
831 struct mem_ctl_info *mci;
832 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300833 int num_channels;
834 int num_csrows;
835 int num_dimms_per_channel;
836 int dev_idx = id->driver_data;
837
838 if (dev_idx >= ARRAY_SIZE(i7core_devs))
839 return -EINVAL;
840
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300841 num_channels = NUM_CHANS;
842
843 /* FIXME: FAKE data, since we currently don't now how to get this */
844 num_dimms_per_channel = 4;
845 num_csrows = num_dimms_per_channel;
846
847 /* allocate a new MC control structure */
848 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
849 if (mci == NULL)
850 return -ENOMEM;
851
852 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
853
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300854 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300855 dev_set_drvdata(mci->dev, mci);
856
857 pvt = mci->pvt_info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300858
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300859// pvt->system_address = pdev; /* Record this device in our private */
860// pvt->maxch = num_channels;
861// pvt->maxdimmperch = num_dimms_per_channel;
862
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300863 mci->mc_idx = 0;
864 mci->mtype_cap = MEM_FLAG_FB_DDR2; /* FIXME: it uses DDR3 */
865 mci->edac_ctl_cap = EDAC_FLAG_NONE;
866 mci->edac_cap = EDAC_FLAG_NONE;
867 mci->mod_name = "i7core_edac.c";
868 mci->mod_ver = I7CORE_REVISION;
869 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
870 mci->dev_name = pci_name(pdev);
871 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300872 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300873
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300874 /* 'get' the pci devices we want to reserve for our use */
875 if (i7core_get_devices(mci, pdev))
876 goto fail0;
877
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300878 /* add this new MC control structure to EDAC's list of MCs */
879 if (edac_mc_add_mc(mci)) {
880 debugf0("MC: " __FILE__
881 ": %s(): failed edac_mc_add_mc()\n", __func__);
882 /* FIXME: perhaps some code should go here that disables error
883 * reporting if we just enabled it
884 */
885 goto fail1;
886 }
887
888 /* allocating generic PCI control info */
889 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
890 if (!i7core_pci) {
891 printk(KERN_WARNING
892 "%s(): Unable to create PCI control\n",
893 __func__);
894 printk(KERN_WARNING
895 "%s(): PCI error report via EDAC not setup\n",
896 __func__);
897 }
898
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300899 /* Default error mask is any memory */
900 pvt->inject.channel = -1;
901 pvt->inject.dimm = -1;
902 pvt->inject.rank = -1;
903 pvt->inject.bank = -1;
904 pvt->inject.page = -1;
905 pvt->inject.col = -1;
906
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300907 /* Get dimm basic config */
908 get_dimm_config(mci);
909
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300910 return 0;
911
912fail1:
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300913 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300914
915fail0:
916 edac_mc_free(mci);
917 return -ENODEV;
918}
919
920/*
921 * i7core_remove destructor for one instance of device
922 *
923 */
924static void __devexit i7core_remove(struct pci_dev *pdev)
925{
926 struct mem_ctl_info *mci;
927
928 debugf0(__FILE__ ": %s()\n", __func__);
929
930 if (i7core_pci)
931 edac_pci_release_generic_ctl(i7core_pci);
932
933 mci = edac_mc_del_mc(&pdev->dev);
934 if (!mci)
935 return;
936
937 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300938 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300939
940 edac_mc_free(mci);
941}
942
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300943MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
944
945/*
946 * i7core_driver pci_driver structure for this module
947 *
948 */
949static struct pci_driver i7core_driver = {
950 .name = "i7core_edac",
951 .probe = i7core_probe,
952 .remove = __devexit_p(i7core_remove),
953 .id_table = i7core_pci_tbl,
954};
955
956/*
957 * i7core_init Module entry function
958 * Try to initialize this module for its devices
959 */
960static int __init i7core_init(void)
961{
962 int pci_rc;
963
964 debugf2("MC: " __FILE__ ": %s()\n", __func__);
965
966 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
967 opstate_init();
968
969 pci_rc = pci_register_driver(&i7core_driver);
970
971 return (pci_rc < 0) ? pci_rc : 0;
972}
973
974/*
975 * i7core_exit() Module exit function
976 * Unregister the driver
977 */
978static void __exit i7core_exit(void)
979{
980 debugf2("MC: " __FILE__ ": %s()\n", __func__);
981 pci_unregister_driver(&i7core_driver);
982}
983
984module_init(i7core_init);
985module_exit(i7core_exit);
986
987MODULE_LICENSE("GPL");
988MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
989MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
990MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
991 I7CORE_REVISION);
992
993module_param(edac_op_state, int, 0444);
994MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");