blob: e013004745de27eae103fc14043fd27e53a1509e [file] [log] [blame]
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001/* Intel 7 core Memory Controller kernel module (Nehalem)
2 *
3 * This file may be distributed under the terms of the
4 * GNU General Public License version 2 only.
5 *
6 * Copyright (c) 2009 by:
7 * Mauro Carvalho Chehab <mchehab@redhat.com>
8 *
9 * Red Hat Inc. http://www.redhat.com
10 *
11 * Forked and adapted from the i5400_edac driver
12 *
13 * Based on the following public Intel datasheets:
14 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
15 * Datasheet, Volume 2:
16 * http://download.intel.com/design/processor/datashts/320835.pdf
17 * Intel Xeon Processor 5500 Series Datasheet Volume 2
18 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
19 * also available at:
20 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
21 */
22
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030023#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/pci.h>
26#include <linux/pci_ids.h>
27#include <linux/slab.h>
28#include <linux/edac.h>
29#include <linux/mmzone.h>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030030#include <linux/edac_mce.h>
31#include <linux/spinlock.h>
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030032#include <linux/smp.h>
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -030033#include <asm/processor.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030034
35#include "edac_core.h"
36
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030037/*
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030038 * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
39 * registers start at bus 255, and are not reported by BIOS.
40 * We currently find devices with only 2 sockets. In order to support more QPI
41 * Quick Path Interconnect, just increment this number.
42 */
43#define MAX_SOCKET_BUSES 2
44
45
46/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030047 * Alter this version for the module when modifications are made
48 */
49#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
50#define EDAC_MOD_STR "i7core_edac"
51
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030052/*
53 * Debug macros
54 */
55#define i7core_printk(level, fmt, arg...) \
56 edac_printk(level, "i7core", fmt, ##arg)
57
58#define i7core_mc_printk(mci, level, fmt, arg...) \
59 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
60
61/*
62 * i7core Memory Controller Registers
63 */
64
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030065 /* OFFSETS for Device 0 Function 0 */
66
67#define MC_CFG_CONTROL 0x90
68
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030069 /* OFFSETS for Device 3 Function 0 */
70
71#define MC_CONTROL 0x48
72#define MC_STATUS 0x4c
73#define MC_MAX_DOD 0x64
74
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030075/*
76 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
77 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
78 */
79
80#define MC_TEST_ERR_RCV1 0x60
81 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
82
83#define MC_TEST_ERR_RCV0 0x64
84 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
85 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
86
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -030087/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
88#define MC_COR_ECC_CNT_0 0x80
89#define MC_COR_ECC_CNT_1 0x84
90#define MC_COR_ECC_CNT_2 0x88
91#define MC_COR_ECC_CNT_3 0x8c
92#define MC_COR_ECC_CNT_4 0x90
93#define MC_COR_ECC_CNT_5 0x94
94
95#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
96#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
97
98
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030099 /* OFFSETS for Devices 4,5 and 6 Function 0 */
100
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300101#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
102 #define THREE_DIMMS_PRESENT (1 << 24)
103 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
104 #define QUAD_RANK_PRESENT (1 << 22)
105 #define REGISTERED_DIMM (1 << 15)
106
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300107#define MC_CHANNEL_MAPPER 0x60
108 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
109 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
110
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300111#define MC_CHANNEL_RANK_PRESENT 0x7c
112 #define RANK_PRESENT_MASK 0xffff
113
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300114#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300115#define MC_CHANNEL_ERROR_MASK 0xf8
116#define MC_CHANNEL_ERROR_INJECT 0xfc
117 #define INJECT_ADDR_PARITY 0x10
118 #define INJECT_ECC 0x08
119 #define MASK_CACHELINE 0x06
120 #define MASK_FULL_CACHELINE 0x06
121 #define MASK_MSB32_CACHELINE 0x04
122 #define MASK_LSB32_CACHELINE 0x02
123 #define NO_MASK_CACHELINE 0x00
124 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300125
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300126 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300127
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300128#define MC_DOD_CH_DIMM0 0x48
129#define MC_DOD_CH_DIMM1 0x4c
130#define MC_DOD_CH_DIMM2 0x50
131 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
132 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
133 #define DIMM_PRESENT_MASK (1 << 9)
134 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300135 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
136 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
137 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
138 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300139 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300140 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300141 #define MC_DOD_NUMCOL_MASK 3
142 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300143
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300144#define MC_RANK_PRESENT 0x7c
145
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300146#define MC_SAG_CH_0 0x80
147#define MC_SAG_CH_1 0x84
148#define MC_SAG_CH_2 0x88
149#define MC_SAG_CH_3 0x8c
150#define MC_SAG_CH_4 0x90
151#define MC_SAG_CH_5 0x94
152#define MC_SAG_CH_6 0x98
153#define MC_SAG_CH_7 0x9c
154
155#define MC_RIR_LIMIT_CH_0 0x40
156#define MC_RIR_LIMIT_CH_1 0x44
157#define MC_RIR_LIMIT_CH_2 0x48
158#define MC_RIR_LIMIT_CH_3 0x4C
159#define MC_RIR_LIMIT_CH_4 0x50
160#define MC_RIR_LIMIT_CH_5 0x54
161#define MC_RIR_LIMIT_CH_6 0x58
162#define MC_RIR_LIMIT_CH_7 0x5C
163#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
164
165#define MC_RIR_WAY_CH 0x80
166 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
167 #define MC_RIR_WAY_RANK_MASK 0x7
168
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300169/*
170 * i7core structs
171 */
172
173#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300174#define MAX_DIMMS 3 /* Max DIMMS per channel */
175#define MAX_MCR_FUNC 4
176#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300177
178struct i7core_info {
179 u32 mc_control;
180 u32 mc_status;
181 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300182 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300183};
184
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300185
186struct i7core_inject {
187 int enable;
188
189 u32 section;
190 u32 type;
191 u32 eccmask;
192
193 /* Error address mask */
194 int channel, dimm, rank, bank, page, col;
195};
196
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300197struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300198 u32 ranks;
199 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300200};
201
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300202struct pci_id_descr {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300203 int dev;
204 int func;
205 int dev_id;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300206};
207
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300208struct i7core_dev {
209 struct list_head list;
210 u8 socket;
211 struct pci_dev **pdev;
212 struct mem_ctl_info *mci;
213};
214
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300215struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300216 struct pci_dev *pci_noncore;
217 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
218 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
219
220 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300221
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300222 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300223 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300224 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300225
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300226 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300227
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300228 int ce_count_available;
229 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300230
231 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300232 unsigned long udimm_ce_count[MAX_DIMMS];
233 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300234 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300235 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
236 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300237
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300238 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300239
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300240 /* mcelog glue */
241 struct edac_mce edac_mce;
242 struct mce mce_entry[MCE_LOG_LEN];
243 unsigned mce_count;
244 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300245};
246
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300247/* Static vars */
248static LIST_HEAD(i7core_edac_list);
249static DEFINE_MUTEX(i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300250
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300251#define PCI_DESCR(device, function, device_id) \
252 .dev = (device), \
253 .func = (function), \
254 .dev_id = (device_id)
255
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300256struct pci_id_descr pci_dev_descr[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300257 /* Memory controller */
258 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
259 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300260 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300261 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
262
263 /* Channel 0 */
264 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
265 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
266 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
267 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
268
269 /* Channel 1 */
270 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
271 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
272 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
273 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
274
275 /* Channel 2 */
276 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
277 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
278 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
279 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300280
281 /* Generic Non-core registers */
282 /*
283 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
284 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
285 * the probing code needs to test for the other address in case of
286 * failure of this one
287 */
288 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
289
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300290};
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300291#define N_DEVS ARRAY_SIZE(pci_dev_descr)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300292
293/*
294 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300295 */
296static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300297 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300298 {0,} /* 0 terminated list. */
299};
300
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300301static struct edac_pci_ctl_info *i7core_pci;
302
303/****************************************************************************
304 Anciliary status routines
305 ****************************************************************************/
306
307 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300308#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
309#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300310
311 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300312#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300313#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300314
315 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300316static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300317{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300318 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300319}
320
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300321static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300322{
323 static int ranks[4] = { 1, 2, 4, -EINVAL };
324
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300325 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300326}
327
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300328static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300329{
330 static int banks[4] = { 4, 8, 16, -EINVAL };
331
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300332 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300333}
334
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300335static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300336{
337 static int rows[8] = {
338 1 << 12, 1 << 13, 1 << 14, 1 << 15,
339 1 << 16, -EINVAL, -EINVAL, -EINVAL,
340 };
341
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300342 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300343}
344
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300345static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300346{
347 static int cols[8] = {
348 1 << 10, 1 << 11, 1 << 12, -EINVAL,
349 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300350 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300351}
352
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300353static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300354{
355 struct i7core_dev *i7core_dev;
356
357 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
358 if (i7core_dev->socket == socket)
359 return i7core_dev;
360 }
361
362 return NULL;
363}
364
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300365/****************************************************************************
366 Memory check routines
367 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300368static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
369 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300370{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300371 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300372 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300373
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300374 if (!i7core_dev)
375 return NULL;
376
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300377 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300378 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300379 continue;
380
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300381 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
382 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
383 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300384 }
385 }
386
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300387 return NULL;
388}
389
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300390/**
391 * i7core_get_active_channels() - gets the number of channels and csrows
392 * @socket: Quick Path Interconnect socket
393 * @channels: Number of channels that will be returned
394 * @csrows: Number of csrows found
395 *
396 * Since EDAC core needs to know in advance the number of available channels
397 * and csrows, in order to allocate memory for csrows/channels, it is needed
398 * to run two similar steps. At the first step, implemented on this function,
399 * it checks the number of csrows/channels present at one socket.
400 * this is used in order to properly allocate the size of mci components.
401 *
402 * It should be noticed that none of the current available datasheets explain
403 * or even mention how csrows are seen by the memory controller. So, we need
404 * to add a fake description for csrows.
405 * So, this driver is attributing one DIMM memory for one csrow.
406 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300407static int i7core_get_active_channels(u8 socket, unsigned *channels,
408 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300409{
410 struct pci_dev *pdev = NULL;
411 int i, j;
412 u32 status, control;
413
414 *channels = 0;
415 *csrows = 0;
416
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300417 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300418 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300419 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
420 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300421 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300422 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300423
424 /* Device 3 function 0 reads */
425 pci_read_config_dword(pdev, MC_STATUS, &status);
426 pci_read_config_dword(pdev, MC_CONTROL, &control);
427
428 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300429 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300430 /* Check if the channel is active */
431 if (!(control & (1 << (8 + i))))
432 continue;
433
434 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300435 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300436 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300437
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300438 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300439 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300440 i7core_printk(KERN_ERR, "Couldn't find socket %d "
441 "fn %d.%d!!!\n",
442 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300443 return -ENODEV;
444 }
445 /* Devices 4-6 function 1 */
446 pci_read_config_dword(pdev,
447 MC_DOD_CH_DIMM0, &dimm_dod[0]);
448 pci_read_config_dword(pdev,
449 MC_DOD_CH_DIMM1, &dimm_dod[1]);
450 pci_read_config_dword(pdev,
451 MC_DOD_CH_DIMM2, &dimm_dod[2]);
452
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300453 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300454
455 for (j = 0; j < 3; j++) {
456 if (!DIMM_PRESENT(dimm_dod[j]))
457 continue;
458 (*csrows)++;
459 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300460 }
461
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300462 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300463 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300464
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300465 return 0;
466}
467
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300468static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300469{
470 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300471 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300472 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300473 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300474 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300475 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300476 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300477
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300478 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300479 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300480 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300481 return -ENODEV;
482
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300483 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300484 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
485 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
486 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
487 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300488
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300489 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300490 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300491 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300492
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300493 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300494 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300495 if (ECCx8(pvt))
496 mode = EDAC_S8ECD8ED;
497 else
498 mode = EDAC_S4ECD4ED;
499 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300500 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300501 mode = EDAC_NONE;
502 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300503
504 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300505 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
506 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300507 numdimms(pvt->info.max_dod),
508 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300509 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300510 numrow(pvt->info.max_dod >> 6),
511 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300512
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300513 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300514 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300515
516 if (!CH_ACTIVE(pvt, i)) {
517 debugf0("Channel %i is not active\n", i);
518 continue;
519 }
520 if (CH_DISABLED(pvt, i)) {
521 debugf0("Channel %i is disabled\n", i);
522 continue;
523 }
524
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300525 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300526 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300527 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
528
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300529 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300530 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300531
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300532 if (data & REGISTERED_DIMM)
533 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300534 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300535 mtype = MEM_DDR3;
536#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300537 if (data & THREE_DIMMS_PRESENT)
538 pvt->channel[i].dimms = 3;
539 else if (data & SINGLE_QUAD_RANK_PRESENT)
540 pvt->channel[i].dimms = 1;
541 else
542 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300543#endif
544
545 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300546 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300547 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300548 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300549 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300550 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300551 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300552
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300553 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300554 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300555 i,
556 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
557 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300558 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300559 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300560
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300561 for (j = 0; j < 3; j++) {
562 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300563 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300564
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300565 if (!DIMM_PRESENT(dimm_dod[j]))
566 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300567
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300568 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
569 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
570 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
571 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300572
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300573 /* DDR3 has 8 I/O banks */
574 size = (rows * cols * banks * ranks) >> (20 - 3);
575
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300576 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300577
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300578 debugf0("\tdimm %d %d Mb offset: %x, "
579 "bank: %d, rank: %d, row: %#x, col: %#x\n",
580 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300581 RANKOFFSET(dimm_dod[j]),
582 banks, ranks, rows, cols);
583
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300584#if PAGE_SHIFT > 20
585 npages = size >> (PAGE_SHIFT - 20);
586#else
587 npages = size << (20 - PAGE_SHIFT);
588#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300589
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300590 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300591 csr->first_page = last_page + 1;
592 last_page += npages;
593 csr->last_page = last_page;
594 csr->nr_pages = npages;
595
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300596 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300597 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300598 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300599 csr->nr_channels = 1;
600
601 csr->channels[0].chan_idx = i;
602 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300603
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300604 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300605
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300606 switch (banks) {
607 case 4:
608 csr->dtype = DEV_X4;
609 break;
610 case 8:
611 csr->dtype = DEV_X8;
612 break;
613 case 16:
614 csr->dtype = DEV_X16;
615 break;
616 default:
617 csr->dtype = DEV_UNKNOWN;
618 }
619
620 csr->edac_mode = mode;
621 csr->mtype = mtype;
622
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300623 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300624 }
625
626 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
627 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
628 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
629 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
630 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
631 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
632 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
633 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300634 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300635 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300636 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300637 (value[j] >> 27) & 0x1,
638 (value[j] >> 24) & 0x7,
639 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300640 }
641
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300642 return 0;
643}
644
645/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300646 Error insertion routines
647 ****************************************************************************/
648
649/* The i7core has independent error injection features per channel.
650 However, to have a simpler code, we don't allow enabling error injection
651 on more than one channel.
652 Also, since a change at an inject parameter will be applied only at enable,
653 we're disabling error injection on all write calls to the sysfs nodes that
654 controls the error code injection.
655 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300656static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300657{
658 struct i7core_pvt *pvt = mci->pvt_info;
659
660 pvt->inject.enable = 0;
661
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300662 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300663 return -ENODEV;
664
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300665 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300666 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300667
668 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300669}
670
671/*
672 * i7core inject inject.section
673 *
674 * accept and store error injection inject.section value
675 * bit 0 - refers to the lower 32-byte half cacheline
676 * bit 1 - refers to the upper 32-byte half cacheline
677 */
678static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
679 const char *data, size_t count)
680{
681 struct i7core_pvt *pvt = mci->pvt_info;
682 unsigned long value;
683 int rc;
684
685 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300686 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300687
688 rc = strict_strtoul(data, 10, &value);
689 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300690 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300691
692 pvt->inject.section = (u32) value;
693 return count;
694}
695
696static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
697 char *data)
698{
699 struct i7core_pvt *pvt = mci->pvt_info;
700 return sprintf(data, "0x%08x\n", pvt->inject.section);
701}
702
703/*
704 * i7core inject.type
705 *
706 * accept and store error injection inject.section value
707 * bit 0 - repeat enable - Enable error repetition
708 * bit 1 - inject ECC error
709 * bit 2 - inject parity error
710 */
711static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
712 const char *data, size_t count)
713{
714 struct i7core_pvt *pvt = mci->pvt_info;
715 unsigned long value;
716 int rc;
717
718 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300719 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300720
721 rc = strict_strtoul(data, 10, &value);
722 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300723 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300724
725 pvt->inject.type = (u32) value;
726 return count;
727}
728
729static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
730 char *data)
731{
732 struct i7core_pvt *pvt = mci->pvt_info;
733 return sprintf(data, "0x%08x\n", pvt->inject.type);
734}
735
736/*
737 * i7core_inject_inject.eccmask_store
738 *
739 * The type of error (UE/CE) will depend on the inject.eccmask value:
740 * Any bits set to a 1 will flip the corresponding ECC bit
741 * Correctable errors can be injected by flipping 1 bit or the bits within
742 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
743 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
744 * uncorrectable error to be injected.
745 */
746static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
747 const char *data, size_t count)
748{
749 struct i7core_pvt *pvt = mci->pvt_info;
750 unsigned long value;
751 int rc;
752
753 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300754 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300755
756 rc = strict_strtoul(data, 10, &value);
757 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300758 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300759
760 pvt->inject.eccmask = (u32) value;
761 return count;
762}
763
764static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
765 char *data)
766{
767 struct i7core_pvt *pvt = mci->pvt_info;
768 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
769}
770
771/*
772 * i7core_addrmatch
773 *
774 * The type of error (UE/CE) will depend on the inject.eccmask value:
775 * Any bits set to a 1 will flip the corresponding ECC bit
776 * Correctable errors can be injected by flipping 1 bit or the bits within
777 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
778 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
779 * uncorrectable error to be injected.
780 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300781
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300782#define DECLARE_ADDR_MATCH(param, limit) \
783static ssize_t i7core_inject_store_##param( \
784 struct mem_ctl_info *mci, \
785 const char *data, size_t count) \
786{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300787 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300788 long value; \
789 int rc; \
790 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300791 debugf1("%s()\n", __func__); \
792 pvt = mci->pvt_info; \
793 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300794 if (pvt->inject.enable) \
795 disable_inject(mci); \
796 \
797 if (!strcasecmp(data, "any")) \
798 value = -1; \
799 else { \
800 rc = strict_strtoul(data, 10, &value); \
801 if ((rc < 0) || (value >= limit)) \
802 return -EIO; \
803 } \
804 \
805 pvt->inject.param = value; \
806 \
807 return count; \
808} \
809 \
810static ssize_t i7core_inject_show_##param( \
811 struct mem_ctl_info *mci, \
812 char *data) \
813{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300814 struct i7core_pvt *pvt; \
815 \
816 pvt = mci->pvt_info; \
817 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300818 if (pvt->inject.param < 0) \
819 return sprintf(data, "any\n"); \
820 else \
821 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300822}
823
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300824#define ATTR_ADDR_MATCH(param) \
825 { \
826 .attr = { \
827 .name = #param, \
828 .mode = (S_IRUGO | S_IWUSR) \
829 }, \
830 .show = i7core_inject_show_##param, \
831 .store = i7core_inject_store_##param, \
832 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300833
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300834DECLARE_ADDR_MATCH(channel, 3);
835DECLARE_ADDR_MATCH(dimm, 3);
836DECLARE_ADDR_MATCH(rank, 4);
837DECLARE_ADDR_MATCH(bank, 32);
838DECLARE_ADDR_MATCH(page, 0x10000);
839DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300840
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300841static int write_and_test(struct pci_dev *dev, int where, u32 val)
842{
843 u32 read;
844 int count;
845
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300846 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
847 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
848 where, val);
849
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300850 for (count = 0; count < 10; count++) {
851 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300852 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300853 pci_write_config_dword(dev, where, val);
854 pci_read_config_dword(dev, where, &read);
855
856 if (read == val)
857 return 0;
858 }
859
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300860 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
861 "write=%08x. Read=%08x\n",
862 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
863 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300864
865 return -EINVAL;
866}
867
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300868/*
869 * This routine prepares the Memory Controller for error injection.
870 * The error will be injected when some process tries to write to the
871 * memory that matches the given criteria.
872 * The criteria can be set in terms of a mask where dimm, rank, bank, page
873 * and col can be specified.
874 * A -1 value for any of the mask items will make the MCU to ignore
875 * that matching criteria for error injection.
876 *
877 * It should be noticed that the error will only happen after a write operation
878 * on a memory that matches the condition. if REPEAT_EN is not enabled at
879 * inject mask, then it will produce just one error. Otherwise, it will repeat
880 * until the injectmask would be cleaned.
881 *
882 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
883 * is reliable enough to check if the MC is using the
884 * three channels. However, this is not clear at the datasheet.
885 */
886static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
887 const char *data, size_t count)
888{
889 struct i7core_pvt *pvt = mci->pvt_info;
890 u32 injectmask;
891 u64 mask = 0;
892 int rc;
893 long enable;
894
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300895 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300896 return 0;
897
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300898 rc = strict_strtoul(data, 10, &enable);
899 if ((rc < 0))
900 return 0;
901
902 if (enable) {
903 pvt->inject.enable = 1;
904 } else {
905 disable_inject(mci);
906 return count;
907 }
908
909 /* Sets pvt->inject.dimm mask */
910 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300911 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300912 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300913 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300914 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300915 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300916 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300917 }
918
919 /* Sets pvt->inject.rank mask */
920 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300921 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300922 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300923 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300924 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300925 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300926 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300927 }
928
929 /* Sets pvt->inject.bank mask */
930 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300931 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300932 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300933 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300934
935 /* Sets pvt->inject.page mask */
936 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300937 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300938 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300939 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300940
941 /* Sets pvt->inject.column mask */
942 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300943 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300944 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300945 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300946
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300947 /*
948 * bit 0: REPEAT_EN
949 * bits 1-2: MASK_HALF_CACHELINE
950 * bit 3: INJECT_ECC
951 * bit 4: INJECT_ADDR_PARITY
952 */
953
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300954 injectmask = (pvt->inject.type & 1) |
955 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300956 (pvt->inject.type & 0x6) << (3 - 1);
957
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300958 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300959 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300960 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300961
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300962 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300963 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300964 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300965 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
966
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300967 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300968 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
969
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300970 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300971 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300972
973 /*
974 * This is something undocumented, based on my tests
975 * Without writing 8 to this register, errors aren't injected. Not sure
976 * why.
977 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300978 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300979 MC_CFG_CONTROL, 8);
980
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300981 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
982 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300983 mask, pvt->inject.eccmask, injectmask);
984
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300985
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300986 return count;
987}
988
989static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
990 char *data)
991{
992 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300993 u32 injectmask;
994
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300995 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300996 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300997
998 debugf0("Inject error read: 0x%018x\n", injectmask);
999
1000 if (injectmask & 0x0c)
1001 pvt->inject.enable = 1;
1002
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001003 return sprintf(data, "%d\n", pvt->inject.enable);
1004}
1005
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001006static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1007{
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001008 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001009 struct i7core_pvt *pvt = mci->pvt_info;
1010
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001011 if (!pvt->ce_count_available) {
1012 count = sprintf(data, "data unavailable\n");
1013 return 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001014 }
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001015 if (!pvt->is_registered) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001016 count = sprintf(data, "all channels "
1017 "UDIMM0: %lu UDIMM1: %lu UDIMM2: %lu\n",
1018 pvt->udimm_ce_count[0],
1019 pvt->udimm_ce_count[1],
1020 pvt->udimm_ce_count[2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001021 data += count;
1022 total += count;
1023 } else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001024 for (i = 0; i < NUM_CHANS; i++) {
1025 count = sprintf(data, "channel %d RDIMM0: %lu "
1026 "RDIMM1: %lu RDIMM2: %lu\n",
1027 i,
1028 pvt->rdimm_ce_count[i][0],
1029 pvt->rdimm_ce_count[i][1],
1030 pvt->rdimm_ce_count[i][2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001031 data += count;
1032 total += count;
1033 }
1034 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001035
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001036 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001037}
1038
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001039/*
1040 * Sysfs struct
1041 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001042
1043
1044static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
1045 ATTR_ADDR_MATCH(channel),
1046 ATTR_ADDR_MATCH(dimm),
1047 ATTR_ADDR_MATCH(rank),
1048 ATTR_ADDR_MATCH(bank),
1049 ATTR_ADDR_MATCH(page),
1050 ATTR_ADDR_MATCH(col),
1051 { .attr = { .name = NULL } }
1052};
1053
1054
1055static struct mcidev_sysfs_group i7core_inject_addrmatch = {
1056 .name = "inject_addrmatch",
1057 .mcidev_attr = i7core_addrmatch_attrs,
1058};
1059
1060static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001061 {
1062 .attr = {
1063 .name = "inject_section",
1064 .mode = (S_IRUGO | S_IWUSR)
1065 },
1066 .show = i7core_inject_section_show,
1067 .store = i7core_inject_section_store,
1068 }, {
1069 .attr = {
1070 .name = "inject_type",
1071 .mode = (S_IRUGO | S_IWUSR)
1072 },
1073 .show = i7core_inject_type_show,
1074 .store = i7core_inject_type_store,
1075 }, {
1076 .attr = {
1077 .name = "inject_eccmask",
1078 .mode = (S_IRUGO | S_IWUSR)
1079 },
1080 .show = i7core_inject_eccmask_show,
1081 .store = i7core_inject_eccmask_store,
1082 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001083 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001084 }, {
1085 .attr = {
1086 .name = "inject_enable",
1087 .mode = (S_IRUGO | S_IWUSR)
1088 },
1089 .show = i7core_inject_enable_show,
1090 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001091 }, {
1092 .attr = {
1093 .name = "corrected_error_counts",
1094 .mode = (S_IRUGO | S_IWUSR)
1095 },
1096 .show = i7core_ce_regs_show,
1097 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001098 },
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001099 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001100};
1101
1102/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001103 Device initialization routines: put/get, init/exit
1104 ****************************************************************************/
1105
1106/*
1107 * i7core_put_devices 'put' all the devices that we have
1108 * reserved via 'get'
1109 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001110static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001111{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001112 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001113
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001114 debugf0(__FILE__ ": %s()\n", __func__);
1115 for (i = 0; i < N_DEVS; i++) {
1116 struct pci_dev *pdev = i7core_dev->pdev[i];
1117 if (!pdev)
1118 continue;
1119 debugf0("Removing dev %02x:%02x.%d\n",
1120 pdev->bus->number,
1121 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1122 pci_dev_put(pdev);
1123 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001124 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001125 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001126 kfree(i7core_dev);
1127}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001128
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001129static void i7core_put_all_devices(void)
1130{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001131 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001132
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001133 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001134 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001135}
1136
Keith Manntheybc2d7242009-09-03 00:05:05 -03001137static void i7core_xeon_pci_fixup(void)
1138{
1139 struct pci_dev *pdev = NULL;
1140 int i;
1141 /*
1142 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1143 * aren't announced by acpi. So, we need to use a legacy scan probing
1144 * to detect them
1145 */
1146 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001147 pci_dev_descr[0].dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001148 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001149 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001150 pcibios_scan_specific_bus(255-i);
1151 }
1152}
1153
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001154/*
1155 * i7core_get_devices Find and perform 'get' operation on the MCH's
1156 * device/functions we want to reference for this driver
1157 *
1158 * Need to 'get' device 16 func 1 and func 2
1159 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001160int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001161{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001162 struct i7core_dev *i7core_dev;
1163
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001164 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001165 u8 bus = 0;
1166 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001167
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001168 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001169 pci_dev_descr[devno].dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001170
1171 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001172 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1173 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1174 * to probe for the alternate address in case of failure
1175 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001176 if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001177 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1178 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001179
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001180 if (!pdev) {
1181 if (*prev) {
1182 *prev = pdev;
1183 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001184 }
1185
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001186 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001187 * Dev 3 function 2 only exists on chips with RDIMMs
1188 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001189 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001190 if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001191 *prev = pdev;
1192 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001193 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001194
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001195 i7core_printk(KERN_ERR,
1196 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001197 pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1198 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001199
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001200 /* End of list, leave */
1201 return -ENODEV;
1202 }
1203 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001204
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001205 if (bus == 0x3f)
1206 socket = 0;
1207 else
1208 socket = 255 - bus;
1209
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001210 i7core_dev = get_i7core_dev(socket);
1211 if (!i7core_dev) {
1212 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1213 if (!i7core_dev)
1214 return -ENOMEM;
1215 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
1216 GFP_KERNEL);
1217 if (!i7core_dev->pdev)
1218 return -ENOMEM;
1219 i7core_dev->socket = socket;
1220 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001221 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001222
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001223 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001224 i7core_printk(KERN_ERR,
1225 "Duplicated device for "
1226 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001227 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1228 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001229 pci_dev_put(pdev);
1230 return -ENODEV;
1231 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001232
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001233 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001234
1235 /* Sanity check */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001236 if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
1237 PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001238 i7core_printk(KERN_ERR,
1239 "Device PCI ID %04x:%04x "
1240 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001241 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001242 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001243 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001244 return -ENODEV;
1245 }
1246
1247 /* Be sure that the device is enabled */
1248 if (unlikely(pci_enable_device(pdev) < 0)) {
1249 i7core_printk(KERN_ERR,
1250 "Couldn't enable "
1251 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001252 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1253 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001254 return -ENODEV;
1255 }
1256
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001257 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1258 socket, bus, pci_dev_descr[devno].dev,
1259 pci_dev_descr[devno].func,
1260 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001261
1262 *prev = pdev;
1263
1264 return 0;
1265}
1266
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001267static int i7core_get_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001268{
1269 int i;
1270 struct pci_dev *pdev = NULL;
1271
1272 for (i = 0; i < N_DEVS; i++) {
1273 pdev = NULL;
1274 do {
1275 if (i7core_get_onedevice(&pdev, i) < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001276 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001277 return -ENODEV;
1278 }
1279 } while (pdev);
1280 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001281
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001282 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001283}
1284
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001285static int mci_bind_devs(struct mem_ctl_info *mci,
1286 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001287{
1288 struct i7core_pvt *pvt = mci->pvt_info;
1289 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001290 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001291
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001292 /* Associates i7core_dev and mci for future usage */
1293 pvt->i7core_dev = i7core_dev;
1294 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001295
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001296 pvt->is_registered = 0;
1297 for (i = 0; i < N_DEVS; i++) {
1298 pdev = i7core_dev->pdev[i];
1299 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001300 continue;
1301
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001302 func = PCI_FUNC(pdev->devfn);
1303 slot = PCI_SLOT(pdev->devfn);
1304 if (slot == 3) {
1305 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001306 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001307 pvt->pci_mcr[func] = pdev;
1308 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1309 if (unlikely(func > MAX_CHAN_FUNC))
1310 goto error;
1311 pvt->pci_ch[slot - 4][func] = pdev;
1312 } else if (!slot && !func)
1313 pvt->pci_noncore = pdev;
1314 else
1315 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001316
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001317 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1318 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1319 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001320
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001321 if (PCI_SLOT(pdev->devfn) == 3 &&
1322 PCI_FUNC(pdev->devfn) == 2)
1323 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001324 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001325
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001326 return 0;
1327
1328error:
1329 i7core_printk(KERN_ERR, "Device %d, function %d "
1330 "is out of the expected range\n",
1331 slot, func);
1332 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001333}
1334
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001335/****************************************************************************
1336 Error check routines
1337 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001338static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001339 int chan, int dimm, int add)
1340{
1341 char *msg;
1342 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001343 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001344
1345 for (i = 0; i < add; i++) {
1346 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001347 "(Socket=%d channel=%d dimm=%d)",
1348 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001349
1350 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1351 kfree (msg);
1352 }
1353}
1354
1355static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001356 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001357{
1358 struct i7core_pvt *pvt = mci->pvt_info;
1359 int add0 = 0, add1 = 0, add2 = 0;
1360 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001361 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001362 /* Updates CE counters */
1363
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001364 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1365 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1366 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001367
1368 if (add2 < 0)
1369 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001370 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001371
1372 if (add1 < 0)
1373 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001374 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001375
1376 if (add0 < 0)
1377 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001378 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001379 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001380 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001381
1382 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001383 pvt->rdimm_last_ce_count[chan][2] = new2;
1384 pvt->rdimm_last_ce_count[chan][1] = new1;
1385 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001386
1387 /*updated the edac core */
1388 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001389 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001390 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001391 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001392 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001393 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001394
1395}
1396
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001397static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001398{
1399 struct i7core_pvt *pvt = mci->pvt_info;
1400 u32 rcv[3][2];
1401 int i, new0, new1, new2;
1402
1403 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001404 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001405 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001406 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001407 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001408 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001409 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001410 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001411 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001412 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001413 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001414 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001415 &rcv[2][1]);
1416 for (i = 0 ; i < 3; i++) {
1417 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1418 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1419 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001420 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001421 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1422 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1423 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1424 } else {
1425 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1426 DIMM_BOT_COR_ERR(rcv[i][0]);
1427 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1428 DIMM_BOT_COR_ERR(rcv[i][1]);
1429 new2 = 0;
1430 }
1431
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001432 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001433 }
1434}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001435
1436/* This function is based on the device 3 function 4 registers as described on:
1437 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1438 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1439 * also available at:
1440 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1441 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001442static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001443{
1444 struct i7core_pvt *pvt = mci->pvt_info;
1445 u32 rcv1, rcv0;
1446 int new0, new1, new2;
1447
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001448 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001449 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001450 return;
1451 }
1452
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001453 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001454 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1455 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001456
1457 /* Store the new values */
1458 new2 = DIMM2_COR_ERR(rcv1);
1459 new1 = DIMM1_COR_ERR(rcv0);
1460 new0 = DIMM0_COR_ERR(rcv0);
1461
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001462 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001463 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001464 /* Updates CE counters */
1465 int add0, add1, add2;
1466
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001467 add2 = new2 - pvt->udimm_last_ce_count[2];
1468 add1 = new1 - pvt->udimm_last_ce_count[1];
1469 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001470
1471 if (add2 < 0)
1472 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001473 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001474
1475 if (add1 < 0)
1476 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001477 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001478
1479 if (add0 < 0)
1480 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001481 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001482
1483 if (add0 | add1 | add2)
1484 i7core_printk(KERN_ERR, "New Corrected error(s): "
1485 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1486 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001487 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001488 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001489
1490 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001491 pvt->udimm_last_ce_count[2] = new2;
1492 pvt->udimm_last_ce_count[1] = new1;
1493 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001494}
1495
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001496/*
1497 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1498 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001499 * Nehalem are defined as family 0x06, model 0x1a
1500 *
1501 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001502 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001503 * m->status MSR_IA32_MC8_STATUS
1504 * m->addr MSR_IA32_MC8_ADDR
1505 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001506 * In the case of Nehalem, the error information is masked at .status and .misc
1507 * fields
1508 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001509static void i7core_mce_output_error(struct mem_ctl_info *mci,
1510 struct mce *m)
1511{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001512 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001513 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001514 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001515 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001516 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1517 u32 dimm = (m->misc >> 16) & 0x3;
1518 u32 channel = (m->misc >> 18) & 0x3;
1519 u32 syndrome = m->misc >> 32;
1520 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001521 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001522
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001523 if (m->mcgstatus & 1)
1524 type = "FATAL";
1525 else
1526 type = "NON_FATAL";
1527
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001528 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001529 case 0:
1530 optype = "generic undef request";
1531 break;
1532 case 1:
1533 optype = "read error";
1534 break;
1535 case 2:
1536 optype = "write error";
1537 break;
1538 case 3:
1539 optype = "addr/cmd error";
1540 break;
1541 case 4:
1542 optype = "scrubbing error";
1543 break;
1544 default:
1545 optype = "reserved";
1546 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001547 }
1548
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001549 switch (errnum) {
1550 case 16:
1551 err = "read ECC error";
1552 break;
1553 case 17:
1554 err = "RAS ECC error";
1555 break;
1556 case 18:
1557 err = "write parity error";
1558 break;
1559 case 19:
1560 err = "redundacy loss";
1561 break;
1562 case 20:
1563 err = "reserved";
1564 break;
1565 case 21:
1566 err = "memory range error";
1567 break;
1568 case 22:
1569 err = "RTID out of range";
1570 break;
1571 case 23:
1572 err = "address parity error";
1573 break;
1574 case 24:
1575 err = "byte enable parity error";
1576 break;
1577 default:
1578 err = "unknown";
1579 }
1580
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001581 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001582 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001583 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001584 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001585 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001586 syndrome, core_err_cnt, (long long)m->status,
1587 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001588
1589 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001590
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001591 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001592
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001593 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001594 if (m->mcgstatus & 1)
1595 edac_mc_handle_fbd_ue(mci, csrow, 0,
1596 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001597 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001598 edac_mc_handle_fbd_ce(mci, csrow,
1599 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001600
1601 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001602}
1603
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001604/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001605 * i7core_check_error Retrieve and process errors reported by the
1606 * hardware. Called by the Core module.
1607 */
1608static void i7core_check_error(struct mem_ctl_info *mci)
1609{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001610 struct i7core_pvt *pvt = mci->pvt_info;
1611 int i;
1612 unsigned count = 0;
1613 struct mce *m = NULL;
1614 unsigned long flags;
1615
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001616 /* Copy all mce errors into a temporary buffer */
1617 spin_lock_irqsave(&pvt->mce_lock, flags);
1618 if (pvt->mce_count) {
1619 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001620
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001621 if (m) {
1622 count = pvt->mce_count;
1623 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1624 }
1625 pvt->mce_count = 0;
1626 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001627
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001628 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1629
1630 /* proccess mcelog errors */
1631 for (i = 0; i < count; i++)
1632 i7core_mce_output_error(mci, &m[i]);
1633
1634 kfree(m);
1635
1636 /* check memory count errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001637 if (!pvt->is_registered)
1638 i7core_udimm_check_mc_ecc_err(mci);
1639 else
1640 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001641}
1642
1643/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001644 * i7core_mce_check_error Replicates mcelog routine to get errors
1645 * This routine simply queues mcelog errors, and
1646 * return. The error itself should be handled later
1647 * by i7core_check_error.
1648 */
1649static int i7core_mce_check_error(void *priv, struct mce *mce)
1650{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001651 struct mem_ctl_info *mci = priv;
1652 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001653 unsigned long flags;
1654
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001655 /*
1656 * Just let mcelog handle it if the error is
1657 * outside the memory controller
1658 */
1659 if (((mce->status & 0xffff) >> 7) != 1)
1660 return 0;
1661
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001662 /* Bank 8 registers are the only ones that we know how to handle */
1663 if (mce->bank != 8)
1664 return 0;
1665
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001666 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001667 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) {
1668 debugf0("mc%d: ignoring mce log for socket %d. "
1669 "Another mc should get it.\n",
1670 pvt->i7core_dev->socket,
1671 cpu_data(mce->cpu).phys_proc_id);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001672 return 0;
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001673 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001674
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001675 spin_lock_irqsave(&pvt->mce_lock, flags);
1676 if (pvt->mce_count < MCE_LOG_LEN) {
1677 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1678 pvt->mce_count++;
1679 }
1680 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1681
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001682 /* Handle fatal errors immediately */
1683 if (mce->mcgstatus & 1)
1684 i7core_check_error(mci);
1685
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001686 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001687 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001688}
1689
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001690static int i7core_register_mci(struct i7core_dev *i7core_dev,
1691 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001692{
1693 struct mem_ctl_info *mci;
1694 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001695 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001696 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001697
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001698 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001699 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1700 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001701 if (unlikely(!mci))
1702 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001703
1704 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1705
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001706 /* record ptr to the generic device */
1707 mci->dev = &i7core_dev->pdev[0]->dev;
1708
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001709 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001710 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001711
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001712 /*
1713 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1714 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1715 * memory channels
1716 */
1717 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001718 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1719 mci->edac_cap = EDAC_FLAG_NONE;
1720 mci->mod_name = "i7core_edac.c";
1721 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001722 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1723 i7core_dev->socket);
1724 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001725 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001726 mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001727 /* Set the function pointer to an actual operation function */
1728 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001729
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001730 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001731 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001732 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001733 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001734
1735 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001736 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001737
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001738 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001739 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001740 debugf0("MC: " __FILE__
1741 ": %s(): failed edac_mc_add_mc()\n", __func__);
1742 /* FIXME: perhaps some code should go here that disables error
1743 * reporting if we just enabled it
1744 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001745
1746 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001747 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001748 }
1749
1750 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001751 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1752 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001753 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001754 printk(KERN_WARNING
1755 "%s(): Unable to create PCI control\n",
1756 __func__);
1757 printk(KERN_WARNING
1758 "%s(): PCI error report via EDAC not setup\n",
1759 __func__);
1760 }
1761
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001762 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001763 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001764 pvt->inject.dimm = -1;
1765 pvt->inject.rank = -1;
1766 pvt->inject.bank = -1;
1767 pvt->inject.page = -1;
1768 pvt->inject.col = -1;
1769
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001770 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001771 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001772 pvt->edac_mce.check_error = i7core_mce_check_error;
1773 spin_lock_init(&pvt->mce_lock);
1774
1775 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001776 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001777 debugf0("MC: " __FILE__
1778 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001779 }
1780
1781fail:
1782 edac_mc_free(mci);
1783 return rc;
1784}
1785
1786/*
1787 * i7core_probe Probe for ONE instance of device to see if it is
1788 * present.
1789 * return:
1790 * 0 for FOUND a device
1791 * < 0 for error code
1792 */
1793static int __devinit i7core_probe(struct pci_dev *pdev,
1794 const struct pci_device_id *id)
1795{
1796 int dev_idx = id->driver_data;
1797 int rc;
1798 struct i7core_dev *i7core_dev;
1799
1800 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001801 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001802 */
1803 if (unlikely(dev_idx >= 1))
1804 return -EINVAL;
1805
1806 /* get the pci devices we want to reserve for our use */
1807 mutex_lock(&i7core_edac_lock);
1808 rc = i7core_get_devices();
1809 if (unlikely(rc < 0))
1810 goto fail0;
1811
1812 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1813 int channels;
1814 int csrows;
1815
1816 /* Check the number of active and not disabled channels */
1817 rc = i7core_get_active_channels(i7core_dev->socket,
1818 &channels, &csrows);
1819 if (unlikely(rc < 0))
1820 goto fail1;
1821
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001822 rc = i7core_register_mci(i7core_dev, channels, csrows);
1823 if (unlikely(rc < 0))
1824 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001825 }
1826
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001827 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001828
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001829 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001830 return 0;
1831
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001832fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001833 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001834fail0:
1835 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001836 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001837}
1838
1839/*
1840 * i7core_remove destructor for one instance of device
1841 *
1842 */
1843static void __devexit i7core_remove(struct pci_dev *pdev)
1844{
1845 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001846 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001847
1848 debugf0(__FILE__ ": %s()\n", __func__);
1849
1850 if (i7core_pci)
1851 edac_pci_release_generic_ctl(i7core_pci);
1852
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001853 /*
1854 * we have a trouble here: pdev value for removal will be wrong, since
1855 * it will point to the X58 register used to detect that the machine
1856 * is a Nehalem or upper design. However, due to the way several PCI
1857 * devices are grouped together to provide MC functionality, we need
1858 * to use a different method for releasing the devices
1859 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001860
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001861 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001862 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1863 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1864 if (mci) {
1865 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001866
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001867 i7core_dev = pvt->i7core_dev;
1868 edac_mce_unregister(&pvt->edac_mce);
1869 kfree(mci->ctl_name);
1870 edac_mc_free(mci);
1871 i7core_put_devices(i7core_dev);
1872 } else {
1873 i7core_printk(KERN_ERR,
1874 "Couldn't find mci for socket %d\n",
1875 i7core_dev->socket);
1876 }
1877 }
1878 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001879}
1880
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001881MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1882
1883/*
1884 * i7core_driver pci_driver structure for this module
1885 *
1886 */
1887static struct pci_driver i7core_driver = {
1888 .name = "i7core_edac",
1889 .probe = i7core_probe,
1890 .remove = __devexit_p(i7core_remove),
1891 .id_table = i7core_pci_tbl,
1892};
1893
1894/*
1895 * i7core_init Module entry function
1896 * Try to initialize this module for its devices
1897 */
1898static int __init i7core_init(void)
1899{
1900 int pci_rc;
1901
1902 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1903
1904 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1905 opstate_init();
1906
Keith Manntheybc2d7242009-09-03 00:05:05 -03001907 i7core_xeon_pci_fixup();
1908
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001909 pci_rc = pci_register_driver(&i7core_driver);
1910
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001911 if (pci_rc >= 0)
1912 return 0;
1913
1914 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1915 pci_rc);
1916
1917 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001918}
1919
1920/*
1921 * i7core_exit() Module exit function
1922 * Unregister the driver
1923 */
1924static void __exit i7core_exit(void)
1925{
1926 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1927 pci_unregister_driver(&i7core_driver);
1928}
1929
1930module_init(i7core_init);
1931module_exit(i7core_exit);
1932
1933MODULE_LICENSE("GPL");
1934MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1935MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1936MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1937 I7CORE_REVISION);
1938
1939module_param(edac_op_state, int, 0444);
1940MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");