blob: 391348bf93d2bfa028570cae81b8cf8e322237cf [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 Chehabf4742942009-09-05 02:35:08 -0300474 u8 socket = pvt->i7core_dev->socket;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300475 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300476 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300477 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300478
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300479 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300480 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300481 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300482 return -ENODEV;
483
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300484 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300485 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
486 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
487 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
488 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300489
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300490 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
491 socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300492 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300493
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300494 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300495 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300496 if (ECCx8(pvt))
497 mode = EDAC_S8ECD8ED;
498 else
499 mode = EDAC_S4ECD4ED;
500 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300501 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300502 mode = EDAC_NONE;
503 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300504
505 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300506 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
507 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300508 numdimms(pvt->info.max_dod),
509 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300510 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300511 numrow(pvt->info.max_dod >> 6),
512 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300513
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300514 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300515 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300516
517 if (!CH_ACTIVE(pvt, i)) {
518 debugf0("Channel %i is not active\n", i);
519 continue;
520 }
521 if (CH_DISABLED(pvt, i)) {
522 debugf0("Channel %i is disabled\n", i);
523 continue;
524 }
525
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300526 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300527 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300528 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
529
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300530 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300531 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300532
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300533 if (data & REGISTERED_DIMM)
534 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300535 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300536 mtype = MEM_DDR3;
537#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300538 if (data & THREE_DIMMS_PRESENT)
539 pvt->channel[i].dimms = 3;
540 else if (data & SINGLE_QUAD_RANK_PRESENT)
541 pvt->channel[i].dimms = 1;
542 else
543 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300544#endif
545
546 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300547 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300548 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300549 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300550 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300551 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300552 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300553
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300554 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300555 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300556 i,
557 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
558 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300559 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300560 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300561
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300562 for (j = 0; j < 3; j++) {
563 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300564 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300565
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300566 if (!DIMM_PRESENT(dimm_dod[j]))
567 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300568
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300569 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
570 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
571 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
572 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300573
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300574 /* DDR3 has 8 I/O banks */
575 size = (rows * cols * banks * ranks) >> (20 - 3);
576
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300577 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300578
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300579 debugf0("\tdimm %d %d Mb offset: %x, "
580 "bank: %d, rank: %d, row: %#x, col: %#x\n",
581 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300582 RANKOFFSET(dimm_dod[j]),
583 banks, ranks, rows, cols);
584
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300585#if PAGE_SHIFT > 20
586 npages = size >> (PAGE_SHIFT - 20);
587#else
588 npages = size << (20 - PAGE_SHIFT);
589#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300590
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300591 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300592 csr->first_page = last_page + 1;
593 last_page += npages;
594 csr->last_page = last_page;
595 csr->nr_pages = npages;
596
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300597 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300598 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300599 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300600 csr->nr_channels = 1;
601
602 csr->channels[0].chan_idx = i;
603 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300604
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300605 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300606
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300607 switch (banks) {
608 case 4:
609 csr->dtype = DEV_X4;
610 break;
611 case 8:
612 csr->dtype = DEV_X8;
613 break;
614 case 16:
615 csr->dtype = DEV_X16;
616 break;
617 default:
618 csr->dtype = DEV_UNKNOWN;
619 }
620
621 csr->edac_mode = mode;
622 csr->mtype = mtype;
623
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300624 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300625 }
626
627 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
628 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
629 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
630 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
631 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
632 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
633 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
634 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300635 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300636 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300637 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300638 (value[j] >> 27) & 0x1,
639 (value[j] >> 24) & 0x7,
640 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300641 }
642
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300643 return 0;
644}
645
646/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300647 Error insertion routines
648 ****************************************************************************/
649
650/* The i7core has independent error injection features per channel.
651 However, to have a simpler code, we don't allow enabling error injection
652 on more than one channel.
653 Also, since a change at an inject parameter will be applied only at enable,
654 we're disabling error injection on all write calls to the sysfs nodes that
655 controls the error code injection.
656 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300657static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300658{
659 struct i7core_pvt *pvt = mci->pvt_info;
660
661 pvt->inject.enable = 0;
662
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300663 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300664 return -ENODEV;
665
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300666 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300667 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300668
669 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300670}
671
672/*
673 * i7core inject inject.section
674 *
675 * accept and store error injection inject.section value
676 * bit 0 - refers to the lower 32-byte half cacheline
677 * bit 1 - refers to the upper 32-byte half cacheline
678 */
679static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
680 const char *data, size_t count)
681{
682 struct i7core_pvt *pvt = mci->pvt_info;
683 unsigned long value;
684 int rc;
685
686 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300687 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300688
689 rc = strict_strtoul(data, 10, &value);
690 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300691 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300692
693 pvt->inject.section = (u32) value;
694 return count;
695}
696
697static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
698 char *data)
699{
700 struct i7core_pvt *pvt = mci->pvt_info;
701 return sprintf(data, "0x%08x\n", pvt->inject.section);
702}
703
704/*
705 * i7core inject.type
706 *
707 * accept and store error injection inject.section value
708 * bit 0 - repeat enable - Enable error repetition
709 * bit 1 - inject ECC error
710 * bit 2 - inject parity error
711 */
712static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
713 const char *data, size_t count)
714{
715 struct i7core_pvt *pvt = mci->pvt_info;
716 unsigned long value;
717 int rc;
718
719 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300720 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300721
722 rc = strict_strtoul(data, 10, &value);
723 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300724 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300725
726 pvt->inject.type = (u32) value;
727 return count;
728}
729
730static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
731 char *data)
732{
733 struct i7core_pvt *pvt = mci->pvt_info;
734 return sprintf(data, "0x%08x\n", pvt->inject.type);
735}
736
737/*
738 * i7core_inject_inject.eccmask_store
739 *
740 * The type of error (UE/CE) will depend on the inject.eccmask value:
741 * Any bits set to a 1 will flip the corresponding ECC bit
742 * Correctable errors can be injected by flipping 1 bit or the bits within
743 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
744 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
745 * uncorrectable error to be injected.
746 */
747static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
748 const char *data, size_t count)
749{
750 struct i7core_pvt *pvt = mci->pvt_info;
751 unsigned long value;
752 int rc;
753
754 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300755 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300756
757 rc = strict_strtoul(data, 10, &value);
758 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300759 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300760
761 pvt->inject.eccmask = (u32) value;
762 return count;
763}
764
765static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
766 char *data)
767{
768 struct i7core_pvt *pvt = mci->pvt_info;
769 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
770}
771
772/*
773 * i7core_addrmatch
774 *
775 * The type of error (UE/CE) will depend on the inject.eccmask value:
776 * Any bits set to a 1 will flip the corresponding ECC bit
777 * Correctable errors can be injected by flipping 1 bit or the bits within
778 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
779 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
780 * uncorrectable error to be injected.
781 */
782static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
783 const char *data, size_t count)
784{
785 struct i7core_pvt *pvt = mci->pvt_info;
786 char *cmd, *val;
787 long value;
788 int rc;
789
790 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300791 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300792
793 do {
794 cmd = strsep((char **) &data, ":");
795 if (!cmd)
796 break;
797 val = strsep((char **) &data, " \n\t");
798 if (!val)
799 return cmd - data;
800
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300801 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300802 value = -1;
803 else {
804 rc = strict_strtol(val, 10, &value);
805 if ((rc < 0) || (value < 0))
806 return cmd - data;
807 }
808
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300809 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300810 if (value < 3)
811 pvt->inject.channel = value;
812 else
813 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300814 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300815 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300816 pvt->inject.dimm = value;
817 else
818 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300819 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300820 if (value < 4)
821 pvt->inject.rank = value;
822 else
823 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300824 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300825 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300826 pvt->inject.bank = value;
827 else
828 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300829 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300830 if (value <= 0xffff)
831 pvt->inject.page = value;
832 else
833 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300834 } else if (!strcasecmp(cmd, "col") ||
835 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300836 if (value <= 0x3fff)
837 pvt->inject.col = value;
838 else
839 return cmd - data;
840 }
841 } while (1);
842
843 return count;
844}
845
846static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
847 char *data)
848{
849 struct i7core_pvt *pvt = mci->pvt_info;
850 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
851
852 if (pvt->inject.channel < 0)
853 sprintf(channel, "any");
854 else
855 sprintf(channel, "%d", pvt->inject.channel);
856 if (pvt->inject.dimm < 0)
857 sprintf(dimm, "any");
858 else
859 sprintf(dimm, "%d", pvt->inject.dimm);
860 if (pvt->inject.bank < 0)
861 sprintf(bank, "any");
862 else
863 sprintf(bank, "%d", pvt->inject.bank);
864 if (pvt->inject.rank < 0)
865 sprintf(rank, "any");
866 else
867 sprintf(rank, "%d", pvt->inject.rank);
868 if (pvt->inject.page < 0)
869 sprintf(page, "any");
870 else
871 sprintf(page, "0x%04x", pvt->inject.page);
872 if (pvt->inject.col < 0)
873 sprintf(col, "any");
874 else
875 sprintf(col, "0x%04x", pvt->inject.col);
876
877 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
878 "rank: %s\npage: %s\ncolumn: %s\n",
879 channel, dimm, bank, rank, page, col);
880}
881
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300882static int write_and_test(struct pci_dev *dev, int where, u32 val)
883{
884 u32 read;
885 int count;
886
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300887 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
888 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
889 where, val);
890
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300891 for (count = 0; count < 10; count++) {
892 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300893 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300894 pci_write_config_dword(dev, where, val);
895 pci_read_config_dword(dev, where, &read);
896
897 if (read == val)
898 return 0;
899 }
900
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300901 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
902 "write=%08x. Read=%08x\n",
903 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
904 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300905
906 return -EINVAL;
907}
908
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300909/*
910 * This routine prepares the Memory Controller for error injection.
911 * The error will be injected when some process tries to write to the
912 * memory that matches the given criteria.
913 * The criteria can be set in terms of a mask where dimm, rank, bank, page
914 * and col can be specified.
915 * A -1 value for any of the mask items will make the MCU to ignore
916 * that matching criteria for error injection.
917 *
918 * It should be noticed that the error will only happen after a write operation
919 * on a memory that matches the condition. if REPEAT_EN is not enabled at
920 * inject mask, then it will produce just one error. Otherwise, it will repeat
921 * until the injectmask would be cleaned.
922 *
923 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
924 * is reliable enough to check if the MC is using the
925 * three channels. However, this is not clear at the datasheet.
926 */
927static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
928 const char *data, size_t count)
929{
930 struct i7core_pvt *pvt = mci->pvt_info;
931 u32 injectmask;
932 u64 mask = 0;
933 int rc;
934 long enable;
935
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300936 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300937 return 0;
938
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300939 rc = strict_strtoul(data, 10, &enable);
940 if ((rc < 0))
941 return 0;
942
943 if (enable) {
944 pvt->inject.enable = 1;
945 } else {
946 disable_inject(mci);
947 return count;
948 }
949
950 /* Sets pvt->inject.dimm mask */
951 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300952 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300953 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300954 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300955 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300956 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300957 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300958 }
959
960 /* Sets pvt->inject.rank mask */
961 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300962 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300963 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300964 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300965 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300966 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300967 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300968 }
969
970 /* Sets pvt->inject.bank mask */
971 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300972 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300973 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300974 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300975
976 /* Sets pvt->inject.page mask */
977 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300978 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300979 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300980 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300981
982 /* Sets pvt->inject.column mask */
983 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300984 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300985 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300986 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300987
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300988 /*
989 * bit 0: REPEAT_EN
990 * bits 1-2: MASK_HALF_CACHELINE
991 * bit 3: INJECT_ECC
992 * bit 4: INJECT_ADDR_PARITY
993 */
994
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300995 injectmask = (pvt->inject.type & 1) |
996 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300997 (pvt->inject.type & 0x6) << (3 - 1);
998
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300999 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001000 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001001 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001002
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001003 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001004 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001005 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001006 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1007
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001008 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001009 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1010
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001011 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001012 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001013
1014 /*
1015 * This is something undocumented, based on my tests
1016 * Without writing 8 to this register, errors aren't injected. Not sure
1017 * why.
1018 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001019 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001020 MC_CFG_CONTROL, 8);
1021
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001022 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1023 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001024 mask, pvt->inject.eccmask, injectmask);
1025
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001026
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001027 return count;
1028}
1029
1030static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1031 char *data)
1032{
1033 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001034 u32 injectmask;
1035
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001036 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001037 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001038
1039 debugf0("Inject error read: 0x%018x\n", injectmask);
1040
1041 if (injectmask & 0x0c)
1042 pvt->inject.enable = 1;
1043
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001044 return sprintf(data, "%d\n", pvt->inject.enable);
1045}
1046
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001047static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1048{
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001049 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001050 struct i7core_pvt *pvt = mci->pvt_info;
1051
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001052 if (!pvt->ce_count_available) {
1053 count = sprintf(data, "data unavailable\n");
1054 return 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001055 }
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001056 if (!pvt->is_registered) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001057 count = sprintf(data, "all channels "
1058 "UDIMM0: %lu UDIMM1: %lu UDIMM2: %lu\n",
1059 pvt->udimm_ce_count[0],
1060 pvt->udimm_ce_count[1],
1061 pvt->udimm_ce_count[2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001062 data += count;
1063 total += count;
1064 } else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001065 for (i = 0; i < NUM_CHANS; i++) {
1066 count = sprintf(data, "channel %d RDIMM0: %lu "
1067 "RDIMM1: %lu RDIMM2: %lu\n",
1068 i,
1069 pvt->rdimm_ce_count[i][0],
1070 pvt->rdimm_ce_count[i][1],
1071 pvt->rdimm_ce_count[i][2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001072 data += count;
1073 total += count;
1074 }
1075 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001076
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001077 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001078}
1079
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001080/*
1081 * Sysfs struct
1082 */
1083static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001084 {
1085 .attr = {
1086 .name = "inject_section",
1087 .mode = (S_IRUGO | S_IWUSR)
1088 },
1089 .show = i7core_inject_section_show,
1090 .store = i7core_inject_section_store,
1091 }, {
1092 .attr = {
1093 .name = "inject_type",
1094 .mode = (S_IRUGO | S_IWUSR)
1095 },
1096 .show = i7core_inject_type_show,
1097 .store = i7core_inject_type_store,
1098 }, {
1099 .attr = {
1100 .name = "inject_eccmask",
1101 .mode = (S_IRUGO | S_IWUSR)
1102 },
1103 .show = i7core_inject_eccmask_show,
1104 .store = i7core_inject_eccmask_store,
1105 }, {
1106 .attr = {
1107 .name = "inject_addrmatch",
1108 .mode = (S_IRUGO | S_IWUSR)
1109 },
1110 .show = i7core_inject_addrmatch_show,
1111 .store = i7core_inject_addrmatch_store,
1112 }, {
1113 .attr = {
1114 .name = "inject_enable",
1115 .mode = (S_IRUGO | S_IWUSR)
1116 },
1117 .show = i7core_inject_enable_show,
1118 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001119 }, {
1120 .attr = {
1121 .name = "corrected_error_counts",
1122 .mode = (S_IRUGO | S_IWUSR)
1123 },
1124 .show = i7core_ce_regs_show,
1125 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001126 },
1127};
1128
1129/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001130 Device initialization routines: put/get, init/exit
1131 ****************************************************************************/
1132
1133/*
1134 * i7core_put_devices 'put' all the devices that we have
1135 * reserved via 'get'
1136 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001137static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001138{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001139 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001140
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001141 for (i = 0; i < N_DEVS; i++)
1142 pci_dev_put(i7core_dev->pdev[i]);
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001143
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001144 list_del(&i7core_dev->list);
1145 kfree(i7core_dev->pdev);
1146 kfree(i7core_dev);
1147}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001148
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001149static void i7core_put_all_devices(void)
1150{
1151 struct i7core_dev *i7core_dev;
1152
1153 list_for_each_entry(i7core_dev, &i7core_edac_list, list)
1154 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001155}
1156
Keith Manntheybc2d7242009-09-03 00:05:05 -03001157static void i7core_xeon_pci_fixup(void)
1158{
1159 struct pci_dev *pdev = NULL;
1160 int i;
1161 /*
1162 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1163 * aren't announced by acpi. So, we need to use a legacy scan probing
1164 * to detect them
1165 */
1166 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001167 pci_dev_descr[0].dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001168 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001169 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001170 pcibios_scan_specific_bus(255-i);
1171 }
1172}
1173
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001174/*
1175 * i7core_get_devices Find and perform 'get' operation on the MCH's
1176 * device/functions we want to reference for this driver
1177 *
1178 * Need to 'get' device 16 func 1 and func 2
1179 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001180int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001181{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001182 struct i7core_dev *i7core_dev;
1183
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001184 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001185 u8 bus = 0;
1186 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001187
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001188 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001189 pci_dev_descr[devno].dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001190
1191 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001192 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1193 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1194 * to probe for the alternate address in case of failure
1195 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001196 if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001197 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1198 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001199
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001200 if (!pdev) {
1201 if (*prev) {
1202 *prev = pdev;
1203 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001204 }
1205
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001206 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001207 * Dev 3 function 2 only exists on chips with RDIMMs
1208 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001209 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001210 if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001211 *prev = pdev;
1212 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001213 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001214
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001215 i7core_printk(KERN_ERR,
1216 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001217 pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1218 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001219
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001220 /* End of list, leave */
1221 return -ENODEV;
1222 }
1223 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001224
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001225 if (bus == 0x3f)
1226 socket = 0;
1227 else
1228 socket = 255 - bus;
1229
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001230 i7core_dev = get_i7core_dev(socket);
1231 if (!i7core_dev) {
1232 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1233 if (!i7core_dev)
1234 return -ENOMEM;
1235 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
1236 GFP_KERNEL);
1237 if (!i7core_dev->pdev)
1238 return -ENOMEM;
1239 i7core_dev->socket = socket;
1240 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001241 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001242
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001243 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001244 i7core_printk(KERN_ERR,
1245 "Duplicated device for "
1246 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001247 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1248 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001249 pci_dev_put(pdev);
1250 return -ENODEV;
1251 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001252
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001253 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001254
1255 /* Sanity check */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001256 if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
1257 PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001258 i7core_printk(KERN_ERR,
1259 "Device PCI ID %04x:%04x "
1260 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001261 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001262 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001263 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001264 return -ENODEV;
1265 }
1266
1267 /* Be sure that the device is enabled */
1268 if (unlikely(pci_enable_device(pdev) < 0)) {
1269 i7core_printk(KERN_ERR,
1270 "Couldn't enable "
1271 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001272 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1273 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001274 return -ENODEV;
1275 }
1276
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001277 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1278 socket, bus, pci_dev_descr[devno].dev,
1279 pci_dev_descr[devno].func,
1280 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001281
1282 *prev = pdev;
1283
1284 return 0;
1285}
1286
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001287static int i7core_get_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001288{
1289 int i;
1290 struct pci_dev *pdev = NULL;
1291
1292 for (i = 0; i < N_DEVS; i++) {
1293 pdev = NULL;
1294 do {
1295 if (i7core_get_onedevice(&pdev, i) < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001296 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001297 return -ENODEV;
1298 }
1299 } while (pdev);
1300 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001301
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001302 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001303}
1304
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001305static int mci_bind_devs(struct mem_ctl_info *mci,
1306 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001307{
1308 struct i7core_pvt *pvt = mci->pvt_info;
1309 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001310 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001311
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001312 /* Associates i7core_dev and mci for future usage */
1313 pvt->i7core_dev = i7core_dev;
1314 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001315
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001316 pvt->is_registered = 0;
1317 for (i = 0; i < N_DEVS; i++) {
1318 pdev = i7core_dev->pdev[i];
1319 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001320 continue;
1321
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001322 func = PCI_FUNC(pdev->devfn);
1323 slot = PCI_SLOT(pdev->devfn);
1324 if (slot == 3) {
1325 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001326 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001327 pvt->pci_mcr[func] = pdev;
1328 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1329 if (unlikely(func > MAX_CHAN_FUNC))
1330 goto error;
1331 pvt->pci_ch[slot - 4][func] = pdev;
1332 } else if (!slot && !func)
1333 pvt->pci_noncore = pdev;
1334 else
1335 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001336
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001337 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1338 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1339 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001340
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001341 if (PCI_SLOT(pdev->devfn) == 3 &&
1342 PCI_FUNC(pdev->devfn) == 2)
1343 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001344 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001345
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001346 return 0;
1347
1348error:
1349 i7core_printk(KERN_ERR, "Device %d, function %d "
1350 "is out of the expected range\n",
1351 slot, func);
1352 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001353}
1354
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001355/****************************************************************************
1356 Error check routines
1357 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001358static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001359 int chan, int dimm, int add)
1360{
1361 char *msg;
1362 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001363 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001364
1365 for (i = 0; i < add; i++) {
1366 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001367 "(Socket=%d channel=%d dimm=%d)",
1368 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001369
1370 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1371 kfree (msg);
1372 }
1373}
1374
1375static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001376 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001377{
1378 struct i7core_pvt *pvt = mci->pvt_info;
1379 int add0 = 0, add1 = 0, add2 = 0;
1380 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001381 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001382 /* Updates CE counters */
1383
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001384 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1385 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1386 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001387
1388 if (add2 < 0)
1389 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001390 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001391
1392 if (add1 < 0)
1393 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001394 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001395
1396 if (add0 < 0)
1397 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001398 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001399 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001400 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001401
1402 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001403 pvt->rdimm_last_ce_count[chan][2] = new2;
1404 pvt->rdimm_last_ce_count[chan][1] = new1;
1405 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001406
1407 /*updated the edac core */
1408 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001409 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001410 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001411 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001412 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001413 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001414
1415}
1416
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001417static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001418{
1419 struct i7core_pvt *pvt = mci->pvt_info;
1420 u32 rcv[3][2];
1421 int i, new0, new1, new2;
1422
1423 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001424 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001425 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001426 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001427 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001428 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001429 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001430 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001431 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001432 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001433 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001434 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001435 &rcv[2][1]);
1436 for (i = 0 ; i < 3; i++) {
1437 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1438 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1439 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001440 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001441 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1442 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1443 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1444 } else {
1445 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1446 DIMM_BOT_COR_ERR(rcv[i][0]);
1447 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1448 DIMM_BOT_COR_ERR(rcv[i][1]);
1449 new2 = 0;
1450 }
1451
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001452 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001453 }
1454}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001455
1456/* This function is based on the device 3 function 4 registers as described on:
1457 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1458 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1459 * also available at:
1460 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1461 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001462static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001463{
1464 struct i7core_pvt *pvt = mci->pvt_info;
1465 u32 rcv1, rcv0;
1466 int new0, new1, new2;
1467
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001468 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001469 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001470 return;
1471 }
1472
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001473 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001474 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1475 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001476
1477 /* Store the new values */
1478 new2 = DIMM2_COR_ERR(rcv1);
1479 new1 = DIMM1_COR_ERR(rcv0);
1480 new0 = DIMM0_COR_ERR(rcv0);
1481
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001482 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001483 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001484 /* Updates CE counters */
1485 int add0, add1, add2;
1486
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001487 add2 = new2 - pvt->udimm_last_ce_count[2];
1488 add1 = new1 - pvt->udimm_last_ce_count[1];
1489 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001490
1491 if (add2 < 0)
1492 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001493 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001494
1495 if (add1 < 0)
1496 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001497 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001498
1499 if (add0 < 0)
1500 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001501 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001502
1503 if (add0 | add1 | add2)
1504 i7core_printk(KERN_ERR, "New Corrected error(s): "
1505 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1506 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001507 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001508 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001509
1510 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001511 pvt->udimm_last_ce_count[2] = new2;
1512 pvt->udimm_last_ce_count[1] = new1;
1513 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001514}
1515
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001516/*
1517 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1518 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001519 * Nehalem are defined as family 0x06, model 0x1a
1520 *
1521 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001522 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001523 * m->status MSR_IA32_MC8_STATUS
1524 * m->addr MSR_IA32_MC8_ADDR
1525 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001526 * In the case of Nehalem, the error information is masked at .status and .misc
1527 * fields
1528 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001529static void i7core_mce_output_error(struct mem_ctl_info *mci,
1530 struct mce *m)
1531{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001532 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001533 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001534 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001535 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001536 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1537 u32 dimm = (m->misc >> 16) & 0x3;
1538 u32 channel = (m->misc >> 18) & 0x3;
1539 u32 syndrome = m->misc >> 32;
1540 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001541 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001542
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001543 if (m->mcgstatus & 1)
1544 type = "FATAL";
1545 else
1546 type = "NON_FATAL";
1547
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001548 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001549 case 0:
1550 optype = "generic undef request";
1551 break;
1552 case 1:
1553 optype = "read error";
1554 break;
1555 case 2:
1556 optype = "write error";
1557 break;
1558 case 3:
1559 optype = "addr/cmd error";
1560 break;
1561 case 4:
1562 optype = "scrubbing error";
1563 break;
1564 default:
1565 optype = "reserved";
1566 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001567 }
1568
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001569 switch (errnum) {
1570 case 16:
1571 err = "read ECC error";
1572 break;
1573 case 17:
1574 err = "RAS ECC error";
1575 break;
1576 case 18:
1577 err = "write parity error";
1578 break;
1579 case 19:
1580 err = "redundacy loss";
1581 break;
1582 case 20:
1583 err = "reserved";
1584 break;
1585 case 21:
1586 err = "memory range error";
1587 break;
1588 case 22:
1589 err = "RTID out of range";
1590 break;
1591 case 23:
1592 err = "address parity error";
1593 break;
1594 case 24:
1595 err = "byte enable parity error";
1596 break;
1597 default:
1598 err = "unknown";
1599 }
1600
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001601 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001602 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001603 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001604 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001605 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001606 syndrome, core_err_cnt, (long long)m->status,
1607 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001608
1609 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001610
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001611 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001612
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001613 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001614 if (m->mcgstatus & 1)
1615 edac_mc_handle_fbd_ue(mci, csrow, 0,
1616 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001617 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001618 edac_mc_handle_fbd_ce(mci, csrow,
1619 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001620
1621 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001622}
1623
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001624/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001625 * i7core_check_error Retrieve and process errors reported by the
1626 * hardware. Called by the Core module.
1627 */
1628static void i7core_check_error(struct mem_ctl_info *mci)
1629{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001630 struct i7core_pvt *pvt = mci->pvt_info;
1631 int i;
1632 unsigned count = 0;
1633 struct mce *m = NULL;
1634 unsigned long flags;
1635
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001636 /* Copy all mce errors into a temporary buffer */
1637 spin_lock_irqsave(&pvt->mce_lock, flags);
1638 if (pvt->mce_count) {
1639 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001640
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001641 if (m) {
1642 count = pvt->mce_count;
1643 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1644 }
1645 pvt->mce_count = 0;
1646 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001647
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001648 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1649
1650 /* proccess mcelog errors */
1651 for (i = 0; i < count; i++)
1652 i7core_mce_output_error(mci, &m[i]);
1653
1654 kfree(m);
1655
1656 /* check memory count errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001657 if (!pvt->is_registered)
1658 i7core_udimm_check_mc_ecc_err(mci);
1659 else
1660 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001661}
1662
1663/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001664 * i7core_mce_check_error Replicates mcelog routine to get errors
1665 * This routine simply queues mcelog errors, and
1666 * return. The error itself should be handled later
1667 * by i7core_check_error.
1668 */
1669static int i7core_mce_check_error(void *priv, struct mce *mce)
1670{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001671 struct mem_ctl_info *mci = priv;
1672 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001673 unsigned long flags;
1674
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001675 /*
1676 * Just let mcelog handle it if the error is
1677 * outside the memory controller
1678 */
1679 if (((mce->status & 0xffff) >> 7) != 1)
1680 return 0;
1681
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001682 /* Bank 8 registers are the only ones that we know how to handle */
1683 if (mce->bank != 8)
1684 return 0;
1685
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001686 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001687 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) {
1688 debugf0("mc%d: ignoring mce log for socket %d. "
1689 "Another mc should get it.\n",
1690 pvt->i7core_dev->socket,
1691 cpu_data(mce->cpu).phys_proc_id);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001692 return 0;
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001693 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001694
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001695 spin_lock_irqsave(&pvt->mce_lock, flags);
1696 if (pvt->mce_count < MCE_LOG_LEN) {
1697 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1698 pvt->mce_count++;
1699 }
1700 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1701
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001702 /* Handle fatal errors immediately */
1703 if (mce->mcgstatus & 1)
1704 i7core_check_error(mci);
1705
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001706 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001707 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001708}
1709
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001710static int i7core_register_mci(struct i7core_dev *i7core_dev,
1711 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001712{
1713 struct mem_ctl_info *mci;
1714 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001715 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001716 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001717
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001718 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001719 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1720 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001721 if (unlikely(!mci))
1722 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001723
1724 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1725
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001726 /* record ptr to the generic device */
1727 mci->dev = &i7core_dev->pdev[0]->dev;
1728
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001729 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001730 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001731
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001732 /*
1733 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1734 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1735 * memory channels
1736 */
1737 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001738 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1739 mci->edac_cap = EDAC_FLAG_NONE;
1740 mci->mod_name = "i7core_edac.c";
1741 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001742 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1743 i7core_dev->socket);
1744 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001745 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001746 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001747 /* Set the function pointer to an actual operation function */
1748 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001749
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001750 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001751 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001752 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001753 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001754
1755 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001756 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001757
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001758 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001759 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001760 debugf0("MC: " __FILE__
1761 ": %s(): failed edac_mc_add_mc()\n", __func__);
1762 /* FIXME: perhaps some code should go here that disables error
1763 * reporting if we just enabled it
1764 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001765
1766 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001767 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001768 }
1769
1770 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001771 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1772 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001773 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001774 printk(KERN_WARNING
1775 "%s(): Unable to create PCI control\n",
1776 __func__);
1777 printk(KERN_WARNING
1778 "%s(): PCI error report via EDAC not setup\n",
1779 __func__);
1780 }
1781
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001782 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001783 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001784 pvt->inject.dimm = -1;
1785 pvt->inject.rank = -1;
1786 pvt->inject.bank = -1;
1787 pvt->inject.page = -1;
1788 pvt->inject.col = -1;
1789
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001790 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001791 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001792 pvt->edac_mce.check_error = i7core_mce_check_error;
1793 spin_lock_init(&pvt->mce_lock);
1794
1795 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001796 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001797 debugf0("MC: " __FILE__
1798 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001799 }
1800
1801fail:
1802 edac_mc_free(mci);
1803 return rc;
1804}
1805
1806/*
1807 * i7core_probe Probe for ONE instance of device to see if it is
1808 * present.
1809 * return:
1810 * 0 for FOUND a device
1811 * < 0 for error code
1812 */
1813static int __devinit i7core_probe(struct pci_dev *pdev,
1814 const struct pci_device_id *id)
1815{
1816 int dev_idx = id->driver_data;
1817 int rc;
1818 struct i7core_dev *i7core_dev;
1819
1820 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001821 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001822 */
1823 if (unlikely(dev_idx >= 1))
1824 return -EINVAL;
1825
1826 /* get the pci devices we want to reserve for our use */
1827 mutex_lock(&i7core_edac_lock);
1828 rc = i7core_get_devices();
1829 if (unlikely(rc < 0))
1830 goto fail0;
1831
1832 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1833 int channels;
1834 int csrows;
1835
1836 /* Check the number of active and not disabled channels */
1837 rc = i7core_get_active_channels(i7core_dev->socket,
1838 &channels, &csrows);
1839 if (unlikely(rc < 0))
1840 goto fail1;
1841
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001842 rc = i7core_register_mci(i7core_dev, channels, csrows);
1843 if (unlikely(rc < 0))
1844 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001845 }
1846
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001847 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001848
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001849 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001850 return 0;
1851
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001852fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001853 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001854fail0:
1855 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001856 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001857}
1858
1859/*
1860 * i7core_remove destructor for one instance of device
1861 *
1862 */
1863static void __devexit i7core_remove(struct pci_dev *pdev)
1864{
1865 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001866 struct i7core_pvt *pvt;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001867 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001868
1869 debugf0(__FILE__ ": %s()\n", __func__);
1870
1871 if (i7core_pci)
1872 edac_pci_release_generic_ctl(i7core_pci);
1873
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001874
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001875 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001876 if (!mci)
1877 return;
1878
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001879 /* Unregisters on edac_mce in order to receive memory errors */
1880 pvt = mci->pvt_info;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001881 i7core_dev = pvt->i7core_dev;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001882 edac_mce_unregister(&pvt->edac_mce);
1883
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001884 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001885 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001886 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001887 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001888
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001889 kfree(mci->ctl_name);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001890 edac_mc_free(mci);
1891}
1892
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001893MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1894
1895/*
1896 * i7core_driver pci_driver structure for this module
1897 *
1898 */
1899static struct pci_driver i7core_driver = {
1900 .name = "i7core_edac",
1901 .probe = i7core_probe,
1902 .remove = __devexit_p(i7core_remove),
1903 .id_table = i7core_pci_tbl,
1904};
1905
1906/*
1907 * i7core_init Module entry function
1908 * Try to initialize this module for its devices
1909 */
1910static int __init i7core_init(void)
1911{
1912 int pci_rc;
1913
1914 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1915
1916 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1917 opstate_init();
1918
Keith Manntheybc2d7242009-09-03 00:05:05 -03001919 i7core_xeon_pci_fixup();
1920
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001921 pci_rc = pci_register_driver(&i7core_driver);
1922
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001923 if (pci_rc >= 0)
1924 return 0;
1925
1926 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1927 pci_rc);
1928
1929 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001930}
1931
1932/*
1933 * i7core_exit() Module exit function
1934 * Unregister the driver
1935 */
1936static void __exit i7core_exit(void)
1937{
1938 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1939 pci_unregister_driver(&i7core_driver);
1940}
1941
1942module_init(i7core_init);
1943module_exit(i7core_exit);
1944
1945MODULE_LICENSE("GPL");
1946MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1947MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1948MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1949 I7CORE_REVISION);
1950
1951module_param(edac_op_state, int, 0444);
1952MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");