blob: 0478cc85e92006c9ff7d9569c133c7ab861f7417 [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 */
781static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
782 const char *data, size_t count)
783{
784 struct i7core_pvt *pvt = mci->pvt_info;
785 char *cmd, *val;
786 long value;
787 int rc;
788
789 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300790 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300791
792 do {
793 cmd = strsep((char **) &data, ":");
794 if (!cmd)
795 break;
796 val = strsep((char **) &data, " \n\t");
797 if (!val)
798 return cmd - data;
799
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300800 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300801 value = -1;
802 else {
803 rc = strict_strtol(val, 10, &value);
804 if ((rc < 0) || (value < 0))
805 return cmd - data;
806 }
807
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300808 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300809 if (value < 3)
810 pvt->inject.channel = value;
811 else
812 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300813 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300814 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300815 pvt->inject.dimm = value;
816 else
817 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300818 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300819 if (value < 4)
820 pvt->inject.rank = value;
821 else
822 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300823 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300824 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300825 pvt->inject.bank = value;
826 else
827 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300828 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300829 if (value <= 0xffff)
830 pvt->inject.page = value;
831 else
832 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300833 } else if (!strcasecmp(cmd, "col") ||
834 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300835 if (value <= 0x3fff)
836 pvt->inject.col = value;
837 else
838 return cmd - data;
839 }
840 } while (1);
841
842 return count;
843}
844
845static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
846 char *data)
847{
848 struct i7core_pvt *pvt = mci->pvt_info;
849 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
850
851 if (pvt->inject.channel < 0)
852 sprintf(channel, "any");
853 else
854 sprintf(channel, "%d", pvt->inject.channel);
855 if (pvt->inject.dimm < 0)
856 sprintf(dimm, "any");
857 else
858 sprintf(dimm, "%d", pvt->inject.dimm);
859 if (pvt->inject.bank < 0)
860 sprintf(bank, "any");
861 else
862 sprintf(bank, "%d", pvt->inject.bank);
863 if (pvt->inject.rank < 0)
864 sprintf(rank, "any");
865 else
866 sprintf(rank, "%d", pvt->inject.rank);
867 if (pvt->inject.page < 0)
868 sprintf(page, "any");
869 else
870 sprintf(page, "0x%04x", pvt->inject.page);
871 if (pvt->inject.col < 0)
872 sprintf(col, "any");
873 else
874 sprintf(col, "0x%04x", pvt->inject.col);
875
876 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
877 "rank: %s\npage: %s\ncolumn: %s\n",
878 channel, dimm, bank, rank, page, col);
879}
880
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300881static int write_and_test(struct pci_dev *dev, int where, u32 val)
882{
883 u32 read;
884 int count;
885
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300886 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
887 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
888 where, val);
889
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300890 for (count = 0; count < 10; count++) {
891 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300892 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300893 pci_write_config_dword(dev, where, val);
894 pci_read_config_dword(dev, where, &read);
895
896 if (read == val)
897 return 0;
898 }
899
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300900 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
901 "write=%08x. Read=%08x\n",
902 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
903 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300904
905 return -EINVAL;
906}
907
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300908/*
909 * This routine prepares the Memory Controller for error injection.
910 * The error will be injected when some process tries to write to the
911 * memory that matches the given criteria.
912 * The criteria can be set in terms of a mask where dimm, rank, bank, page
913 * and col can be specified.
914 * A -1 value for any of the mask items will make the MCU to ignore
915 * that matching criteria for error injection.
916 *
917 * It should be noticed that the error will only happen after a write operation
918 * on a memory that matches the condition. if REPEAT_EN is not enabled at
919 * inject mask, then it will produce just one error. Otherwise, it will repeat
920 * until the injectmask would be cleaned.
921 *
922 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
923 * is reliable enough to check if the MC is using the
924 * three channels. However, this is not clear at the datasheet.
925 */
926static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
927 const char *data, size_t count)
928{
929 struct i7core_pvt *pvt = mci->pvt_info;
930 u32 injectmask;
931 u64 mask = 0;
932 int rc;
933 long enable;
934
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300935 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300936 return 0;
937
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300938 rc = strict_strtoul(data, 10, &enable);
939 if ((rc < 0))
940 return 0;
941
942 if (enable) {
943 pvt->inject.enable = 1;
944 } else {
945 disable_inject(mci);
946 return count;
947 }
948
949 /* Sets pvt->inject.dimm mask */
950 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300951 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300952 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300953 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300954 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300955 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300956 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300957 }
958
959 /* Sets pvt->inject.rank mask */
960 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300961 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300962 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300963 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300964 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300965 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300966 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300967 }
968
969 /* Sets pvt->inject.bank mask */
970 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300971 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300972 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300973 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300974
975 /* Sets pvt->inject.page mask */
976 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300977 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300978 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300979 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300980
981 /* Sets pvt->inject.column mask */
982 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300983 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300984 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300985 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300986
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300987 /*
988 * bit 0: REPEAT_EN
989 * bits 1-2: MASK_HALF_CACHELINE
990 * bit 3: INJECT_ECC
991 * bit 4: INJECT_ADDR_PARITY
992 */
993
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300994 injectmask = (pvt->inject.type & 1) |
995 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300996 (pvt->inject.type & 0x6) << (3 - 1);
997
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300998 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300999 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001000 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001001
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001002 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001003 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001004 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001005 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1006
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001007 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001008 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1009
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001010 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001011 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001012
1013 /*
1014 * This is something undocumented, based on my tests
1015 * Without writing 8 to this register, errors aren't injected. Not sure
1016 * why.
1017 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001018 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001019 MC_CFG_CONTROL, 8);
1020
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001021 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1022 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001023 mask, pvt->inject.eccmask, injectmask);
1024
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001025
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001026 return count;
1027}
1028
1029static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1030 char *data)
1031{
1032 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001033 u32 injectmask;
1034
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001035 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001036 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001037
1038 debugf0("Inject error read: 0x%018x\n", injectmask);
1039
1040 if (injectmask & 0x0c)
1041 pvt->inject.enable = 1;
1042
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001043 return sprintf(data, "%d\n", pvt->inject.enable);
1044}
1045
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001046static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1047{
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001048 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001049 struct i7core_pvt *pvt = mci->pvt_info;
1050
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001051 if (!pvt->ce_count_available) {
1052 count = sprintf(data, "data unavailable\n");
1053 return 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001054 }
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001055 if (!pvt->is_registered) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001056 count = sprintf(data, "all channels "
1057 "UDIMM0: %lu UDIMM1: %lu UDIMM2: %lu\n",
1058 pvt->udimm_ce_count[0],
1059 pvt->udimm_ce_count[1],
1060 pvt->udimm_ce_count[2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001061 data += count;
1062 total += count;
1063 } else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001064 for (i = 0; i < NUM_CHANS; i++) {
1065 count = sprintf(data, "channel %d RDIMM0: %lu "
1066 "RDIMM1: %lu RDIMM2: %lu\n",
1067 i,
1068 pvt->rdimm_ce_count[i][0],
1069 pvt->rdimm_ce_count[i][1],
1070 pvt->rdimm_ce_count[i][2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001071 data += count;
1072 total += count;
1073 }
1074 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001075
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001076 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001077}
1078
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001079/*
1080 * Sysfs struct
1081 */
1082static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001083 {
1084 .attr = {
1085 .name = "inject_section",
1086 .mode = (S_IRUGO | S_IWUSR)
1087 },
1088 .show = i7core_inject_section_show,
1089 .store = i7core_inject_section_store,
1090 }, {
1091 .attr = {
1092 .name = "inject_type",
1093 .mode = (S_IRUGO | S_IWUSR)
1094 },
1095 .show = i7core_inject_type_show,
1096 .store = i7core_inject_type_store,
1097 }, {
1098 .attr = {
1099 .name = "inject_eccmask",
1100 .mode = (S_IRUGO | S_IWUSR)
1101 },
1102 .show = i7core_inject_eccmask_show,
1103 .store = i7core_inject_eccmask_store,
1104 }, {
1105 .attr = {
1106 .name = "inject_addrmatch",
1107 .mode = (S_IRUGO | S_IWUSR)
1108 },
1109 .show = i7core_inject_addrmatch_show,
1110 .store = i7core_inject_addrmatch_store,
1111 }, {
1112 .attr = {
1113 .name = "inject_enable",
1114 .mode = (S_IRUGO | S_IWUSR)
1115 },
1116 .show = i7core_inject_enable_show,
1117 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001118 }, {
1119 .attr = {
1120 .name = "corrected_error_counts",
1121 .mode = (S_IRUGO | S_IWUSR)
1122 },
1123 .show = i7core_ce_regs_show,
1124 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001125 },
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001126 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001127};
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 Chehab22e6bcb2009-09-05 23:06:50 -03001141 debugf0(__FILE__ ": %s()\n", __func__);
1142 for (i = 0; i < N_DEVS; i++) {
1143 struct pci_dev *pdev = i7core_dev->pdev[i];
1144 if (!pdev)
1145 continue;
1146 debugf0("Removing dev %02x:%02x.%d\n",
1147 pdev->bus->number,
1148 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1149 pci_dev_put(pdev);
1150 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001151 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001152 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001153 kfree(i7core_dev);
1154}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001155
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001156static void i7core_put_all_devices(void)
1157{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001158 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001159
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001160 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001161 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001162}
1163
Keith Manntheybc2d7242009-09-03 00:05:05 -03001164static void i7core_xeon_pci_fixup(void)
1165{
1166 struct pci_dev *pdev = NULL;
1167 int i;
1168 /*
1169 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1170 * aren't announced by acpi. So, we need to use a legacy scan probing
1171 * to detect them
1172 */
1173 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001174 pci_dev_descr[0].dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001175 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001176 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001177 pcibios_scan_specific_bus(255-i);
1178 }
1179}
1180
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001181/*
1182 * i7core_get_devices Find and perform 'get' operation on the MCH's
1183 * device/functions we want to reference for this driver
1184 *
1185 * Need to 'get' device 16 func 1 and func 2
1186 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001187int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001188{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001189 struct i7core_dev *i7core_dev;
1190
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001191 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001192 u8 bus = 0;
1193 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001194
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001195 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001196 pci_dev_descr[devno].dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001197
1198 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001199 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1200 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1201 * to probe for the alternate address in case of failure
1202 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001203 if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001204 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1205 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001206
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001207 if (!pdev) {
1208 if (*prev) {
1209 *prev = pdev;
1210 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001211 }
1212
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001213 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001214 * Dev 3 function 2 only exists on chips with RDIMMs
1215 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001216 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001217 if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001218 *prev = pdev;
1219 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001220 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001221
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001222 i7core_printk(KERN_ERR,
1223 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001224 pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1225 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001226
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001227 /* End of list, leave */
1228 return -ENODEV;
1229 }
1230 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001231
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001232 if (bus == 0x3f)
1233 socket = 0;
1234 else
1235 socket = 255 - bus;
1236
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001237 i7core_dev = get_i7core_dev(socket);
1238 if (!i7core_dev) {
1239 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1240 if (!i7core_dev)
1241 return -ENOMEM;
1242 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
1243 GFP_KERNEL);
1244 if (!i7core_dev->pdev)
1245 return -ENOMEM;
1246 i7core_dev->socket = socket;
1247 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001248 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001249
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001250 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001251 i7core_printk(KERN_ERR,
1252 "Duplicated device for "
1253 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001254 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1255 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001256 pci_dev_put(pdev);
1257 return -ENODEV;
1258 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001259
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001260 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001261
1262 /* Sanity check */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001263 if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
1264 PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001265 i7core_printk(KERN_ERR,
1266 "Device PCI ID %04x:%04x "
1267 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001268 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001269 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001270 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001271 return -ENODEV;
1272 }
1273
1274 /* Be sure that the device is enabled */
1275 if (unlikely(pci_enable_device(pdev) < 0)) {
1276 i7core_printk(KERN_ERR,
1277 "Couldn't enable "
1278 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001279 bus, pci_dev_descr[devno].dev, 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 return -ENODEV;
1282 }
1283
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001284 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1285 socket, bus, pci_dev_descr[devno].dev,
1286 pci_dev_descr[devno].func,
1287 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001288
1289 *prev = pdev;
1290
1291 return 0;
1292}
1293
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001294static int i7core_get_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001295{
1296 int i;
1297 struct pci_dev *pdev = NULL;
1298
1299 for (i = 0; i < N_DEVS; i++) {
1300 pdev = NULL;
1301 do {
1302 if (i7core_get_onedevice(&pdev, i) < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001303 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001304 return -ENODEV;
1305 }
1306 } while (pdev);
1307 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001308
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001309 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001310}
1311
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001312static int mci_bind_devs(struct mem_ctl_info *mci,
1313 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001314{
1315 struct i7core_pvt *pvt = mci->pvt_info;
1316 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001317 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001318
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001319 /* Associates i7core_dev and mci for future usage */
1320 pvt->i7core_dev = i7core_dev;
1321 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001322
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001323 pvt->is_registered = 0;
1324 for (i = 0; i < N_DEVS; i++) {
1325 pdev = i7core_dev->pdev[i];
1326 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001327 continue;
1328
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001329 func = PCI_FUNC(pdev->devfn);
1330 slot = PCI_SLOT(pdev->devfn);
1331 if (slot == 3) {
1332 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001333 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001334 pvt->pci_mcr[func] = pdev;
1335 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1336 if (unlikely(func > MAX_CHAN_FUNC))
1337 goto error;
1338 pvt->pci_ch[slot - 4][func] = pdev;
1339 } else if (!slot && !func)
1340 pvt->pci_noncore = pdev;
1341 else
1342 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001343
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001344 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1345 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1346 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001347
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001348 if (PCI_SLOT(pdev->devfn) == 3 &&
1349 PCI_FUNC(pdev->devfn) == 2)
1350 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001351 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001352
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001353 return 0;
1354
1355error:
1356 i7core_printk(KERN_ERR, "Device %d, function %d "
1357 "is out of the expected range\n",
1358 slot, func);
1359 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001360}
1361
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001362/****************************************************************************
1363 Error check routines
1364 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001365static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001366 int chan, int dimm, int add)
1367{
1368 char *msg;
1369 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001370 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001371
1372 for (i = 0; i < add; i++) {
1373 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001374 "(Socket=%d channel=%d dimm=%d)",
1375 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001376
1377 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1378 kfree (msg);
1379 }
1380}
1381
1382static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001383 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001384{
1385 struct i7core_pvt *pvt = mci->pvt_info;
1386 int add0 = 0, add1 = 0, add2 = 0;
1387 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001388 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001389 /* Updates CE counters */
1390
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001391 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1392 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1393 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001394
1395 if (add2 < 0)
1396 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001397 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001398
1399 if (add1 < 0)
1400 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001401 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001402
1403 if (add0 < 0)
1404 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001405 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001406 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001407 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001408
1409 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001410 pvt->rdimm_last_ce_count[chan][2] = new2;
1411 pvt->rdimm_last_ce_count[chan][1] = new1;
1412 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001413
1414 /*updated the edac core */
1415 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001416 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001417 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001418 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001419 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001420 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001421
1422}
1423
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001424static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001425{
1426 struct i7core_pvt *pvt = mci->pvt_info;
1427 u32 rcv[3][2];
1428 int i, new0, new1, new2;
1429
1430 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001431 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001432 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001433 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001434 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001435 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001436 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001437 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001438 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001439 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001440 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001441 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001442 &rcv[2][1]);
1443 for (i = 0 ; i < 3; i++) {
1444 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1445 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1446 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001447 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001448 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1449 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1450 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1451 } else {
1452 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1453 DIMM_BOT_COR_ERR(rcv[i][0]);
1454 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1455 DIMM_BOT_COR_ERR(rcv[i][1]);
1456 new2 = 0;
1457 }
1458
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001459 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001460 }
1461}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001462
1463/* This function is based on the device 3 function 4 registers as described on:
1464 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1465 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1466 * also available at:
1467 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1468 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001469static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001470{
1471 struct i7core_pvt *pvt = mci->pvt_info;
1472 u32 rcv1, rcv0;
1473 int new0, new1, new2;
1474
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001475 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001476 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001477 return;
1478 }
1479
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001480 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001481 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1482 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001483
1484 /* Store the new values */
1485 new2 = DIMM2_COR_ERR(rcv1);
1486 new1 = DIMM1_COR_ERR(rcv0);
1487 new0 = DIMM0_COR_ERR(rcv0);
1488
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001489 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001490 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001491 /* Updates CE counters */
1492 int add0, add1, add2;
1493
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001494 add2 = new2 - pvt->udimm_last_ce_count[2];
1495 add1 = new1 - pvt->udimm_last_ce_count[1];
1496 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001497
1498 if (add2 < 0)
1499 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001500 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001501
1502 if (add1 < 0)
1503 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001504 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001505
1506 if (add0 < 0)
1507 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001508 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001509
1510 if (add0 | add1 | add2)
1511 i7core_printk(KERN_ERR, "New Corrected error(s): "
1512 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1513 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001514 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001515 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001516
1517 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001518 pvt->udimm_last_ce_count[2] = new2;
1519 pvt->udimm_last_ce_count[1] = new1;
1520 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001521}
1522
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001523/*
1524 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1525 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001526 * Nehalem are defined as family 0x06, model 0x1a
1527 *
1528 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001529 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001530 * m->status MSR_IA32_MC8_STATUS
1531 * m->addr MSR_IA32_MC8_ADDR
1532 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001533 * In the case of Nehalem, the error information is masked at .status and .misc
1534 * fields
1535 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001536static void i7core_mce_output_error(struct mem_ctl_info *mci,
1537 struct mce *m)
1538{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001539 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001540 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001541 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001542 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001543 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1544 u32 dimm = (m->misc >> 16) & 0x3;
1545 u32 channel = (m->misc >> 18) & 0x3;
1546 u32 syndrome = m->misc >> 32;
1547 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001548 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001549
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001550 if (m->mcgstatus & 1)
1551 type = "FATAL";
1552 else
1553 type = "NON_FATAL";
1554
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001555 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001556 case 0:
1557 optype = "generic undef request";
1558 break;
1559 case 1:
1560 optype = "read error";
1561 break;
1562 case 2:
1563 optype = "write error";
1564 break;
1565 case 3:
1566 optype = "addr/cmd error";
1567 break;
1568 case 4:
1569 optype = "scrubbing error";
1570 break;
1571 default:
1572 optype = "reserved";
1573 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001574 }
1575
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001576 switch (errnum) {
1577 case 16:
1578 err = "read ECC error";
1579 break;
1580 case 17:
1581 err = "RAS ECC error";
1582 break;
1583 case 18:
1584 err = "write parity error";
1585 break;
1586 case 19:
1587 err = "redundacy loss";
1588 break;
1589 case 20:
1590 err = "reserved";
1591 break;
1592 case 21:
1593 err = "memory range error";
1594 break;
1595 case 22:
1596 err = "RTID out of range";
1597 break;
1598 case 23:
1599 err = "address parity error";
1600 break;
1601 case 24:
1602 err = "byte enable parity error";
1603 break;
1604 default:
1605 err = "unknown";
1606 }
1607
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001608 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001609 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001610 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001611 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001612 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001613 syndrome, core_err_cnt, (long long)m->status,
1614 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001615
1616 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001617
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001618 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001619
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001620 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001621 if (m->mcgstatus & 1)
1622 edac_mc_handle_fbd_ue(mci, csrow, 0,
1623 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001624 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001625 edac_mc_handle_fbd_ce(mci, csrow,
1626 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001627
1628 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001629}
1630
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001631/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001632 * i7core_check_error Retrieve and process errors reported by the
1633 * hardware. Called by the Core module.
1634 */
1635static void i7core_check_error(struct mem_ctl_info *mci)
1636{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001637 struct i7core_pvt *pvt = mci->pvt_info;
1638 int i;
1639 unsigned count = 0;
1640 struct mce *m = NULL;
1641 unsigned long flags;
1642
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001643 /* Copy all mce errors into a temporary buffer */
1644 spin_lock_irqsave(&pvt->mce_lock, flags);
1645 if (pvt->mce_count) {
1646 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001647
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001648 if (m) {
1649 count = pvt->mce_count;
1650 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1651 }
1652 pvt->mce_count = 0;
1653 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001654
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001655 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1656
1657 /* proccess mcelog errors */
1658 for (i = 0; i < count; i++)
1659 i7core_mce_output_error(mci, &m[i]);
1660
1661 kfree(m);
1662
1663 /* check memory count errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001664 if (!pvt->is_registered)
1665 i7core_udimm_check_mc_ecc_err(mci);
1666 else
1667 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001668}
1669
1670/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001671 * i7core_mce_check_error Replicates mcelog routine to get errors
1672 * This routine simply queues mcelog errors, and
1673 * return. The error itself should be handled later
1674 * by i7core_check_error.
1675 */
1676static int i7core_mce_check_error(void *priv, struct mce *mce)
1677{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001678 struct mem_ctl_info *mci = priv;
1679 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001680 unsigned long flags;
1681
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001682 /*
1683 * Just let mcelog handle it if the error is
1684 * outside the memory controller
1685 */
1686 if (((mce->status & 0xffff) >> 7) != 1)
1687 return 0;
1688
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001689 /* Bank 8 registers are the only ones that we know how to handle */
1690 if (mce->bank != 8)
1691 return 0;
1692
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001693 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001694 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) {
1695 debugf0("mc%d: ignoring mce log for socket %d. "
1696 "Another mc should get it.\n",
1697 pvt->i7core_dev->socket,
1698 cpu_data(mce->cpu).phys_proc_id);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001699 return 0;
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001700 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001701
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001702 spin_lock_irqsave(&pvt->mce_lock, flags);
1703 if (pvt->mce_count < MCE_LOG_LEN) {
1704 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1705 pvt->mce_count++;
1706 }
1707 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1708
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001709 /* Handle fatal errors immediately */
1710 if (mce->mcgstatus & 1)
1711 i7core_check_error(mci);
1712
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001713 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001714 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001715}
1716
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001717static int i7core_register_mci(struct i7core_dev *i7core_dev,
1718 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001719{
1720 struct mem_ctl_info *mci;
1721 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001722 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001723 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001724
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001725 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001726 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1727 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001728 if (unlikely(!mci))
1729 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001730
1731 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1732
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001733 /* record ptr to the generic device */
1734 mci->dev = &i7core_dev->pdev[0]->dev;
1735
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001736 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001737 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001738
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001739 /*
1740 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1741 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1742 * memory channels
1743 */
1744 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001745 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1746 mci->edac_cap = EDAC_FLAG_NONE;
1747 mci->mod_name = "i7core_edac.c";
1748 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001749 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1750 i7core_dev->socket);
1751 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001752 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001753 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001754 /* Set the function pointer to an actual operation function */
1755 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001756
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001757 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001758 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001759 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001760 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001761
1762 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001763 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001764
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001765 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001766 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001767 debugf0("MC: " __FILE__
1768 ": %s(): failed edac_mc_add_mc()\n", __func__);
1769 /* FIXME: perhaps some code should go here that disables error
1770 * reporting if we just enabled it
1771 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001772
1773 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001774 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001775 }
1776
1777 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001778 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1779 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001780 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001781 printk(KERN_WARNING
1782 "%s(): Unable to create PCI control\n",
1783 __func__);
1784 printk(KERN_WARNING
1785 "%s(): PCI error report via EDAC not setup\n",
1786 __func__);
1787 }
1788
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001789 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001790 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001791 pvt->inject.dimm = -1;
1792 pvt->inject.rank = -1;
1793 pvt->inject.bank = -1;
1794 pvt->inject.page = -1;
1795 pvt->inject.col = -1;
1796
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001797 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001798 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001799 pvt->edac_mce.check_error = i7core_mce_check_error;
1800 spin_lock_init(&pvt->mce_lock);
1801
1802 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001803 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001804 debugf0("MC: " __FILE__
1805 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001806 }
1807
1808fail:
1809 edac_mc_free(mci);
1810 return rc;
1811}
1812
1813/*
1814 * i7core_probe Probe for ONE instance of device to see if it is
1815 * present.
1816 * return:
1817 * 0 for FOUND a device
1818 * < 0 for error code
1819 */
1820static int __devinit i7core_probe(struct pci_dev *pdev,
1821 const struct pci_device_id *id)
1822{
1823 int dev_idx = id->driver_data;
1824 int rc;
1825 struct i7core_dev *i7core_dev;
1826
1827 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001828 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001829 */
1830 if (unlikely(dev_idx >= 1))
1831 return -EINVAL;
1832
1833 /* get the pci devices we want to reserve for our use */
1834 mutex_lock(&i7core_edac_lock);
1835 rc = i7core_get_devices();
1836 if (unlikely(rc < 0))
1837 goto fail0;
1838
1839 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1840 int channels;
1841 int csrows;
1842
1843 /* Check the number of active and not disabled channels */
1844 rc = i7core_get_active_channels(i7core_dev->socket,
1845 &channels, &csrows);
1846 if (unlikely(rc < 0))
1847 goto fail1;
1848
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001849 rc = i7core_register_mci(i7core_dev, channels, csrows);
1850 if (unlikely(rc < 0))
1851 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001852 }
1853
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001854 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001855
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001856 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001857 return 0;
1858
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001859fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001860 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001861fail0:
1862 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001863 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001864}
1865
1866/*
1867 * i7core_remove destructor for one instance of device
1868 *
1869 */
1870static void __devexit i7core_remove(struct pci_dev *pdev)
1871{
1872 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001873 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001874
1875 debugf0(__FILE__ ": %s()\n", __func__);
1876
1877 if (i7core_pci)
1878 edac_pci_release_generic_ctl(i7core_pci);
1879
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001880 /*
1881 * we have a trouble here: pdev value for removal will be wrong, since
1882 * it will point to the X58 register used to detect that the machine
1883 * is a Nehalem or upper design. However, due to the way several PCI
1884 * devices are grouped together to provide MC functionality, we need
1885 * to use a different method for releasing the devices
1886 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001887
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001888 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001889 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1890 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1891 if (mci) {
1892 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001893
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001894 i7core_dev = pvt->i7core_dev;
1895 edac_mce_unregister(&pvt->edac_mce);
1896 kfree(mci->ctl_name);
1897 edac_mc_free(mci);
1898 i7core_put_devices(i7core_dev);
1899 } else {
1900 i7core_printk(KERN_ERR,
1901 "Couldn't find mci for socket %d\n",
1902 i7core_dev->socket);
1903 }
1904 }
1905 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001906}
1907
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001908MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1909
1910/*
1911 * i7core_driver pci_driver structure for this module
1912 *
1913 */
1914static struct pci_driver i7core_driver = {
1915 .name = "i7core_edac",
1916 .probe = i7core_probe,
1917 .remove = __devexit_p(i7core_remove),
1918 .id_table = i7core_pci_tbl,
1919};
1920
1921/*
1922 * i7core_init Module entry function
1923 * Try to initialize this module for its devices
1924 */
1925static int __init i7core_init(void)
1926{
1927 int pci_rc;
1928
1929 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1930
1931 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1932 opstate_init();
1933
Keith Manntheybc2d7242009-09-03 00:05:05 -03001934 i7core_xeon_pci_fixup();
1935
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001936 pci_rc = pci_register_driver(&i7core_driver);
1937
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001938 if (pci_rc >= 0)
1939 return 0;
1940
1941 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1942 pci_rc);
1943
1944 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001945}
1946
1947/*
1948 * i7core_exit() Module exit function
1949 * Unregister the driver
1950 */
1951static void __exit i7core_exit(void)
1952{
1953 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1954 pci_unregister_driver(&i7core_driver);
1955}
1956
1957module_init(i7core_init);
1958module_exit(i7core_exit);
1959
1960MODULE_LICENSE("GPL");
1961MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1962MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1963MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1964 I7CORE_REVISION);
1965
1966module_param(edac_op_state, int, 0444);
1967MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");