blob: c2266f820b0ff97448334a05ec1e797969559ecc [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 Chehabf4742942009-09-05 02:35:08 -0300250static u8 max_num_sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300251
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300252#define PCI_DESCR(device, function, device_id) \
253 .dev = (device), \
254 .func = (function), \
255 .dev_id = (device_id)
256
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300257struct pci_id_descr pci_dev_descr[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300258 /* Memory controller */
259 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
260 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300261 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300262 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
263
264 /* Channel 0 */
265 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
266 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
267 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
268 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
269
270 /* Channel 1 */
271 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
272 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
273 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
274 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
275
276 /* Channel 2 */
277 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
278 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
279 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
280 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300281
282 /* Generic Non-core registers */
283 /*
284 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
285 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
286 * the probing code needs to test for the other address in case of
287 * failure of this one
288 */
289 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
290
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300291};
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300292#define N_DEVS ARRAY_SIZE(pci_dev_descr)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300293
294/*
295 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300296 */
297static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300298 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300299 {0,} /* 0 terminated list. */
300};
301
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300302static struct edac_pci_ctl_info *i7core_pci;
303
304/****************************************************************************
305 Anciliary status routines
306 ****************************************************************************/
307
308 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300309#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
310#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300311
312 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300313#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300314#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300315
316 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300317static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300318{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300319 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300320}
321
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300322static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300323{
324 static int ranks[4] = { 1, 2, 4, -EINVAL };
325
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300326 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300327}
328
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300329static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300330{
331 static int banks[4] = { 4, 8, 16, -EINVAL };
332
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300333 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300334}
335
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300336static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300337{
338 static int rows[8] = {
339 1 << 12, 1 << 13, 1 << 14, 1 << 15,
340 1 << 16, -EINVAL, -EINVAL, -EINVAL,
341 };
342
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300343 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300344}
345
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300346static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300347{
348 static int cols[8] = {
349 1 << 10, 1 << 11, 1 << 12, -EINVAL,
350 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300351 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300352}
353
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300354static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300355{
356 struct i7core_dev *i7core_dev;
357
358 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
359 if (i7core_dev->socket == socket)
360 return i7core_dev;
361 }
362
363 return NULL;
364}
365
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300366/****************************************************************************
367 Memory check routines
368 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300369static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
370 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300371{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300372 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300373 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300374
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300375 if (!i7core_dev)
376 return NULL;
377
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300378 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300379 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300380 continue;
381
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300382 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
383 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
384 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300385 }
386 }
387
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300388 return NULL;
389}
390
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300391/**
392 * i7core_get_active_channels() - gets the number of channels and csrows
393 * @socket: Quick Path Interconnect socket
394 * @channels: Number of channels that will be returned
395 * @csrows: Number of csrows found
396 *
397 * Since EDAC core needs to know in advance the number of available channels
398 * and csrows, in order to allocate memory for csrows/channels, it is needed
399 * to run two similar steps. At the first step, implemented on this function,
400 * it checks the number of csrows/channels present at one socket.
401 * this is used in order to properly allocate the size of mci components.
402 *
403 * It should be noticed that none of the current available datasheets explain
404 * or even mention how csrows are seen by the memory controller. So, we need
405 * to add a fake description for csrows.
406 * So, this driver is attributing one DIMM memory for one csrow.
407 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300408static int i7core_get_active_channels(u8 socket, unsigned *channels,
409 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300410{
411 struct pci_dev *pdev = NULL;
412 int i, j;
413 u32 status, control;
414
415 *channels = 0;
416 *csrows = 0;
417
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300418 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300419 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300420 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
421 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300422 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300423 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300424
425 /* Device 3 function 0 reads */
426 pci_read_config_dword(pdev, MC_STATUS, &status);
427 pci_read_config_dword(pdev, MC_CONTROL, &control);
428
429 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300430 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300431 /* Check if the channel is active */
432 if (!(control & (1 << (8 + i))))
433 continue;
434
435 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300436 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300437 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300438
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300439 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300440 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300441 i7core_printk(KERN_ERR, "Couldn't find socket %d "
442 "fn %d.%d!!!\n",
443 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300444 return -ENODEV;
445 }
446 /* Devices 4-6 function 1 */
447 pci_read_config_dword(pdev,
448 MC_DOD_CH_DIMM0, &dimm_dod[0]);
449 pci_read_config_dword(pdev,
450 MC_DOD_CH_DIMM1, &dimm_dod[1]);
451 pci_read_config_dword(pdev,
452 MC_DOD_CH_DIMM2, &dimm_dod[2]);
453
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300454 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300455
456 for (j = 0; j < 3; j++) {
457 if (!DIMM_PRESENT(dimm_dod[j]))
458 continue;
459 (*csrows)++;
460 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300461 }
462
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300463 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300464 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300465
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300466 return 0;
467}
468
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300469static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300470{
471 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300472 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300473 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300474 int i, j;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300475 u8 socket = pvt->i7core_dev->socket;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300476 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300477 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300478 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300479
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300480 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300481 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300482 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300483 return -ENODEV;
484
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300485 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300486 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
487 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
488 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
489 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300490
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300491 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
492 socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300493 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300494
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300495 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300496 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300497 if (ECCx8(pvt))
498 mode = EDAC_S8ECD8ED;
499 else
500 mode = EDAC_S4ECD4ED;
501 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300502 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300503 mode = EDAC_NONE;
504 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300505
506 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300507 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
508 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300509 numdimms(pvt->info.max_dod),
510 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300511 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300512 numrow(pvt->info.max_dod >> 6),
513 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300514
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300515 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300516 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300517
518 if (!CH_ACTIVE(pvt, i)) {
519 debugf0("Channel %i is not active\n", i);
520 continue;
521 }
522 if (CH_DISABLED(pvt, i)) {
523 debugf0("Channel %i is disabled\n", i);
524 continue;
525 }
526
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300527 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300528 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300529 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
530
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300531 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300532 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300533
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300534 if (data & REGISTERED_DIMM)
535 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300536 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300537 mtype = MEM_DDR3;
538#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300539 if (data & THREE_DIMMS_PRESENT)
540 pvt->channel[i].dimms = 3;
541 else if (data & SINGLE_QUAD_RANK_PRESENT)
542 pvt->channel[i].dimms = 1;
543 else
544 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300545#endif
546
547 /* Devices 4-6 function 1 */
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_DIMM0, &dimm_dod[0]);
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_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300552 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300553 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300554
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300555 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300556 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300557 i,
558 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
559 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300560 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300561 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300562
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300563 for (j = 0; j < 3; j++) {
564 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300565 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300566
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300567 if (!DIMM_PRESENT(dimm_dod[j]))
568 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300569
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300570 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
571 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
572 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
573 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300574
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300575 /* DDR3 has 8 I/O banks */
576 size = (rows * cols * banks * ranks) >> (20 - 3);
577
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300578 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300579
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300580 debugf0("\tdimm %d %d Mb offset: %x, "
581 "bank: %d, rank: %d, row: %#x, col: %#x\n",
582 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300583 RANKOFFSET(dimm_dod[j]),
584 banks, ranks, rows, cols);
585
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300586#if PAGE_SHIFT > 20
587 npages = size >> (PAGE_SHIFT - 20);
588#else
589 npages = size << (20 - PAGE_SHIFT);
590#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300591
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300592 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300593 csr->first_page = last_page + 1;
594 last_page += npages;
595 csr->last_page = last_page;
596 csr->nr_pages = npages;
597
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300598 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300599 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300600 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300601 csr->nr_channels = 1;
602
603 csr->channels[0].chan_idx = i;
604 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300605
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300606 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300607
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300608 switch (banks) {
609 case 4:
610 csr->dtype = DEV_X4;
611 break;
612 case 8:
613 csr->dtype = DEV_X8;
614 break;
615 case 16:
616 csr->dtype = DEV_X16;
617 break;
618 default:
619 csr->dtype = DEV_UNKNOWN;
620 }
621
622 csr->edac_mode = mode;
623 csr->mtype = mtype;
624
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300625 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300626 }
627
628 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
629 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
630 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
631 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
632 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
633 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
634 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
635 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300636 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300637 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300638 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300639 (value[j] >> 27) & 0x1,
640 (value[j] >> 24) & 0x7,
641 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300642 }
643
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300644 return 0;
645}
646
647/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300648 Error insertion routines
649 ****************************************************************************/
650
651/* The i7core has independent error injection features per channel.
652 However, to have a simpler code, we don't allow enabling error injection
653 on more than one channel.
654 Also, since a change at an inject parameter will be applied only at enable,
655 we're disabling error injection on all write calls to the sysfs nodes that
656 controls the error code injection.
657 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300658static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300659{
660 struct i7core_pvt *pvt = mci->pvt_info;
661
662 pvt->inject.enable = 0;
663
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300664 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300665 return -ENODEV;
666
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300667 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300668 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300669
670 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300671}
672
673/*
674 * i7core inject inject.section
675 *
676 * accept and store error injection inject.section value
677 * bit 0 - refers to the lower 32-byte half cacheline
678 * bit 1 - refers to the upper 32-byte half cacheline
679 */
680static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
681 const char *data, size_t count)
682{
683 struct i7core_pvt *pvt = mci->pvt_info;
684 unsigned long value;
685 int rc;
686
687 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300688 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300689
690 rc = strict_strtoul(data, 10, &value);
691 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300692 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300693
694 pvt->inject.section = (u32) value;
695 return count;
696}
697
698static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
699 char *data)
700{
701 struct i7core_pvt *pvt = mci->pvt_info;
702 return sprintf(data, "0x%08x\n", pvt->inject.section);
703}
704
705/*
706 * i7core inject.type
707 *
708 * accept and store error injection inject.section value
709 * bit 0 - repeat enable - Enable error repetition
710 * bit 1 - inject ECC error
711 * bit 2 - inject parity error
712 */
713static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
714 const char *data, size_t count)
715{
716 struct i7core_pvt *pvt = mci->pvt_info;
717 unsigned long value;
718 int rc;
719
720 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300721 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300722
723 rc = strict_strtoul(data, 10, &value);
724 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300725 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300726
727 pvt->inject.type = (u32) value;
728 return count;
729}
730
731static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
732 char *data)
733{
734 struct i7core_pvt *pvt = mci->pvt_info;
735 return sprintf(data, "0x%08x\n", pvt->inject.type);
736}
737
738/*
739 * i7core_inject_inject.eccmask_store
740 *
741 * The type of error (UE/CE) will depend on the inject.eccmask value:
742 * Any bits set to a 1 will flip the corresponding ECC bit
743 * Correctable errors can be injected by flipping 1 bit or the bits within
744 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
745 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
746 * uncorrectable error to be injected.
747 */
748static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
749 const char *data, size_t count)
750{
751 struct i7core_pvt *pvt = mci->pvt_info;
752 unsigned long value;
753 int rc;
754
755 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300756 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300757
758 rc = strict_strtoul(data, 10, &value);
759 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300760 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300761
762 pvt->inject.eccmask = (u32) value;
763 return count;
764}
765
766static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
767 char *data)
768{
769 struct i7core_pvt *pvt = mci->pvt_info;
770 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
771}
772
773/*
774 * i7core_addrmatch
775 *
776 * The type of error (UE/CE) will depend on the inject.eccmask value:
777 * Any bits set to a 1 will flip the corresponding ECC bit
778 * Correctable errors can be injected by flipping 1 bit or the bits within
779 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
780 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
781 * uncorrectable error to be injected.
782 */
783static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
784 const char *data, size_t count)
785{
786 struct i7core_pvt *pvt = mci->pvt_info;
787 char *cmd, *val;
788 long value;
789 int rc;
790
791 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300792 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300793
794 do {
795 cmd = strsep((char **) &data, ":");
796 if (!cmd)
797 break;
798 val = strsep((char **) &data, " \n\t");
799 if (!val)
800 return cmd - data;
801
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300802 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300803 value = -1;
804 else {
805 rc = strict_strtol(val, 10, &value);
806 if ((rc < 0) || (value < 0))
807 return cmd - data;
808 }
809
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300810 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300811 if (value < 3)
812 pvt->inject.channel = value;
813 else
814 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300815 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300816 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300817 pvt->inject.dimm = value;
818 else
819 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300820 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300821 if (value < 4)
822 pvt->inject.rank = value;
823 else
824 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300825 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300826 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300827 pvt->inject.bank = value;
828 else
829 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300830 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300831 if (value <= 0xffff)
832 pvt->inject.page = value;
833 else
834 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300835 } else if (!strcasecmp(cmd, "col") ||
836 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300837 if (value <= 0x3fff)
838 pvt->inject.col = value;
839 else
840 return cmd - data;
841 }
842 } while (1);
843
844 return count;
845}
846
847static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
848 char *data)
849{
850 struct i7core_pvt *pvt = mci->pvt_info;
851 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
852
853 if (pvt->inject.channel < 0)
854 sprintf(channel, "any");
855 else
856 sprintf(channel, "%d", pvt->inject.channel);
857 if (pvt->inject.dimm < 0)
858 sprintf(dimm, "any");
859 else
860 sprintf(dimm, "%d", pvt->inject.dimm);
861 if (pvt->inject.bank < 0)
862 sprintf(bank, "any");
863 else
864 sprintf(bank, "%d", pvt->inject.bank);
865 if (pvt->inject.rank < 0)
866 sprintf(rank, "any");
867 else
868 sprintf(rank, "%d", pvt->inject.rank);
869 if (pvt->inject.page < 0)
870 sprintf(page, "any");
871 else
872 sprintf(page, "0x%04x", pvt->inject.page);
873 if (pvt->inject.col < 0)
874 sprintf(col, "any");
875 else
876 sprintf(col, "0x%04x", pvt->inject.col);
877
878 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
879 "rank: %s\npage: %s\ncolumn: %s\n",
880 channel, dimm, bank, rank, page, col);
881}
882
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300883static int write_and_test(struct pci_dev *dev, int where, u32 val)
884{
885 u32 read;
886 int count;
887
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300888 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
889 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
890 where, val);
891
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300892 for (count = 0; count < 10; count++) {
893 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300894 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300895 pci_write_config_dword(dev, where, val);
896 pci_read_config_dword(dev, where, &read);
897
898 if (read == val)
899 return 0;
900 }
901
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300902 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
903 "write=%08x. Read=%08x\n",
904 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
905 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300906
907 return -EINVAL;
908}
909
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300910/*
911 * This routine prepares the Memory Controller for error injection.
912 * The error will be injected when some process tries to write to the
913 * memory that matches the given criteria.
914 * The criteria can be set in terms of a mask where dimm, rank, bank, page
915 * and col can be specified.
916 * A -1 value for any of the mask items will make the MCU to ignore
917 * that matching criteria for error injection.
918 *
919 * It should be noticed that the error will only happen after a write operation
920 * on a memory that matches the condition. if REPEAT_EN is not enabled at
921 * inject mask, then it will produce just one error. Otherwise, it will repeat
922 * until the injectmask would be cleaned.
923 *
924 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
925 * is reliable enough to check if the MC is using the
926 * three channels. However, this is not clear at the datasheet.
927 */
928static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
929 const char *data, size_t count)
930{
931 struct i7core_pvt *pvt = mci->pvt_info;
932 u32 injectmask;
933 u64 mask = 0;
934 int rc;
935 long enable;
936
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300937 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300938 return 0;
939
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300940 rc = strict_strtoul(data, 10, &enable);
941 if ((rc < 0))
942 return 0;
943
944 if (enable) {
945 pvt->inject.enable = 1;
946 } else {
947 disable_inject(mci);
948 return count;
949 }
950
951 /* Sets pvt->inject.dimm mask */
952 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300953 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300954 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300955 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300956 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300957 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300958 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300959 }
960
961 /* Sets pvt->inject.rank mask */
962 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300963 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300964 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300965 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300966 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300967 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300968 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300969 }
970
971 /* Sets pvt->inject.bank mask */
972 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300973 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300974 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300975 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300976
977 /* Sets pvt->inject.page mask */
978 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300979 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300980 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300981 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300982
983 /* Sets pvt->inject.column mask */
984 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300985 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300986 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300987 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300988
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300989 /*
990 * bit 0: REPEAT_EN
991 * bits 1-2: MASK_HALF_CACHELINE
992 * bit 3: INJECT_ECC
993 * bit 4: INJECT_ADDR_PARITY
994 */
995
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300996 injectmask = (pvt->inject.type & 1) |
997 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300998 (pvt->inject.type & 0x6) << (3 - 1);
999
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001000 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001001 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001002 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001003
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001004 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001005 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001006 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001007 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1008
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001009 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001010 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1011
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001012 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001013 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001014
1015 /*
1016 * This is something undocumented, based on my tests
1017 * Without writing 8 to this register, errors aren't injected. Not sure
1018 * why.
1019 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001020 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001021 MC_CFG_CONTROL, 8);
1022
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001023 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1024 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001025 mask, pvt->inject.eccmask, injectmask);
1026
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001027
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001028 return count;
1029}
1030
1031static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1032 char *data)
1033{
1034 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001035 u32 injectmask;
1036
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001037 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001038 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001039
1040 debugf0("Inject error read: 0x%018x\n", injectmask);
1041
1042 if (injectmask & 0x0c)
1043 pvt->inject.enable = 1;
1044
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001045 return sprintf(data, "%d\n", pvt->inject.enable);
1046}
1047
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001048static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1049{
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001050 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001051 struct i7core_pvt *pvt = mci->pvt_info;
1052
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001053 if (!pvt->ce_count_available) {
1054 count = sprintf(data, "data unavailable\n");
1055 return 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001056 }
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001057 if (!pvt->is_registered) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001058 count = sprintf(data, "all channels "
1059 "UDIMM0: %lu UDIMM1: %lu UDIMM2: %lu\n",
1060 pvt->udimm_ce_count[0],
1061 pvt->udimm_ce_count[1],
1062 pvt->udimm_ce_count[2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001063 data += count;
1064 total += count;
1065 } else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001066 for (i = 0; i < NUM_CHANS; i++) {
1067 count = sprintf(data, "channel %d RDIMM0: %lu "
1068 "RDIMM1: %lu RDIMM2: %lu\n",
1069 i,
1070 pvt->rdimm_ce_count[i][0],
1071 pvt->rdimm_ce_count[i][1],
1072 pvt->rdimm_ce_count[i][2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001073 data += count;
1074 total += count;
1075 }
1076 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001077
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001078 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001079}
1080
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001081/*
1082 * Sysfs struct
1083 */
1084static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001085 {
1086 .attr = {
1087 .name = "inject_section",
1088 .mode = (S_IRUGO | S_IWUSR)
1089 },
1090 .show = i7core_inject_section_show,
1091 .store = i7core_inject_section_store,
1092 }, {
1093 .attr = {
1094 .name = "inject_type",
1095 .mode = (S_IRUGO | S_IWUSR)
1096 },
1097 .show = i7core_inject_type_show,
1098 .store = i7core_inject_type_store,
1099 }, {
1100 .attr = {
1101 .name = "inject_eccmask",
1102 .mode = (S_IRUGO | S_IWUSR)
1103 },
1104 .show = i7core_inject_eccmask_show,
1105 .store = i7core_inject_eccmask_store,
1106 }, {
1107 .attr = {
1108 .name = "inject_addrmatch",
1109 .mode = (S_IRUGO | S_IWUSR)
1110 },
1111 .show = i7core_inject_addrmatch_show,
1112 .store = i7core_inject_addrmatch_store,
1113 }, {
1114 .attr = {
1115 .name = "inject_enable",
1116 .mode = (S_IRUGO | S_IWUSR)
1117 },
1118 .show = i7core_inject_enable_show,
1119 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001120 }, {
1121 .attr = {
1122 .name = "corrected_error_counts",
1123 .mode = (S_IRUGO | S_IWUSR)
1124 },
1125 .show = i7core_ce_regs_show,
1126 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001127 },
1128};
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 Chehab13d6e9b2009-09-05 12:15:20 -03001142 for (i = 0; i < N_DEVS; i++)
1143 pci_dev_put(i7core_dev->pdev[i]);
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001144
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001145 list_del(&i7core_dev->list);
1146 kfree(i7core_dev->pdev);
1147 kfree(i7core_dev);
1148}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001149
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001150static void i7core_put_all_devices(void)
1151{
1152 struct i7core_dev *i7core_dev;
1153
1154 list_for_each_entry(i7core_dev, &i7core_edac_list, list)
1155 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001156}
1157
Keith Manntheybc2d7242009-09-03 00:05:05 -03001158static void i7core_xeon_pci_fixup(void)
1159{
1160 struct pci_dev *pdev = NULL;
1161 int i;
1162 /*
1163 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1164 * aren't announced by acpi. So, we need to use a legacy scan probing
1165 * to detect them
1166 */
1167 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001168 pci_dev_descr[0].dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001169 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001170 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001171 pcibios_scan_specific_bus(255-i);
1172 }
1173}
1174
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001175/*
1176 * i7core_get_devices Find and perform 'get' operation on the MCH's
1177 * device/functions we want to reference for this driver
1178 *
1179 * Need to 'get' device 16 func 1 and func 2
1180 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001181int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001182{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001183 struct i7core_dev *i7core_dev;
1184
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001185 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001186 u8 bus = 0;
1187 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001188
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001189 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001190 pci_dev_descr[devno].dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001191
1192 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001193 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1194 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1195 * to probe for the alternate address in case of failure
1196 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001197 if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001198 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1199 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001200
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001201 if (!pdev) {
1202 if (*prev) {
1203 *prev = pdev;
1204 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001205 }
1206
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001207 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001208 * Dev 3 function 2 only exists on chips with RDIMMs
1209 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001210 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001211 if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001212 *prev = pdev;
1213 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001214 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001215
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001216 i7core_printk(KERN_ERR,
1217 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001218 pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1219 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001220
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001221 /* End of list, leave */
1222 return -ENODEV;
1223 }
1224 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001225
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001226 if (bus == 0x3f)
1227 socket = 0;
1228 else
1229 socket = 255 - bus;
1230
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001231 i7core_dev = get_i7core_dev(socket);
1232 if (!i7core_dev) {
1233 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1234 if (!i7core_dev)
1235 return -ENOMEM;
1236 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
1237 GFP_KERNEL);
1238 if (!i7core_dev->pdev)
1239 return -ENOMEM;
1240 i7core_dev->socket = socket;
1241 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001242 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001243
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001244 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001245 i7core_printk(KERN_ERR,
1246 "Duplicated device for "
1247 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001248 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1249 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001250 pci_dev_put(pdev);
1251 return -ENODEV;
1252 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001253
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001254 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001255
1256 /* Sanity check */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001257 if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
1258 PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001259 i7core_printk(KERN_ERR,
1260 "Device PCI ID %04x:%04x "
1261 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001262 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001263 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001264 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001265 return -ENODEV;
1266 }
1267
1268 /* Be sure that the device is enabled */
1269 if (unlikely(pci_enable_device(pdev) < 0)) {
1270 i7core_printk(KERN_ERR,
1271 "Couldn't enable "
1272 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001273 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1274 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001275 return -ENODEV;
1276 }
1277
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001278 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1279 socket, bus, pci_dev_descr[devno].dev,
1280 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
1283 *prev = pdev;
1284
1285 return 0;
1286}
1287
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001288static int i7core_get_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001289{
1290 int i;
1291 struct pci_dev *pdev = NULL;
1292
1293 for (i = 0; i < N_DEVS; i++) {
1294 pdev = NULL;
1295 do {
1296 if (i7core_get_onedevice(&pdev, i) < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001297 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001298 return -ENODEV;
1299 }
1300 } while (pdev);
1301 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001302
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001303 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001304}
1305
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001306static int mci_bind_devs(struct mem_ctl_info *mci,
1307 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001308{
1309 struct i7core_pvt *pvt = mci->pvt_info;
1310 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001311 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001312
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001313 /* Associates i7core_dev and mci for future usage */
1314 pvt->i7core_dev = i7core_dev;
1315 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001316
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001317 pvt->is_registered = 0;
1318 for (i = 0; i < N_DEVS; i++) {
1319 pdev = i7core_dev->pdev[i];
1320 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001321 continue;
1322
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001323 func = PCI_FUNC(pdev->devfn);
1324 slot = PCI_SLOT(pdev->devfn);
1325 if (slot == 3) {
1326 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001327 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001328 pvt->pci_mcr[func] = pdev;
1329 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1330 if (unlikely(func > MAX_CHAN_FUNC))
1331 goto error;
1332 pvt->pci_ch[slot - 4][func] = pdev;
1333 } else if (!slot && !func)
1334 pvt->pci_noncore = pdev;
1335 else
1336 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001337
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001338 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1339 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1340 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001341
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001342 if (PCI_SLOT(pdev->devfn) == 3 &&
1343 PCI_FUNC(pdev->devfn) == 2)
1344 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001345 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001346
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001347 return 0;
1348
1349error:
1350 i7core_printk(KERN_ERR, "Device %d, function %d "
1351 "is out of the expected range\n",
1352 slot, func);
1353 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001354}
1355
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001356/****************************************************************************
1357 Error check routines
1358 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001359static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001360 int chan, int dimm, int add)
1361{
1362 char *msg;
1363 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001364 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001365
1366 for (i = 0; i < add; i++) {
1367 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001368 "(Socket=%d channel=%d dimm=%d)",
1369 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001370
1371 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1372 kfree (msg);
1373 }
1374}
1375
1376static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001377 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001378{
1379 struct i7core_pvt *pvt = mci->pvt_info;
1380 int add0 = 0, add1 = 0, add2 = 0;
1381 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001382 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001383 /* Updates CE counters */
1384
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001385 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1386 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1387 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001388
1389 if (add2 < 0)
1390 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001391 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001392
1393 if (add1 < 0)
1394 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001395 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001396
1397 if (add0 < 0)
1398 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001399 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001400 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001401 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001402
1403 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001404 pvt->rdimm_last_ce_count[chan][2] = new2;
1405 pvt->rdimm_last_ce_count[chan][1] = new1;
1406 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001407
1408 /*updated the edac core */
1409 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001410 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001411 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001412 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001413 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001414 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001415
1416}
1417
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001418static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001419{
1420 struct i7core_pvt *pvt = mci->pvt_info;
1421 u32 rcv[3][2];
1422 int i, new0, new1, new2;
1423
1424 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001425 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001426 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001427 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001428 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001429 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001430 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001431 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001432 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001433 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001434 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001435 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001436 &rcv[2][1]);
1437 for (i = 0 ; i < 3; i++) {
1438 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1439 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1440 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001441 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001442 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1443 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1444 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1445 } else {
1446 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1447 DIMM_BOT_COR_ERR(rcv[i][0]);
1448 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1449 DIMM_BOT_COR_ERR(rcv[i][1]);
1450 new2 = 0;
1451 }
1452
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001453 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001454 }
1455}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001456
1457/* This function is based on the device 3 function 4 registers as described on:
1458 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1459 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1460 * also available at:
1461 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1462 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001463static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001464{
1465 struct i7core_pvt *pvt = mci->pvt_info;
1466 u32 rcv1, rcv0;
1467 int new0, new1, new2;
1468
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001469 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001470 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001471 return;
1472 }
1473
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001474 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001475 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1476 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001477
1478 /* Store the new values */
1479 new2 = DIMM2_COR_ERR(rcv1);
1480 new1 = DIMM1_COR_ERR(rcv0);
1481 new0 = DIMM0_COR_ERR(rcv0);
1482
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001483 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001484 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001485 /* Updates CE counters */
1486 int add0, add1, add2;
1487
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001488 add2 = new2 - pvt->udimm_last_ce_count[2];
1489 add1 = new1 - pvt->udimm_last_ce_count[1];
1490 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001491
1492 if (add2 < 0)
1493 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001494 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001495
1496 if (add1 < 0)
1497 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001498 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001499
1500 if (add0 < 0)
1501 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001502 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001503
1504 if (add0 | add1 | add2)
1505 i7core_printk(KERN_ERR, "New Corrected error(s): "
1506 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1507 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001508 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001509 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001510
1511 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001512 pvt->udimm_last_ce_count[2] = new2;
1513 pvt->udimm_last_ce_count[1] = new1;
1514 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001515}
1516
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001517/*
1518 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1519 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001520 * Nehalem are defined as family 0x06, model 0x1a
1521 *
1522 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001523 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001524 * m->status MSR_IA32_MC8_STATUS
1525 * m->addr MSR_IA32_MC8_ADDR
1526 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001527 * In the case of Nehalem, the error information is masked at .status and .misc
1528 * fields
1529 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001530static void i7core_mce_output_error(struct mem_ctl_info *mci,
1531 struct mce *m)
1532{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001533 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001534 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001535 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001536 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001537 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1538 u32 dimm = (m->misc >> 16) & 0x3;
1539 u32 channel = (m->misc >> 18) & 0x3;
1540 u32 syndrome = m->misc >> 32;
1541 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001542 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001543
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001544 if (m->mcgstatus & 1)
1545 type = "FATAL";
1546 else
1547 type = "NON_FATAL";
1548
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001549 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001550 case 0:
1551 optype = "generic undef request";
1552 break;
1553 case 1:
1554 optype = "read error";
1555 break;
1556 case 2:
1557 optype = "write error";
1558 break;
1559 case 3:
1560 optype = "addr/cmd error";
1561 break;
1562 case 4:
1563 optype = "scrubbing error";
1564 break;
1565 default:
1566 optype = "reserved";
1567 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001568 }
1569
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001570 switch (errnum) {
1571 case 16:
1572 err = "read ECC error";
1573 break;
1574 case 17:
1575 err = "RAS ECC error";
1576 break;
1577 case 18:
1578 err = "write parity error";
1579 break;
1580 case 19:
1581 err = "redundacy loss";
1582 break;
1583 case 20:
1584 err = "reserved";
1585 break;
1586 case 21:
1587 err = "memory range error";
1588 break;
1589 case 22:
1590 err = "RTID out of range";
1591 break;
1592 case 23:
1593 err = "address parity error";
1594 break;
1595 case 24:
1596 err = "byte enable parity error";
1597 break;
1598 default:
1599 err = "unknown";
1600 }
1601
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001602 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001603 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001604 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001605 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001606 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001607 syndrome, core_err_cnt, (long long)m->status,
1608 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001609
1610 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001611
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001612 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001613
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001614 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001615 if (m->mcgstatus & 1)
1616 edac_mc_handle_fbd_ue(mci, csrow, 0,
1617 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001618 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001619 edac_mc_handle_fbd_ce(mci, csrow,
1620 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001621
1622 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001623}
1624
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001625/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001626 * i7core_check_error Retrieve and process errors reported by the
1627 * hardware. Called by the Core module.
1628 */
1629static void i7core_check_error(struct mem_ctl_info *mci)
1630{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001631 struct i7core_pvt *pvt = mci->pvt_info;
1632 int i;
1633 unsigned count = 0;
1634 struct mce *m = NULL;
1635 unsigned long flags;
1636
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001637 /* Copy all mce errors into a temporary buffer */
1638 spin_lock_irqsave(&pvt->mce_lock, flags);
1639 if (pvt->mce_count) {
1640 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001641
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001642 if (m) {
1643 count = pvt->mce_count;
1644 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1645 }
1646 pvt->mce_count = 0;
1647 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001648
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001649 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1650
1651 /* proccess mcelog errors */
1652 for (i = 0; i < count; i++)
1653 i7core_mce_output_error(mci, &m[i]);
1654
1655 kfree(m);
1656
1657 /* check memory count errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001658 if (!pvt->is_registered)
1659 i7core_udimm_check_mc_ecc_err(mci);
1660 else
1661 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001662}
1663
1664/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001665 * i7core_mce_check_error Replicates mcelog routine to get errors
1666 * This routine simply queues mcelog errors, and
1667 * return. The error itself should be handled later
1668 * by i7core_check_error.
1669 */
1670static int i7core_mce_check_error(void *priv, struct mce *mce)
1671{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001672 struct mem_ctl_info *mci = priv;
1673 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001674 unsigned long flags;
1675
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001676 /*
1677 * Just let mcelog handle it if the error is
1678 * outside the memory controller
1679 */
1680 if (((mce->status & 0xffff) >> 7) != 1)
1681 return 0;
1682
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001683 /* Bank 8 registers are the only ones that we know how to handle */
1684 if (mce->bank != 8)
1685 return 0;
1686
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001687 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001688 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) {
1689 debugf0("mc%d: ignoring mce log for socket %d. "
1690 "Another mc should get it.\n",
1691 pvt->i7core_dev->socket,
1692 cpu_data(mce->cpu).phys_proc_id);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001693 return 0;
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001694 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001695
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001696 spin_lock_irqsave(&pvt->mce_lock, flags);
1697 if (pvt->mce_count < MCE_LOG_LEN) {
1698 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1699 pvt->mce_count++;
1700 }
1701 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1702
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001703 /* Handle fatal errors immediately */
1704 if (mce->mcgstatus & 1)
1705 i7core_check_error(mci);
1706
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001707 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001708 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001709}
1710
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001711static int i7core_register_mci(struct i7core_dev *i7core_dev,
1712 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001713{
1714 struct mem_ctl_info *mci;
1715 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001716 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001717 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001718
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001719 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001720 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1721 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001722 if (unlikely(!mci))
1723 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001724
1725 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1726
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001727 /* record ptr to the generic device */
1728 mci->dev = &i7core_dev->pdev[0]->dev;
1729
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001730 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001731 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001732
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001733 /*
1734 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1735 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1736 * memory channels
1737 */
1738 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001739 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1740 mci->edac_cap = EDAC_FLAG_NONE;
1741 mci->mod_name = "i7core_edac.c";
1742 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001743 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1744 i7core_dev->socket);
1745 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001746 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001747 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001748 /* Set the function pointer to an actual operation function */
1749 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001750
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001751 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001752 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001753 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001754 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001755
1756 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001757 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001758
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001759 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001760 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001761 debugf0("MC: " __FILE__
1762 ": %s(): failed edac_mc_add_mc()\n", __func__);
1763 /* FIXME: perhaps some code should go here that disables error
1764 * reporting if we just enabled it
1765 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001766
1767 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001768 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001769 }
1770
1771 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001772 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1773 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001774 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001775 printk(KERN_WARNING
1776 "%s(): Unable to create PCI control\n",
1777 __func__);
1778 printk(KERN_WARNING
1779 "%s(): PCI error report via EDAC not setup\n",
1780 __func__);
1781 }
1782
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001783 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001784 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001785 pvt->inject.dimm = -1;
1786 pvt->inject.rank = -1;
1787 pvt->inject.bank = -1;
1788 pvt->inject.page = -1;
1789 pvt->inject.col = -1;
1790
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001791 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001792 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001793 pvt->edac_mce.check_error = i7core_mce_check_error;
1794 spin_lock_init(&pvt->mce_lock);
1795
1796 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001797 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001798 debugf0("MC: " __FILE__
1799 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001800 }
1801
1802fail:
1803 edac_mc_free(mci);
1804 return rc;
1805}
1806
1807/*
1808 * i7core_probe Probe for ONE instance of device to see if it is
1809 * present.
1810 * return:
1811 * 0 for FOUND a device
1812 * < 0 for error code
1813 */
1814static int __devinit i7core_probe(struct pci_dev *pdev,
1815 const struct pci_device_id *id)
1816{
1817 int dev_idx = id->driver_data;
1818 int rc;
1819 struct i7core_dev *i7core_dev;
1820
1821 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001822 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001823 */
1824 if (unlikely(dev_idx >= 1))
1825 return -EINVAL;
1826
1827 /* get the pci devices we want to reserve for our use */
1828 mutex_lock(&i7core_edac_lock);
1829 rc = i7core_get_devices();
1830 if (unlikely(rc < 0))
1831 goto fail0;
1832
1833 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1834 int channels;
1835 int csrows;
1836
1837 /* Check the number of active and not disabled channels */
1838 rc = i7core_get_active_channels(i7core_dev->socket,
1839 &channels, &csrows);
1840 if (unlikely(rc < 0))
1841 goto fail1;
1842
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001843 rc = i7core_register_mci(i7core_dev, channels, csrows);
1844 if (unlikely(rc < 0))
1845 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001846 }
1847
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001848 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001849
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001850 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001851 return 0;
1852
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001853fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001854 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001855fail0:
1856 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001857 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001858}
1859
1860/*
1861 * i7core_remove destructor for one instance of device
1862 *
1863 */
1864static void __devexit i7core_remove(struct pci_dev *pdev)
1865{
1866 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001867 struct i7core_pvt *pvt;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001868 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001869
1870 debugf0(__FILE__ ": %s()\n", __func__);
1871
1872 if (i7core_pci)
1873 edac_pci_release_generic_ctl(i7core_pci);
1874
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001875
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001876 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001877 if (!mci)
1878 return;
1879
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001880 /* Unregisters on edac_mce in order to receive memory errors */
1881 pvt = mci->pvt_info;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001882 i7core_dev = pvt->i7core_dev;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001883 edac_mce_unregister(&pvt->edac_mce);
1884
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001885 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001886 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001887 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001888 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001889
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001890 kfree(mci->ctl_name);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001891 edac_mc_free(mci);
1892}
1893
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001894MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1895
1896/*
1897 * i7core_driver pci_driver structure for this module
1898 *
1899 */
1900static struct pci_driver i7core_driver = {
1901 .name = "i7core_edac",
1902 .probe = i7core_probe,
1903 .remove = __devexit_p(i7core_remove),
1904 .id_table = i7core_pci_tbl,
1905};
1906
1907/*
1908 * i7core_init Module entry function
1909 * Try to initialize this module for its devices
1910 */
1911static int __init i7core_init(void)
1912{
1913 int pci_rc;
1914
1915 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1916
1917 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1918 opstate_init();
1919
Keith Manntheybc2d7242009-09-03 00:05:05 -03001920 i7core_xeon_pci_fixup();
1921
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001922 pci_rc = pci_register_driver(&i7core_driver);
1923
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001924 if (pci_rc >= 0)
1925 return 0;
1926
1927 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1928 pci_rc);
1929
1930 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001931}
1932
1933/*
1934 * i7core_exit() Module exit function
1935 * Unregister the driver
1936 */
1937static void __exit i7core_exit(void)
1938{
1939 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1940 pci_unregister_driver(&i7core_driver);
1941}
1942
1943module_init(i7core_init);
1944module_exit(i7core_exit);
1945
1946MODULE_LICENSE("GPL");
1947MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1948MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1949MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1950 I7CORE_REVISION);
1951
1952module_param(edac_op_state, int, 0444);
1953MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");