blob: afa5281e8df95f5e0419ba3ec775d02af98046d7 [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 Chehabf4742942009-09-05 02:35:08 -030032#include <linux/smp.h>
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -030033#include <asm/processor.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030034
35#include "edac_core.h"
36
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030037/*
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030038 * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
39 * registers start at bus 255, and are not reported by BIOS.
40 * We currently find devices with only 2 sockets. In order to support more QPI
41 * Quick Path Interconnect, just increment this number.
42 */
43#define MAX_SOCKET_BUSES 2
44
45
46/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030047 * Alter this version for the module when modifications are made
48 */
49#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
50#define EDAC_MOD_STR "i7core_edac"
51
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030052/*
53 * Debug macros
54 */
55#define i7core_printk(level, fmt, arg...) \
56 edac_printk(level, "i7core", fmt, ##arg)
57
58#define i7core_mc_printk(mci, level, fmt, arg...) \
59 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
60
61/*
62 * i7core Memory Controller Registers
63 */
64
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030065 /* OFFSETS for Device 0 Function 0 */
66
67#define MC_CFG_CONTROL 0x90
68
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030069 /* OFFSETS for Device 3 Function 0 */
70
71#define MC_CONTROL 0x48
72#define MC_STATUS 0x4c
73#define MC_MAX_DOD 0x64
74
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030075/*
76 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
77 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
78 */
79
80#define MC_TEST_ERR_RCV1 0x60
81 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
82
83#define MC_TEST_ERR_RCV0 0x64
84 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
85 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
86
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -030087/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
88#define MC_COR_ECC_CNT_0 0x80
89#define MC_COR_ECC_CNT_1 0x84
90#define MC_COR_ECC_CNT_2 0x88
91#define MC_COR_ECC_CNT_3 0x8c
92#define MC_COR_ECC_CNT_4 0x90
93#define MC_COR_ECC_CNT_5 0x94
94
95#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
96#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
97
98
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030099 /* OFFSETS for Devices 4,5 and 6 Function 0 */
100
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300101#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
102 #define THREE_DIMMS_PRESENT (1 << 24)
103 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
104 #define QUAD_RANK_PRESENT (1 << 22)
105 #define REGISTERED_DIMM (1 << 15)
106
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300107#define MC_CHANNEL_MAPPER 0x60
108 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
109 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
110
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300111#define MC_CHANNEL_RANK_PRESENT 0x7c
112 #define RANK_PRESENT_MASK 0xffff
113
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300114#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300115#define MC_CHANNEL_ERROR_MASK 0xf8
116#define MC_CHANNEL_ERROR_INJECT 0xfc
117 #define INJECT_ADDR_PARITY 0x10
118 #define INJECT_ECC 0x08
119 #define MASK_CACHELINE 0x06
120 #define MASK_FULL_CACHELINE 0x06
121 #define MASK_MSB32_CACHELINE 0x04
122 #define MASK_LSB32_CACHELINE 0x02
123 #define NO_MASK_CACHELINE 0x00
124 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300125
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300126 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300127
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300128#define MC_DOD_CH_DIMM0 0x48
129#define MC_DOD_CH_DIMM1 0x4c
130#define MC_DOD_CH_DIMM2 0x50
131 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
132 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
133 #define DIMM_PRESENT_MASK (1 << 9)
134 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300135 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
136 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
137 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
138 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300139 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300140 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300141 #define MC_DOD_NUMCOL_MASK 3
142 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300143
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300144#define MC_RANK_PRESENT 0x7c
145
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300146#define MC_SAG_CH_0 0x80
147#define MC_SAG_CH_1 0x84
148#define MC_SAG_CH_2 0x88
149#define MC_SAG_CH_3 0x8c
150#define MC_SAG_CH_4 0x90
151#define MC_SAG_CH_5 0x94
152#define MC_SAG_CH_6 0x98
153#define MC_SAG_CH_7 0x9c
154
155#define MC_RIR_LIMIT_CH_0 0x40
156#define MC_RIR_LIMIT_CH_1 0x44
157#define MC_RIR_LIMIT_CH_2 0x48
158#define MC_RIR_LIMIT_CH_3 0x4C
159#define MC_RIR_LIMIT_CH_4 0x50
160#define MC_RIR_LIMIT_CH_5 0x54
161#define MC_RIR_LIMIT_CH_6 0x58
162#define MC_RIR_LIMIT_CH_7 0x5C
163#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
164
165#define MC_RIR_WAY_CH 0x80
166 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
167 #define MC_RIR_WAY_RANK_MASK 0x7
168
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300169/*
170 * i7core structs
171 */
172
173#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300174#define MAX_DIMMS 3 /* Max DIMMS per channel */
175#define MAX_MCR_FUNC 4
176#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300177
178struct i7core_info {
179 u32 mc_control;
180 u32 mc_status;
181 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300182 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300183};
184
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300185
186struct i7core_inject {
187 int enable;
188
189 u32 section;
190 u32 type;
191 u32 eccmask;
192
193 /* Error address mask */
194 int channel, dimm, rank, bank, page, col;
195};
196
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300197struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300198 u32 ranks;
199 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300200};
201
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300202struct pci_id_descr {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300203 int dev;
204 int func;
205 int dev_id;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300206};
207
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300208struct i7core_dev {
209 struct list_head list;
210 u8 socket;
211 struct pci_dev **pdev;
212 struct mem_ctl_info *mci;
213};
214
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300215struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300216 struct pci_dev *pci_noncore;
217 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
218 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
219
220 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300221
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300222 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300223 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300224 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300225
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300226 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300227
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300228 int ce_count_available;
229 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300230
231 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300232 unsigned long udimm_ce_count[MAX_DIMMS];
233 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300234 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300235 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
236 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300237
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300238 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300239
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300240 /* mcelog glue */
241 struct edac_mce edac_mce;
242 struct mce mce_entry[MCE_LOG_LEN];
243 unsigned mce_count;
244 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300245};
246
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300247/* Static vars */
248static LIST_HEAD(i7core_edac_list);
249static DEFINE_MUTEX(i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300250
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300251#define PCI_DESCR(device, function, device_id) \
252 .dev = (device), \
253 .func = (function), \
254 .dev_id = (device_id)
255
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300256struct pci_id_descr pci_dev_descr[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300257 /* Memory controller */
258 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
259 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300260 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300261 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
262
263 /* Channel 0 */
264 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
265 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
266 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
267 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
268
269 /* Channel 1 */
270 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
271 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
272 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
273 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
274
275 /* Channel 2 */
276 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
277 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
278 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
279 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300280
281 /* Generic Non-core registers */
282 /*
283 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
284 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
285 * the probing code needs to test for the other address in case of
286 * failure of this one
287 */
288 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
289
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300290};
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300291#define N_DEVS ARRAY_SIZE(pci_dev_descr)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300292
293/*
294 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300295 */
296static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300297 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300298 {0,} /* 0 terminated list. */
299};
300
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300301static struct edac_pci_ctl_info *i7core_pci;
302
303/****************************************************************************
304 Anciliary status routines
305 ****************************************************************************/
306
307 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300308#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
309#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300310
311 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300312#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300313#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300314
315 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300316static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300317{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300318 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300319}
320
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300321static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300322{
323 static int ranks[4] = { 1, 2, 4, -EINVAL };
324
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300325 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300326}
327
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300328static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300329{
330 static int banks[4] = { 4, 8, 16, -EINVAL };
331
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300332 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300333}
334
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300335static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300336{
337 static int rows[8] = {
338 1 << 12, 1 << 13, 1 << 14, 1 << 15,
339 1 << 16, -EINVAL, -EINVAL, -EINVAL,
340 };
341
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300342 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300343}
344
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300345static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300346{
347 static int cols[8] = {
348 1 << 10, 1 << 11, 1 << 12, -EINVAL,
349 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300350 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300351}
352
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300353static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300354{
355 struct i7core_dev *i7core_dev;
356
357 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
358 if (i7core_dev->socket == socket)
359 return i7core_dev;
360 }
361
362 return NULL;
363}
364
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300365/****************************************************************************
366 Memory check routines
367 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300368static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
369 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300370{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300371 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300372 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300373
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300374 if (!i7core_dev)
375 return NULL;
376
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300377 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300378 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300379 continue;
380
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300381 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
382 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
383 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300384 }
385 }
386
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300387 return NULL;
388}
389
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300390/**
391 * i7core_get_active_channels() - gets the number of channels and csrows
392 * @socket: Quick Path Interconnect socket
393 * @channels: Number of channels that will be returned
394 * @csrows: Number of csrows found
395 *
396 * Since EDAC core needs to know in advance the number of available channels
397 * and csrows, in order to allocate memory for csrows/channels, it is needed
398 * to run two similar steps. At the first step, implemented on this function,
399 * it checks the number of csrows/channels present at one socket.
400 * this is used in order to properly allocate the size of mci components.
401 *
402 * It should be noticed that none of the current available datasheets explain
403 * or even mention how csrows are seen by the memory controller. So, we need
404 * to add a fake description for csrows.
405 * So, this driver is attributing one DIMM memory for one csrow.
406 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300407static int i7core_get_active_channels(u8 socket, unsigned *channels,
408 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300409{
410 struct pci_dev *pdev = NULL;
411 int i, j;
412 u32 status, control;
413
414 *channels = 0;
415 *csrows = 0;
416
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300417 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300418 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300419 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
420 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300421 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300422 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300423
424 /* Device 3 function 0 reads */
425 pci_read_config_dword(pdev, MC_STATUS, &status);
426 pci_read_config_dword(pdev, MC_CONTROL, &control);
427
428 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300429 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300430 /* Check if the channel is active */
431 if (!(control & (1 << (8 + i))))
432 continue;
433
434 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300435 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300436 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300437
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300438 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300439 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300440 i7core_printk(KERN_ERR, "Couldn't find socket %d "
441 "fn %d.%d!!!\n",
442 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300443 return -ENODEV;
444 }
445 /* Devices 4-6 function 1 */
446 pci_read_config_dword(pdev,
447 MC_DOD_CH_DIMM0, &dimm_dod[0]);
448 pci_read_config_dword(pdev,
449 MC_DOD_CH_DIMM1, &dimm_dod[1]);
450 pci_read_config_dword(pdev,
451 MC_DOD_CH_DIMM2, &dimm_dod[2]);
452
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300453 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300454
455 for (j = 0; j < 3; j++) {
456 if (!DIMM_PRESENT(dimm_dod[j]))
457 continue;
458 (*csrows)++;
459 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300460 }
461
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300462 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300463 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300464
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300465 return 0;
466}
467
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300468static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300469{
470 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300471 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300472 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300473 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300474 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300475 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300476 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300477
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300478 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300479 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300480 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300481 return -ENODEV;
482
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300483 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300484 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
485 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
486 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
487 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300488
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300489 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300490 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300491 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300492
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300493 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300494 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300495 if (ECCx8(pvt))
496 mode = EDAC_S8ECD8ED;
497 else
498 mode = EDAC_S4ECD4ED;
499 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300500 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300501 mode = EDAC_NONE;
502 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300503
504 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300505 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
506 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300507 numdimms(pvt->info.max_dod),
508 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300509 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300510 numrow(pvt->info.max_dod >> 6),
511 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300512
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300513 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300514 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300515
516 if (!CH_ACTIVE(pvt, i)) {
517 debugf0("Channel %i is not active\n", i);
518 continue;
519 }
520 if (CH_DISABLED(pvt, i)) {
521 debugf0("Channel %i is disabled\n", i);
522 continue;
523 }
524
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300525 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300526 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300527 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
528
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300529 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300530 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300531
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300532 if (data & REGISTERED_DIMM)
533 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300534 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300535 mtype = MEM_DDR3;
536#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300537 if (data & THREE_DIMMS_PRESENT)
538 pvt->channel[i].dimms = 3;
539 else if (data & SINGLE_QUAD_RANK_PRESENT)
540 pvt->channel[i].dimms = 1;
541 else
542 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300543#endif
544
545 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300546 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300547 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300548 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300549 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300550 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300551 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300552
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300553 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300554 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300555 i,
556 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
557 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300558 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300559 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300560
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300561 for (j = 0; j < 3; j++) {
562 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300563 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300564
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300565 if (!DIMM_PRESENT(dimm_dod[j]))
566 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300567
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300568 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
569 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
570 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
571 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300572
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300573 /* DDR3 has 8 I/O banks */
574 size = (rows * cols * banks * ranks) >> (20 - 3);
575
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300576 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300577
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300578 debugf0("\tdimm %d %d Mb offset: %x, "
579 "bank: %d, rank: %d, row: %#x, col: %#x\n",
580 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300581 RANKOFFSET(dimm_dod[j]),
582 banks, ranks, rows, cols);
583
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300584#if PAGE_SHIFT > 20
585 npages = size >> (PAGE_SHIFT - 20);
586#else
587 npages = size << (20 - PAGE_SHIFT);
588#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300589
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300590 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300591 csr->first_page = last_page + 1;
592 last_page += npages;
593 csr->last_page = last_page;
594 csr->nr_pages = npages;
595
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300596 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300597 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300598 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300599 csr->nr_channels = 1;
600
601 csr->channels[0].chan_idx = i;
602 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300603
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300604 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300605
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300606 switch (banks) {
607 case 4:
608 csr->dtype = DEV_X4;
609 break;
610 case 8:
611 csr->dtype = DEV_X8;
612 break;
613 case 16:
614 csr->dtype = DEV_X16;
615 break;
616 default:
617 csr->dtype = DEV_UNKNOWN;
618 }
619
620 csr->edac_mode = mode;
621 csr->mtype = mtype;
622
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300623 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300624 }
625
626 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
627 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
628 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
629 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
630 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
631 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
632 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
633 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300634 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300635 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300636 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300637 (value[j] >> 27) & 0x1,
638 (value[j] >> 24) & 0x7,
639 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300640 }
641
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300642 return 0;
643}
644
645/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300646 Error insertion routines
647 ****************************************************************************/
648
649/* The i7core has independent error injection features per channel.
650 However, to have a simpler code, we don't allow enabling error injection
651 on more than one channel.
652 Also, since a change at an inject parameter will be applied only at enable,
653 we're disabling error injection on all write calls to the sysfs nodes that
654 controls the error code injection.
655 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300656static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300657{
658 struct i7core_pvt *pvt = mci->pvt_info;
659
660 pvt->inject.enable = 0;
661
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300662 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300663 return -ENODEV;
664
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300665 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300666 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300667
668 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300669}
670
671/*
672 * i7core inject inject.section
673 *
674 * accept and store error injection inject.section value
675 * bit 0 - refers to the lower 32-byte half cacheline
676 * bit 1 - refers to the upper 32-byte half cacheline
677 */
678static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
679 const char *data, size_t count)
680{
681 struct i7core_pvt *pvt = mci->pvt_info;
682 unsigned long value;
683 int rc;
684
685 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300686 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300687
688 rc = strict_strtoul(data, 10, &value);
689 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300690 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300691
692 pvt->inject.section = (u32) value;
693 return count;
694}
695
696static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
697 char *data)
698{
699 struct i7core_pvt *pvt = mci->pvt_info;
700 return sprintf(data, "0x%08x\n", pvt->inject.section);
701}
702
703/*
704 * i7core inject.type
705 *
706 * accept and store error injection inject.section value
707 * bit 0 - repeat enable - Enable error repetition
708 * bit 1 - inject ECC error
709 * bit 2 - inject parity error
710 */
711static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
712 const char *data, size_t count)
713{
714 struct i7core_pvt *pvt = mci->pvt_info;
715 unsigned long value;
716 int rc;
717
718 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300719 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300720
721 rc = strict_strtoul(data, 10, &value);
722 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300723 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300724
725 pvt->inject.type = (u32) value;
726 return count;
727}
728
729static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
730 char *data)
731{
732 struct i7core_pvt *pvt = mci->pvt_info;
733 return sprintf(data, "0x%08x\n", pvt->inject.type);
734}
735
736/*
737 * i7core_inject_inject.eccmask_store
738 *
739 * The type of error (UE/CE) will depend on the inject.eccmask value:
740 * Any bits set to a 1 will flip the corresponding ECC bit
741 * Correctable errors can be injected by flipping 1 bit or the bits within
742 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
743 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
744 * uncorrectable error to be injected.
745 */
746static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
747 const char *data, size_t count)
748{
749 struct i7core_pvt *pvt = mci->pvt_info;
750 unsigned long value;
751 int rc;
752
753 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300754 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300755
756 rc = strict_strtoul(data, 10, &value);
757 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300758 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300759
760 pvt->inject.eccmask = (u32) value;
761 return count;
762}
763
764static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
765 char *data)
766{
767 struct i7core_pvt *pvt = mci->pvt_info;
768 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
769}
770
771/*
772 * i7core_addrmatch
773 *
774 * The type of error (UE/CE) will depend on the inject.eccmask value:
775 * Any bits set to a 1 will flip the corresponding ECC bit
776 * Correctable errors can be injected by flipping 1 bit or the bits within
777 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
778 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
779 * uncorrectable error to be injected.
780 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300781
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300782#define DECLARE_ADDR_MATCH(param, limit) \
783static ssize_t i7core_inject_store_##param( \
784 struct mem_ctl_info *mci, \
785 const char *data, size_t count) \
786{ \
787 struct i7core_pvt *pvt = mci->pvt_info; \
788 long value; \
789 int rc; \
790 \
791 if (pvt->inject.enable) \
792 disable_inject(mci); \
793 \
794 if (!strcasecmp(data, "any")) \
795 value = -1; \
796 else { \
797 rc = strict_strtoul(data, 10, &value); \
798 if ((rc < 0) || (value >= limit)) \
799 return -EIO; \
800 } \
801 \
802 pvt->inject.param = value; \
803 \
804 return count; \
805} \
806 \
807static ssize_t i7core_inject_show_##param( \
808 struct mem_ctl_info *mci, \
809 char *data) \
810{ \
811 struct i7core_pvt *pvt = mci->pvt_info; \
812 if (pvt->inject.param < 0) \
813 return sprintf(data, "any\n"); \
814 else \
815 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300816}
817
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300818#define ATTR_ADDR_MATCH(param) \
819 { \
820 .attr = { \
821 .name = #param, \
822 .mode = (S_IRUGO | S_IWUSR) \
823 }, \
824 .show = i7core_inject_show_##param, \
825 .store = i7core_inject_store_##param, \
826 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300827
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300828DECLARE_ADDR_MATCH(channel, 3);
829DECLARE_ADDR_MATCH(dimm, 3);
830DECLARE_ADDR_MATCH(rank, 4);
831DECLARE_ADDR_MATCH(bank, 32);
832DECLARE_ADDR_MATCH(page, 0x10000);
833DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300834
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300835static int write_and_test(struct pci_dev *dev, int where, u32 val)
836{
837 u32 read;
838 int count;
839
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300840 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
841 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
842 where, val);
843
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300844 for (count = 0; count < 10; count++) {
845 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300846 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300847 pci_write_config_dword(dev, where, val);
848 pci_read_config_dword(dev, where, &read);
849
850 if (read == val)
851 return 0;
852 }
853
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300854 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
855 "write=%08x. Read=%08x\n",
856 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
857 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300858
859 return -EINVAL;
860}
861
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300862/*
863 * This routine prepares the Memory Controller for error injection.
864 * The error will be injected when some process tries to write to the
865 * memory that matches the given criteria.
866 * The criteria can be set in terms of a mask where dimm, rank, bank, page
867 * and col can be specified.
868 * A -1 value for any of the mask items will make the MCU to ignore
869 * that matching criteria for error injection.
870 *
871 * It should be noticed that the error will only happen after a write operation
872 * on a memory that matches the condition. if REPEAT_EN is not enabled at
873 * inject mask, then it will produce just one error. Otherwise, it will repeat
874 * until the injectmask would be cleaned.
875 *
876 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
877 * is reliable enough to check if the MC is using the
878 * three channels. However, this is not clear at the datasheet.
879 */
880static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
881 const char *data, size_t count)
882{
883 struct i7core_pvt *pvt = mci->pvt_info;
884 u32 injectmask;
885 u64 mask = 0;
886 int rc;
887 long enable;
888
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300889 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300890 return 0;
891
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300892 rc = strict_strtoul(data, 10, &enable);
893 if ((rc < 0))
894 return 0;
895
896 if (enable) {
897 pvt->inject.enable = 1;
898 } else {
899 disable_inject(mci);
900 return count;
901 }
902
903 /* Sets pvt->inject.dimm mask */
904 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300905 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300906 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300907 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300908 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300909 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300910 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300911 }
912
913 /* Sets pvt->inject.rank mask */
914 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300915 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300916 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300917 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300918 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300919 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300920 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300921 }
922
923 /* Sets pvt->inject.bank mask */
924 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300925 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300926 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300927 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300928
929 /* Sets pvt->inject.page mask */
930 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300931 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300932 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300933 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300934
935 /* Sets pvt->inject.column mask */
936 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300937 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300938 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300939 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300940
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300941 /*
942 * bit 0: REPEAT_EN
943 * bits 1-2: MASK_HALF_CACHELINE
944 * bit 3: INJECT_ECC
945 * bit 4: INJECT_ADDR_PARITY
946 */
947
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300948 injectmask = (pvt->inject.type & 1) |
949 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300950 (pvt->inject.type & 0x6) << (3 - 1);
951
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300952 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300953 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300954 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300955
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300956 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300957 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300958 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300959 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
960
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300961 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300962 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
963
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300964 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300965 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300966
967 /*
968 * This is something undocumented, based on my tests
969 * Without writing 8 to this register, errors aren't injected. Not sure
970 * why.
971 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300972 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300973 MC_CFG_CONTROL, 8);
974
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300975 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
976 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300977 mask, pvt->inject.eccmask, injectmask);
978
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300979
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300980 return count;
981}
982
983static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
984 char *data)
985{
986 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300987 u32 injectmask;
988
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300989 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300990 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300991
992 debugf0("Inject error read: 0x%018x\n", injectmask);
993
994 if (injectmask & 0x0c)
995 pvt->inject.enable = 1;
996
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300997 return sprintf(data, "%d\n", pvt->inject.enable);
998}
999
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001000static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1001{
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001002 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001003 struct i7core_pvt *pvt = mci->pvt_info;
1004
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001005 if (!pvt->ce_count_available) {
1006 count = sprintf(data, "data unavailable\n");
1007 return 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001008 }
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001009 if (!pvt->is_registered) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001010 count = sprintf(data, "all channels "
1011 "UDIMM0: %lu UDIMM1: %lu UDIMM2: %lu\n",
1012 pvt->udimm_ce_count[0],
1013 pvt->udimm_ce_count[1],
1014 pvt->udimm_ce_count[2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001015 data += count;
1016 total += count;
1017 } else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001018 for (i = 0; i < NUM_CHANS; i++) {
1019 count = sprintf(data, "channel %d RDIMM0: %lu "
1020 "RDIMM1: %lu RDIMM2: %lu\n",
1021 i,
1022 pvt->rdimm_ce_count[i][0],
1023 pvt->rdimm_ce_count[i][1],
1024 pvt->rdimm_ce_count[i][2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001025 data += count;
1026 total += count;
1027 }
1028 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001029
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001030 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001031}
1032
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001033/*
1034 * Sysfs struct
1035 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001036
1037
1038static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
1039 ATTR_ADDR_MATCH(channel),
1040 ATTR_ADDR_MATCH(dimm),
1041 ATTR_ADDR_MATCH(rank),
1042 ATTR_ADDR_MATCH(bank),
1043 ATTR_ADDR_MATCH(page),
1044 ATTR_ADDR_MATCH(col),
1045 { .attr = { .name = NULL } }
1046};
1047
1048
1049static struct mcidev_sysfs_group i7core_inject_addrmatch = {
1050 .name = "inject_addrmatch",
1051 .mcidev_attr = i7core_addrmatch_attrs,
1052};
1053
1054static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001055 {
1056 .attr = {
1057 .name = "inject_section",
1058 .mode = (S_IRUGO | S_IWUSR)
1059 },
1060 .show = i7core_inject_section_show,
1061 .store = i7core_inject_section_store,
1062 }, {
1063 .attr = {
1064 .name = "inject_type",
1065 .mode = (S_IRUGO | S_IWUSR)
1066 },
1067 .show = i7core_inject_type_show,
1068 .store = i7core_inject_type_store,
1069 }, {
1070 .attr = {
1071 .name = "inject_eccmask",
1072 .mode = (S_IRUGO | S_IWUSR)
1073 },
1074 .show = i7core_inject_eccmask_show,
1075 .store = i7core_inject_eccmask_store,
1076 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001077 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001078 }, {
1079 .attr = {
1080 .name = "inject_enable",
1081 .mode = (S_IRUGO | S_IWUSR)
1082 },
1083 .show = i7core_inject_enable_show,
1084 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001085 }, {
1086 .attr = {
1087 .name = "corrected_error_counts",
1088 .mode = (S_IRUGO | S_IWUSR)
1089 },
1090 .show = i7core_ce_regs_show,
1091 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001092 },
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001093 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001094};
1095
1096/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001097 Device initialization routines: put/get, init/exit
1098 ****************************************************************************/
1099
1100/*
1101 * i7core_put_devices 'put' all the devices that we have
1102 * reserved via 'get'
1103 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001104static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001105{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001106 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001107
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001108 debugf0(__FILE__ ": %s()\n", __func__);
1109 for (i = 0; i < N_DEVS; i++) {
1110 struct pci_dev *pdev = i7core_dev->pdev[i];
1111 if (!pdev)
1112 continue;
1113 debugf0("Removing dev %02x:%02x.%d\n",
1114 pdev->bus->number,
1115 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1116 pci_dev_put(pdev);
1117 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001118 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001119 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001120 kfree(i7core_dev);
1121}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001122
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001123static void i7core_put_all_devices(void)
1124{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001125 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001126
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001127 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001128 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001129}
1130
Keith Manntheybc2d7242009-09-03 00:05:05 -03001131static void i7core_xeon_pci_fixup(void)
1132{
1133 struct pci_dev *pdev = NULL;
1134 int i;
1135 /*
1136 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1137 * aren't announced by acpi. So, we need to use a legacy scan probing
1138 * to detect them
1139 */
1140 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001141 pci_dev_descr[0].dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001142 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001143 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001144 pcibios_scan_specific_bus(255-i);
1145 }
1146}
1147
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001148/*
1149 * i7core_get_devices Find and perform 'get' operation on the MCH's
1150 * device/functions we want to reference for this driver
1151 *
1152 * Need to 'get' device 16 func 1 and func 2
1153 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001154int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001155{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001156 struct i7core_dev *i7core_dev;
1157
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001158 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001159 u8 bus = 0;
1160 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001161
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001162 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001163 pci_dev_descr[devno].dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001164
1165 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001166 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1167 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1168 * to probe for the alternate address in case of failure
1169 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001170 if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001171 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1172 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001173
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001174 if (!pdev) {
1175 if (*prev) {
1176 *prev = pdev;
1177 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001178 }
1179
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001180 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001181 * Dev 3 function 2 only exists on chips with RDIMMs
1182 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001183 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001184 if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001185 *prev = pdev;
1186 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001187 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001188
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001189 i7core_printk(KERN_ERR,
1190 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001191 pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1192 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001193
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001194 /* End of list, leave */
1195 return -ENODEV;
1196 }
1197 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001198
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001199 if (bus == 0x3f)
1200 socket = 0;
1201 else
1202 socket = 255 - bus;
1203
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001204 i7core_dev = get_i7core_dev(socket);
1205 if (!i7core_dev) {
1206 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1207 if (!i7core_dev)
1208 return -ENOMEM;
1209 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
1210 GFP_KERNEL);
1211 if (!i7core_dev->pdev)
1212 return -ENOMEM;
1213 i7core_dev->socket = socket;
1214 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001215 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001216
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001217 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001218 i7core_printk(KERN_ERR,
1219 "Duplicated device for "
1220 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001221 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1222 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001223 pci_dev_put(pdev);
1224 return -ENODEV;
1225 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001226
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001227 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001228
1229 /* Sanity check */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001230 if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
1231 PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001232 i7core_printk(KERN_ERR,
1233 "Device PCI ID %04x:%04x "
1234 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001235 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001236 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001237 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001238 return -ENODEV;
1239 }
1240
1241 /* Be sure that the device is enabled */
1242 if (unlikely(pci_enable_device(pdev) < 0)) {
1243 i7core_printk(KERN_ERR,
1244 "Couldn't enable "
1245 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001246 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1247 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001248 return -ENODEV;
1249 }
1250
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001251 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1252 socket, bus, pci_dev_descr[devno].dev,
1253 pci_dev_descr[devno].func,
1254 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001255
1256 *prev = pdev;
1257
1258 return 0;
1259}
1260
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001261static int i7core_get_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001262{
1263 int i;
1264 struct pci_dev *pdev = NULL;
1265
1266 for (i = 0; i < N_DEVS; i++) {
1267 pdev = NULL;
1268 do {
1269 if (i7core_get_onedevice(&pdev, i) < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001270 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001271 return -ENODEV;
1272 }
1273 } while (pdev);
1274 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001275
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001276 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001277}
1278
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001279static int mci_bind_devs(struct mem_ctl_info *mci,
1280 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001281{
1282 struct i7core_pvt *pvt = mci->pvt_info;
1283 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001284 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001285
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001286 /* Associates i7core_dev and mci for future usage */
1287 pvt->i7core_dev = i7core_dev;
1288 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001289
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001290 pvt->is_registered = 0;
1291 for (i = 0; i < N_DEVS; i++) {
1292 pdev = i7core_dev->pdev[i];
1293 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001294 continue;
1295
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001296 func = PCI_FUNC(pdev->devfn);
1297 slot = PCI_SLOT(pdev->devfn);
1298 if (slot == 3) {
1299 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001300 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001301 pvt->pci_mcr[func] = pdev;
1302 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1303 if (unlikely(func > MAX_CHAN_FUNC))
1304 goto error;
1305 pvt->pci_ch[slot - 4][func] = pdev;
1306 } else if (!slot && !func)
1307 pvt->pci_noncore = pdev;
1308 else
1309 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001310
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001311 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1312 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1313 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001314
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001315 if (PCI_SLOT(pdev->devfn) == 3 &&
1316 PCI_FUNC(pdev->devfn) == 2)
1317 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001318 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001319
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001320 return 0;
1321
1322error:
1323 i7core_printk(KERN_ERR, "Device %d, function %d "
1324 "is out of the expected range\n",
1325 slot, func);
1326 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001327}
1328
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001329/****************************************************************************
1330 Error check routines
1331 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001332static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001333 int chan, int dimm, int add)
1334{
1335 char *msg;
1336 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001337 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001338
1339 for (i = 0; i < add; i++) {
1340 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001341 "(Socket=%d channel=%d dimm=%d)",
1342 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001343
1344 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1345 kfree (msg);
1346 }
1347}
1348
1349static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001350 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001351{
1352 struct i7core_pvt *pvt = mci->pvt_info;
1353 int add0 = 0, add1 = 0, add2 = 0;
1354 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001355 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001356 /* Updates CE counters */
1357
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001358 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1359 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1360 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001361
1362 if (add2 < 0)
1363 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001364 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001365
1366 if (add1 < 0)
1367 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001368 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001369
1370 if (add0 < 0)
1371 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001372 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001373 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001374 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001375
1376 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001377 pvt->rdimm_last_ce_count[chan][2] = new2;
1378 pvt->rdimm_last_ce_count[chan][1] = new1;
1379 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001380
1381 /*updated the edac core */
1382 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001383 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001384 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001385 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001386 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001387 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001388
1389}
1390
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001391static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001392{
1393 struct i7core_pvt *pvt = mci->pvt_info;
1394 u32 rcv[3][2];
1395 int i, new0, new1, new2;
1396
1397 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001398 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001399 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001400 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001401 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001402 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001403 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001404 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001405 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001406 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001407 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001408 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001409 &rcv[2][1]);
1410 for (i = 0 ; i < 3; i++) {
1411 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1412 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1413 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001414 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001415 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1416 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1417 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1418 } else {
1419 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1420 DIMM_BOT_COR_ERR(rcv[i][0]);
1421 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1422 DIMM_BOT_COR_ERR(rcv[i][1]);
1423 new2 = 0;
1424 }
1425
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001426 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001427 }
1428}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001429
1430/* This function is based on the device 3 function 4 registers as described on:
1431 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1432 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1433 * also available at:
1434 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1435 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001436static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001437{
1438 struct i7core_pvt *pvt = mci->pvt_info;
1439 u32 rcv1, rcv0;
1440 int new0, new1, new2;
1441
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001442 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001443 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001444 return;
1445 }
1446
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001447 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001448 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1449 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001450
1451 /* Store the new values */
1452 new2 = DIMM2_COR_ERR(rcv1);
1453 new1 = DIMM1_COR_ERR(rcv0);
1454 new0 = DIMM0_COR_ERR(rcv0);
1455
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001456 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001457 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001458 /* Updates CE counters */
1459 int add0, add1, add2;
1460
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001461 add2 = new2 - pvt->udimm_last_ce_count[2];
1462 add1 = new1 - pvt->udimm_last_ce_count[1];
1463 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001464
1465 if (add2 < 0)
1466 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001467 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001468
1469 if (add1 < 0)
1470 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001471 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001472
1473 if (add0 < 0)
1474 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001475 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001476
1477 if (add0 | add1 | add2)
1478 i7core_printk(KERN_ERR, "New Corrected error(s): "
1479 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1480 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001481 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001482 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001483
1484 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001485 pvt->udimm_last_ce_count[2] = new2;
1486 pvt->udimm_last_ce_count[1] = new1;
1487 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001488}
1489
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001490/*
1491 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1492 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001493 * Nehalem are defined as family 0x06, model 0x1a
1494 *
1495 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001496 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001497 * m->status MSR_IA32_MC8_STATUS
1498 * m->addr MSR_IA32_MC8_ADDR
1499 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001500 * In the case of Nehalem, the error information is masked at .status and .misc
1501 * fields
1502 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001503static void i7core_mce_output_error(struct mem_ctl_info *mci,
1504 struct mce *m)
1505{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001506 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001507 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001508 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001509 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001510 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1511 u32 dimm = (m->misc >> 16) & 0x3;
1512 u32 channel = (m->misc >> 18) & 0x3;
1513 u32 syndrome = m->misc >> 32;
1514 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001515 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001516
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001517 if (m->mcgstatus & 1)
1518 type = "FATAL";
1519 else
1520 type = "NON_FATAL";
1521
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001522 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001523 case 0:
1524 optype = "generic undef request";
1525 break;
1526 case 1:
1527 optype = "read error";
1528 break;
1529 case 2:
1530 optype = "write error";
1531 break;
1532 case 3:
1533 optype = "addr/cmd error";
1534 break;
1535 case 4:
1536 optype = "scrubbing error";
1537 break;
1538 default:
1539 optype = "reserved";
1540 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001541 }
1542
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001543 switch (errnum) {
1544 case 16:
1545 err = "read ECC error";
1546 break;
1547 case 17:
1548 err = "RAS ECC error";
1549 break;
1550 case 18:
1551 err = "write parity error";
1552 break;
1553 case 19:
1554 err = "redundacy loss";
1555 break;
1556 case 20:
1557 err = "reserved";
1558 break;
1559 case 21:
1560 err = "memory range error";
1561 break;
1562 case 22:
1563 err = "RTID out of range";
1564 break;
1565 case 23:
1566 err = "address parity error";
1567 break;
1568 case 24:
1569 err = "byte enable parity error";
1570 break;
1571 default:
1572 err = "unknown";
1573 }
1574
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001575 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001576 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001577 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001578 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001579 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001580 syndrome, core_err_cnt, (long long)m->status,
1581 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001582
1583 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001584
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001585 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001586
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001587 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001588 if (m->mcgstatus & 1)
1589 edac_mc_handle_fbd_ue(mci, csrow, 0,
1590 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001591 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001592 edac_mc_handle_fbd_ce(mci, csrow,
1593 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001594
1595 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001596}
1597
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001598/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001599 * i7core_check_error Retrieve and process errors reported by the
1600 * hardware. Called by the Core module.
1601 */
1602static void i7core_check_error(struct mem_ctl_info *mci)
1603{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001604 struct i7core_pvt *pvt = mci->pvt_info;
1605 int i;
1606 unsigned count = 0;
1607 struct mce *m = NULL;
1608 unsigned long flags;
1609
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001610 /* Copy all mce errors into a temporary buffer */
1611 spin_lock_irqsave(&pvt->mce_lock, flags);
1612 if (pvt->mce_count) {
1613 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001614
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001615 if (m) {
1616 count = pvt->mce_count;
1617 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1618 }
1619 pvt->mce_count = 0;
1620 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001621
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001622 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1623
1624 /* proccess mcelog errors */
1625 for (i = 0; i < count; i++)
1626 i7core_mce_output_error(mci, &m[i]);
1627
1628 kfree(m);
1629
1630 /* check memory count errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001631 if (!pvt->is_registered)
1632 i7core_udimm_check_mc_ecc_err(mci);
1633 else
1634 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001635}
1636
1637/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001638 * i7core_mce_check_error Replicates mcelog routine to get errors
1639 * This routine simply queues mcelog errors, and
1640 * return. The error itself should be handled later
1641 * by i7core_check_error.
1642 */
1643static int i7core_mce_check_error(void *priv, struct mce *mce)
1644{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001645 struct mem_ctl_info *mci = priv;
1646 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001647 unsigned long flags;
1648
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001649 /*
1650 * Just let mcelog handle it if the error is
1651 * outside the memory controller
1652 */
1653 if (((mce->status & 0xffff) >> 7) != 1)
1654 return 0;
1655
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001656 /* Bank 8 registers are the only ones that we know how to handle */
1657 if (mce->bank != 8)
1658 return 0;
1659
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001660 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001661 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) {
1662 debugf0("mc%d: ignoring mce log for socket %d. "
1663 "Another mc should get it.\n",
1664 pvt->i7core_dev->socket,
1665 cpu_data(mce->cpu).phys_proc_id);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001666 return 0;
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001667 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001668
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001669 spin_lock_irqsave(&pvt->mce_lock, flags);
1670 if (pvt->mce_count < MCE_LOG_LEN) {
1671 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1672 pvt->mce_count++;
1673 }
1674 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1675
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001676 /* Handle fatal errors immediately */
1677 if (mce->mcgstatus & 1)
1678 i7core_check_error(mci);
1679
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001680 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001681 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001682}
1683
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001684static int i7core_register_mci(struct i7core_dev *i7core_dev,
1685 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001686{
1687 struct mem_ctl_info *mci;
1688 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001689 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001690 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001691
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001692 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001693 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1694 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001695 if (unlikely(!mci))
1696 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001697
1698 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1699
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001700 /* record ptr to the generic device */
1701 mci->dev = &i7core_dev->pdev[0]->dev;
1702
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001703 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001704 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001705
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001706 /*
1707 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1708 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1709 * memory channels
1710 */
1711 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001712 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1713 mci->edac_cap = EDAC_FLAG_NONE;
1714 mci->mod_name = "i7core_edac.c";
1715 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001716 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1717 i7core_dev->socket);
1718 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001719 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001720 mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001721 /* Set the function pointer to an actual operation function */
1722 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001723
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001724 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001725 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001726 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001727 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001728
1729 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001730 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001731
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001732 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001733 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001734 debugf0("MC: " __FILE__
1735 ": %s(): failed edac_mc_add_mc()\n", __func__);
1736 /* FIXME: perhaps some code should go here that disables error
1737 * reporting if we just enabled it
1738 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001739
1740 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001741 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001742 }
1743
1744 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001745 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1746 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001747 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001748 printk(KERN_WARNING
1749 "%s(): Unable to create PCI control\n",
1750 __func__);
1751 printk(KERN_WARNING
1752 "%s(): PCI error report via EDAC not setup\n",
1753 __func__);
1754 }
1755
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001756 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001757 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001758 pvt->inject.dimm = -1;
1759 pvt->inject.rank = -1;
1760 pvt->inject.bank = -1;
1761 pvt->inject.page = -1;
1762 pvt->inject.col = -1;
1763
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001764 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001765 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001766 pvt->edac_mce.check_error = i7core_mce_check_error;
1767 spin_lock_init(&pvt->mce_lock);
1768
1769 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001770 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001771 debugf0("MC: " __FILE__
1772 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001773 }
1774
1775fail:
1776 edac_mc_free(mci);
1777 return rc;
1778}
1779
1780/*
1781 * i7core_probe Probe for ONE instance of device to see if it is
1782 * present.
1783 * return:
1784 * 0 for FOUND a device
1785 * < 0 for error code
1786 */
1787static int __devinit i7core_probe(struct pci_dev *pdev,
1788 const struct pci_device_id *id)
1789{
1790 int dev_idx = id->driver_data;
1791 int rc;
1792 struct i7core_dev *i7core_dev;
1793
1794 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001795 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001796 */
1797 if (unlikely(dev_idx >= 1))
1798 return -EINVAL;
1799
1800 /* get the pci devices we want to reserve for our use */
1801 mutex_lock(&i7core_edac_lock);
1802 rc = i7core_get_devices();
1803 if (unlikely(rc < 0))
1804 goto fail0;
1805
1806 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1807 int channels;
1808 int csrows;
1809
1810 /* Check the number of active and not disabled channels */
1811 rc = i7core_get_active_channels(i7core_dev->socket,
1812 &channels, &csrows);
1813 if (unlikely(rc < 0))
1814 goto fail1;
1815
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001816 rc = i7core_register_mci(i7core_dev, channels, csrows);
1817 if (unlikely(rc < 0))
1818 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001819 }
1820
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001821 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001822
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001823 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001824 return 0;
1825
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001826fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001827 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001828fail0:
1829 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001830 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001831}
1832
1833/*
1834 * i7core_remove destructor for one instance of device
1835 *
1836 */
1837static void __devexit i7core_remove(struct pci_dev *pdev)
1838{
1839 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001840 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001841
1842 debugf0(__FILE__ ": %s()\n", __func__);
1843
1844 if (i7core_pci)
1845 edac_pci_release_generic_ctl(i7core_pci);
1846
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001847 /*
1848 * we have a trouble here: pdev value for removal will be wrong, since
1849 * it will point to the X58 register used to detect that the machine
1850 * is a Nehalem or upper design. However, due to the way several PCI
1851 * devices are grouped together to provide MC functionality, we need
1852 * to use a different method for releasing the devices
1853 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001854
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001855 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001856 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1857 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1858 if (mci) {
1859 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001860
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001861 i7core_dev = pvt->i7core_dev;
1862 edac_mce_unregister(&pvt->edac_mce);
1863 kfree(mci->ctl_name);
1864 edac_mc_free(mci);
1865 i7core_put_devices(i7core_dev);
1866 } else {
1867 i7core_printk(KERN_ERR,
1868 "Couldn't find mci for socket %d\n",
1869 i7core_dev->socket);
1870 }
1871 }
1872 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001873}
1874
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001875MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1876
1877/*
1878 * i7core_driver pci_driver structure for this module
1879 *
1880 */
1881static struct pci_driver i7core_driver = {
1882 .name = "i7core_edac",
1883 .probe = i7core_probe,
1884 .remove = __devexit_p(i7core_remove),
1885 .id_table = i7core_pci_tbl,
1886};
1887
1888/*
1889 * i7core_init Module entry function
1890 * Try to initialize this module for its devices
1891 */
1892static int __init i7core_init(void)
1893{
1894 int pci_rc;
1895
1896 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1897
1898 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1899 opstate_init();
1900
Keith Manntheybc2d7242009-09-03 00:05:05 -03001901 i7core_xeon_pci_fixup();
1902
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001903 pci_rc = pci_register_driver(&i7core_driver);
1904
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001905 if (pci_rc >= 0)
1906 return 0;
1907
1908 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1909 pci_rc);
1910
1911 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001912}
1913
1914/*
1915 * i7core_exit() Module exit function
1916 * Unregister the driver
1917 */
1918static void __exit i7core_exit(void)
1919{
1920 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1921 pci_unregister_driver(&i7core_driver);
1922}
1923
1924module_init(i7core_init);
1925module_exit(i7core_exit);
1926
1927MODULE_LICENSE("GPL");
1928MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1929MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1930MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1931 I7CORE_REVISION);
1932
1933module_param(edac_op_state, int, 0444);
1934MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");