blob: a6e798349e937539eaaf37cd8fe143f95eae82d8 [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;
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300362 struct pci_dev *pdev = pvt->pci_mcr[0];
363 int i, j, csrow = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300364 enum edac_type mode;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300365
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300366 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300367 return -ENODEV;
368
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300369 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300370 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
371 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
372 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
373 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300374
375 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
376 pvt->info.mc_control, pvt->info.mc_status,
377 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300378
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300379 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300380 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300381 if (ECCx8(pvt))
382 mode = EDAC_S8ECD8ED;
383 else
384 mode = EDAC_S4ECD4ED;
385 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300386 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300387 mode = EDAC_NONE;
388 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300389
390 /* FIXME: need to handle the error codes */
391 debugf0("DOD Maximum limits: DIMMS: %d, %d-ranked, %d-banked\n",
392 maxnumdimms(pvt), maxnumrank(pvt), maxnumbank(pvt));
393 debugf0("DOD Maximum rows x colums = 0x%x x 0x%x\n",
394 maxnumrow(pvt), maxnumcol(pvt));
395
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300396 debugf0("Memory channel configuration:\n");
397
398 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300399 u32 data, value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300400
401 if (!CH_ACTIVE(pvt, i)) {
402 debugf0("Channel %i is not active\n", i);
403 continue;
404 }
405 if (CH_DISABLED(pvt, i)) {
406 debugf0("Channel %i is disabled\n", i);
407 continue;
408 }
409
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300410 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300411 pci_read_config_dword(pvt->pci_ch[i][0],
412 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
413
414 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT)? 4 : 2;
415
416 if (data & THREE_DIMMS_PRESENT)
417 pvt->channel[i].dimms = 3;
418 else if (data & SINGLE_QUAD_RANK_PRESENT)
419 pvt->channel[i].dimms = 1;
420 else
421 pvt->channel[i].dimms = 2;
422
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300423 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300424 "%d ranks, %d %cDIMMs, offset = %d\n\t"
425 "present: %i, numbank: %#x, numrank: %#x, "
426 "numrow: %#x, numcol: %#x\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300427 i,
428 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
429 data,
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300430 pvt->channel[i].ranks, pvt->channel[i].dimms,
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300431 (data & REGISTERED_DIMM)? 'R' : 'U',
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300432 RANKOFFSET(data),
433 DIMM_PRESENT(data),
434 NUMBANK(data), NUMRANK(data),
435 NUMROW(data), NUMCOL(data));
436
437 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
438 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
439 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
440 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
441 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
442 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
443 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
444 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
445 printk("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
446 for (j = 0; j < 8; j++)
447 printk("\t\t%#x\t%#x\t%#x\n",
448 (value[j] >> 27) & 0x1,
449 (value[j] >> 24) & 0x7,
450 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300451
452 csr = &mci->csrows[csrow];
453 csr->first_page = 0;
454 csr->last_page = 0;
455 csr->page_mask = 0;
456 csr->nr_pages = 0;
457 csr->grain = 0;
458 csr->csrow_idx = csrow;
459 csr->dtype = DEV_X8; /* FIXME: check this */
460
461 if (data & REGISTERED_DIMM)
462 csr->mtype = MEM_RDDR3;
463 else
464 csr->mtype = MEM_DDR3;
465 csr->edac_mode = mode;
466
467 csrow++;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300468 }
469
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300470 return 0;
471}
472
473/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300474 Error insertion routines
475 ****************************************************************************/
476
477/* The i7core has independent error injection features per channel.
478 However, to have a simpler code, we don't allow enabling error injection
479 on more than one channel.
480 Also, since a change at an inject parameter will be applied only at enable,
481 we're disabling error injection on all write calls to the sysfs nodes that
482 controls the error code injection.
483 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300484static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300485{
486 struct i7core_pvt *pvt = mci->pvt_info;
487
488 pvt->inject.enable = 0;
489
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300490 if (!pvt->pci_ch[pvt->inject.channel][0])
491 return -ENODEV;
492
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300493 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
494 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300495
496 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300497}
498
499/*
500 * i7core inject inject.section
501 *
502 * accept and store error injection inject.section value
503 * bit 0 - refers to the lower 32-byte half cacheline
504 * bit 1 - refers to the upper 32-byte half cacheline
505 */
506static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
507 const char *data, size_t count)
508{
509 struct i7core_pvt *pvt = mci->pvt_info;
510 unsigned long value;
511 int rc;
512
513 if (pvt->inject.enable)
514 disable_inject(mci);
515
516 rc = strict_strtoul(data, 10, &value);
517 if ((rc < 0) || (value > 3))
518 return 0;
519
520 pvt->inject.section = (u32) value;
521 return count;
522}
523
524static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
525 char *data)
526{
527 struct i7core_pvt *pvt = mci->pvt_info;
528 return sprintf(data, "0x%08x\n", pvt->inject.section);
529}
530
531/*
532 * i7core inject.type
533 *
534 * accept and store error injection inject.section value
535 * bit 0 - repeat enable - Enable error repetition
536 * bit 1 - inject ECC error
537 * bit 2 - inject parity error
538 */
539static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
540 const char *data, size_t count)
541{
542 struct i7core_pvt *pvt = mci->pvt_info;
543 unsigned long value;
544 int rc;
545
546 if (pvt->inject.enable)
547 disable_inject(mci);
548
549 rc = strict_strtoul(data, 10, &value);
550 if ((rc < 0) || (value > 7))
551 return 0;
552
553 pvt->inject.type = (u32) value;
554 return count;
555}
556
557static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
558 char *data)
559{
560 struct i7core_pvt *pvt = mci->pvt_info;
561 return sprintf(data, "0x%08x\n", pvt->inject.type);
562}
563
564/*
565 * i7core_inject_inject.eccmask_store
566 *
567 * The type of error (UE/CE) will depend on the inject.eccmask value:
568 * Any bits set to a 1 will flip the corresponding ECC bit
569 * Correctable errors can be injected by flipping 1 bit or the bits within
570 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
571 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
572 * uncorrectable error to be injected.
573 */
574static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
575 const char *data, size_t count)
576{
577 struct i7core_pvt *pvt = mci->pvt_info;
578 unsigned long value;
579 int rc;
580
581 if (pvt->inject.enable)
582 disable_inject(mci);
583
584 rc = strict_strtoul(data, 10, &value);
585 if (rc < 0)
586 return 0;
587
588 pvt->inject.eccmask = (u32) value;
589 return count;
590}
591
592static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
593 char *data)
594{
595 struct i7core_pvt *pvt = mci->pvt_info;
596 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
597}
598
599/*
600 * i7core_addrmatch
601 *
602 * The type of error (UE/CE) will depend on the inject.eccmask value:
603 * Any bits set to a 1 will flip the corresponding ECC bit
604 * Correctable errors can be injected by flipping 1 bit or the bits within
605 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
606 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
607 * uncorrectable error to be injected.
608 */
609static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
610 const char *data, size_t count)
611{
612 struct i7core_pvt *pvt = mci->pvt_info;
613 char *cmd, *val;
614 long value;
615 int rc;
616
617 if (pvt->inject.enable)
618 disable_inject(mci);
619
620 do {
621 cmd = strsep((char **) &data, ":");
622 if (!cmd)
623 break;
624 val = strsep((char **) &data, " \n\t");
625 if (!val)
626 return cmd - data;
627
628 if (!strcasecmp(val,"any"))
629 value = -1;
630 else {
631 rc = strict_strtol(val, 10, &value);
632 if ((rc < 0) || (value < 0))
633 return cmd - data;
634 }
635
636 if (!strcasecmp(cmd,"channel")) {
637 if (value < 3)
638 pvt->inject.channel = value;
639 else
640 return cmd - data;
641 } else if (!strcasecmp(cmd,"dimm")) {
642 if (value < 4)
643 pvt->inject.dimm = value;
644 else
645 return cmd - data;
646 } else if (!strcasecmp(cmd,"rank")) {
647 if (value < 4)
648 pvt->inject.rank = value;
649 else
650 return cmd - data;
651 } else if (!strcasecmp(cmd,"bank")) {
652 if (value < 4)
653 pvt->inject.bank = value;
654 else
655 return cmd - data;
656 } else if (!strcasecmp(cmd,"page")) {
657 if (value <= 0xffff)
658 pvt->inject.page = value;
659 else
660 return cmd - data;
661 } else if (!strcasecmp(cmd,"col") ||
662 !strcasecmp(cmd,"column")) {
663 if (value <= 0x3fff)
664 pvt->inject.col = value;
665 else
666 return cmd - data;
667 }
668 } while (1);
669
670 return count;
671}
672
673static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
674 char *data)
675{
676 struct i7core_pvt *pvt = mci->pvt_info;
677 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
678
679 if (pvt->inject.channel < 0)
680 sprintf(channel, "any");
681 else
682 sprintf(channel, "%d", pvt->inject.channel);
683 if (pvt->inject.dimm < 0)
684 sprintf(dimm, "any");
685 else
686 sprintf(dimm, "%d", pvt->inject.dimm);
687 if (pvt->inject.bank < 0)
688 sprintf(bank, "any");
689 else
690 sprintf(bank, "%d", pvt->inject.bank);
691 if (pvt->inject.rank < 0)
692 sprintf(rank, "any");
693 else
694 sprintf(rank, "%d", pvt->inject.rank);
695 if (pvt->inject.page < 0)
696 sprintf(page, "any");
697 else
698 sprintf(page, "0x%04x", pvt->inject.page);
699 if (pvt->inject.col < 0)
700 sprintf(col, "any");
701 else
702 sprintf(col, "0x%04x", pvt->inject.col);
703
704 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
705 "rank: %s\npage: %s\ncolumn: %s\n",
706 channel, dimm, bank, rank, page, col);
707}
708
709/*
710 * This routine prepares the Memory Controller for error injection.
711 * The error will be injected when some process tries to write to the
712 * memory that matches the given criteria.
713 * The criteria can be set in terms of a mask where dimm, rank, bank, page
714 * and col can be specified.
715 * A -1 value for any of the mask items will make the MCU to ignore
716 * that matching criteria for error injection.
717 *
718 * It should be noticed that the error will only happen after a write operation
719 * on a memory that matches the condition. if REPEAT_EN is not enabled at
720 * inject mask, then it will produce just one error. Otherwise, it will repeat
721 * until the injectmask would be cleaned.
722 *
723 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
724 * is reliable enough to check if the MC is using the
725 * three channels. However, this is not clear at the datasheet.
726 */
727static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
728 const char *data, size_t count)
729{
730 struct i7core_pvt *pvt = mci->pvt_info;
731 u32 injectmask;
732 u64 mask = 0;
733 int rc;
734 long enable;
735
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300736 if (!pvt->pci_ch[pvt->inject.channel][0])
737 return 0;
738
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300739 rc = strict_strtoul(data, 10, &enable);
740 if ((rc < 0))
741 return 0;
742
743 if (enable) {
744 pvt->inject.enable = 1;
745 } else {
746 disable_inject(mci);
747 return count;
748 }
749
750 /* Sets pvt->inject.dimm mask */
751 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300752 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300753 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300754 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300755 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300756 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300757 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300758 }
759
760 /* Sets pvt->inject.rank mask */
761 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300762 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300763 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300764 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300765 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300766 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300767 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300768 }
769
770 /* Sets pvt->inject.bank mask */
771 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300772 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300773 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300774 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300775
776 /* Sets pvt->inject.page mask */
777 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300778 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300779 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300780 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300781
782 /* Sets pvt->inject.column mask */
783 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300784 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300785 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300786 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300787
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300788#if USE_QWORD
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300789 pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0],
790 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300791#else
792 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
793 MC_CHANNEL_ADDR_MATCH, mask);
794 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
795 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
796#endif
797
798#if 1
799#if USE_QWORD
800 u64 rdmask;
801 pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0],
802 MC_CHANNEL_ADDR_MATCH, &rdmask);
803 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
804 mask, rdmask);
805#else
806 u32 rdmask1, rdmask2;
807
808 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
809 MC_CHANNEL_ADDR_MATCH, &rdmask1);
810 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
811 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
812
813 debugf0("Inject addr match write 0x%016llx, read: 0x%08x%08x\n",
814 mask, rdmask1, rdmask2);
815#endif
816#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300817
818 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
819 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
820
821 /*
822 * bit 0: REPEAT_EN
823 * bits 1-2: MASK_HALF_CACHELINE
824 * bit 3: INJECT_ECC
825 * bit 4: INJECT_ADDR_PARITY
826 */
827
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300828 injectmask = (pvt->inject.type & 1) |
829 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300830 (pvt->inject.type & 0x6) << (3 - 1);
831
832 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
833 MC_CHANNEL_ERROR_MASK, injectmask);
834
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300835 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
836 mask, pvt->inject.eccmask, injectmask);
837
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300838
839
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300840 return count;
841}
842
843static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
844 char *data)
845{
846 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300847 u32 injectmask;
848
849 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
850 MC_CHANNEL_ERROR_MASK, &injectmask);
851
852 debugf0("Inject error read: 0x%018x\n", injectmask);
853
854 if (injectmask & 0x0c)
855 pvt->inject.enable = 1;
856
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300857 return sprintf(data, "%d\n", pvt->inject.enable);
858}
859
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300860static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
861{
862 struct i7core_pvt *pvt = mci->pvt_info;
863
864 if (!pvt->ce_count_available)
865 return sprintf(data, "unavailable\n");
866
867 return sprintf(data, "dimm0: %lu\ndimm1: %lu\ndimm2: %lu\n",
868 pvt->ce_count[0],
869 pvt->ce_count[1],
870 pvt->ce_count[2]);
871}
872
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300873/*
874 * Sysfs struct
875 */
876static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
877
878 {
879 .attr = {
880 .name = "inject_section",
881 .mode = (S_IRUGO | S_IWUSR)
882 },
883 .show = i7core_inject_section_show,
884 .store = i7core_inject_section_store,
885 }, {
886 .attr = {
887 .name = "inject_type",
888 .mode = (S_IRUGO | S_IWUSR)
889 },
890 .show = i7core_inject_type_show,
891 .store = i7core_inject_type_store,
892 }, {
893 .attr = {
894 .name = "inject_eccmask",
895 .mode = (S_IRUGO | S_IWUSR)
896 },
897 .show = i7core_inject_eccmask_show,
898 .store = i7core_inject_eccmask_store,
899 }, {
900 .attr = {
901 .name = "inject_addrmatch",
902 .mode = (S_IRUGO | S_IWUSR)
903 },
904 .show = i7core_inject_addrmatch_show,
905 .store = i7core_inject_addrmatch_store,
906 }, {
907 .attr = {
908 .name = "inject_enable",
909 .mode = (S_IRUGO | S_IWUSR)
910 },
911 .show = i7core_inject_enable_show,
912 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300913 }, {
914 .attr = {
915 .name = "corrected_error_counts",
916 .mode = (S_IRUGO | S_IWUSR)
917 },
918 .show = i7core_ce_regs_show,
919 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300920 },
921};
922
923/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300924 Device initialization routines: put/get, init/exit
925 ****************************************************************************/
926
927/*
928 * i7core_put_devices 'put' all the devices that we have
929 * reserved via 'get'
930 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300931static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300932{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300933 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300934
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300935 for (i = 0; i < N_DEVS; i++)
936 pci_dev_put(pci_devs[i].pdev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300937}
938
939/*
940 * i7core_get_devices Find and perform 'get' operation on the MCH's
941 * device/functions we want to reference for this driver
942 *
943 * Need to 'get' device 16 func 1 and func 2
944 */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300945static int i7core_get_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300946{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300947 int rc, i;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300948 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300949
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300950 for (i = 0; i < N_DEVS; i++) {
951 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
952 pci_devs[i].dev_id, NULL);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300953 if (likely(pdev))
954 pci_devs[i].pdev = pdev;
955 else {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300956 i7core_printk(KERN_ERR,
957 "Device not found: PCI ID %04x:%04x "
958 "(dev %d, func %d)\n",
959 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
960 pci_devs[i].dev,pci_devs[i].func);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300961
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300962 /* Dev 3 function 2 only exists on chips with RDIMMs */
963 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
964 continue;
965
966 /* End of list, leave */
967 rc = -ENODEV;
968 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300969 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300970
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300971 /* Sanity check */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300972 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev ||
973 PCI_FUNC(pdev->devfn) != pci_devs[i].func)) {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300974 i7core_printk(KERN_ERR,
975 "Device PCI ID %04x:%04x "
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300976 "has fn %d.%d instead of fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300977 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300978 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
979 pci_devs[i].dev, pci_devs[i].func);
980 rc = -EINVAL;
981 goto error;
982 }
983
984 /* Be sure that the device is enabled */
985 rc = pci_enable_device(pdev);
986 if (unlikely(rc < 0)) {
987 i7core_printk(KERN_ERR,
988 "Couldn't enable PCI ID %04x:%04x "
989 "fn %d.%d\n",
990 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
991 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
992 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300993 }
994
995 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300996 "Registered device %0x:%0x fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300997 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
998 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300999 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001000
1001 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001002
1003error:
1004 i7core_put_devices();
1005 return -EINVAL;
1006}
1007
1008static int mci_bind_devs(struct mem_ctl_info *mci)
1009{
1010 struct i7core_pvt *pvt = mci->pvt_info;
1011 struct pci_dev *pdev;
1012 int i, func, slot;
1013
1014 for (i = 0; i < N_DEVS; i++) {
1015 pdev = pci_devs[i].pdev;
1016 if (!pdev)
1017 continue;
1018
1019 func = PCI_FUNC(pdev->devfn);
1020 slot = PCI_SLOT(pdev->devfn);
1021 if (slot == 3) {
1022 if (unlikely(func > MAX_MCR_FUNC))
1023 goto error;
1024 pvt->pci_mcr[func] = pdev;
1025 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1026 if (unlikely(func > MAX_CHAN_FUNC))
1027 goto error;
1028 pvt->pci_ch[slot - 4][func] = pdev;
1029 } else
1030 goto error;
1031
1032 debugf0("Associated fn %d.%d, dev = %p\n",
1033 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev);
1034 }
1035 return 0;
1036
1037error:
1038 i7core_printk(KERN_ERR, "Device %d, function %d "
1039 "is out of the expected range\n",
1040 slot, func);
1041 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001042}
1043
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001044/****************************************************************************
1045 Error check routines
1046 ****************************************************************************/
1047
1048/* This function is based on the device 3 function 4 registers as described on:
1049 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1050 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1051 * also available at:
1052 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1053 */
1054static void check_mc_test_err(struct mem_ctl_info *mci)
1055{
1056 struct i7core_pvt *pvt = mci->pvt_info;
1057 u32 rcv1, rcv0;
1058 int new0, new1, new2;
1059
1060 if (!pvt->pci_mcr[4]) {
1061 debugf0("%s MCR registers not found\n",__func__);
1062 return;
1063 }
1064
1065 /* Corrected error reads */
1066 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1067 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
1068
1069 /* Store the new values */
1070 new2 = DIMM2_COR_ERR(rcv1);
1071 new1 = DIMM1_COR_ERR(rcv0);
1072 new0 = DIMM0_COR_ERR(rcv0);
1073
1074 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1075 (pvt->ce_count_available ? "UPDATE" : "READ"),
1076 rcv1, rcv0, new0, new1, new2);
1077
1078 /* Updates CE counters if it is not the first time here */
1079 if (pvt->ce_count_available) {
1080 /* Updates CE counters */
1081 int add0, add1, add2;
1082
1083 add2 = new2 - pvt->last_ce_count[2];
1084 add1 = new1 - pvt->last_ce_count[1];
1085 add0 = new0 - pvt->last_ce_count[0];
1086
1087 if (add2 < 0)
1088 add2 += 0x7fff;
1089 pvt->ce_count[2] += add2;
1090
1091 if (add1 < 0)
1092 add1 += 0x7fff;
1093 pvt->ce_count[1] += add1;
1094
1095 if (add0 < 0)
1096 add0 += 0x7fff;
1097 pvt->ce_count[0] += add0;
1098 } else
1099 pvt->ce_count_available = 1;
1100
1101 /* Store the new values */
1102 pvt->last_ce_count[2] = new2;
1103 pvt->last_ce_count[1] = new1;
1104 pvt->last_ce_count[0] = new0;
1105}
1106
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001107/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001108 * i7core_check_error Retrieve and process errors reported by the
1109 * hardware. Called by the Core module.
1110 */
1111static void i7core_check_error(struct mem_ctl_info *mci)
1112{
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001113 check_mc_test_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001114}
1115
1116/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001117 * i7core_probe Probe for ONE instance of device to see if it is
1118 * present.
1119 * return:
1120 * 0 for FOUND a device
1121 * < 0 for error code
1122 */
1123static int __devinit i7core_probe(struct pci_dev *pdev,
1124 const struct pci_device_id *id)
1125{
1126 struct mem_ctl_info *mci;
1127 struct i7core_pvt *pvt;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001128 int num_channels = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001129 int num_csrows;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001130 int dev_idx = id->driver_data;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001131 int rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001132
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001133 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001134 return -EINVAL;
1135
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001136 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001137 rc = i7core_get_devices();
1138 if (unlikely(rc < 0))
1139 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001140
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001141 /* Check the number of active and not disabled channels */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001142 rc = i7core_get_active_channels(&num_channels);
1143 if (unlikely (rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001144 goto fail0;
1145
1146 /* FIXME: we currently don't know the number of csrows */
1147 num_csrows = num_channels;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001148
1149 /* allocate a new MC control structure */
1150 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001151 if (unlikely (!mci)) {
1152 rc = -ENOMEM;
1153 goto fail0;
1154 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001155
1156 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1157
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001158 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001159
1160 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001161 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001162
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001163 mci->mc_idx = 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001164 mci->mtype_cap = MEM_FLAG_DDR3; /* FIXME: how to handle RDDR3? */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001165 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1166 mci->edac_cap = EDAC_FLAG_NONE;
1167 mci->mod_name = "i7core_edac.c";
1168 mci->mod_ver = I7CORE_REVISION;
1169 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1170 mci->dev_name = pci_name(pdev);
1171 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001172 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001173 /* Set the function pointer to an actual operation function */
1174 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001175
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001176 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001177 rc = mci_bind_devs(mci);
1178 if (unlikely (rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001179 goto fail1;
1180
1181 /* Get dimm basic config */
1182 get_dimm_config(mci);
1183
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001184 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001185 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001186 debugf0("MC: " __FILE__
1187 ": %s(): failed edac_mc_add_mc()\n", __func__);
1188 /* FIXME: perhaps some code should go here that disables error
1189 * reporting if we just enabled it
1190 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001191
1192 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001193 goto fail1;
1194 }
1195
1196 /* allocating generic PCI control info */
1197 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001198 if (unlikely (!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001199 printk(KERN_WARNING
1200 "%s(): Unable to create PCI control\n",
1201 __func__);
1202 printk(KERN_WARNING
1203 "%s(): PCI error report via EDAC not setup\n",
1204 __func__);
1205 }
1206
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001207 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001208 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001209 pvt->inject.dimm = -1;
1210 pvt->inject.rank = -1;
1211 pvt->inject.bank = -1;
1212 pvt->inject.page = -1;
1213 pvt->inject.col = -1;
1214
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001215 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001216
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001217 return 0;
1218
1219fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001220 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001221
1222fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001223 i7core_put_devices();
1224 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001225}
1226
1227/*
1228 * i7core_remove destructor for one instance of device
1229 *
1230 */
1231static void __devexit i7core_remove(struct pci_dev *pdev)
1232{
1233 struct mem_ctl_info *mci;
1234
1235 debugf0(__FILE__ ": %s()\n", __func__);
1236
1237 if (i7core_pci)
1238 edac_pci_release_generic_ctl(i7core_pci);
1239
1240 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001241
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001242 if (!mci)
1243 return;
1244
1245 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001246 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001247
1248 edac_mc_free(mci);
1249}
1250
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001251MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1252
1253/*
1254 * i7core_driver pci_driver structure for this module
1255 *
1256 */
1257static struct pci_driver i7core_driver = {
1258 .name = "i7core_edac",
1259 .probe = i7core_probe,
1260 .remove = __devexit_p(i7core_remove),
1261 .id_table = i7core_pci_tbl,
1262};
1263
1264/*
1265 * i7core_init Module entry function
1266 * Try to initialize this module for its devices
1267 */
1268static int __init i7core_init(void)
1269{
1270 int pci_rc;
1271
1272 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1273
1274 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1275 opstate_init();
1276
1277 pci_rc = pci_register_driver(&i7core_driver);
1278
1279 return (pci_rc < 0) ? pci_rc : 0;
1280}
1281
1282/*
1283 * i7core_exit() Module exit function
1284 * Unregister the driver
1285 */
1286static void __exit i7core_exit(void)
1287{
1288 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1289 pci_unregister_driver(&i7core_driver);
1290}
1291
1292module_init(i7core_init);
1293module_exit(i7core_exit);
1294
1295MODULE_LICENSE("GPL");
1296MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1297MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1298MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1299 I7CORE_REVISION);
1300
1301module_param(edac_op_state, int, 0444);
1302MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");