blob: 62ae472c4e27ded4d16706831f0171026fb7eff4 [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
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030065/*
66 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
67 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
68 */
69
70#define MC_TEST_ERR_RCV1 0x60
71 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
72
73#define MC_TEST_ERR_RCV0 0x64
74 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
75 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
76
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030077 /* OFFSETS for Devices 4,5 and 6 Function 0 */
78
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030079#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
80 #define THREE_DIMMS_PRESENT (1 << 24)
81 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
82 #define QUAD_RANK_PRESENT (1 << 22)
83 #define REGISTERED_DIMM (1 << 15)
84
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030085#define MC_CHANNEL_MAPPER 0x60
86 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
87 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
88
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030089#define MC_CHANNEL_RANK_PRESENT 0x7c
90 #define RANK_PRESENT_MASK 0xffff
91
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030092#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030093#define MC_CHANNEL_ERROR_MASK 0xf8
94#define MC_CHANNEL_ERROR_INJECT 0xfc
95 #define INJECT_ADDR_PARITY 0x10
96 #define INJECT_ECC 0x08
97 #define MASK_CACHELINE 0x06
98 #define MASK_FULL_CACHELINE 0x06
99 #define MASK_MSB32_CACHELINE 0x04
100 #define MASK_LSB32_CACHELINE 0x02
101 #define NO_MASK_CACHELINE 0x00
102 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300103
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300104 /* OFFSETS for Devices 4,5 and 6 Function 1 */
105#define MC_DOD_CH_DIMM0 0x48
106#define MC_DOD_CH_DIMM1 0x4c
107#define MC_DOD_CH_DIMM2 0x50
108 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
109 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
110 #define DIMM_PRESENT_MASK (1 << 9)
111 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
112 #define NUMBANK_MASK ((1 << 8) | (1 << 7))
113 #define NUMBANK(x) (((x) & NUMBANK_MASK) >> 7)
114 #define NUMRANK_MASK ((1 << 6) | (1 << 5))
115 #define NUMRANK(x) (((x) & NUMRANK_MASK) >> 5)
116 #define NUMROW_MASK ((1 << 4) | (1 << 3))
117 #define NUMROW(x) (((x) & NUMROW_MASK) >> 3)
118 #define NUMCOL_MASK 3
119 #define NUMCOL(x) ((x) & NUMCOL_MASK)
120
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300121#define MC_RANK_PRESENT 0x7c
122
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300123#define MC_SAG_CH_0 0x80
124#define MC_SAG_CH_1 0x84
125#define MC_SAG_CH_2 0x88
126#define MC_SAG_CH_3 0x8c
127#define MC_SAG_CH_4 0x90
128#define MC_SAG_CH_5 0x94
129#define MC_SAG_CH_6 0x98
130#define MC_SAG_CH_7 0x9c
131
132#define MC_RIR_LIMIT_CH_0 0x40
133#define MC_RIR_LIMIT_CH_1 0x44
134#define MC_RIR_LIMIT_CH_2 0x48
135#define MC_RIR_LIMIT_CH_3 0x4C
136#define MC_RIR_LIMIT_CH_4 0x50
137#define MC_RIR_LIMIT_CH_5 0x54
138#define MC_RIR_LIMIT_CH_6 0x58
139#define MC_RIR_LIMIT_CH_7 0x5C
140#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
141
142#define MC_RIR_WAY_CH 0x80
143 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
144 #define MC_RIR_WAY_RANK_MASK 0x7
145
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300146/*
147 * i7core structs
148 */
149
150#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300151#define MAX_DIMMS 3 /* Max DIMMS per channel */
152#define MAX_MCR_FUNC 4
153#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300154
155struct i7core_info {
156 u32 mc_control;
157 u32 mc_status;
158 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300159 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300160};
161
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300162
163struct i7core_inject {
164 int enable;
165
166 u32 section;
167 u32 type;
168 u32 eccmask;
169
170 /* Error address mask */
171 int channel, dimm, rank, bank, page, col;
172};
173
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300174struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300175 u32 ranks;
176 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300177};
178
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300179struct pci_id_descr {
180 int dev;
181 int func;
182 int dev_id;
183 struct pci_dev *pdev;
184};
185
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300186struct i7core_pvt {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300187 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
188 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300189 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300190 struct i7core_inject inject;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300191 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300192 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300193
194 int ce_count_available;
195 unsigned long ce_count[MAX_DIMMS]; /* ECC corrected errors counts per dimm */
196 int last_ce_count[MAX_DIMMS];
197
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300198};
199
200/* Device name and register DID (Device ID) */
201struct i7core_dev_info {
202 const char *ctl_name; /* name for this device */
203 u16 fsb_mapping_errors; /* DID for the branchmap,control */
204};
205
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300206#define PCI_DESCR(device, function, device_id) \
207 .dev = (device), \
208 .func = (function), \
209 .dev_id = (device_id)
210
211struct pci_id_descr pci_devs[] = {
212 /* Memory controller */
213 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
214 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
215 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
216 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
217
218 /* Channel 0 */
219 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
220 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
221 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
222 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
223
224 /* Channel 1 */
225 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
226 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
227 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
228 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
229
230 /* Channel 2 */
231 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
232 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
233 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
234 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300235};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300236#define N_DEVS ARRAY_SIZE(pci_devs)
237
238/*
239 * pci_device_id table for which devices we are looking for
240 * This should match the first device at pci_devs table
241 */
242static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
243 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)},
244 {0,} /* 0 terminated list. */
245};
246
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300247
248/* Table of devices attributes supported by this driver */
249static const struct i7core_dev_info i7core_devs[] = {
250 {
251 .ctl_name = "i7 Core",
252 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
253 },
254};
255
256static struct edac_pci_ctl_info *i7core_pci;
257
258/****************************************************************************
259 Anciliary status routines
260 ****************************************************************************/
261
262 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300263#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
264#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300265
266 /* MC_STATUS bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300267#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
268#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300269
270 /* MC_MAX_DOD read functions */
271static inline int maxnumdimms(struct i7core_pvt *pvt)
272{
273 return (pvt->info.max_dod & 0x3) + 1;
274}
275
276static inline int maxnumrank(struct i7core_pvt *pvt)
277{
278 static int ranks[4] = { 1, 2, 4, -EINVAL };
279
280 return ranks[(pvt->info.max_dod >> 2) & 0x3];
281}
282
283static inline int maxnumbank(struct i7core_pvt *pvt)
284{
285 static int banks[4] = { 4, 8, 16, -EINVAL };
286
287 return banks[(pvt->info.max_dod >> 4) & 0x3];
288}
289
290static inline int maxnumrow(struct i7core_pvt *pvt)
291{
292 static int rows[8] = {
293 1 << 12, 1 << 13, 1 << 14, 1 << 15,
294 1 << 16, -EINVAL, -EINVAL, -EINVAL,
295 };
296
297 return rows[((pvt->info.max_dod >> 6) & 0x7)];
298}
299
300static inline int maxnumcol(struct i7core_pvt *pvt)
301{
302 static int cols[8] = {
303 1 << 10, 1 << 11, 1 << 12, -EINVAL,
304 };
305 return cols[((pvt->info.max_dod >> 9) & 0x3) << 12];
306}
307
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300308
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300309/****************************************************************************
310 Memory check routines
311 ****************************************************************************/
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300312static int i7core_get_active_channels(int *channels)
313{
314 struct pci_dev *pdev = NULL;
315 int i;
316 u32 status, control;
317
318 *channels = 0;
319
320 for (i = 0; i < N_DEVS; i++) {
321 if (!pci_devs[i].pdev)
322 continue;
323
324 if (PCI_SLOT(pci_devs[i].pdev->devfn) == 3 &&
325 PCI_FUNC(pci_devs[i].pdev->devfn) == 0) {
326 pdev = pci_devs[i].pdev;
327 break;
328 }
329 }
330
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300331 if (!pdev) {
332 i7core_printk(KERN_ERR, "Couldn't find fn 3.0!!!\n");
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300333 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300334 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300335
336 /* Device 3 function 0 reads */
337 pci_read_config_dword(pdev, MC_STATUS, &status);
338 pci_read_config_dword(pdev, MC_CONTROL, &control);
339
340 for (i = 0; i < NUM_CHANS; i++) {
341 /* Check if the channel is active */
342 if (!(control & (1 << (8 + i))))
343 continue;
344
345 /* Check if the channel is disabled */
346 if (status & (1 << i)) {
347 continue;
348 }
349
350 (*channels)++;
351 }
352
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300353 debugf0("Number of active channels: %d\n", *channels);
354
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300355 return 0;
356}
357
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300358static int get_dimm_config(struct mem_ctl_info *mci)
359{
360 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300361 struct csrow_info *csr;
362 int i, csrow = 0;
363 enum edac_type mode;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300364
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300365 if (!pvt->pci_mcr[0])
366 return -ENODEV;
367
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300368 /* Device 3 function 0 reads */
369 pci_read_config_dword(pvt->pci_mcr[0], MC_CONTROL,
370 &pvt->info.mc_control);
371 pci_read_config_dword(pvt->pci_mcr[0], MC_STATUS,
372 &pvt->info.mc_status);
373 pci_read_config_dword(pvt->pci_mcr[0], MC_MAX_DOD,
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300374 &pvt->info.max_dod);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300375 pci_read_config_dword(pvt->pci_mcr[0], MC_CHANNEL_MAPPER,
376 &pvt->info.ch_map);
377
378 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
379 pvt->info.mc_control, pvt->info.mc_status,
380 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300381
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300382 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300383 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300384 if (ECCx8(pvt))
385 mode = EDAC_S8ECD8ED;
386 else
387 mode = EDAC_S4ECD4ED;
388 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300389 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300390 mode = EDAC_NONE;
391 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300392
393 /* FIXME: need to handle the error codes */
394 debugf0("DOD Maximum limits: DIMMS: %d, %d-ranked, %d-banked\n",
395 maxnumdimms(pvt), maxnumrank(pvt), maxnumbank(pvt));
396 debugf0("DOD Maximum rows x colums = 0x%x x 0x%x\n",
397 maxnumrow(pvt), maxnumcol(pvt));
398
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300399 debugf0("Memory channel configuration:\n");
400
401 for (i = 0; i < NUM_CHANS; i++) {
402 u32 data;
403
404 if (!CH_ACTIVE(pvt, i)) {
405 debugf0("Channel %i is not active\n", i);
406 continue;
407 }
408 if (CH_DISABLED(pvt, i)) {
409 debugf0("Channel %i is disabled\n", i);
410 continue;
411 }
412
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300413 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300414 pci_read_config_dword(pvt->pci_ch[i][0],
415 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
416
417 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT)? 4 : 2;
418
419 if (data & THREE_DIMMS_PRESENT)
420 pvt->channel[i].dimms = 3;
421 else if (data & SINGLE_QUAD_RANK_PRESENT)
422 pvt->channel[i].dimms = 1;
423 else
424 pvt->channel[i].dimms = 2;
425
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300426 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
427 "%d ranks, %d %cDIMMs, offset = %d\n",
428 i,
429 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
430 data,
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300431 pvt->channel[i].ranks, pvt->channel[i].dimms,
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300432 (data & REGISTERED_DIMM)? 'R' : 'U',
433 RANKOFFSET(data));
434
435 csr = &mci->csrows[csrow];
436 csr->first_page = 0;
437 csr->last_page = 0;
438 csr->page_mask = 0;
439 csr->nr_pages = 0;
440 csr->grain = 0;
441 csr->csrow_idx = csrow;
442 csr->dtype = DEV_X8; /* FIXME: check this */
443
444 if (data & REGISTERED_DIMM)
445 csr->mtype = MEM_RDDR3;
446 else
447 csr->mtype = MEM_DDR3;
448 csr->edac_mode = mode;
449
450 csrow++;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300451 }
452
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300453 return 0;
454}
455
456/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300457 Error insertion routines
458 ****************************************************************************/
459
460/* The i7core has independent error injection features per channel.
461 However, to have a simpler code, we don't allow enabling error injection
462 on more than one channel.
463 Also, since a change at an inject parameter will be applied only at enable,
464 we're disabling error injection on all write calls to the sysfs nodes that
465 controls the error code injection.
466 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300467static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300468{
469 struct i7core_pvt *pvt = mci->pvt_info;
470
471 pvt->inject.enable = 0;
472
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300473 if (!pvt->pci_ch[pvt->inject.channel][0])
474 return -ENODEV;
475
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300476 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
477 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300478
479 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300480}
481
482/*
483 * i7core inject inject.section
484 *
485 * accept and store error injection inject.section value
486 * bit 0 - refers to the lower 32-byte half cacheline
487 * bit 1 - refers to the upper 32-byte half cacheline
488 */
489static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
490 const char *data, size_t count)
491{
492 struct i7core_pvt *pvt = mci->pvt_info;
493 unsigned long value;
494 int rc;
495
496 if (pvt->inject.enable)
497 disable_inject(mci);
498
499 rc = strict_strtoul(data, 10, &value);
500 if ((rc < 0) || (value > 3))
501 return 0;
502
503 pvt->inject.section = (u32) value;
504 return count;
505}
506
507static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
508 char *data)
509{
510 struct i7core_pvt *pvt = mci->pvt_info;
511 return sprintf(data, "0x%08x\n", pvt->inject.section);
512}
513
514/*
515 * i7core inject.type
516 *
517 * accept and store error injection inject.section value
518 * bit 0 - repeat enable - Enable error repetition
519 * bit 1 - inject ECC error
520 * bit 2 - inject parity error
521 */
522static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
523 const char *data, size_t count)
524{
525 struct i7core_pvt *pvt = mci->pvt_info;
526 unsigned long value;
527 int rc;
528
529 if (pvt->inject.enable)
530 disable_inject(mci);
531
532 rc = strict_strtoul(data, 10, &value);
533 if ((rc < 0) || (value > 7))
534 return 0;
535
536 pvt->inject.type = (u32) value;
537 return count;
538}
539
540static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
541 char *data)
542{
543 struct i7core_pvt *pvt = mci->pvt_info;
544 return sprintf(data, "0x%08x\n", pvt->inject.type);
545}
546
547/*
548 * i7core_inject_inject.eccmask_store
549 *
550 * The type of error (UE/CE) will depend on the inject.eccmask value:
551 * Any bits set to a 1 will flip the corresponding ECC bit
552 * Correctable errors can be injected by flipping 1 bit or the bits within
553 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
554 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
555 * uncorrectable error to be injected.
556 */
557static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
558 const char *data, size_t count)
559{
560 struct i7core_pvt *pvt = mci->pvt_info;
561 unsigned long value;
562 int rc;
563
564 if (pvt->inject.enable)
565 disable_inject(mci);
566
567 rc = strict_strtoul(data, 10, &value);
568 if (rc < 0)
569 return 0;
570
571 pvt->inject.eccmask = (u32) value;
572 return count;
573}
574
575static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
576 char *data)
577{
578 struct i7core_pvt *pvt = mci->pvt_info;
579 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
580}
581
582/*
583 * i7core_addrmatch
584 *
585 * The type of error (UE/CE) will depend on the inject.eccmask value:
586 * Any bits set to a 1 will flip the corresponding ECC bit
587 * Correctable errors can be injected by flipping 1 bit or the bits within
588 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
589 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
590 * uncorrectable error to be injected.
591 */
592static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
593 const char *data, size_t count)
594{
595 struct i7core_pvt *pvt = mci->pvt_info;
596 char *cmd, *val;
597 long value;
598 int rc;
599
600 if (pvt->inject.enable)
601 disable_inject(mci);
602
603 do {
604 cmd = strsep((char **) &data, ":");
605 if (!cmd)
606 break;
607 val = strsep((char **) &data, " \n\t");
608 if (!val)
609 return cmd - data;
610
611 if (!strcasecmp(val,"any"))
612 value = -1;
613 else {
614 rc = strict_strtol(val, 10, &value);
615 if ((rc < 0) || (value < 0))
616 return cmd - data;
617 }
618
619 if (!strcasecmp(cmd,"channel")) {
620 if (value < 3)
621 pvt->inject.channel = value;
622 else
623 return cmd - data;
624 } else if (!strcasecmp(cmd,"dimm")) {
625 if (value < 4)
626 pvt->inject.dimm = value;
627 else
628 return cmd - data;
629 } else if (!strcasecmp(cmd,"rank")) {
630 if (value < 4)
631 pvt->inject.rank = value;
632 else
633 return cmd - data;
634 } else if (!strcasecmp(cmd,"bank")) {
635 if (value < 4)
636 pvt->inject.bank = value;
637 else
638 return cmd - data;
639 } else if (!strcasecmp(cmd,"page")) {
640 if (value <= 0xffff)
641 pvt->inject.page = value;
642 else
643 return cmd - data;
644 } else if (!strcasecmp(cmd,"col") ||
645 !strcasecmp(cmd,"column")) {
646 if (value <= 0x3fff)
647 pvt->inject.col = value;
648 else
649 return cmd - data;
650 }
651 } while (1);
652
653 return count;
654}
655
656static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
657 char *data)
658{
659 struct i7core_pvt *pvt = mci->pvt_info;
660 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
661
662 if (pvt->inject.channel < 0)
663 sprintf(channel, "any");
664 else
665 sprintf(channel, "%d", pvt->inject.channel);
666 if (pvt->inject.dimm < 0)
667 sprintf(dimm, "any");
668 else
669 sprintf(dimm, "%d", pvt->inject.dimm);
670 if (pvt->inject.bank < 0)
671 sprintf(bank, "any");
672 else
673 sprintf(bank, "%d", pvt->inject.bank);
674 if (pvt->inject.rank < 0)
675 sprintf(rank, "any");
676 else
677 sprintf(rank, "%d", pvt->inject.rank);
678 if (pvt->inject.page < 0)
679 sprintf(page, "any");
680 else
681 sprintf(page, "0x%04x", pvt->inject.page);
682 if (pvt->inject.col < 0)
683 sprintf(col, "any");
684 else
685 sprintf(col, "0x%04x", pvt->inject.col);
686
687 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
688 "rank: %s\npage: %s\ncolumn: %s\n",
689 channel, dimm, bank, rank, page, col);
690}
691
692/*
693 * This routine prepares the Memory Controller for error injection.
694 * The error will be injected when some process tries to write to the
695 * memory that matches the given criteria.
696 * The criteria can be set in terms of a mask where dimm, rank, bank, page
697 * and col can be specified.
698 * A -1 value for any of the mask items will make the MCU to ignore
699 * that matching criteria for error injection.
700 *
701 * It should be noticed that the error will only happen after a write operation
702 * on a memory that matches the condition. if REPEAT_EN is not enabled at
703 * inject mask, then it will produce just one error. Otherwise, it will repeat
704 * until the injectmask would be cleaned.
705 *
706 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
707 * is reliable enough to check if the MC is using the
708 * three channels. However, this is not clear at the datasheet.
709 */
710static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
711 const char *data, size_t count)
712{
713 struct i7core_pvt *pvt = mci->pvt_info;
714 u32 injectmask;
715 u64 mask = 0;
716 int rc;
717 long enable;
718
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300719 if (!pvt->pci_ch[pvt->inject.channel][0])
720 return 0;
721
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300722 rc = strict_strtoul(data, 10, &enable);
723 if ((rc < 0))
724 return 0;
725
726 if (enable) {
727 pvt->inject.enable = 1;
728 } else {
729 disable_inject(mci);
730 return count;
731 }
732
733 /* Sets pvt->inject.dimm mask */
734 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300735 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300736 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300737 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300738 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300739 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300740 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300741 }
742
743 /* Sets pvt->inject.rank mask */
744 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300745 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300746 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300747 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300748 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300749 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300750 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300751 }
752
753 /* Sets pvt->inject.bank mask */
754 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300755 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300756 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300757 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300758
759 /* Sets pvt->inject.page mask */
760 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300761 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300762 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300763 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300764
765 /* Sets pvt->inject.column mask */
766 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300767 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300768 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300769 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300770
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300771#if USE_QWORD
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300772 pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0],
773 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300774#else
775 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
776 MC_CHANNEL_ADDR_MATCH, mask);
777 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
778 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
779#endif
780
781#if 1
782#if USE_QWORD
783 u64 rdmask;
784 pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0],
785 MC_CHANNEL_ADDR_MATCH, &rdmask);
786 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
787 mask, rdmask);
788#else
789 u32 rdmask1, rdmask2;
790
791 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
792 MC_CHANNEL_ADDR_MATCH, &rdmask1);
793 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
794 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
795
796 debugf0("Inject addr match write 0x%016llx, read: 0x%08x%08x\n",
797 mask, rdmask1, rdmask2);
798#endif
799#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300800
801 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
802 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
803
804 /*
805 * bit 0: REPEAT_EN
806 * bits 1-2: MASK_HALF_CACHELINE
807 * bit 3: INJECT_ECC
808 * bit 4: INJECT_ADDR_PARITY
809 */
810
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300811 injectmask = (pvt->inject.type & 1) |
812 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300813 (pvt->inject.type & 0x6) << (3 - 1);
814
815 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
816 MC_CHANNEL_ERROR_MASK, injectmask);
817
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300818 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
819 mask, pvt->inject.eccmask, injectmask);
820
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300821
822
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300823 return count;
824}
825
826static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
827 char *data)
828{
829 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300830 u32 injectmask;
831
832 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
833 MC_CHANNEL_ERROR_MASK, &injectmask);
834
835 debugf0("Inject error read: 0x%018x\n", injectmask);
836
837 if (injectmask & 0x0c)
838 pvt->inject.enable = 1;
839
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300840 return sprintf(data, "%d\n", pvt->inject.enable);
841}
842
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300843static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
844{
845 struct i7core_pvt *pvt = mci->pvt_info;
846
847 if (!pvt->ce_count_available)
848 return sprintf(data, "unavailable\n");
849
850 return sprintf(data, "dimm0: %lu\ndimm1: %lu\ndimm2: %lu\n",
851 pvt->ce_count[0],
852 pvt->ce_count[1],
853 pvt->ce_count[2]);
854}
855
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300856/*
857 * Sysfs struct
858 */
859static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
860
861 {
862 .attr = {
863 .name = "inject_section",
864 .mode = (S_IRUGO | S_IWUSR)
865 },
866 .show = i7core_inject_section_show,
867 .store = i7core_inject_section_store,
868 }, {
869 .attr = {
870 .name = "inject_type",
871 .mode = (S_IRUGO | S_IWUSR)
872 },
873 .show = i7core_inject_type_show,
874 .store = i7core_inject_type_store,
875 }, {
876 .attr = {
877 .name = "inject_eccmask",
878 .mode = (S_IRUGO | S_IWUSR)
879 },
880 .show = i7core_inject_eccmask_show,
881 .store = i7core_inject_eccmask_store,
882 }, {
883 .attr = {
884 .name = "inject_addrmatch",
885 .mode = (S_IRUGO | S_IWUSR)
886 },
887 .show = i7core_inject_addrmatch_show,
888 .store = i7core_inject_addrmatch_store,
889 }, {
890 .attr = {
891 .name = "inject_enable",
892 .mode = (S_IRUGO | S_IWUSR)
893 },
894 .show = i7core_inject_enable_show,
895 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300896 }, {
897 .attr = {
898 .name = "corrected_error_counts",
899 .mode = (S_IRUGO | S_IWUSR)
900 },
901 .show = i7core_ce_regs_show,
902 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300903 },
904};
905
906/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300907 Device initialization routines: put/get, init/exit
908 ****************************************************************************/
909
910/*
911 * i7core_put_devices 'put' all the devices that we have
912 * reserved via 'get'
913 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300914static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300915{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300916 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300917
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300918 for (i = 0; i < N_DEVS; i++)
919 pci_dev_put(pci_devs[i].pdev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300920}
921
922/*
923 * i7core_get_devices Find and perform 'get' operation on the MCH's
924 * device/functions we want to reference for this driver
925 *
926 * Need to 'get' device 16 func 1 and func 2
927 */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300928static int i7core_get_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300929{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300930 int rc, i;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300931 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300932
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300933 for (i = 0; i < N_DEVS; i++) {
934 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
935 pci_devs[i].dev_id, NULL);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300936 if (likely(pdev))
937 pci_devs[i].pdev = pdev;
938 else {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300939 i7core_printk(KERN_ERR,
940 "Device not found: PCI ID %04x:%04x "
941 "(dev %d, func %d)\n",
942 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
943 pci_devs[i].dev,pci_devs[i].func);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300944
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300945 /* Dev 3 function 2 only exists on chips with RDIMMs */
946 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
947 continue;
948
949 /* End of list, leave */
950 rc = -ENODEV;
951 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300952 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300953
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300954 /* Sanity check */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300955 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev ||
956 PCI_FUNC(pdev->devfn) != pci_devs[i].func)) {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300957 i7core_printk(KERN_ERR,
958 "Device PCI ID %04x:%04x "
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300959 "has fn %d.%d instead of fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300960 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300961 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
962 pci_devs[i].dev, pci_devs[i].func);
963 rc = -EINVAL;
964 goto error;
965 }
966
967 /* Be sure that the device is enabled */
968 rc = pci_enable_device(pdev);
969 if (unlikely(rc < 0)) {
970 i7core_printk(KERN_ERR,
971 "Couldn't enable PCI ID %04x:%04x "
972 "fn %d.%d\n",
973 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
974 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
975 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300976 }
977
978 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300979 "Registered device %0x:%0x fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300980 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
981 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300982 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300983
984 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300985
986error:
987 i7core_put_devices();
988 return -EINVAL;
989}
990
991static int mci_bind_devs(struct mem_ctl_info *mci)
992{
993 struct i7core_pvt *pvt = mci->pvt_info;
994 struct pci_dev *pdev;
995 int i, func, slot;
996
997 for (i = 0; i < N_DEVS; i++) {
998 pdev = pci_devs[i].pdev;
999 if (!pdev)
1000 continue;
1001
1002 func = PCI_FUNC(pdev->devfn);
1003 slot = PCI_SLOT(pdev->devfn);
1004 if (slot == 3) {
1005 if (unlikely(func > MAX_MCR_FUNC))
1006 goto error;
1007 pvt->pci_mcr[func] = pdev;
1008 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1009 if (unlikely(func > MAX_CHAN_FUNC))
1010 goto error;
1011 pvt->pci_ch[slot - 4][func] = pdev;
1012 } else
1013 goto error;
1014
1015 debugf0("Associated fn %d.%d, dev = %p\n",
1016 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev);
1017 }
1018 return 0;
1019
1020error:
1021 i7core_printk(KERN_ERR, "Device %d, function %d "
1022 "is out of the expected range\n",
1023 slot, func);
1024 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001025}
1026
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001027/****************************************************************************
1028 Error check routines
1029 ****************************************************************************/
1030
1031/* This function is based on the device 3 function 4 registers as described on:
1032 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1033 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1034 * also available at:
1035 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1036 */
1037static void check_mc_test_err(struct mem_ctl_info *mci)
1038{
1039 struct i7core_pvt *pvt = mci->pvt_info;
1040 u32 rcv1, rcv0;
1041 int new0, new1, new2;
1042
1043 if (!pvt->pci_mcr[4]) {
1044 debugf0("%s MCR registers not found\n",__func__);
1045 return;
1046 }
1047
1048 /* Corrected error reads */
1049 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1050 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
1051
1052 /* Store the new values */
1053 new2 = DIMM2_COR_ERR(rcv1);
1054 new1 = DIMM1_COR_ERR(rcv0);
1055 new0 = DIMM0_COR_ERR(rcv0);
1056
1057 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1058 (pvt->ce_count_available ? "UPDATE" : "READ"),
1059 rcv1, rcv0, new0, new1, new2);
1060
1061 /* Updates CE counters if it is not the first time here */
1062 if (pvt->ce_count_available) {
1063 /* Updates CE counters */
1064 int add0, add1, add2;
1065
1066 add2 = new2 - pvt->last_ce_count[2];
1067 add1 = new1 - pvt->last_ce_count[1];
1068 add0 = new0 - pvt->last_ce_count[0];
1069
1070 if (add2 < 0)
1071 add2 += 0x7fff;
1072 pvt->ce_count[2] += add2;
1073
1074 if (add1 < 0)
1075 add1 += 0x7fff;
1076 pvt->ce_count[1] += add1;
1077
1078 if (add0 < 0)
1079 add0 += 0x7fff;
1080 pvt->ce_count[0] += add0;
1081 } else
1082 pvt->ce_count_available = 1;
1083
1084 /* Store the new values */
1085 pvt->last_ce_count[2] = new2;
1086 pvt->last_ce_count[1] = new1;
1087 pvt->last_ce_count[0] = new0;
1088}
1089
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001090/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001091 * i7core_check_error Retrieve and process errors reported by the
1092 * hardware. Called by the Core module.
1093 */
1094static void i7core_check_error(struct mem_ctl_info *mci)
1095{
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001096 check_mc_test_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001097}
1098
1099/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001100 * i7core_probe Probe for ONE instance of device to see if it is
1101 * present.
1102 * return:
1103 * 0 for FOUND a device
1104 * < 0 for error code
1105 */
1106static int __devinit i7core_probe(struct pci_dev *pdev,
1107 const struct pci_device_id *id)
1108{
1109 struct mem_ctl_info *mci;
1110 struct i7core_pvt *pvt;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001111 int num_channels = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001112 int num_csrows;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001113 int dev_idx = id->driver_data;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001114 int rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001115
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001116 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001117 return -EINVAL;
1118
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001119 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001120 rc = i7core_get_devices();
1121 if (unlikely(rc < 0))
1122 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001123
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001124 /* Check the number of active and not disabled channels */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001125 rc = i7core_get_active_channels(&num_channels);
1126 if (unlikely (rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001127 goto fail0;
1128
1129 /* FIXME: we currently don't know the number of csrows */
1130 num_csrows = num_channels;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001131
1132 /* allocate a new MC control structure */
1133 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001134 if (unlikely (!mci)) {
1135 rc = -ENOMEM;
1136 goto fail0;
1137 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001138
1139 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1140
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001141 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001142
1143 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001144 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001145
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001146 mci->mc_idx = 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001147 mci->mtype_cap = MEM_FLAG_DDR3; /* FIXME: how to handle RDDR3? */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001148 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1149 mci->edac_cap = EDAC_FLAG_NONE;
1150 mci->mod_name = "i7core_edac.c";
1151 mci->mod_ver = I7CORE_REVISION;
1152 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1153 mci->dev_name = pci_name(pdev);
1154 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001155 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001156 /* Set the function pointer to an actual operation function */
1157 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001158
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001159 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001160 rc = mci_bind_devs(mci);
1161 if (unlikely (rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001162 goto fail1;
1163
1164 /* Get dimm basic config */
1165 get_dimm_config(mci);
1166
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001167 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001168 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001169 debugf0("MC: " __FILE__
1170 ": %s(): failed edac_mc_add_mc()\n", __func__);
1171 /* FIXME: perhaps some code should go here that disables error
1172 * reporting if we just enabled it
1173 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001174
1175 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001176 goto fail1;
1177 }
1178
1179 /* allocating generic PCI control info */
1180 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001181 if (unlikely (!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001182 printk(KERN_WARNING
1183 "%s(): Unable to create PCI control\n",
1184 __func__);
1185 printk(KERN_WARNING
1186 "%s(): PCI error report via EDAC not setup\n",
1187 __func__);
1188 }
1189
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001190 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001191 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001192 pvt->inject.dimm = -1;
1193 pvt->inject.rank = -1;
1194 pvt->inject.bank = -1;
1195 pvt->inject.page = -1;
1196 pvt->inject.col = -1;
1197
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001198 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001199
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001200 return 0;
1201
1202fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001203 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001204
1205fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001206 i7core_put_devices();
1207 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001208}
1209
1210/*
1211 * i7core_remove destructor for one instance of device
1212 *
1213 */
1214static void __devexit i7core_remove(struct pci_dev *pdev)
1215{
1216 struct mem_ctl_info *mci;
1217
1218 debugf0(__FILE__ ": %s()\n", __func__);
1219
1220 if (i7core_pci)
1221 edac_pci_release_generic_ctl(i7core_pci);
1222
1223 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001224
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001225 if (!mci)
1226 return;
1227
1228 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001229 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001230
1231 edac_mc_free(mci);
1232}
1233
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001234MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1235
1236/*
1237 * i7core_driver pci_driver structure for this module
1238 *
1239 */
1240static struct pci_driver i7core_driver = {
1241 .name = "i7core_edac",
1242 .probe = i7core_probe,
1243 .remove = __devexit_p(i7core_remove),
1244 .id_table = i7core_pci_tbl,
1245};
1246
1247/*
1248 * i7core_init Module entry function
1249 * Try to initialize this module for its devices
1250 */
1251static int __init i7core_init(void)
1252{
1253 int pci_rc;
1254
1255 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1256
1257 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1258 opstate_init();
1259
1260 pci_rc = pci_register_driver(&i7core_driver);
1261
1262 return (pci_rc < 0) ? pci_rc : 0;
1263}
1264
1265/*
1266 * i7core_exit() Module exit function
1267 * Unregister the driver
1268 */
1269static void __exit i7core_exit(void)
1270{
1271 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1272 pci_unregister_driver(&i7core_driver);
1273}
1274
1275module_init(i7core_init);
1276module_exit(i7core_exit);
1277
1278MODULE_LICENSE("GPL");
1279MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1280MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1281MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1282 I7CORE_REVISION);
1283
1284module_param(edac_op_state, int, 0444);
1285MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");