blob: 2e4b0abf9d43630925699c1620ec54d6219ebd4b [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 },
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001127 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001128};
1129
1130/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001131 Device initialization routines: put/get, init/exit
1132 ****************************************************************************/
1133
1134/*
1135 * i7core_put_devices 'put' all the devices that we have
1136 * reserved via 'get'
1137 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001138static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001139{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001140 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001141
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001142 debugf0(__FILE__ ": %s()\n", __func__);
1143 for (i = 0; i < N_DEVS; i++) {
1144 struct pci_dev *pdev = i7core_dev->pdev[i];
1145 if (!pdev)
1146 continue;
1147 debugf0("Removing dev %02x:%02x.%d\n",
1148 pdev->bus->number,
1149 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1150 pci_dev_put(pdev);
1151 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001152 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001153 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001154 kfree(i7core_dev);
1155}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001156
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001157static void i7core_put_all_devices(void)
1158{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001159 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001160
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001161 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001162 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001163}
1164
Keith Manntheybc2d7242009-09-03 00:05:05 -03001165static void i7core_xeon_pci_fixup(void)
1166{
1167 struct pci_dev *pdev = NULL;
1168 int i;
1169 /*
1170 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1171 * aren't announced by acpi. So, we need to use a legacy scan probing
1172 * to detect them
1173 */
1174 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001175 pci_dev_descr[0].dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001176 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001177 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001178 pcibios_scan_specific_bus(255-i);
1179 }
1180}
1181
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001182/*
1183 * i7core_get_devices Find and perform 'get' operation on the MCH's
1184 * device/functions we want to reference for this driver
1185 *
1186 * Need to 'get' device 16 func 1 and func 2
1187 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001188int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001189{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001190 struct i7core_dev *i7core_dev;
1191
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001192 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001193 u8 bus = 0;
1194 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001195
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001196 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001197 pci_dev_descr[devno].dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001198
1199 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001200 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1201 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1202 * to probe for the alternate address in case of failure
1203 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001204 if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001205 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1206 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001207
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001208 if (!pdev) {
1209 if (*prev) {
1210 *prev = pdev;
1211 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001212 }
1213
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001214 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001215 * Dev 3 function 2 only exists on chips with RDIMMs
1216 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001217 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001218 if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001219 *prev = pdev;
1220 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001221 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001222
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001223 i7core_printk(KERN_ERR,
1224 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001225 pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1226 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001227
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001228 /* End of list, leave */
1229 return -ENODEV;
1230 }
1231 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001232
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001233 if (bus == 0x3f)
1234 socket = 0;
1235 else
1236 socket = 255 - bus;
1237
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001238 i7core_dev = get_i7core_dev(socket);
1239 if (!i7core_dev) {
1240 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1241 if (!i7core_dev)
1242 return -ENOMEM;
1243 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
1244 GFP_KERNEL);
1245 if (!i7core_dev->pdev)
1246 return -ENOMEM;
1247 i7core_dev->socket = socket;
1248 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001249 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001250
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001251 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001252 i7core_printk(KERN_ERR,
1253 "Duplicated device for "
1254 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001255 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1256 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001257 pci_dev_put(pdev);
1258 return -ENODEV;
1259 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001260
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001261 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001262
1263 /* Sanity check */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001264 if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
1265 PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001266 i7core_printk(KERN_ERR,
1267 "Device PCI ID %04x:%04x "
1268 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001269 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001270 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001271 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001272 return -ENODEV;
1273 }
1274
1275 /* Be sure that the device is enabled */
1276 if (unlikely(pci_enable_device(pdev) < 0)) {
1277 i7core_printk(KERN_ERR,
1278 "Couldn't enable "
1279 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001280 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1281 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001282 return -ENODEV;
1283 }
1284
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001285 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1286 socket, bus, pci_dev_descr[devno].dev,
1287 pci_dev_descr[devno].func,
1288 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001289
1290 *prev = pdev;
1291
1292 return 0;
1293}
1294
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001295static int i7core_get_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001296{
1297 int i;
1298 struct pci_dev *pdev = NULL;
1299
1300 for (i = 0; i < N_DEVS; i++) {
1301 pdev = NULL;
1302 do {
1303 if (i7core_get_onedevice(&pdev, i) < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001304 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001305 return -ENODEV;
1306 }
1307 } while (pdev);
1308 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001309
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001310 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001311}
1312
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001313static int mci_bind_devs(struct mem_ctl_info *mci,
1314 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001315{
1316 struct i7core_pvt *pvt = mci->pvt_info;
1317 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001318 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001319
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001320 /* Associates i7core_dev and mci for future usage */
1321 pvt->i7core_dev = i7core_dev;
1322 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001323
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001324 pvt->is_registered = 0;
1325 for (i = 0; i < N_DEVS; i++) {
1326 pdev = i7core_dev->pdev[i];
1327 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001328 continue;
1329
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001330 func = PCI_FUNC(pdev->devfn);
1331 slot = PCI_SLOT(pdev->devfn);
1332 if (slot == 3) {
1333 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001334 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001335 pvt->pci_mcr[func] = pdev;
1336 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1337 if (unlikely(func > MAX_CHAN_FUNC))
1338 goto error;
1339 pvt->pci_ch[slot - 4][func] = pdev;
1340 } else if (!slot && !func)
1341 pvt->pci_noncore = pdev;
1342 else
1343 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001344
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001345 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1346 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1347 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001348
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001349 if (PCI_SLOT(pdev->devfn) == 3 &&
1350 PCI_FUNC(pdev->devfn) == 2)
1351 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001352 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001353
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001354 return 0;
1355
1356error:
1357 i7core_printk(KERN_ERR, "Device %d, function %d "
1358 "is out of the expected range\n",
1359 slot, func);
1360 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001361}
1362
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001363/****************************************************************************
1364 Error check routines
1365 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001366static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001367 int chan, int dimm, int add)
1368{
1369 char *msg;
1370 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001371 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001372
1373 for (i = 0; i < add; i++) {
1374 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001375 "(Socket=%d channel=%d dimm=%d)",
1376 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001377
1378 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1379 kfree (msg);
1380 }
1381}
1382
1383static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001384 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001385{
1386 struct i7core_pvt *pvt = mci->pvt_info;
1387 int add0 = 0, add1 = 0, add2 = 0;
1388 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001389 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001390 /* Updates CE counters */
1391
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001392 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1393 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1394 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001395
1396 if (add2 < 0)
1397 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001398 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001399
1400 if (add1 < 0)
1401 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001402 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001403
1404 if (add0 < 0)
1405 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001406 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001407 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001408 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001409
1410 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001411 pvt->rdimm_last_ce_count[chan][2] = new2;
1412 pvt->rdimm_last_ce_count[chan][1] = new1;
1413 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001414
1415 /*updated the edac core */
1416 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001417 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001418 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001419 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001420 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001421 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001422
1423}
1424
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001425static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001426{
1427 struct i7core_pvt *pvt = mci->pvt_info;
1428 u32 rcv[3][2];
1429 int i, new0, new1, new2;
1430
1431 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001432 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001433 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001434 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001435 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001436 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001437 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001438 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001439 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001440 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001441 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001442 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001443 &rcv[2][1]);
1444 for (i = 0 ; i < 3; i++) {
1445 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1446 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1447 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001448 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001449 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1450 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1451 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1452 } else {
1453 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1454 DIMM_BOT_COR_ERR(rcv[i][0]);
1455 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1456 DIMM_BOT_COR_ERR(rcv[i][1]);
1457 new2 = 0;
1458 }
1459
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001460 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001461 }
1462}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001463
1464/* This function is based on the device 3 function 4 registers as described on:
1465 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1466 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1467 * also available at:
1468 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1469 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001470static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001471{
1472 struct i7core_pvt *pvt = mci->pvt_info;
1473 u32 rcv1, rcv0;
1474 int new0, new1, new2;
1475
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001476 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001477 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001478 return;
1479 }
1480
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001481 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001482 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1483 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001484
1485 /* Store the new values */
1486 new2 = DIMM2_COR_ERR(rcv1);
1487 new1 = DIMM1_COR_ERR(rcv0);
1488 new0 = DIMM0_COR_ERR(rcv0);
1489
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001490 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001491 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001492 /* Updates CE counters */
1493 int add0, add1, add2;
1494
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001495 add2 = new2 - pvt->udimm_last_ce_count[2];
1496 add1 = new1 - pvt->udimm_last_ce_count[1];
1497 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001498
1499 if (add2 < 0)
1500 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001501 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001502
1503 if (add1 < 0)
1504 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001505 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001506
1507 if (add0 < 0)
1508 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001509 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001510
1511 if (add0 | add1 | add2)
1512 i7core_printk(KERN_ERR, "New Corrected error(s): "
1513 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1514 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001515 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001516 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001517
1518 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001519 pvt->udimm_last_ce_count[2] = new2;
1520 pvt->udimm_last_ce_count[1] = new1;
1521 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001522}
1523
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001524/*
1525 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1526 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001527 * Nehalem are defined as family 0x06, model 0x1a
1528 *
1529 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001530 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001531 * m->status MSR_IA32_MC8_STATUS
1532 * m->addr MSR_IA32_MC8_ADDR
1533 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001534 * In the case of Nehalem, the error information is masked at .status and .misc
1535 * fields
1536 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001537static void i7core_mce_output_error(struct mem_ctl_info *mci,
1538 struct mce *m)
1539{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001540 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001541 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001542 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001543 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001544 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1545 u32 dimm = (m->misc >> 16) & 0x3;
1546 u32 channel = (m->misc >> 18) & 0x3;
1547 u32 syndrome = m->misc >> 32;
1548 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001549 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001550
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001551 if (m->mcgstatus & 1)
1552 type = "FATAL";
1553 else
1554 type = "NON_FATAL";
1555
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001556 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001557 case 0:
1558 optype = "generic undef request";
1559 break;
1560 case 1:
1561 optype = "read error";
1562 break;
1563 case 2:
1564 optype = "write error";
1565 break;
1566 case 3:
1567 optype = "addr/cmd error";
1568 break;
1569 case 4:
1570 optype = "scrubbing error";
1571 break;
1572 default:
1573 optype = "reserved";
1574 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001575 }
1576
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001577 switch (errnum) {
1578 case 16:
1579 err = "read ECC error";
1580 break;
1581 case 17:
1582 err = "RAS ECC error";
1583 break;
1584 case 18:
1585 err = "write parity error";
1586 break;
1587 case 19:
1588 err = "redundacy loss";
1589 break;
1590 case 20:
1591 err = "reserved";
1592 break;
1593 case 21:
1594 err = "memory range error";
1595 break;
1596 case 22:
1597 err = "RTID out of range";
1598 break;
1599 case 23:
1600 err = "address parity error";
1601 break;
1602 case 24:
1603 err = "byte enable parity error";
1604 break;
1605 default:
1606 err = "unknown";
1607 }
1608
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001609 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001610 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001611 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001612 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001613 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001614 syndrome, core_err_cnt, (long long)m->status,
1615 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001616
1617 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001618
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001619 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001620
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001621 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001622 if (m->mcgstatus & 1)
1623 edac_mc_handle_fbd_ue(mci, csrow, 0,
1624 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001625 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001626 edac_mc_handle_fbd_ce(mci, csrow,
1627 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001628
1629 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001630}
1631
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001632/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001633 * i7core_check_error Retrieve and process errors reported by the
1634 * hardware. Called by the Core module.
1635 */
1636static void i7core_check_error(struct mem_ctl_info *mci)
1637{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001638 struct i7core_pvt *pvt = mci->pvt_info;
1639 int i;
1640 unsigned count = 0;
1641 struct mce *m = NULL;
1642 unsigned long flags;
1643
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001644 /* Copy all mce errors into a temporary buffer */
1645 spin_lock_irqsave(&pvt->mce_lock, flags);
1646 if (pvt->mce_count) {
1647 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001648
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001649 if (m) {
1650 count = pvt->mce_count;
1651 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1652 }
1653 pvt->mce_count = 0;
1654 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001655
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001656 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1657
1658 /* proccess mcelog errors */
1659 for (i = 0; i < count; i++)
1660 i7core_mce_output_error(mci, &m[i]);
1661
1662 kfree(m);
1663
1664 /* check memory count errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001665 if (!pvt->is_registered)
1666 i7core_udimm_check_mc_ecc_err(mci);
1667 else
1668 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001669}
1670
1671/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001672 * i7core_mce_check_error Replicates mcelog routine to get errors
1673 * This routine simply queues mcelog errors, and
1674 * return. The error itself should be handled later
1675 * by i7core_check_error.
1676 */
1677static int i7core_mce_check_error(void *priv, struct mce *mce)
1678{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001679 struct mem_ctl_info *mci = priv;
1680 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001681 unsigned long flags;
1682
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001683 /*
1684 * Just let mcelog handle it if the error is
1685 * outside the memory controller
1686 */
1687 if (((mce->status & 0xffff) >> 7) != 1)
1688 return 0;
1689
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001690 /* Bank 8 registers are the only ones that we know how to handle */
1691 if (mce->bank != 8)
1692 return 0;
1693
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001694 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001695 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) {
1696 debugf0("mc%d: ignoring mce log for socket %d. "
1697 "Another mc should get it.\n",
1698 pvt->i7core_dev->socket,
1699 cpu_data(mce->cpu).phys_proc_id);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001700 return 0;
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001701 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001702
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001703 spin_lock_irqsave(&pvt->mce_lock, flags);
1704 if (pvt->mce_count < MCE_LOG_LEN) {
1705 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1706 pvt->mce_count++;
1707 }
1708 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1709
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001710 /* Handle fatal errors immediately */
1711 if (mce->mcgstatus & 1)
1712 i7core_check_error(mci);
1713
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001714 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001715 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001716}
1717
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001718static int i7core_register_mci(struct i7core_dev *i7core_dev,
1719 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001720{
1721 struct mem_ctl_info *mci;
1722 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001723 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001724 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001725
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001726 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001727 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1728 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001729 if (unlikely(!mci))
1730 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001731
1732 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1733
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001734 /* record ptr to the generic device */
1735 mci->dev = &i7core_dev->pdev[0]->dev;
1736
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001737 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001738 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001739
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001740 /*
1741 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1742 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1743 * memory channels
1744 */
1745 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001746 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1747 mci->edac_cap = EDAC_FLAG_NONE;
1748 mci->mod_name = "i7core_edac.c";
1749 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001750 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1751 i7core_dev->socket);
1752 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001753 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001754 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001755 /* Set the function pointer to an actual operation function */
1756 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001757
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001758 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001759 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001760 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001761 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001762
1763 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001764 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001765
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001766 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001767 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001768 debugf0("MC: " __FILE__
1769 ": %s(): failed edac_mc_add_mc()\n", __func__);
1770 /* FIXME: perhaps some code should go here that disables error
1771 * reporting if we just enabled it
1772 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001773
1774 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001775 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001776 }
1777
1778 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001779 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1780 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001781 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001782 printk(KERN_WARNING
1783 "%s(): Unable to create PCI control\n",
1784 __func__);
1785 printk(KERN_WARNING
1786 "%s(): PCI error report via EDAC not setup\n",
1787 __func__);
1788 }
1789
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001790 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001791 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001792 pvt->inject.dimm = -1;
1793 pvt->inject.rank = -1;
1794 pvt->inject.bank = -1;
1795 pvt->inject.page = -1;
1796 pvt->inject.col = -1;
1797
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001798 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001799 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001800 pvt->edac_mce.check_error = i7core_mce_check_error;
1801 spin_lock_init(&pvt->mce_lock);
1802
1803 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001804 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001805 debugf0("MC: " __FILE__
1806 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001807 }
1808
1809fail:
1810 edac_mc_free(mci);
1811 return rc;
1812}
1813
1814/*
1815 * i7core_probe Probe for ONE instance of device to see if it is
1816 * present.
1817 * return:
1818 * 0 for FOUND a device
1819 * < 0 for error code
1820 */
1821static int __devinit i7core_probe(struct pci_dev *pdev,
1822 const struct pci_device_id *id)
1823{
1824 int dev_idx = id->driver_data;
1825 int rc;
1826 struct i7core_dev *i7core_dev;
1827
1828 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001829 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001830 */
1831 if (unlikely(dev_idx >= 1))
1832 return -EINVAL;
1833
1834 /* get the pci devices we want to reserve for our use */
1835 mutex_lock(&i7core_edac_lock);
1836 rc = i7core_get_devices();
1837 if (unlikely(rc < 0))
1838 goto fail0;
1839
1840 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1841 int channels;
1842 int csrows;
1843
1844 /* Check the number of active and not disabled channels */
1845 rc = i7core_get_active_channels(i7core_dev->socket,
1846 &channels, &csrows);
1847 if (unlikely(rc < 0))
1848 goto fail1;
1849
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001850 rc = i7core_register_mci(i7core_dev, channels, csrows);
1851 if (unlikely(rc < 0))
1852 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001853 }
1854
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001855 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001856
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001857 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001858 return 0;
1859
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001860fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001861 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001862fail0:
1863 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001864 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001865}
1866
1867/*
1868 * i7core_remove destructor for one instance of device
1869 *
1870 */
1871static void __devexit i7core_remove(struct pci_dev *pdev)
1872{
1873 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001874 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001875
1876 debugf0(__FILE__ ": %s()\n", __func__);
1877
1878 if (i7core_pci)
1879 edac_pci_release_generic_ctl(i7core_pci);
1880
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001881 /*
1882 * we have a trouble here: pdev value for removal will be wrong, since
1883 * it will point to the X58 register used to detect that the machine
1884 * is a Nehalem or upper design. However, due to the way several PCI
1885 * devices are grouped together to provide MC functionality, we need
1886 * to use a different method for releasing the devices
1887 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001888
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001889 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001890 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1891 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1892 if (mci) {
1893 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001894
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001895 i7core_dev = pvt->i7core_dev;
1896 edac_mce_unregister(&pvt->edac_mce);
1897 kfree(mci->ctl_name);
1898 edac_mc_free(mci);
1899 i7core_put_devices(i7core_dev);
1900 } else {
1901 i7core_printk(KERN_ERR,
1902 "Couldn't find mci for socket %d\n",
1903 i7core_dev->socket);
1904 }
1905 }
1906 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001907}
1908
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001909MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1910
1911/*
1912 * i7core_driver pci_driver structure for this module
1913 *
1914 */
1915static struct pci_driver i7core_driver = {
1916 .name = "i7core_edac",
1917 .probe = i7core_probe,
1918 .remove = __devexit_p(i7core_remove),
1919 .id_table = i7core_pci_tbl,
1920};
1921
1922/*
1923 * i7core_init Module entry function
1924 * Try to initialize this module for its devices
1925 */
1926static int __init i7core_init(void)
1927{
1928 int pci_rc;
1929
1930 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1931
1932 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1933 opstate_init();
1934
Keith Manntheybc2d7242009-09-03 00:05:05 -03001935 i7core_xeon_pci_fixup();
1936
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001937 pci_rc = pci_register_driver(&i7core_driver);
1938
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001939 if (pci_rc >= 0)
1940 return 0;
1941
1942 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1943 pci_rc);
1944
1945 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001946}
1947
1948/*
1949 * i7core_exit() Module exit function
1950 * Unregister the driver
1951 */
1952static void __exit i7core_exit(void)
1953{
1954 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1955 pci_unregister_driver(&i7core_driver);
1956}
1957
1958module_init(i7core_init);
1959module_exit(i7core_exit);
1960
1961MODULE_LICENSE("GPL");
1962MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1963MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1964MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1965 I7CORE_REVISION);
1966
1967module_param(edac_op_state, int, 0444);
1968MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");