blob: 3c7bb5f405f66e92451f2d725c4d3c750c3440e6 [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>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030030#include <linux/edac_mce.h>
31#include <linux/spinlock.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030032
33#include "edac_core.h"
34
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -030035/* To use the new pci_[read/write]_config_qword instead of two dword */
36#define USE_QWORD 1
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030037
38/*
39 * Alter this version for the module when modifications are made
40 */
41#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
42#define EDAC_MOD_STR "i7core_edac"
43
44/* HACK: temporary, just to enable all logs, for now */
45#undef debugf0
46#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
47
48/*
49 * Debug macros
50 */
51#define i7core_printk(level, fmt, arg...) \
52 edac_printk(level, "i7core", fmt, ##arg)
53
54#define i7core_mc_printk(mci, level, fmt, arg...) \
55 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
56
57/*
58 * i7core Memory Controller Registers
59 */
60
61 /* OFFSETS for Device 3 Function 0 */
62
63#define MC_CONTROL 0x48
64#define MC_STATUS 0x4c
65#define MC_MAX_DOD 0x64
66
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030067/*
68 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
69 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
70 */
71
72#define MC_TEST_ERR_RCV1 0x60
73 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
74
75#define MC_TEST_ERR_RCV0 0x64
76 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
77 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
78
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030079 /* OFFSETS for Devices 4,5 and 6 Function 0 */
80
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030081#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
82 #define THREE_DIMMS_PRESENT (1 << 24)
83 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
84 #define QUAD_RANK_PRESENT (1 << 22)
85 #define REGISTERED_DIMM (1 << 15)
86
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030087#define MC_CHANNEL_MAPPER 0x60
88 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
89 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
90
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030091#define MC_CHANNEL_RANK_PRESENT 0x7c
92 #define RANK_PRESENT_MASK 0xffff
93
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030094#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030095#define MC_CHANNEL_ERROR_MASK 0xf8
96#define MC_CHANNEL_ERROR_INJECT 0xfc
97 #define INJECT_ADDR_PARITY 0x10
98 #define INJECT_ECC 0x08
99 #define MASK_CACHELINE 0x06
100 #define MASK_FULL_CACHELINE 0x06
101 #define MASK_MSB32_CACHELINE 0x04
102 #define MASK_LSB32_CACHELINE 0x02
103 #define NO_MASK_CACHELINE 0x00
104 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300105
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300106 /* OFFSETS for Devices 4,5 and 6 Function 1 */
107#define MC_DOD_CH_DIMM0 0x48
108#define MC_DOD_CH_DIMM1 0x4c
109#define MC_DOD_CH_DIMM2 0x50
110 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
111 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
112 #define DIMM_PRESENT_MASK (1 << 9)
113 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300114 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
115 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
116 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
117 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300118 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300119 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300120 #define MC_DOD_NUMCOL_MASK 3
121 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300122
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300123#define MC_RANK_PRESENT 0x7c
124
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300125#define MC_SAG_CH_0 0x80
126#define MC_SAG_CH_1 0x84
127#define MC_SAG_CH_2 0x88
128#define MC_SAG_CH_3 0x8c
129#define MC_SAG_CH_4 0x90
130#define MC_SAG_CH_5 0x94
131#define MC_SAG_CH_6 0x98
132#define MC_SAG_CH_7 0x9c
133
134#define MC_RIR_LIMIT_CH_0 0x40
135#define MC_RIR_LIMIT_CH_1 0x44
136#define MC_RIR_LIMIT_CH_2 0x48
137#define MC_RIR_LIMIT_CH_3 0x4C
138#define MC_RIR_LIMIT_CH_4 0x50
139#define MC_RIR_LIMIT_CH_5 0x54
140#define MC_RIR_LIMIT_CH_6 0x58
141#define MC_RIR_LIMIT_CH_7 0x5C
142#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
143
144#define MC_RIR_WAY_CH 0x80
145 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
146 #define MC_RIR_WAY_RANK_MASK 0x7
147
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300148/*
149 * i7core structs
150 */
151
152#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300153#define MAX_DIMMS 3 /* Max DIMMS per channel */
154#define MAX_MCR_FUNC 4
155#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300156
157struct i7core_info {
158 u32 mc_control;
159 u32 mc_status;
160 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300161 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300162};
163
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300164
165struct i7core_inject {
166 int enable;
167
168 u32 section;
169 u32 type;
170 u32 eccmask;
171
172 /* Error address mask */
173 int channel, dimm, rank, bank, page, col;
174};
175
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300176struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300177 u32 ranks;
178 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300179};
180
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300181struct pci_id_descr {
182 int dev;
183 int func;
184 int dev_id;
185 struct pci_dev *pdev;
186};
187
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300188struct i7core_pvt {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300189 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
190 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300191 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300192 struct i7core_inject inject;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300193 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300194 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300195
196 int ce_count_available;
197 unsigned long ce_count[MAX_DIMMS]; /* ECC corrected errors counts per dimm */
198 int last_ce_count[MAX_DIMMS];
199
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300200 /* mcelog glue */
201 struct edac_mce edac_mce;
202 struct mce mce_entry[MCE_LOG_LEN];
203 unsigned mce_count;
204 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300205};
206
207/* Device name and register DID (Device ID) */
208struct i7core_dev_info {
209 const char *ctl_name; /* name for this device */
210 u16 fsb_mapping_errors; /* DID for the branchmap,control */
211};
212
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300213#define PCI_DESCR(device, function, device_id) \
214 .dev = (device), \
215 .func = (function), \
216 .dev_id = (device_id)
217
218struct pci_id_descr pci_devs[] = {
219 /* Memory controller */
220 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
221 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
222 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
223 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
224
225 /* Channel 0 */
226 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
227 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
228 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
229 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
230
231 /* Channel 1 */
232 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
233 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
234 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
235 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
236
237 /* Channel 2 */
238 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
239 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
240 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
241 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300242};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300243#define N_DEVS ARRAY_SIZE(pci_devs)
244
245/*
246 * pci_device_id table for which devices we are looking for
247 * This should match the first device at pci_devs table
248 */
249static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
250 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)},
251 {0,} /* 0 terminated list. */
252};
253
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300254
255/* Table of devices attributes supported by this driver */
256static const struct i7core_dev_info i7core_devs[] = {
257 {
258 .ctl_name = "i7 Core",
259 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
260 },
261};
262
263static struct edac_pci_ctl_info *i7core_pci;
264
265/****************************************************************************
266 Anciliary status routines
267 ****************************************************************************/
268
269 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300270#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
271#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300272
273 /* MC_STATUS bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300274#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
275#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300276
277 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300278static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300279{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300280 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300281}
282
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300283static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300284{
285 static int ranks[4] = { 1, 2, 4, -EINVAL };
286
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300287 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300288}
289
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300290static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300291{
292 static int banks[4] = { 4, 8, 16, -EINVAL };
293
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300294 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300295}
296
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300297static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300298{
299 static int rows[8] = {
300 1 << 12, 1 << 13, 1 << 14, 1 << 15,
301 1 << 16, -EINVAL, -EINVAL, -EINVAL,
302 };
303
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300304 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300305}
306
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300307static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300308{
309 static int cols[8] = {
310 1 << 10, 1 << 11, 1 << 12, -EINVAL,
311 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300312 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300313}
314
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300315
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300316/****************************************************************************
317 Memory check routines
318 ****************************************************************************/
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300319static struct pci_dev *get_pdev_slot_func(int slot, int func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300320{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300321 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300322
323 for (i = 0; i < N_DEVS; i++) {
324 if (!pci_devs[i].pdev)
325 continue;
326
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300327 if (PCI_SLOT(pci_devs[i].pdev->devfn) == slot &&
328 PCI_FUNC(pci_devs[i].pdev->devfn) == func) {
329 return pci_devs[i].pdev;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300330 }
331 }
332
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300333 return NULL;
334}
335
336static int i7core_get_active_channels(int *channels, int *csrows)
337{
338 struct pci_dev *pdev = NULL;
339 int i, j;
340 u32 status, control;
341
342 *channels = 0;
343 *csrows = 0;
344
345 pdev = get_pdev_slot_func(3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300346 if (!pdev) {
347 i7core_printk(KERN_ERR, "Couldn't find fn 3.0!!!\n");
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300348 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300349 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300350
351 /* Device 3 function 0 reads */
352 pci_read_config_dword(pdev, MC_STATUS, &status);
353 pci_read_config_dword(pdev, MC_CONTROL, &control);
354
355 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300356 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300357 /* Check if the channel is active */
358 if (!(control & (1 << (8 + i))))
359 continue;
360
361 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300362 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300363 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300364
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300365 pdev = get_pdev_slot_func(i + 4, 1);
366 if (!pdev) {
367 i7core_printk(KERN_ERR, "Couldn't find fn %d.%d!!!\n",
368 i + 4, 1);
369 return -ENODEV;
370 }
371 /* Devices 4-6 function 1 */
372 pci_read_config_dword(pdev,
373 MC_DOD_CH_DIMM0, &dimm_dod[0]);
374 pci_read_config_dword(pdev,
375 MC_DOD_CH_DIMM1, &dimm_dod[1]);
376 pci_read_config_dword(pdev,
377 MC_DOD_CH_DIMM2, &dimm_dod[2]);
378
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300379 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300380
381 for (j = 0; j < 3; j++) {
382 if (!DIMM_PRESENT(dimm_dod[j]))
383 continue;
384 (*csrows)++;
385 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300386 }
387
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300388 debugf0("Number of active channels: %d\n", *channels);
389
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300390 return 0;
391}
392
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300393static int get_dimm_config(struct mem_ctl_info *mci)
394{
395 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300396 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300397 struct pci_dev *pdev;
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300398 int i, j, csrow = 0;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300399 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300400 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300401 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300402
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300403 /* Get data from the MC register, function 0 */
404 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300405 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300406 return -ENODEV;
407
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300408 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300409 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
410 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
411 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
412 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300413
414 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
415 pvt->info.mc_control, pvt->info.mc_status,
416 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300417
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300418 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300419 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300420 if (ECCx8(pvt))
421 mode = EDAC_S8ECD8ED;
422 else
423 mode = EDAC_S4ECD4ED;
424 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300425 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300426 mode = EDAC_NONE;
427 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300428
429 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300430 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked\n",
431 numdimms(pvt->info.max_dod),
432 numrank(pvt->info.max_dod >> 2),
433 numbank(pvt->info.max_dod >> 4));
434 debugf0("DOD Max rows x colums = 0x%x x 0x%x\n",
435 numrow(pvt->info.max_dod >> 6),
436 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300437
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300438 debugf0("Memory channel configuration:\n");
439
440 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300441 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300442
443 if (!CH_ACTIVE(pvt, i)) {
444 debugf0("Channel %i is not active\n", i);
445 continue;
446 }
447 if (CH_DISABLED(pvt, i)) {
448 debugf0("Channel %i is disabled\n", i);
449 continue;
450 }
451
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300452 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300453 pci_read_config_dword(pvt->pci_ch[i][0],
454 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
455
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300456 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ? 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300457
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300458 if (data & REGISTERED_DIMM)
459 mtype = MEM_RDDR3;
460 else
461 mtype = MEM_DDR3;
462#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300463 if (data & THREE_DIMMS_PRESENT)
464 pvt->channel[i].dimms = 3;
465 else if (data & SINGLE_QUAD_RANK_PRESENT)
466 pvt->channel[i].dimms = 1;
467 else
468 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300469#endif
470
471 /* Devices 4-6 function 1 */
472 pci_read_config_dword(pvt->pci_ch[i][1],
473 MC_DOD_CH_DIMM0, &dimm_dod[0]);
474 pci_read_config_dword(pvt->pci_ch[i][1],
475 MC_DOD_CH_DIMM1, &dimm_dod[1]);
476 pci_read_config_dword(pvt->pci_ch[i][1],
477 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300478
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300479 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300480 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300481 i,
482 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
483 data,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300484 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300485 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300486
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300487 for (j = 0; j < 3; j++) {
488 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300489 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300490
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300491 if (!DIMM_PRESENT(dimm_dod[j]))
492 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300493
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300494 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
495 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
496 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
497 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300498
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300499 /* DDR3 has 8 I/O banks */
500 size = (rows * cols * banks * ranks) >> (20 - 3);
501
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300502 pvt->channel[i].dimms++;
503
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300504 debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, "
505 "numbank: %d,\n\t\t"
506 "numrank: %d, numrow: %#x, numcol: %#x\n",
507 j, dimm_dod[j], size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300508 RANKOFFSET(dimm_dod[j]),
509 banks, ranks, rows, cols);
510
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300511#if PAGE_SHIFT > 20
512 npages = size >> (PAGE_SHIFT - 20);
513#else
514 npages = size << (20 - PAGE_SHIFT);
515#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300516
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300517 csr = &mci->csrows[csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300518 csr->first_page = last_page + 1;
519 last_page += npages;
520 csr->last_page = last_page;
521 csr->nr_pages = npages;
522
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300523 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300524 csr->grain = 8;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300525 csr->csrow_idx = csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300526 csr->nr_channels = 1;
527
528 csr->channels[0].chan_idx = i;
529 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300530
531 switch (banks) {
532 case 4:
533 csr->dtype = DEV_X4;
534 break;
535 case 8:
536 csr->dtype = DEV_X8;
537 break;
538 case 16:
539 csr->dtype = DEV_X16;
540 break;
541 default:
542 csr->dtype = DEV_UNKNOWN;
543 }
544
545 csr->edac_mode = mode;
546 csr->mtype = mtype;
547
548 csrow++;
549 }
550
551 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
552 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
553 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
554 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
555 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
556 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
557 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
558 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300559 debugf0("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300560 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300561 debugf0("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300562 (value[j] >> 27) & 0x1,
563 (value[j] >> 24) & 0x7,
564 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300565 }
566
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300567 return 0;
568}
569
570/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300571 Error insertion routines
572 ****************************************************************************/
573
574/* The i7core has independent error injection features per channel.
575 However, to have a simpler code, we don't allow enabling error injection
576 on more than one channel.
577 Also, since a change at an inject parameter will be applied only at enable,
578 we're disabling error injection on all write calls to the sysfs nodes that
579 controls the error code injection.
580 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300581static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300582{
583 struct i7core_pvt *pvt = mci->pvt_info;
584
585 pvt->inject.enable = 0;
586
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300587 if (!pvt->pci_ch[pvt->inject.channel][0])
588 return -ENODEV;
589
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300590 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
591 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300592
593 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300594}
595
596/*
597 * i7core inject inject.section
598 *
599 * accept and store error injection inject.section value
600 * bit 0 - refers to the lower 32-byte half cacheline
601 * bit 1 - refers to the upper 32-byte half cacheline
602 */
603static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
604 const char *data, size_t count)
605{
606 struct i7core_pvt *pvt = mci->pvt_info;
607 unsigned long value;
608 int rc;
609
610 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300611 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300612
613 rc = strict_strtoul(data, 10, &value);
614 if ((rc < 0) || (value > 3))
615 return 0;
616
617 pvt->inject.section = (u32) value;
618 return count;
619}
620
621static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
622 char *data)
623{
624 struct i7core_pvt *pvt = mci->pvt_info;
625 return sprintf(data, "0x%08x\n", pvt->inject.section);
626}
627
628/*
629 * i7core inject.type
630 *
631 * accept and store error injection inject.section value
632 * bit 0 - repeat enable - Enable error repetition
633 * bit 1 - inject ECC error
634 * bit 2 - inject parity error
635 */
636static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
637 const char *data, size_t count)
638{
639 struct i7core_pvt *pvt = mci->pvt_info;
640 unsigned long value;
641 int rc;
642
643 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300644 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300645
646 rc = strict_strtoul(data, 10, &value);
647 if ((rc < 0) || (value > 7))
648 return 0;
649
650 pvt->inject.type = (u32) value;
651 return count;
652}
653
654static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
655 char *data)
656{
657 struct i7core_pvt *pvt = mci->pvt_info;
658 return sprintf(data, "0x%08x\n", pvt->inject.type);
659}
660
661/*
662 * i7core_inject_inject.eccmask_store
663 *
664 * The type of error (UE/CE) will depend on the inject.eccmask value:
665 * Any bits set to a 1 will flip the corresponding ECC bit
666 * Correctable errors can be injected by flipping 1 bit or the bits within
667 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
668 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
669 * uncorrectable error to be injected.
670 */
671static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
672 const char *data, size_t count)
673{
674 struct i7core_pvt *pvt = mci->pvt_info;
675 unsigned long value;
676 int rc;
677
678 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300679 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300680
681 rc = strict_strtoul(data, 10, &value);
682 if (rc < 0)
683 return 0;
684
685 pvt->inject.eccmask = (u32) value;
686 return count;
687}
688
689static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
690 char *data)
691{
692 struct i7core_pvt *pvt = mci->pvt_info;
693 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
694}
695
696/*
697 * i7core_addrmatch
698 *
699 * The type of error (UE/CE) will depend on the inject.eccmask value:
700 * Any bits set to a 1 will flip the corresponding ECC bit
701 * Correctable errors can be injected by flipping 1 bit or the bits within
702 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
703 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
704 * uncorrectable error to be injected.
705 */
706static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
707 const char *data, size_t count)
708{
709 struct i7core_pvt *pvt = mci->pvt_info;
710 char *cmd, *val;
711 long value;
712 int rc;
713
714 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300715 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300716
717 do {
718 cmd = strsep((char **) &data, ":");
719 if (!cmd)
720 break;
721 val = strsep((char **) &data, " \n\t");
722 if (!val)
723 return cmd - data;
724
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300725 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300726 value = -1;
727 else {
728 rc = strict_strtol(val, 10, &value);
729 if ((rc < 0) || (value < 0))
730 return cmd - data;
731 }
732
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300733 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300734 if (value < 3)
735 pvt->inject.channel = value;
736 else
737 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300738 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300739 if (value < 4)
740 pvt->inject.dimm = value;
741 else
742 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300743 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300744 if (value < 4)
745 pvt->inject.rank = value;
746 else
747 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300748 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300749 if (value < 4)
750 pvt->inject.bank = value;
751 else
752 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300753 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300754 if (value <= 0xffff)
755 pvt->inject.page = value;
756 else
757 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300758 } else if (!strcasecmp(cmd, "col") ||
759 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300760 if (value <= 0x3fff)
761 pvt->inject.col = value;
762 else
763 return cmd - data;
764 }
765 } while (1);
766
767 return count;
768}
769
770static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
771 char *data)
772{
773 struct i7core_pvt *pvt = mci->pvt_info;
774 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
775
776 if (pvt->inject.channel < 0)
777 sprintf(channel, "any");
778 else
779 sprintf(channel, "%d", pvt->inject.channel);
780 if (pvt->inject.dimm < 0)
781 sprintf(dimm, "any");
782 else
783 sprintf(dimm, "%d", pvt->inject.dimm);
784 if (pvt->inject.bank < 0)
785 sprintf(bank, "any");
786 else
787 sprintf(bank, "%d", pvt->inject.bank);
788 if (pvt->inject.rank < 0)
789 sprintf(rank, "any");
790 else
791 sprintf(rank, "%d", pvt->inject.rank);
792 if (pvt->inject.page < 0)
793 sprintf(page, "any");
794 else
795 sprintf(page, "0x%04x", pvt->inject.page);
796 if (pvt->inject.col < 0)
797 sprintf(col, "any");
798 else
799 sprintf(col, "0x%04x", pvt->inject.col);
800
801 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
802 "rank: %s\npage: %s\ncolumn: %s\n",
803 channel, dimm, bank, rank, page, col);
804}
805
806/*
807 * This routine prepares the Memory Controller for error injection.
808 * The error will be injected when some process tries to write to the
809 * memory that matches the given criteria.
810 * The criteria can be set in terms of a mask where dimm, rank, bank, page
811 * and col can be specified.
812 * A -1 value for any of the mask items will make the MCU to ignore
813 * that matching criteria for error injection.
814 *
815 * It should be noticed that the error will only happen after a write operation
816 * on a memory that matches the condition. if REPEAT_EN is not enabled at
817 * inject mask, then it will produce just one error. Otherwise, it will repeat
818 * until the injectmask would be cleaned.
819 *
820 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
821 * is reliable enough to check if the MC is using the
822 * three channels. However, this is not clear at the datasheet.
823 */
824static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
825 const char *data, size_t count)
826{
827 struct i7core_pvt *pvt = mci->pvt_info;
828 u32 injectmask;
829 u64 mask = 0;
830 int rc;
831 long enable;
832
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300833 if (!pvt->pci_ch[pvt->inject.channel][0])
834 return 0;
835
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300836 rc = strict_strtoul(data, 10, &enable);
837 if ((rc < 0))
838 return 0;
839
840 if (enable) {
841 pvt->inject.enable = 1;
842 } else {
843 disable_inject(mci);
844 return count;
845 }
846
847 /* Sets pvt->inject.dimm mask */
848 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300849 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300850 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300851 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300852 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300853 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300854 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300855 }
856
857 /* Sets pvt->inject.rank mask */
858 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300859 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300860 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300861 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300862 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300863 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300864 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300865 }
866
867 /* Sets pvt->inject.bank mask */
868 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300869 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300870 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300871 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300872
873 /* Sets pvt->inject.page mask */
874 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300875 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300876 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300877 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300878
879 /* Sets pvt->inject.column mask */
880 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300881 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300882 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300883 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300884
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300885#if USE_QWORD
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300886 pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0],
887 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300888#else
889 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
890 MC_CHANNEL_ADDR_MATCH, mask);
891 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
892 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
893#endif
894
895#if 1
896#if USE_QWORD
897 u64 rdmask;
898 pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0],
899 MC_CHANNEL_ADDR_MATCH, &rdmask);
900 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
901 mask, rdmask);
902#else
903 u32 rdmask1, rdmask2;
904
905 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
906 MC_CHANNEL_ADDR_MATCH, &rdmask1);
907 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
908 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
909
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300910 debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n",
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300911 mask, rdmask1, rdmask2);
912#endif
913#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300914
915 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
916 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
917
918 /*
919 * bit 0: REPEAT_EN
920 * bits 1-2: MASK_HALF_CACHELINE
921 * bit 3: INJECT_ECC
922 * bit 4: INJECT_ADDR_PARITY
923 */
924
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300925 injectmask = (pvt->inject.type & 1) |
926 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300927 (pvt->inject.type & 0x6) << (3 - 1);
928
929 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
930 MC_CHANNEL_ERROR_MASK, injectmask);
931
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300932 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
933 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300934 mask, pvt->inject.eccmask, injectmask);
935
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300936
937
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300938 return count;
939}
940
941static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
942 char *data)
943{
944 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300945 u32 injectmask;
946
947 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
948 MC_CHANNEL_ERROR_MASK, &injectmask);
949
950 debugf0("Inject error read: 0x%018x\n", injectmask);
951
952 if (injectmask & 0x0c)
953 pvt->inject.enable = 1;
954
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300955 return sprintf(data, "%d\n", pvt->inject.enable);
956}
957
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300958static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
959{
960 struct i7core_pvt *pvt = mci->pvt_info;
961
962 if (!pvt->ce_count_available)
963 return sprintf(data, "unavailable\n");
964
965 return sprintf(data, "dimm0: %lu\ndimm1: %lu\ndimm2: %lu\n",
966 pvt->ce_count[0],
967 pvt->ce_count[1],
968 pvt->ce_count[2]);
969}
970
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300971/*
972 * Sysfs struct
973 */
974static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
975
976 {
977 .attr = {
978 .name = "inject_section",
979 .mode = (S_IRUGO | S_IWUSR)
980 },
981 .show = i7core_inject_section_show,
982 .store = i7core_inject_section_store,
983 }, {
984 .attr = {
985 .name = "inject_type",
986 .mode = (S_IRUGO | S_IWUSR)
987 },
988 .show = i7core_inject_type_show,
989 .store = i7core_inject_type_store,
990 }, {
991 .attr = {
992 .name = "inject_eccmask",
993 .mode = (S_IRUGO | S_IWUSR)
994 },
995 .show = i7core_inject_eccmask_show,
996 .store = i7core_inject_eccmask_store,
997 }, {
998 .attr = {
999 .name = "inject_addrmatch",
1000 .mode = (S_IRUGO | S_IWUSR)
1001 },
1002 .show = i7core_inject_addrmatch_show,
1003 .store = i7core_inject_addrmatch_store,
1004 }, {
1005 .attr = {
1006 .name = "inject_enable",
1007 .mode = (S_IRUGO | S_IWUSR)
1008 },
1009 .show = i7core_inject_enable_show,
1010 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001011 }, {
1012 .attr = {
1013 .name = "corrected_error_counts",
1014 .mode = (S_IRUGO | S_IWUSR)
1015 },
1016 .show = i7core_ce_regs_show,
1017 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001018 },
1019};
1020
1021/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001022 Device initialization routines: put/get, init/exit
1023 ****************************************************************************/
1024
1025/*
1026 * i7core_put_devices 'put' all the devices that we have
1027 * reserved via 'get'
1028 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001029static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001030{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001031 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001032
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001033 for (i = 0; i < N_DEVS; i++)
1034 pci_dev_put(pci_devs[i].pdev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001035}
1036
1037/*
1038 * i7core_get_devices Find and perform 'get' operation on the MCH's
1039 * device/functions we want to reference for this driver
1040 *
1041 * Need to 'get' device 16 func 1 and func 2
1042 */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001043static int i7core_get_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001044{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001045 int rc, i;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001046 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001047
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001048 for (i = 0; i < N_DEVS; i++) {
1049 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1050 pci_devs[i].dev_id, NULL);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001051 if (likely(pdev))
1052 pci_devs[i].pdev = pdev;
1053 else {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001054 i7core_printk(KERN_ERR,
1055 "Device not found: PCI ID %04x:%04x "
1056 "(dev %d, func %d)\n",
1057 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001058 pci_devs[i].dev, pci_devs[i].func);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001059
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001060 /* Dev 3 function 2 only exists on chips with RDIMMs */
1061 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
1062 continue;
1063
1064 /* End of list, leave */
1065 rc = -ENODEV;
1066 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001067 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001068
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001069 /* Sanity check */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001070 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev ||
1071 PCI_FUNC(pdev->devfn) != pci_devs[i].func)) {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001072 i7core_printk(KERN_ERR,
1073 "Device PCI ID %04x:%04x "
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001074 "has fn %d.%d instead of fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001075 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001076 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1077 pci_devs[i].dev, pci_devs[i].func);
1078 rc = -EINVAL;
1079 goto error;
1080 }
1081
1082 /* Be sure that the device is enabled */
1083 rc = pci_enable_device(pdev);
1084 if (unlikely(rc < 0)) {
1085 i7core_printk(KERN_ERR,
1086 "Couldn't enable PCI ID %04x:%04x "
1087 "fn %d.%d\n",
1088 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1089 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1090 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001091 }
1092
1093 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001094 "Registered device %0x:%0x fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001095 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1096 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001097 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001098
1099 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001100
1101error:
1102 i7core_put_devices();
1103 return -EINVAL;
1104}
1105
1106static int mci_bind_devs(struct mem_ctl_info *mci)
1107{
1108 struct i7core_pvt *pvt = mci->pvt_info;
1109 struct pci_dev *pdev;
1110 int i, func, slot;
1111
1112 for (i = 0; i < N_DEVS; i++) {
1113 pdev = pci_devs[i].pdev;
1114 if (!pdev)
1115 continue;
1116
1117 func = PCI_FUNC(pdev->devfn);
1118 slot = PCI_SLOT(pdev->devfn);
1119 if (slot == 3) {
1120 if (unlikely(func > MAX_MCR_FUNC))
1121 goto error;
1122 pvt->pci_mcr[func] = pdev;
1123 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1124 if (unlikely(func > MAX_CHAN_FUNC))
1125 goto error;
1126 pvt->pci_ch[slot - 4][func] = pdev;
1127 } else
1128 goto error;
1129
1130 debugf0("Associated fn %d.%d, dev = %p\n",
1131 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev);
1132 }
1133 return 0;
1134
1135error:
1136 i7core_printk(KERN_ERR, "Device %d, function %d "
1137 "is out of the expected range\n",
1138 slot, func);
1139 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001140}
1141
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001142/****************************************************************************
1143 Error check routines
1144 ****************************************************************************/
1145
1146/* This function is based on the device 3 function 4 registers as described on:
1147 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1148 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1149 * also available at:
1150 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1151 */
1152static void check_mc_test_err(struct mem_ctl_info *mci)
1153{
1154 struct i7core_pvt *pvt = mci->pvt_info;
1155 u32 rcv1, rcv0;
1156 int new0, new1, new2;
1157
1158 if (!pvt->pci_mcr[4]) {
1159 debugf0("%s MCR registers not found\n",__func__);
1160 return;
1161 }
1162
1163 /* Corrected error reads */
1164 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1165 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
1166
1167 /* Store the new values */
1168 new2 = DIMM2_COR_ERR(rcv1);
1169 new1 = DIMM1_COR_ERR(rcv0);
1170 new0 = DIMM0_COR_ERR(rcv0);
1171
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001172#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001173 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1174 (pvt->ce_count_available ? "UPDATE" : "READ"),
1175 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001176#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001177
1178 /* Updates CE counters if it is not the first time here */
1179 if (pvt->ce_count_available) {
1180 /* Updates CE counters */
1181 int add0, add1, add2;
1182
1183 add2 = new2 - pvt->last_ce_count[2];
1184 add1 = new1 - pvt->last_ce_count[1];
1185 add0 = new0 - pvt->last_ce_count[0];
1186
1187 if (add2 < 0)
1188 add2 += 0x7fff;
1189 pvt->ce_count[2] += add2;
1190
1191 if (add1 < 0)
1192 add1 += 0x7fff;
1193 pvt->ce_count[1] += add1;
1194
1195 if (add0 < 0)
1196 add0 += 0x7fff;
1197 pvt->ce_count[0] += add0;
1198 } else
1199 pvt->ce_count_available = 1;
1200
1201 /* Store the new values */
1202 pvt->last_ce_count[2] = new2;
1203 pvt->last_ce_count[1] = new1;
1204 pvt->last_ce_count[0] = new0;
1205}
1206
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001207static void i7core_mce_output_error(struct mem_ctl_info *mci,
1208 struct mce *m)
1209{
1210 debugf0("CPU %d: Machine Check Exception: %16Lx"
1211 "Bank %d: %016Lx\n",
1212 m->cpu, m->mcgstatus, m->bank, m->status);
1213 if (m->ip) {
1214 debugf0("RIP%s %02x:<%016Lx>\n",
1215 !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "",
1216 m->cs, m->ip);
1217 }
1218 printk(KERN_EMERG "TSC %llx ", m->tsc);
1219 if (m->addr)
1220 printk("ADDR %llx ", m->addr);
1221 if (m->misc)
1222 printk("MISC %llx ", m->misc);
1223
1224#if 0
1225 snprintf(msg, sizeof(msg),
1226 "%s (Branch=%d DRAM-Bank=%d Buffer ID = %d RDWR=%s "
1227 "RAS=%d CAS=%d %s Err=0x%lx (%s))",
1228 type, branch >> 1, bank, buf_id, rdwr_str(rdwr), ras, cas,
1229 type, allErrors, error_name[errnum]);
1230
1231 /* Call the helper to output message */
1232 edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg);
1233#endif
1234}
1235
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001236/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001237 * i7core_check_error Retrieve and process errors reported by the
1238 * hardware. Called by the Core module.
1239 */
1240static void i7core_check_error(struct mem_ctl_info *mci)
1241{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001242 struct i7core_pvt *pvt = mci->pvt_info;
1243 int i;
1244 unsigned count = 0;
1245 struct mce *m = NULL;
1246 unsigned long flags;
1247
1248 debugf0(__FILE__ ": %s()\n", __func__);
1249
1250 /* Copy all mce errors into a temporary buffer */
1251 spin_lock_irqsave(&pvt->mce_lock, flags);
1252 if (pvt->mce_count) {
1253 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1254 if (m) {
1255 count = pvt->mce_count;
1256 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1257 }
1258 pvt->mce_count = 0;
1259 }
1260 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1261
1262 /* proccess mcelog errors */
1263 for (i = 0; i < count; i++)
1264 i7core_mce_output_error(mci, &m[i]);
1265
1266 kfree(m);
1267
1268 /* check memory count errors */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001269 check_mc_test_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001270}
1271
1272/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001273 * i7core_mce_check_error Replicates mcelog routine to get errors
1274 * This routine simply queues mcelog errors, and
1275 * return. The error itself should be handled later
1276 * by i7core_check_error.
1277 */
1278static int i7core_mce_check_error(void *priv, struct mce *mce)
1279{
1280 struct i7core_pvt *pvt = priv;
1281 unsigned long flags;
1282
1283 debugf0(__FILE__ ": %s()\n", __func__);
1284
1285 spin_lock_irqsave(&pvt->mce_lock, flags);
1286 if (pvt->mce_count < MCE_LOG_LEN) {
1287 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1288 pvt->mce_count++;
1289 }
1290 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1291
1292 /* Advice mcelog that the error were handled */
1293// return 1;
1294 return 0; // Let's duplicate the log
1295}
1296
1297/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001298 * i7core_probe Probe for ONE instance of device to see if it is
1299 * present.
1300 * return:
1301 * 0 for FOUND a device
1302 * < 0 for error code
1303 */
1304static int __devinit i7core_probe(struct pci_dev *pdev,
1305 const struct pci_device_id *id)
1306{
1307 struct mem_ctl_info *mci;
1308 struct i7core_pvt *pvt;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -03001309 int num_channels;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001310 int num_csrows;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001311 int dev_idx = id->driver_data;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001312 int rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001313
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001314 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001315 return -EINVAL;
1316
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001317 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001318 rc = i7core_get_devices();
1319 if (unlikely(rc < 0))
1320 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001321
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001322 /* Check the number of active and not disabled channels */
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -03001323 rc = i7core_get_active_channels(&num_channels, &num_csrows);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001324 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001325 goto fail0;
1326
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001327 /* allocate a new MC control structure */
1328 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001329 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001330 rc = -ENOMEM;
1331 goto fail0;
1332 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001333
1334 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1335
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001336 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001337
1338 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001339 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001340
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001341 mci->mc_idx = 0;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001342 /*
1343 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1344 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1345 * memory channels
1346 */
1347 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001348 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1349 mci->edac_cap = EDAC_FLAG_NONE;
1350 mci->mod_name = "i7core_edac.c";
1351 mci->mod_ver = I7CORE_REVISION;
1352 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1353 mci->dev_name = pci_name(pdev);
1354 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001355 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001356 /* Set the function pointer to an actual operation function */
1357 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001358
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001359 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001360 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001361 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001362 goto fail1;
1363
1364 /* Get dimm basic config */
1365 get_dimm_config(mci);
1366
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001367 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001368 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001369 debugf0("MC: " __FILE__
1370 ": %s(): failed edac_mc_add_mc()\n", __func__);
1371 /* FIXME: perhaps some code should go here that disables error
1372 * reporting if we just enabled it
1373 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001374
1375 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001376 goto fail1;
1377 }
1378
1379 /* allocating generic PCI control info */
1380 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001381 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001382 printk(KERN_WARNING
1383 "%s(): Unable to create PCI control\n",
1384 __func__);
1385 printk(KERN_WARNING
1386 "%s(): PCI error report via EDAC not setup\n",
1387 __func__);
1388 }
1389
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001390 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001391 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001392 pvt->inject.dimm = -1;
1393 pvt->inject.rank = -1;
1394 pvt->inject.bank = -1;
1395 pvt->inject.page = -1;
1396 pvt->inject.col = -1;
1397
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001398 /* Registers on edac_mce in order to receive memory errors */
1399 pvt->edac_mce.priv = pvt;
1400 pvt->edac_mce.check_error = i7core_mce_check_error;
1401 spin_lock_init(&pvt->mce_lock);
1402
1403 rc = edac_mce_register(&pvt->edac_mce);
1404 if (unlikely (rc < 0)) {
1405 debugf0("MC: " __FILE__
1406 ": %s(): failed edac_mce_register()\n", __func__);
1407 goto fail1;
1408 }
1409
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001410 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001411
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001412 return 0;
1413
1414fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001415 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001416
1417fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001418 i7core_put_devices();
1419 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001420}
1421
1422/*
1423 * i7core_remove destructor for one instance of device
1424 *
1425 */
1426static void __devexit i7core_remove(struct pci_dev *pdev)
1427{
1428 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001429 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001430
1431 debugf0(__FILE__ ": %s()\n", __func__);
1432
1433 if (i7core_pci)
1434 edac_pci_release_generic_ctl(i7core_pci);
1435
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001436
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001437 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001438 if (!mci)
1439 return;
1440
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001441 /* Unregisters on edac_mce in order to receive memory errors */
1442 pvt = mci->pvt_info;
1443 edac_mce_unregister(&pvt->edac_mce);
1444
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001445 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001446 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001447
1448 edac_mc_free(mci);
1449}
1450
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001451MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1452
1453/*
1454 * i7core_driver pci_driver structure for this module
1455 *
1456 */
1457static struct pci_driver i7core_driver = {
1458 .name = "i7core_edac",
1459 .probe = i7core_probe,
1460 .remove = __devexit_p(i7core_remove),
1461 .id_table = i7core_pci_tbl,
1462};
1463
1464/*
1465 * i7core_init Module entry function
1466 * Try to initialize this module for its devices
1467 */
1468static int __init i7core_init(void)
1469{
1470 int pci_rc;
1471
1472 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1473
1474 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1475 opstate_init();
1476
1477 pci_rc = pci_register_driver(&i7core_driver);
1478
1479 return (pci_rc < 0) ? pci_rc : 0;
1480}
1481
1482/*
1483 * i7core_exit() Module exit function
1484 * Unregister the driver
1485 */
1486static void __exit i7core_exit(void)
1487{
1488 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1489 pci_unregister_driver(&i7core_driver);
1490}
1491
1492module_init(i7core_init);
1493module_exit(i7core_exit);
1494
1495MODULE_LICENSE("GPL");
1496MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1497MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1498MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1499 I7CORE_REVISION);
1500
1501module_param(edac_op_state, int, 0444);
1502MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");