aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2009-06-22 21:41:15 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-05-10 10:44:45 -0400
commita0c36a1f0fbab42590dab3c13c10fa7d20e6c2cd (patch)
tree1aba0a7bd6f50cf394e747d02c624f456a24fdb8
parent66f41d4c5c8a5deed66fdcc84509376c9a0bf9d8 (diff)
i7core_edac: Add an EDAC memory controller driver for Nehalem chipsets
This driver is meant to support i7 core/i7core extreme desktop processors and Xeon 35xx/55xx series with integrated memory controller. It is likely that it can be expanded in the future to work with other processor series based at the same Memory Controller design. For now, it has just a few MCH status reads. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/edac/Kconfig7
-rw-r--r--drivers/edac/Makefile1
-rw-r--r--drivers/edac/i7core_edac.c462
-rw-r--r--include/linux/pci_ids.h16
4 files changed, 486 insertions, 0 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 55c9c59b3f71..391ddbfb2a34 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -166,6 +166,13 @@ config EDAC_I5400
166 Support for error detection and correction the Intel 166 Support for error detection and correction the Intel
167 i5400 MCH chipset (Seaburg). 167 i5400 MCH chipset (Seaburg).
168 168
169config EDAC_I7CORE
170 tristate "Intel i7 Core (Nehalem) processors"
171 depends on EDAC_MM_EDAC && PCI && X86
172 help
173 Support for error detection and correction the Intel
174 i7 Core (Nehalem) Integrated Memory Controller
175
169config EDAC_I82860 176config EDAC_I82860
170 tristate "Intel 82860" 177 tristate "Intel 82860"
171 depends on EDAC_MM_EDAC && PCI && X86_32 178 depends on EDAC_MM_EDAC && PCI && X86_32
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index bc5dc232a0fb..b9996195b233 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_EDAC_CPC925) += cpc925_edac.o
23obj-$(CONFIG_EDAC_I5000) += i5000_edac.o 23obj-$(CONFIG_EDAC_I5000) += i5000_edac.o
24obj-$(CONFIG_EDAC_I5100) += i5100_edac.o 24obj-$(CONFIG_EDAC_I5100) += i5100_edac.o
25obj-$(CONFIG_EDAC_I5400) += i5400_edac.o 25obj-$(CONFIG_EDAC_I5400) += i5400_edac.o
26obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o
26obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o 27obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o
27obj-$(CONFIG_EDAC_E752X) += e752x_edac.o 28obj-$(CONFIG_EDAC_E752X) += e752x_edac.o
28obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o 29obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
new file mode 100644
index 000000000000..7ecf15e66a3f
--- /dev/null
+++ b/drivers/edac/i7core_edac.c
@@ -0,0 +1,462 @@
1/* 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
23
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/pci.h>
27#include <linux/pci_ids.h>
28#include <linux/slab.h>
29#include <linux/edac.h>
30#include <linux/mmzone.h>
31
32#include "edac_core.h"
33
34
35/*
36 * Alter this version for the module when modifications are made
37 */
38#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
39#define EDAC_MOD_STR "i7core_edac"
40
41/* HACK: temporary, just to enable all logs, for now */
42#undef debugf0
43#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
44
45/*
46 * Debug macros
47 */
48#define i7core_printk(level, fmt, arg...) \
49 edac_printk(level, "i7core", fmt, ##arg)
50
51#define i7core_mc_printk(mci, level, fmt, arg...) \
52 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
53
54/*
55 * i7core Memory Controller Registers
56 */
57
58 /* OFFSETS for Device 3 Function 0 */
59
60#define MC_CONTROL 0x48
61#define MC_STATUS 0x4c
62#define MC_MAX_DOD 0x64
63
64 /* OFFSETS for Devices 4,5 and 6 Function 0 */
65
66#define MC_CHANNEL_ADDR_MATCH 0xf0
67
68#define MC_MASK_DIMM (1 << 41)
69#define MC_MASK_RANK (1 << 40)
70#define MC_MASK_BANK (1 << 39)
71#define MC_MASK_PAGE (1 << 38)
72#define MC_MASK_COL (1 << 37)
73
74/*
75 * i7core structs
76 */
77
78#define NUM_CHANS 3
79#define NUM_FUNCS 1
80
81struct i7core_info {
82 u32 mc_control;
83 u32 mc_status;
84 u32 max_dod;
85};
86
87struct i7core_pvt {
88 struct pci_dev *pci_mcr; /* Dev 3:0 */
89 struct pci_dev *pci_ch[NUM_CHANS][NUM_FUNCS];
90 struct i7core_info info;
91};
92
93/* Device name and register DID (Device ID) */
94struct i7core_dev_info {
95 const char *ctl_name; /* name for this device */
96 u16 fsb_mapping_errors; /* DID for the branchmap,control */
97};
98
99static int chan_pci_ids[NUM_CHANS] = {
100 PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL, /* Dev 4 */
101 PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL, /* Dev 5 */
102 PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL, /* Dev 6 */
103};
104
105/* Table of devices attributes supported by this driver */
106static const struct i7core_dev_info i7core_devs[] = {
107 {
108 .ctl_name = "i7 Core",
109 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
110 },
111};
112
113static struct edac_pci_ctl_info *i7core_pci;
114
115/****************************************************************************
116 Anciliary status routines
117 ****************************************************************************/
118
119 /* MC_CONTROL bits */
120#define CH2_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 10)
121#define CH1_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 9)
122#define CH0_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 8)
123#define ECCx8(pvt) ((pvt)->info.mc_control & 1 << 1)
124
125 /* MC_STATUS bits */
126#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & 1 << 3)
127#define CH2_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 2)
128#define CH1_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 1)
129#define CH0_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 0)
130
131 /* MC_MAX_DOD read functions */
132static inline int maxnumdimms(struct i7core_pvt *pvt)
133{
134 return (pvt->info.max_dod & 0x3) + 1;
135}
136
137static inline int maxnumrank(struct i7core_pvt *pvt)
138{
139 static int ranks[4] = { 1, 2, 4, -EINVAL };
140
141 return ranks[(pvt->info.max_dod >> 2) & 0x3];
142}
143
144static inline int maxnumbank(struct i7core_pvt *pvt)
145{
146 static int banks[4] = { 4, 8, 16, -EINVAL };
147
148 return banks[(pvt->info.max_dod >> 4) & 0x3];
149}
150
151static inline int maxnumrow(struct i7core_pvt *pvt)
152{
153 static int rows[8] = {
154 1 << 12, 1 << 13, 1 << 14, 1 << 15,
155 1 << 16, -EINVAL, -EINVAL, -EINVAL,
156 };
157
158 return rows[((pvt->info.max_dod >> 6) & 0x7)];
159}
160
161static inline int maxnumcol(struct i7core_pvt *pvt)
162{
163 static int cols[8] = {
164 1 << 10, 1 << 11, 1 << 12, -EINVAL,
165 };
166 return cols[((pvt->info.max_dod >> 9) & 0x3) << 12];
167}
168
169/****************************************************************************
170 Memory check routines
171 ****************************************************************************/
172static int get_dimm_config(struct mem_ctl_info *mci)
173{
174 struct i7core_pvt *pvt = mci->pvt_info;
175
176 pci_read_config_dword(pvt->pci_mcr, MC_CONTROL, &pvt->info.mc_control);
177 pci_read_config_dword(pvt->pci_mcr, MC_STATUS, &pvt->info.mc_status);
178 pci_read_config_dword(pvt->pci_mcr, MC_MAX_DOD, &pvt->info.max_dod);
179
180 debugf0("Channels active [%c][%c][%c] - enabled [%c][%c][%c]\n",
181 CH0_ACTIVE(pvt)?'0':'-',
182 CH1_ACTIVE(pvt)?'1':'-',
183 CH2_ACTIVE(pvt)?'2':'-',
184 CH0_DISABLED(pvt)?'-':'0',
185 CH1_DISABLED(pvt)?'-':'1',
186 CH2_DISABLED(pvt)?'-':'2');
187
188 if (ECC_ENABLED(pvt))
189 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4);
190 else
191 debugf0("ECC disabled\n");
192
193 /* FIXME: need to handle the error codes */
194 debugf0("DOD Maximum limits: DIMMS: %d, %d-ranked, %d-banked\n",
195 maxnumdimms(pvt), maxnumrank(pvt), maxnumbank(pvt));
196 debugf0("DOD Maximum rows x colums = 0x%x x 0x%x\n",
197 maxnumrow(pvt), maxnumcol(pvt));
198
199 return 0;
200}
201
202/****************************************************************************
203 Device initialization routines: put/get, init/exit
204 ****************************************************************************/
205
206/*
207 * i7core_put_devices 'put' all the devices that we have
208 * reserved via 'get'
209 */
210static void i7core_put_devices(struct mem_ctl_info *mci)
211{
212 struct i7core_pvt *pvt = mci->pvt_info;
213 int i, n;
214
215 pci_dev_put(pvt->pci_mcr);
216
217 /* Release all PCI device functions at MTR channel controllers */
218 for (i = 0; i < NUM_CHANS; i++)
219 for (n = 0; n < NUM_FUNCS; n++)
220 pci_dev_put(pvt->pci_ch[i][n]);
221}
222
223/*
224 * i7core_get_devices Find and perform 'get' operation on the MCH's
225 * device/functions we want to reference for this driver
226 *
227 * Need to 'get' device 16 func 1 and func 2
228 */
229static int i7core_get_devices(struct mem_ctl_info *mci, int dev_idx)
230{
231 struct i7core_pvt *pvt;
232 struct pci_dev *pdev;
233 int i, n, func;
234
235 pvt = mci->pvt_info;
236 memset(pvt, 0, sizeof(*pvt));
237
238 pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR,
239 NULL);
240 if (!pdev) {
241 i7core_printk(KERN_ERR,
242 "Couldn't get PCI ID %04x:%04x function 0\n",
243 PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR);
244 return -ENODEV;
245 }
246 pvt->pci_mcr=pdev;
247
248 /* Get dimm basic config */
249 get_dimm_config(mci);
250
251 /* Retrieve all needed functions at MTR channel controllers */
252 for (i = 0; i < NUM_CHANS; i++) {
253 pdev = NULL;
254 for (n = 0; n < NUM_FUNCS; n++) {
255 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
256 chan_pci_ids[i], pdev);
257 if (!pdev) {
258 /* End of list, leave */
259 i7core_printk(KERN_ERR,
260 "Device not found: PCI ID %04x:%04x "
261 "found only %d functions "
262 "(broken BIOS?)\n",
263 PCI_VENDOR_ID_INTEL,
264 chan_pci_ids[i], n);
265 i7core_put_devices(mci);
266 return -ENODEV;
267 }
268 func = PCI_FUNC(pdev->devfn);
269 pvt->pci_ch[i][func] = pdev;
270 }
271 }
272 i7core_printk(KERN_INFO, "Driver loaded.\n");
273
274 return 0;
275}
276
277/*
278 * i7core_probe Probe for ONE instance of device to see if it is
279 * present.
280 * return:
281 * 0 for FOUND a device
282 * < 0 for error code
283 */
284static int __devinit i7core_probe(struct pci_dev *pdev,
285 const struct pci_device_id *id)
286{
287 struct mem_ctl_info *mci;
288 struct i7core_pvt *pvt;
289 int rc;
290 int num_channels;
291 int num_csrows;
292 int num_dimms_per_channel;
293 int dev_idx = id->driver_data;
294
295 if (dev_idx >= ARRAY_SIZE(i7core_devs))
296 return -EINVAL;
297
298 /* wake up device */
299 rc = pci_enable_device(pdev);
300 if (rc == -EIO)
301 return rc;
302
303 debugf0("MC: " __FILE__ ": %s(), pdev bus %u dev=0x%x fn=0x%x\n",
304 __func__,
305 pdev->bus->number,
306 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
307
308 /* We only are looking for func 0 of the set */
309 if (PCI_FUNC(pdev->devfn) != 0)
310 return -ENODEV;
311
312 num_channels = NUM_CHANS;
313
314 /* FIXME: FAKE data, since we currently don't now how to get this */
315 num_dimms_per_channel = 4;
316 num_csrows = num_dimms_per_channel;
317
318 /* allocate a new MC control structure */
319 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
320 if (mci == NULL)
321 return -ENOMEM;
322
323 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
324
325 mci->dev = &pdev->dev; /* record ptr to the generic device */
326 dev_set_drvdata(mci->dev, mci);
327
328 pvt = mci->pvt_info;
329// pvt->system_address = pdev; /* Record this device in our private */
330// pvt->maxch = num_channels;
331// pvt->maxdimmperch = num_dimms_per_channel;
332
333 /* 'get' the pci devices we want to reserve for our use */
334 if (i7core_get_devices(mci, dev_idx))
335 goto fail0;
336
337 mci->mc_idx = 0;
338 mci->mtype_cap = MEM_FLAG_FB_DDR2; /* FIXME: it uses DDR3 */
339 mci->edac_ctl_cap = EDAC_FLAG_NONE;
340 mci->edac_cap = EDAC_FLAG_NONE;
341 mci->mod_name = "i7core_edac.c";
342 mci->mod_ver = I7CORE_REVISION;
343 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
344 mci->dev_name = pci_name(pdev);
345 mci->ctl_page_to_phys = NULL;
346
347 /* add this new MC control structure to EDAC's list of MCs */
348 if (edac_mc_add_mc(mci)) {
349 debugf0("MC: " __FILE__
350 ": %s(): failed edac_mc_add_mc()\n", __func__);
351 /* FIXME: perhaps some code should go here that disables error
352 * reporting if we just enabled it
353 */
354 goto fail1;
355 }
356
357 /* allocating generic PCI control info */
358 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
359 if (!i7core_pci) {
360 printk(KERN_WARNING
361 "%s(): Unable to create PCI control\n",
362 __func__);
363 printk(KERN_WARNING
364 "%s(): PCI error report via EDAC not setup\n",
365 __func__);
366 }
367
368 return 0;
369
370fail1:
371 i7core_put_devices(mci);
372
373fail0:
374 edac_mc_free(mci);
375 return -ENODEV;
376}
377
378/*
379 * i7core_remove destructor for one instance of device
380 *
381 */
382static void __devexit i7core_remove(struct pci_dev *pdev)
383{
384 struct mem_ctl_info *mci;
385
386 debugf0(__FILE__ ": %s()\n", __func__);
387
388 if (i7core_pci)
389 edac_pci_release_generic_ctl(i7core_pci);
390
391 mci = edac_mc_del_mc(&pdev->dev);
392 if (!mci)
393 return;
394
395 /* retrieve references to resources, and free those resources */
396 i7core_put_devices(mci);
397
398 edac_mc_free(mci);
399}
400
401/*
402 * pci_device_id table for which devices we are looking for
403 *
404 * The "E500P" device is the first device supported.
405 */
406static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
407 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)},
408 {0,} /* 0 terminated list. */
409};
410
411MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
412
413/*
414 * i7core_driver pci_driver structure for this module
415 *
416 */
417static struct pci_driver i7core_driver = {
418 .name = "i7core_edac",
419 .probe = i7core_probe,
420 .remove = __devexit_p(i7core_remove),
421 .id_table = i7core_pci_tbl,
422};
423
424/*
425 * i7core_init Module entry function
426 * Try to initialize this module for its devices
427 */
428static int __init i7core_init(void)
429{
430 int pci_rc;
431
432 debugf2("MC: " __FILE__ ": %s()\n", __func__);
433
434 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
435 opstate_init();
436
437 pci_rc = pci_register_driver(&i7core_driver);
438
439 return (pci_rc < 0) ? pci_rc : 0;
440}
441
442/*
443 * i7core_exit() Module exit function
444 * Unregister the driver
445 */
446static void __exit i7core_exit(void)
447{
448 debugf2("MC: " __FILE__ ": %s()\n", __func__);
449 pci_unregister_driver(&i7core_driver);
450}
451
452module_init(i7core_init);
453module_exit(i7core_exit);
454
455MODULE_LICENSE("GPL");
456MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
457MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
458MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
459 I7CORE_REVISION);
460
461module_param(edac_op_state, int, 0444);
462MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 9f688d243b86..c5dd0994bd7c 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2532,6 +2532,22 @@
2532#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 2532#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930
2533#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 2533#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916
2534#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 2534#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918
2535#define PCI_DEVICE_ID_INTEL_I7_MCR 0x2c18
2536#define PCI_DEVICE_ID_INTEL_I7_MC_TAD 0x2c19
2537#define PCI_DEVICE_ID_INTEL_I7_MC_RAS 0x2c1a
2538#define PCI_DEVICE_ID_INTEL_I7_MC_TEST 0x2c1c
2539#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL 0x2c20
2540#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR 0x2c21
2541#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK 0x2c22
2542#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC 0x2c23
2543#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL 0x2c28
2544#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR 0x2c29
2545#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK 0x2c2a
2546#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC 0x2c2b
2547#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL 0x2c30
2548#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR 0x2c31
2549#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK 0x2c32
2550#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC 0x2c33
2535#define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 2551#define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340
2536#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 2552#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429
2537#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a 2553#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a