blob: 190596af601a98854faaea913b34f196cee93a68 [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
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -030033/* To use the new pci_[read/write]_config_qword instead of two dword */
34#define USE_QWORD 1
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030035
36/*
37 * Alter this version for the module when modifications are made
38 */
39#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
40#define EDAC_MOD_STR "i7core_edac"
41
42/* HACK: temporary, just to enable all logs, for now */
43#undef debugf0
44#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
45
46/*
47 * Debug macros
48 */
49#define i7core_printk(level, fmt, arg...) \
50 edac_printk(level, "i7core", fmt, ##arg)
51
52#define i7core_mc_printk(mci, level, fmt, arg...) \
53 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
54
55/*
56 * i7core Memory Controller Registers
57 */
58
59 /* OFFSETS for Device 3 Function 0 */
60
61#define MC_CONTROL 0x48
62#define MC_STATUS 0x4c
63#define MC_MAX_DOD 0x64
64
65 /* OFFSETS for Devices 4,5 and 6 Function 0 */
66
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030067#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
68 #define THREE_DIMMS_PRESENT (1 << 24)
69 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
70 #define QUAD_RANK_PRESENT (1 << 22)
71 #define REGISTERED_DIMM (1 << 15)
72
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030073#define MC_CHANNEL_MAPPER 0x60
74 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
75 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
76
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030077#define MC_CHANNEL_RANK_PRESENT 0x7c
78 #define RANK_PRESENT_MASK 0xffff
79
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030080#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030081#define MC_CHANNEL_ERROR_MASK 0xf8
82#define MC_CHANNEL_ERROR_INJECT 0xfc
83 #define INJECT_ADDR_PARITY 0x10
84 #define INJECT_ECC 0x08
85 #define MASK_CACHELINE 0x06
86 #define MASK_FULL_CACHELINE 0x06
87 #define MASK_MSB32_CACHELINE 0x04
88 #define MASK_LSB32_CACHELINE 0x02
89 #define NO_MASK_CACHELINE 0x00
90 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030091
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030092 /* OFFSETS for Devices 4,5 and 6 Function 1 */
93#define MC_DOD_CH_DIMM0 0x48
94#define MC_DOD_CH_DIMM1 0x4c
95#define MC_DOD_CH_DIMM2 0x50
96 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
97 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
98 #define DIMM_PRESENT_MASK (1 << 9)
99 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
100 #define NUMBANK_MASK ((1 << 8) | (1 << 7))
101 #define NUMBANK(x) (((x) & NUMBANK_MASK) >> 7)
102 #define NUMRANK_MASK ((1 << 6) | (1 << 5))
103 #define NUMRANK(x) (((x) & NUMRANK_MASK) >> 5)
104 #define NUMROW_MASK ((1 << 4) | (1 << 3))
105 #define NUMROW(x) (((x) & NUMROW_MASK) >> 3)
106 #define NUMCOL_MASK 3
107 #define NUMCOL(x) ((x) & NUMCOL_MASK)
108
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300109#define MC_RANK_PRESENT 0x7c
110
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300111#define MC_SAG_CH_0 0x80
112#define MC_SAG_CH_1 0x84
113#define MC_SAG_CH_2 0x88
114#define MC_SAG_CH_3 0x8c
115#define MC_SAG_CH_4 0x90
116#define MC_SAG_CH_5 0x94
117#define MC_SAG_CH_6 0x98
118#define MC_SAG_CH_7 0x9c
119
120#define MC_RIR_LIMIT_CH_0 0x40
121#define MC_RIR_LIMIT_CH_1 0x44
122#define MC_RIR_LIMIT_CH_2 0x48
123#define MC_RIR_LIMIT_CH_3 0x4C
124#define MC_RIR_LIMIT_CH_4 0x50
125#define MC_RIR_LIMIT_CH_5 0x54
126#define MC_RIR_LIMIT_CH_6 0x58
127#define MC_RIR_LIMIT_CH_7 0x5C
128#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
129
130#define MC_RIR_WAY_CH 0x80
131 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
132 #define MC_RIR_WAY_RANK_MASK 0x7
133
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300134/*
135 * i7core structs
136 */
137
138#define NUM_CHANS 3
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300139#define NUM_MCR_FUNCS 4
140#define NUM_CHAN_FUNCS 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300141
142struct i7core_info {
143 u32 mc_control;
144 u32 mc_status;
145 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300146 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300147};
148
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300149
150struct i7core_inject {
151 int enable;
152
153 u32 section;
154 u32 type;
155 u32 eccmask;
156
157 /* Error address mask */
158 int channel, dimm, rank, bank, page, col;
159};
160
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300161struct i7core_channel {
162 u32 ranks;
163 u32 dimms;
164};
165
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300166struct pci_id_descr {
167 int dev;
168 int func;
169 int dev_id;
170 struct pci_dev *pdev;
171};
172
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300173struct i7core_pvt {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300174 struct pci_dev *pci_mcr[NUM_MCR_FUNCS];
175 struct pci_dev *pci_ch[NUM_CHANS][NUM_CHAN_FUNCS];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300176 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300177 struct i7core_inject inject;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300178 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300179};
180
181/* Device name and register DID (Device ID) */
182struct i7core_dev_info {
183 const char *ctl_name; /* name for this device */
184 u16 fsb_mapping_errors; /* DID for the branchmap,control */
185};
186
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300187#define PCI_DESCR(device, function, device_id) \
188 .dev = (device), \
189 .func = (function), \
190 .dev_id = (device_id)
191
192struct pci_id_descr pci_devs[] = {
193 /* Memory controller */
194 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
195 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
196 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
197 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
198
199 /* Channel 0 */
200 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
201 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
202 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
203 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
204
205 /* Channel 1 */
206 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
207 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
208 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
209 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
210
211 /* Channel 2 */
212 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
213 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
214 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
215 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300216};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300217#define N_DEVS ARRAY_SIZE(pci_devs)
218
219/*
220 * pci_device_id table for which devices we are looking for
221 * This should match the first device at pci_devs table
222 */
223static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
224 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)},
225 {0,} /* 0 terminated list. */
226};
227
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300228
229/* Table of devices attributes supported by this driver */
230static const struct i7core_dev_info i7core_devs[] = {
231 {
232 .ctl_name = "i7 Core",
233 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
234 },
235};
236
237static struct edac_pci_ctl_info *i7core_pci;
238
239/****************************************************************************
240 Anciliary status routines
241 ****************************************************************************/
242
243 /* MC_CONTROL bits */
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300244#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & 1 << (8 + ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300245#define ECCx8(pvt) ((pvt)->info.mc_control & 1 << 1)
246
247 /* MC_STATUS bits */
248#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & 1 << 3)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300249#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & 1 << ch)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300250
251 /* MC_MAX_DOD read functions */
252static inline int maxnumdimms(struct i7core_pvt *pvt)
253{
254 return (pvt->info.max_dod & 0x3) + 1;
255}
256
257static inline int maxnumrank(struct i7core_pvt *pvt)
258{
259 static int ranks[4] = { 1, 2, 4, -EINVAL };
260
261 return ranks[(pvt->info.max_dod >> 2) & 0x3];
262}
263
264static inline int maxnumbank(struct i7core_pvt *pvt)
265{
266 static int banks[4] = { 4, 8, 16, -EINVAL };
267
268 return banks[(pvt->info.max_dod >> 4) & 0x3];
269}
270
271static inline int maxnumrow(struct i7core_pvt *pvt)
272{
273 static int rows[8] = {
274 1 << 12, 1 << 13, 1 << 14, 1 << 15,
275 1 << 16, -EINVAL, -EINVAL, -EINVAL,
276 };
277
278 return rows[((pvt->info.max_dod >> 6) & 0x7)];
279}
280
281static inline int maxnumcol(struct i7core_pvt *pvt)
282{
283 static int cols[8] = {
284 1 << 10, 1 << 11, 1 << 12, -EINVAL,
285 };
286 return cols[((pvt->info.max_dod >> 9) & 0x3) << 12];
287}
288
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300289
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300290/****************************************************************************
291 Memory check routines
292 ****************************************************************************/
293static int get_dimm_config(struct mem_ctl_info *mci)
294{
295 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300296 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300297
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300298 if (!pvt->pci_mcr[0])
299 return -ENODEV;
300
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300301 /* Device 3 function 0 reads */
302 pci_read_config_dword(pvt->pci_mcr[0], MC_CONTROL,
303 &pvt->info.mc_control);
304 pci_read_config_dword(pvt->pci_mcr[0], MC_STATUS,
305 &pvt->info.mc_status);
306 pci_read_config_dword(pvt->pci_mcr[0], MC_MAX_DOD,
307 &pvt->info.max_dod);
308 pci_read_config_dword(pvt->pci_mcr[0], MC_CHANNEL_MAPPER,
309 &pvt->info.ch_map);
310
311 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
312 pvt->info.mc_control, pvt->info.mc_status,
313 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300314
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300315 if (ECC_ENABLED(pvt))
316 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4);
317 else
318 debugf0("ECC disabled\n");
319
320 /* FIXME: need to handle the error codes */
321 debugf0("DOD Maximum limits: DIMMS: %d, %d-ranked, %d-banked\n",
322 maxnumdimms(pvt), maxnumrank(pvt), maxnumbank(pvt));
323 debugf0("DOD Maximum rows x colums = 0x%x x 0x%x\n",
324 maxnumrow(pvt), maxnumcol(pvt));
325
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300326 debugf0("Memory channel configuration:\n");
327
328 for (i = 0; i < NUM_CHANS; i++) {
329 u32 data;
330
331 if (!CH_ACTIVE(pvt, i)) {
332 debugf0("Channel %i is not active\n", i);
333 continue;
334 }
335 if (CH_DISABLED(pvt, i)) {
336 debugf0("Channel %i is disabled\n", i);
337 continue;
338 }
339
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300340 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300341 pci_read_config_dword(pvt->pci_ch[i][0],
342 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
343
344 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT)? 4 : 2;
345
346 if (data & THREE_DIMMS_PRESENT)
347 pvt->channel[i].dimms = 3;
348 else if (data & SINGLE_QUAD_RANK_PRESENT)
349 pvt->channel[i].dimms = 1;
350 else
351 pvt->channel[i].dimms = 2;
352
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300353 debugf0("Ch%d (0x%08x): rd ch %d, wr ch %d, "
354 "%d ranks, %d %cDIMMs\n",
355 i, data,
356 RDLCH(pvt->info.ch_map, i),
357 WRLCH(pvt->info.ch_map, i),
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300358 pvt->channel[i].ranks, pvt->channel[i].dimms,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300359 (data & REGISTERED_DIMM)? 'R' : 'U' );
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300360 }
361
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300362 return 0;
363}
364
365/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300366 Error insertion routines
367 ****************************************************************************/
368
369/* The i7core has independent error injection features per channel.
370 However, to have a simpler code, we don't allow enabling error injection
371 on more than one channel.
372 Also, since a change at an inject parameter will be applied only at enable,
373 we're disabling error injection on all write calls to the sysfs nodes that
374 controls the error code injection.
375 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300376static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300377{
378 struct i7core_pvt *pvt = mci->pvt_info;
379
380 pvt->inject.enable = 0;
381
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300382 if (!pvt->pci_ch[pvt->inject.channel][0])
383 return -ENODEV;
384
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300385 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
386 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300387
388 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300389}
390
391/*
392 * i7core inject inject.section
393 *
394 * accept and store error injection inject.section value
395 * bit 0 - refers to the lower 32-byte half cacheline
396 * bit 1 - refers to the upper 32-byte half cacheline
397 */
398static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
399 const char *data, size_t count)
400{
401 struct i7core_pvt *pvt = mci->pvt_info;
402 unsigned long value;
403 int rc;
404
405 if (pvt->inject.enable)
406 disable_inject(mci);
407
408 rc = strict_strtoul(data, 10, &value);
409 if ((rc < 0) || (value > 3))
410 return 0;
411
412 pvt->inject.section = (u32) value;
413 return count;
414}
415
416static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
417 char *data)
418{
419 struct i7core_pvt *pvt = mci->pvt_info;
420 return sprintf(data, "0x%08x\n", pvt->inject.section);
421}
422
423/*
424 * i7core inject.type
425 *
426 * accept and store error injection inject.section value
427 * bit 0 - repeat enable - Enable error repetition
428 * bit 1 - inject ECC error
429 * bit 2 - inject parity error
430 */
431static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
432 const char *data, size_t count)
433{
434 struct i7core_pvt *pvt = mci->pvt_info;
435 unsigned long value;
436 int rc;
437
438 if (pvt->inject.enable)
439 disable_inject(mci);
440
441 rc = strict_strtoul(data, 10, &value);
442 if ((rc < 0) || (value > 7))
443 return 0;
444
445 pvt->inject.type = (u32) value;
446 return count;
447}
448
449static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
450 char *data)
451{
452 struct i7core_pvt *pvt = mci->pvt_info;
453 return sprintf(data, "0x%08x\n", pvt->inject.type);
454}
455
456/*
457 * i7core_inject_inject.eccmask_store
458 *
459 * The type of error (UE/CE) will depend on the inject.eccmask value:
460 * Any bits set to a 1 will flip the corresponding ECC bit
461 * Correctable errors can be injected by flipping 1 bit or the bits within
462 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
463 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
464 * uncorrectable error to be injected.
465 */
466static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
467 const char *data, size_t count)
468{
469 struct i7core_pvt *pvt = mci->pvt_info;
470 unsigned long value;
471 int rc;
472
473 if (pvt->inject.enable)
474 disable_inject(mci);
475
476 rc = strict_strtoul(data, 10, &value);
477 if (rc < 0)
478 return 0;
479
480 pvt->inject.eccmask = (u32) value;
481 return count;
482}
483
484static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
485 char *data)
486{
487 struct i7core_pvt *pvt = mci->pvt_info;
488 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
489}
490
491/*
492 * i7core_addrmatch
493 *
494 * The type of error (UE/CE) will depend on the inject.eccmask value:
495 * Any bits set to a 1 will flip the corresponding ECC bit
496 * Correctable errors can be injected by flipping 1 bit or the bits within
497 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
498 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
499 * uncorrectable error to be injected.
500 */
501static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
502 const char *data, size_t count)
503{
504 struct i7core_pvt *pvt = mci->pvt_info;
505 char *cmd, *val;
506 long value;
507 int rc;
508
509 if (pvt->inject.enable)
510 disable_inject(mci);
511
512 do {
513 cmd = strsep((char **) &data, ":");
514 if (!cmd)
515 break;
516 val = strsep((char **) &data, " \n\t");
517 if (!val)
518 return cmd - data;
519
520 if (!strcasecmp(val,"any"))
521 value = -1;
522 else {
523 rc = strict_strtol(val, 10, &value);
524 if ((rc < 0) || (value < 0))
525 return cmd - data;
526 }
527
528 if (!strcasecmp(cmd,"channel")) {
529 if (value < 3)
530 pvt->inject.channel = value;
531 else
532 return cmd - data;
533 } else if (!strcasecmp(cmd,"dimm")) {
534 if (value < 4)
535 pvt->inject.dimm = value;
536 else
537 return cmd - data;
538 } else if (!strcasecmp(cmd,"rank")) {
539 if (value < 4)
540 pvt->inject.rank = value;
541 else
542 return cmd - data;
543 } else if (!strcasecmp(cmd,"bank")) {
544 if (value < 4)
545 pvt->inject.bank = value;
546 else
547 return cmd - data;
548 } else if (!strcasecmp(cmd,"page")) {
549 if (value <= 0xffff)
550 pvt->inject.page = value;
551 else
552 return cmd - data;
553 } else if (!strcasecmp(cmd,"col") ||
554 !strcasecmp(cmd,"column")) {
555 if (value <= 0x3fff)
556 pvt->inject.col = value;
557 else
558 return cmd - data;
559 }
560 } while (1);
561
562 return count;
563}
564
565static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
566 char *data)
567{
568 struct i7core_pvt *pvt = mci->pvt_info;
569 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
570
571 if (pvt->inject.channel < 0)
572 sprintf(channel, "any");
573 else
574 sprintf(channel, "%d", pvt->inject.channel);
575 if (pvt->inject.dimm < 0)
576 sprintf(dimm, "any");
577 else
578 sprintf(dimm, "%d", pvt->inject.dimm);
579 if (pvt->inject.bank < 0)
580 sprintf(bank, "any");
581 else
582 sprintf(bank, "%d", pvt->inject.bank);
583 if (pvt->inject.rank < 0)
584 sprintf(rank, "any");
585 else
586 sprintf(rank, "%d", pvt->inject.rank);
587 if (pvt->inject.page < 0)
588 sprintf(page, "any");
589 else
590 sprintf(page, "0x%04x", pvt->inject.page);
591 if (pvt->inject.col < 0)
592 sprintf(col, "any");
593 else
594 sprintf(col, "0x%04x", pvt->inject.col);
595
596 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
597 "rank: %s\npage: %s\ncolumn: %s\n",
598 channel, dimm, bank, rank, page, col);
599}
600
601/*
602 * This routine prepares the Memory Controller for error injection.
603 * The error will be injected when some process tries to write to the
604 * memory that matches the given criteria.
605 * The criteria can be set in terms of a mask where dimm, rank, bank, page
606 * and col can be specified.
607 * A -1 value for any of the mask items will make the MCU to ignore
608 * that matching criteria for error injection.
609 *
610 * It should be noticed that the error will only happen after a write operation
611 * on a memory that matches the condition. if REPEAT_EN is not enabled at
612 * inject mask, then it will produce just one error. Otherwise, it will repeat
613 * until the injectmask would be cleaned.
614 *
615 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
616 * is reliable enough to check if the MC is using the
617 * three channels. However, this is not clear at the datasheet.
618 */
619static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
620 const char *data, size_t count)
621{
622 struct i7core_pvt *pvt = mci->pvt_info;
623 u32 injectmask;
624 u64 mask = 0;
625 int rc;
626 long enable;
627
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300628 if (!pvt->pci_ch[pvt->inject.channel][0])
629 return 0;
630
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300631 rc = strict_strtoul(data, 10, &enable);
632 if ((rc < 0))
633 return 0;
634
635 if (enable) {
636 pvt->inject.enable = 1;
637 } else {
638 disable_inject(mci);
639 return count;
640 }
641
642 /* Sets pvt->inject.dimm mask */
643 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300644 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300645 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300646 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300647 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300648 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300649 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300650 }
651
652 /* Sets pvt->inject.rank mask */
653 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300654 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300655 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300656 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300657 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300658 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300659 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300660 }
661
662 /* Sets pvt->inject.bank mask */
663 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300664 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300665 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300666 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300667
668 /* Sets pvt->inject.page mask */
669 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300670 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300671 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300672 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300673
674 /* Sets pvt->inject.column mask */
675 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300676 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300677 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300678 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300679
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300680#if USE_QWORD
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300681 pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0],
682 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300683#else
684 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
685 MC_CHANNEL_ADDR_MATCH, mask);
686 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
687 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
688#endif
689
690#if 1
691#if USE_QWORD
692 u64 rdmask;
693 pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0],
694 MC_CHANNEL_ADDR_MATCH, &rdmask);
695 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
696 mask, rdmask);
697#else
698 u32 rdmask1, rdmask2;
699
700 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
701 MC_CHANNEL_ADDR_MATCH, &rdmask1);
702 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
703 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
704
705 debugf0("Inject addr match write 0x%016llx, read: 0x%08x%08x\n",
706 mask, rdmask1, rdmask2);
707#endif
708#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300709
710 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
711 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
712
713 /*
714 * bit 0: REPEAT_EN
715 * bits 1-2: MASK_HALF_CACHELINE
716 * bit 3: INJECT_ECC
717 * bit 4: INJECT_ADDR_PARITY
718 */
719
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300720 injectmask = (pvt->inject.type & 1) |
721 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300722 (pvt->inject.type & 0x6) << (3 - 1);
723
724 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
725 MC_CHANNEL_ERROR_MASK, injectmask);
726
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300727 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
728 mask, pvt->inject.eccmask, injectmask);
729
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300730
731
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300732 return count;
733}
734
735static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
736 char *data)
737{
738 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300739 u32 injectmask;
740
741 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
742 MC_CHANNEL_ERROR_MASK, &injectmask);
743
744 debugf0("Inject error read: 0x%018x\n", injectmask);
745
746 if (injectmask & 0x0c)
747 pvt->inject.enable = 1;
748
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300749 return sprintf(data, "%d\n", pvt->inject.enable);
750}
751
752/*
753 * Sysfs struct
754 */
755static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
756
757 {
758 .attr = {
759 .name = "inject_section",
760 .mode = (S_IRUGO | S_IWUSR)
761 },
762 .show = i7core_inject_section_show,
763 .store = i7core_inject_section_store,
764 }, {
765 .attr = {
766 .name = "inject_type",
767 .mode = (S_IRUGO | S_IWUSR)
768 },
769 .show = i7core_inject_type_show,
770 .store = i7core_inject_type_store,
771 }, {
772 .attr = {
773 .name = "inject_eccmask",
774 .mode = (S_IRUGO | S_IWUSR)
775 },
776 .show = i7core_inject_eccmask_show,
777 .store = i7core_inject_eccmask_store,
778 }, {
779 .attr = {
780 .name = "inject_addrmatch",
781 .mode = (S_IRUGO | S_IWUSR)
782 },
783 .show = i7core_inject_addrmatch_show,
784 .store = i7core_inject_addrmatch_store,
785 }, {
786 .attr = {
787 .name = "inject_enable",
788 .mode = (S_IRUGO | S_IWUSR)
789 },
790 .show = i7core_inject_enable_show,
791 .store = i7core_inject_enable_store,
792 },
793};
794
795/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300796 Device initialization routines: put/get, init/exit
797 ****************************************************************************/
798
799/*
800 * i7core_put_devices 'put' all the devices that we have
801 * reserved via 'get'
802 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300803static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300804{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300805 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300806
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300807 for (i = 0; i < N_DEVS; i++)
808 pci_dev_put(pci_devs[i].pdev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300809}
810
811/*
812 * i7core_get_devices Find and perform 'get' operation on the MCH's
813 * device/functions we want to reference for this driver
814 *
815 * Need to 'get' device 16 func 1 and func 2
816 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300817static int i7core_get_devices(struct mem_ctl_info *mci, struct pci_dev *mcidev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300818{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300819 struct i7core_pvt *pvt = mci->pvt_info;
820 int rc, i,func;
821 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300822
823 pvt = mci->pvt_info;
824 memset(pvt, 0, sizeof(*pvt));
825
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300826 for (i = 0; i < N_DEVS; i++) {
827 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
828 pci_devs[i].dev_id, NULL);
829 if (!pdev) {
830 /* End of list, leave */
831 i7core_printk(KERN_ERR,
832 "Device not found: PCI ID %04x:%04x "
833 "(dev %d, func %d)\n",
834 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
835 pci_devs[i].dev,pci_devs[i].func);
836 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
837 continue; /* Only on chips with RDIMMs */
838 else
839 i7core_put_devices();
840 }
841 pci_devs[i].pdev = pdev;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300842
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300843 rc = pci_enable_device(pdev);
844 if (rc < 0) {
845 i7core_printk(KERN_ERR,
846 "Couldn't enable PCI ID %04x:%04x "
847 "(dev %d, func %d)\n",
848 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
849 pci_devs[i].dev, pci_devs[i].func);
850 i7core_put_devices();
851 return rc;
852 }
853 /* Sanity check */
854 if (PCI_FUNC(pdev->devfn) != pci_devs[i].func) {
855 i7core_printk(KERN_ERR,
856 "Device PCI ID %04x:%04x "
857 "has function %d instead of %d\n",
858 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
859 PCI_FUNC(pdev->devfn), pci_devs[i].func);
860 i7core_put_devices();
861 return -EINVAL;
862 }
863
864 i7core_printk(KERN_INFO,
865 "Registered device %0x:%0x fn=%0x %0x\n",
866 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
867 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
868
869 func = PCI_FUNC(pdev->devfn);
870 if (pci_devs[i].dev < 4) {
871 pvt->pci_mcr[func] = pdev;
872 } else {
873 pvt->pci_ch[pci_devs[i].dev - 4][func] = pdev;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300874 }
875 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300876
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300877 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300878
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300879 return 0;
880}
881
882/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -0300883 * i7core_check_error Retrieve and process errors reported by the
884 * hardware. Called by the Core module.
885 */
886static void i7core_check_error(struct mem_ctl_info *mci)
887{
888 /* FIXME: need a real code here */
889}
890
891/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300892 * i7core_probe Probe for ONE instance of device to see if it is
893 * present.
894 * return:
895 * 0 for FOUND a device
896 * < 0 for error code
897 */
898static int __devinit i7core_probe(struct pci_dev *pdev,
899 const struct pci_device_id *id)
900{
901 struct mem_ctl_info *mci;
902 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300903 int num_channels;
904 int num_csrows;
905 int num_dimms_per_channel;
906 int dev_idx = id->driver_data;
907
908 if (dev_idx >= ARRAY_SIZE(i7core_devs))
909 return -EINVAL;
910
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300911 num_channels = NUM_CHANS;
912
913 /* FIXME: FAKE data, since we currently don't now how to get this */
914 num_dimms_per_channel = 4;
915 num_csrows = num_dimms_per_channel;
916
917 /* allocate a new MC control structure */
918 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
919 if (mci == NULL)
920 return -ENOMEM;
921
922 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
923
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -0300924 /* 'get' the pci devices we want to reserve for our use */
925 if (i7core_get_devices(mci, pdev))
926 goto fail0;
927
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300928 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300929
930 pvt = mci->pvt_info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300931
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300932// pvt->system_address = pdev; /* Record this device in our private */
933// pvt->maxch = num_channels;
934// pvt->maxdimmperch = num_dimms_per_channel;
935
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300936 mci->mc_idx = 0;
937 mci->mtype_cap = MEM_FLAG_FB_DDR2; /* FIXME: it uses DDR3 */
938 mci->edac_ctl_cap = EDAC_FLAG_NONE;
939 mci->edac_cap = EDAC_FLAG_NONE;
940 mci->mod_name = "i7core_edac.c";
941 mci->mod_ver = I7CORE_REVISION;
942 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
943 mci->dev_name = pci_name(pdev);
944 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300945 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300946
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -0300947 /* Set the function pointer to an actual operation function */
948 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300949
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300950 /* add this new MC control structure to EDAC's list of MCs */
951 if (edac_mc_add_mc(mci)) {
952 debugf0("MC: " __FILE__
953 ": %s(): failed edac_mc_add_mc()\n", __func__);
954 /* FIXME: perhaps some code should go here that disables error
955 * reporting if we just enabled it
956 */
957 goto fail1;
958 }
959
960 /* allocating generic PCI control info */
961 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
962 if (!i7core_pci) {
963 printk(KERN_WARNING
964 "%s(): Unable to create PCI control\n",
965 __func__);
966 printk(KERN_WARNING
967 "%s(): PCI error report via EDAC not setup\n",
968 __func__);
969 }
970
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300971 /* Default error mask is any memory */
972 pvt->inject.channel = -1;
973 pvt->inject.dimm = -1;
974 pvt->inject.rank = -1;
975 pvt->inject.bank = -1;
976 pvt->inject.page = -1;
977 pvt->inject.col = -1;
978
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300979 /* Get dimm basic config */
980 get_dimm_config(mci);
981
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300982 return 0;
983
984fail1:
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300985 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300986
987fail0:
988 edac_mc_free(mci);
989 return -ENODEV;
990}
991
992/*
993 * i7core_remove destructor for one instance of device
994 *
995 */
996static void __devexit i7core_remove(struct pci_dev *pdev)
997{
998 struct mem_ctl_info *mci;
999
1000 debugf0(__FILE__ ": %s()\n", __func__);
1001
1002 if (i7core_pci)
1003 edac_pci_release_generic_ctl(i7core_pci);
1004
1005 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001006
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001007 if (!mci)
1008 return;
1009
1010 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001011 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001012
1013 edac_mc_free(mci);
1014}
1015
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001016MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1017
1018/*
1019 * i7core_driver pci_driver structure for this module
1020 *
1021 */
1022static struct pci_driver i7core_driver = {
1023 .name = "i7core_edac",
1024 .probe = i7core_probe,
1025 .remove = __devexit_p(i7core_remove),
1026 .id_table = i7core_pci_tbl,
1027};
1028
1029/*
1030 * i7core_init Module entry function
1031 * Try to initialize this module for its devices
1032 */
1033static int __init i7core_init(void)
1034{
1035 int pci_rc;
1036
1037 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1038
1039 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1040 opstate_init();
1041
1042 pci_rc = pci_register_driver(&i7core_driver);
1043
1044 return (pci_rc < 0) ? pci_rc : 0;
1045}
1046
1047/*
1048 * i7core_exit() Module exit function
1049 * Unregister the driver
1050 */
1051static void __exit i7core_exit(void)
1052{
1053 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1054 pci_unregister_driver(&i7core_driver);
1055}
1056
1057module_init(i7core_init);
1058module_exit(i7core_exit);
1059
1060MODULE_LICENSE("GPL");
1061MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1062MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1063MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1064 I7CORE_REVISION);
1065
1066module_param(edac_op_state, int, 0444);
1067MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");