blob: c3fec5de3e51c12c88de84a952e4c897d4801230 [file] [log] [blame]
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001/* Intel 7 core Memory Controller kernel module (Nehalem)
2 *
3 * This file may be distributed under the terms of the
4 * GNU General Public License version 2 only.
5 *
6 * Copyright (c) 2009 by:
7 * Mauro Carvalho Chehab <mchehab@redhat.com>
8 *
9 * Red Hat Inc. http://www.redhat.com
10 *
11 * Forked and adapted from the i5400_edac driver
12 *
13 * Based on the following public Intel datasheets:
14 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
15 * Datasheet, Volume 2:
16 * http://download.intel.com/design/processor/datashts/320835.pdf
17 * Intel Xeon Processor 5500 Series Datasheet Volume 2
18 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
19 * also available at:
20 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
21 */
22
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030023#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/pci.h>
26#include <linux/pci_ids.h>
27#include <linux/slab.h>
28#include <linux/edac.h>
29#include <linux/mmzone.h>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030030#include <linux/edac_mce.h>
31#include <linux/spinlock.h>
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030032#include <linux/smp.h>
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -030033#include <asm/processor.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030034
35#include "edac_core.h"
36
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030037/*
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030038 * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
39 * registers start at bus 255, and are not reported by BIOS.
40 * We currently find devices with only 2 sockets. In order to support more QPI
41 * Quick Path Interconnect, just increment this number.
42 */
43#define MAX_SOCKET_BUSES 2
44
45
46/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030047 * Alter this version for the module when modifications are made
48 */
49#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
50#define EDAC_MOD_STR "i7core_edac"
51
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030052/*
53 * Debug macros
54 */
55#define i7core_printk(level, fmt, arg...) \
56 edac_printk(level, "i7core", fmt, ##arg)
57
58#define i7core_mc_printk(mci, level, fmt, arg...) \
59 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
60
61/*
62 * i7core Memory Controller Registers
63 */
64
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030065 /* OFFSETS for Device 0 Function 0 */
66
67#define MC_CFG_CONTROL 0x90
68
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030069 /* OFFSETS for Device 3 Function 0 */
70
71#define MC_CONTROL 0x48
72#define MC_STATUS 0x4c
73#define MC_MAX_DOD 0x64
74
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030075/*
76 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
77 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
78 */
79
80#define MC_TEST_ERR_RCV1 0x60
81 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
82
83#define MC_TEST_ERR_RCV0 0x64
84 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
85 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
86
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -030087/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
88#define MC_COR_ECC_CNT_0 0x80
89#define MC_COR_ECC_CNT_1 0x84
90#define MC_COR_ECC_CNT_2 0x88
91#define MC_COR_ECC_CNT_3 0x8c
92#define MC_COR_ECC_CNT_4 0x90
93#define MC_COR_ECC_CNT_5 0x94
94
95#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
96#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
97
98
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030099 /* OFFSETS for Devices 4,5 and 6 Function 0 */
100
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300101#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
102 #define THREE_DIMMS_PRESENT (1 << 24)
103 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
104 #define QUAD_RANK_PRESENT (1 << 22)
105 #define REGISTERED_DIMM (1 << 15)
106
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300107#define MC_CHANNEL_MAPPER 0x60
108 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
109 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
110
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300111#define MC_CHANNEL_RANK_PRESENT 0x7c
112 #define RANK_PRESENT_MASK 0xffff
113
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300114#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300115#define MC_CHANNEL_ERROR_MASK 0xf8
116#define MC_CHANNEL_ERROR_INJECT 0xfc
117 #define INJECT_ADDR_PARITY 0x10
118 #define INJECT_ECC 0x08
119 #define MASK_CACHELINE 0x06
120 #define MASK_FULL_CACHELINE 0x06
121 #define MASK_MSB32_CACHELINE 0x04
122 #define MASK_LSB32_CACHELINE 0x02
123 #define NO_MASK_CACHELINE 0x00
124 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300125
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300126 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300127
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300128#define MC_DOD_CH_DIMM0 0x48
129#define MC_DOD_CH_DIMM1 0x4c
130#define MC_DOD_CH_DIMM2 0x50
131 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
132 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
133 #define DIMM_PRESENT_MASK (1 << 9)
134 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300135 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
136 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
137 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
138 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300139 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300140 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300141 #define MC_DOD_NUMCOL_MASK 3
142 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300143
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300144#define MC_RANK_PRESENT 0x7c
145
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300146#define MC_SAG_CH_0 0x80
147#define MC_SAG_CH_1 0x84
148#define MC_SAG_CH_2 0x88
149#define MC_SAG_CH_3 0x8c
150#define MC_SAG_CH_4 0x90
151#define MC_SAG_CH_5 0x94
152#define MC_SAG_CH_6 0x98
153#define MC_SAG_CH_7 0x9c
154
155#define MC_RIR_LIMIT_CH_0 0x40
156#define MC_RIR_LIMIT_CH_1 0x44
157#define MC_RIR_LIMIT_CH_2 0x48
158#define MC_RIR_LIMIT_CH_3 0x4C
159#define MC_RIR_LIMIT_CH_4 0x50
160#define MC_RIR_LIMIT_CH_5 0x54
161#define MC_RIR_LIMIT_CH_6 0x58
162#define MC_RIR_LIMIT_CH_7 0x5C
163#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
164
165#define MC_RIR_WAY_CH 0x80
166 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
167 #define MC_RIR_WAY_RANK_MASK 0x7
168
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300169/*
170 * i7core structs
171 */
172
173#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300174#define MAX_DIMMS 3 /* Max DIMMS per channel */
175#define MAX_MCR_FUNC 4
176#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300177
178struct i7core_info {
179 u32 mc_control;
180 u32 mc_status;
181 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300182 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300183};
184
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300185
186struct i7core_inject {
187 int enable;
188
189 u32 section;
190 u32 type;
191 u32 eccmask;
192
193 /* Error address mask */
194 int channel, dimm, rank, bank, page, col;
195};
196
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300197struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300198 u32 ranks;
199 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300200};
201
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300202struct pci_id_descr {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300203 int dev;
204 int func;
205 int dev_id;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300206};
207
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300208struct i7core_dev {
209 struct list_head list;
210 u8 socket;
211 struct pci_dev **pdev;
212 struct mem_ctl_info *mci;
213};
214
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300215struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300216 struct pci_dev *pci_noncore;
217 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
218 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
219
220 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300221
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300222 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300223 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300224 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300225
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300226 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300227
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300228 int ce_count_available;
229 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300230
231 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300232 unsigned long udimm_ce_count[MAX_DIMMS];
233 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300234 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300235 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
236 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300237
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300238 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300239
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300240 /* mcelog glue */
241 struct edac_mce edac_mce;
242 struct mce mce_entry[MCE_LOG_LEN];
243 unsigned mce_count;
244 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300245};
246
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300247/* Static vars */
248static LIST_HEAD(i7core_edac_list);
249static DEFINE_MUTEX(i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300250
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300251#define PCI_DESCR(device, function, device_id) \
252 .dev = (device), \
253 .func = (function), \
254 .dev_id = (device_id)
255
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300256struct pci_id_descr pci_dev_descr[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300257 /* Memory controller */
258 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
259 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300260 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300261 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
262
263 /* Channel 0 */
264 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
265 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
266 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
267 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
268
269 /* Channel 1 */
270 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
271 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
272 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
273 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
274
275 /* Channel 2 */
276 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
277 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
278 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
279 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300280
281 /* Generic Non-core registers */
282 /*
283 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
284 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
285 * the probing code needs to test for the other address in case of
286 * failure of this one
287 */
288 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
289
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300290};
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300291#define N_DEVS ARRAY_SIZE(pci_dev_descr)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300292
293/*
294 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300295 */
296static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300297 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300298 {0,} /* 0 terminated list. */
299};
300
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300301static struct edac_pci_ctl_info *i7core_pci;
302
303/****************************************************************************
304 Anciliary status routines
305 ****************************************************************************/
306
307 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300308#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
309#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300310
311 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300312#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300313#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300314
315 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300316static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300317{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300318 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300319}
320
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300321static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300322{
323 static int ranks[4] = { 1, 2, 4, -EINVAL };
324
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300325 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300326}
327
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300328static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300329{
330 static int banks[4] = { 4, 8, 16, -EINVAL };
331
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300332 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300333}
334
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300335static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300336{
337 static int rows[8] = {
338 1 << 12, 1 << 13, 1 << 14, 1 << 15,
339 1 << 16, -EINVAL, -EINVAL, -EINVAL,
340 };
341
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300342 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300343}
344
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300345static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300346{
347 static int cols[8] = {
348 1 << 10, 1 << 11, 1 << 12, -EINVAL,
349 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300350 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300351}
352
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300353static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300354{
355 struct i7core_dev *i7core_dev;
356
357 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
358 if (i7core_dev->socket == socket)
359 return i7core_dev;
360 }
361
362 return NULL;
363}
364
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300365/****************************************************************************
366 Memory check routines
367 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300368static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
369 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300370{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300371 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300372 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300373
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300374 if (!i7core_dev)
375 return NULL;
376
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300377 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300378 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300379 continue;
380
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300381 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
382 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
383 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300384 }
385 }
386
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300387 return NULL;
388}
389
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300390/**
391 * i7core_get_active_channels() - gets the number of channels and csrows
392 * @socket: Quick Path Interconnect socket
393 * @channels: Number of channels that will be returned
394 * @csrows: Number of csrows found
395 *
396 * Since EDAC core needs to know in advance the number of available channels
397 * and csrows, in order to allocate memory for csrows/channels, it is needed
398 * to run two similar steps. At the first step, implemented on this function,
399 * it checks the number of csrows/channels present at one socket.
400 * this is used in order to properly allocate the size of mci components.
401 *
402 * It should be noticed that none of the current available datasheets explain
403 * or even mention how csrows are seen by the memory controller. So, we need
404 * to add a fake description for csrows.
405 * So, this driver is attributing one DIMM memory for one csrow.
406 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300407static int i7core_get_active_channels(u8 socket, unsigned *channels,
408 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300409{
410 struct pci_dev *pdev = NULL;
411 int i, j;
412 u32 status, control;
413
414 *channels = 0;
415 *csrows = 0;
416
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300417 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300418 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300419 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
420 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300421 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300422 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300423
424 /* Device 3 function 0 reads */
425 pci_read_config_dword(pdev, MC_STATUS, &status);
426 pci_read_config_dword(pdev, MC_CONTROL, &control);
427
428 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300429 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300430 /* Check if the channel is active */
431 if (!(control & (1 << (8 + i))))
432 continue;
433
434 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300435 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300436 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300437
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300438 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300439 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300440 i7core_printk(KERN_ERR, "Couldn't find socket %d "
441 "fn %d.%d!!!\n",
442 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300443 return -ENODEV;
444 }
445 /* Devices 4-6 function 1 */
446 pci_read_config_dword(pdev,
447 MC_DOD_CH_DIMM0, &dimm_dod[0]);
448 pci_read_config_dword(pdev,
449 MC_DOD_CH_DIMM1, &dimm_dod[1]);
450 pci_read_config_dword(pdev,
451 MC_DOD_CH_DIMM2, &dimm_dod[2]);
452
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300453 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300454
455 for (j = 0; j < 3; j++) {
456 if (!DIMM_PRESENT(dimm_dod[j]))
457 continue;
458 (*csrows)++;
459 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300460 }
461
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300462 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300463 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300464
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300465 return 0;
466}
467
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300468static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300469{
470 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300471 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300472 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300473 int i, j;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300474 u8 socket = pvt->i7core_dev->socket;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300475 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300476 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300477 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300478
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300479 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300480 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300481 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300482 return -ENODEV;
483
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300484 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300485 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
486 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
487 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
488 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300489
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300490 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
491 socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300492 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300493
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300494 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300495 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300496 if (ECCx8(pvt))
497 mode = EDAC_S8ECD8ED;
498 else
499 mode = EDAC_S4ECD4ED;
500 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300501 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300502 mode = EDAC_NONE;
503 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300504
505 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300506 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
507 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300508 numdimms(pvt->info.max_dod),
509 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300510 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300511 numrow(pvt->info.max_dod >> 6),
512 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300513
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300514 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300515 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300516
517 if (!CH_ACTIVE(pvt, i)) {
518 debugf0("Channel %i is not active\n", i);
519 continue;
520 }
521 if (CH_DISABLED(pvt, i)) {
522 debugf0("Channel %i is disabled\n", i);
523 continue;
524 }
525
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300526 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300527 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300528 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
529
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300530 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300531 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300532
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300533 if (data & REGISTERED_DIMM)
534 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300535 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300536 mtype = MEM_DDR3;
537#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300538 if (data & THREE_DIMMS_PRESENT)
539 pvt->channel[i].dimms = 3;
540 else if (data & SINGLE_QUAD_RANK_PRESENT)
541 pvt->channel[i].dimms = 1;
542 else
543 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300544#endif
545
546 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300547 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300548 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300549 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300550 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300551 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300552 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300553
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300554 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300555 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300556 i,
557 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
558 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300559 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300560 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300561
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300562 for (j = 0; j < 3; j++) {
563 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300564 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300565
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300566 if (!DIMM_PRESENT(dimm_dod[j]))
567 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300568
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300569 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
570 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
571 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
572 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300573
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300574 /* DDR3 has 8 I/O banks */
575 size = (rows * cols * banks * ranks) >> (20 - 3);
576
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300577 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300578
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300579 debugf0("\tdimm %d %d Mb offset: %x, "
580 "bank: %d, rank: %d, row: %#x, col: %#x\n",
581 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300582 RANKOFFSET(dimm_dod[j]),
583 banks, ranks, rows, cols);
584
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300585#if PAGE_SHIFT > 20
586 npages = size >> (PAGE_SHIFT - 20);
587#else
588 npages = size << (20 - PAGE_SHIFT);
589#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300590
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300591 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300592 csr->first_page = last_page + 1;
593 last_page += npages;
594 csr->last_page = last_page;
595 csr->nr_pages = npages;
596
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300597 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300598 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300599 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300600 csr->nr_channels = 1;
601
602 csr->channels[0].chan_idx = i;
603 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300604
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300605 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300606
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300607 switch (banks) {
608 case 4:
609 csr->dtype = DEV_X4;
610 break;
611 case 8:
612 csr->dtype = DEV_X8;
613 break;
614 case 16:
615 csr->dtype = DEV_X16;
616 break;
617 default:
618 csr->dtype = DEV_UNKNOWN;
619 }
620
621 csr->edac_mode = mode;
622 csr->mtype = mtype;
623
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300624 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300625 }
626
627 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
628 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
629 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
630 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
631 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
632 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
633 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
634 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300635 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300636 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300637 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300638 (value[j] >> 27) & 0x1,
639 (value[j] >> 24) & 0x7,
640 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300641 }
642
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300643 return 0;
644}
645
646/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300647 Error insertion routines
648 ****************************************************************************/
649
650/* The i7core has independent error injection features per channel.
651 However, to have a simpler code, we don't allow enabling error injection
652 on more than one channel.
653 Also, since a change at an inject parameter will be applied only at enable,
654 we're disabling error injection on all write calls to the sysfs nodes that
655 controls the error code injection.
656 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300657static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300658{
659 struct i7core_pvt *pvt = mci->pvt_info;
660
661 pvt->inject.enable = 0;
662
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300663 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300664 return -ENODEV;
665
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300666 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300667 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300668
669 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300670}
671
672/*
673 * i7core inject inject.section
674 *
675 * accept and store error injection inject.section value
676 * bit 0 - refers to the lower 32-byte half cacheline
677 * bit 1 - refers to the upper 32-byte half cacheline
678 */
679static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
680 const char *data, size_t count)
681{
682 struct i7core_pvt *pvt = mci->pvt_info;
683 unsigned long value;
684 int rc;
685
686 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300687 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300688
689 rc = strict_strtoul(data, 10, &value);
690 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300691 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300692
693 pvt->inject.section = (u32) value;
694 return count;
695}
696
697static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
698 char *data)
699{
700 struct i7core_pvt *pvt = mci->pvt_info;
701 return sprintf(data, "0x%08x\n", pvt->inject.section);
702}
703
704/*
705 * i7core inject.type
706 *
707 * accept and store error injection inject.section value
708 * bit 0 - repeat enable - Enable error repetition
709 * bit 1 - inject ECC error
710 * bit 2 - inject parity error
711 */
712static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
713 const char *data, size_t count)
714{
715 struct i7core_pvt *pvt = mci->pvt_info;
716 unsigned long value;
717 int rc;
718
719 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300720 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300721
722 rc = strict_strtoul(data, 10, &value);
723 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300724 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300725
726 pvt->inject.type = (u32) value;
727 return count;
728}
729
730static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
731 char *data)
732{
733 struct i7core_pvt *pvt = mci->pvt_info;
734 return sprintf(data, "0x%08x\n", pvt->inject.type);
735}
736
737/*
738 * i7core_inject_inject.eccmask_store
739 *
740 * The type of error (UE/CE) will depend on the inject.eccmask value:
741 * Any bits set to a 1 will flip the corresponding ECC bit
742 * Correctable errors can be injected by flipping 1 bit or the bits within
743 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
744 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
745 * uncorrectable error to be injected.
746 */
747static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
748 const char *data, size_t count)
749{
750 struct i7core_pvt *pvt = mci->pvt_info;
751 unsigned long value;
752 int rc;
753
754 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300755 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300756
757 rc = strict_strtoul(data, 10, &value);
758 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300759 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300760
761 pvt->inject.eccmask = (u32) value;
762 return count;
763}
764
765static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
766 char *data)
767{
768 struct i7core_pvt *pvt = mci->pvt_info;
769 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
770}
771
772/*
773 * i7core_addrmatch
774 *
775 * The type of error (UE/CE) will depend on the inject.eccmask value:
776 * Any bits set to a 1 will flip the corresponding ECC bit
777 * Correctable errors can be injected by flipping 1 bit or the bits within
778 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
779 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
780 * uncorrectable error to be injected.
781 */
782static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
783 const char *data, size_t count)
784{
785 struct i7core_pvt *pvt = mci->pvt_info;
786 char *cmd, *val;
787 long value;
788 int rc;
789
790 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300791 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300792
793 do {
794 cmd = strsep((char **) &data, ":");
795 if (!cmd)
796 break;
797 val = strsep((char **) &data, " \n\t");
798 if (!val)
799 return cmd - data;
800
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300801 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300802 value = -1;
803 else {
804 rc = strict_strtol(val, 10, &value);
805 if ((rc < 0) || (value < 0))
806 return cmd - data;
807 }
808
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300809 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300810 if (value < 3)
811 pvt->inject.channel = value;
812 else
813 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300814 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300815 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300816 pvt->inject.dimm = value;
817 else
818 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300819 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300820 if (value < 4)
821 pvt->inject.rank = value;
822 else
823 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300824 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300825 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300826 pvt->inject.bank = value;
827 else
828 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300829 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300830 if (value <= 0xffff)
831 pvt->inject.page = value;
832 else
833 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300834 } else if (!strcasecmp(cmd, "col") ||
835 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300836 if (value <= 0x3fff)
837 pvt->inject.col = value;
838 else
839 return cmd - data;
840 }
841 } while (1);
842
843 return count;
844}
845
846static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
847 char *data)
848{
849 struct i7core_pvt *pvt = mci->pvt_info;
850 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
851
852 if (pvt->inject.channel < 0)
853 sprintf(channel, "any");
854 else
855 sprintf(channel, "%d", pvt->inject.channel);
856 if (pvt->inject.dimm < 0)
857 sprintf(dimm, "any");
858 else
859 sprintf(dimm, "%d", pvt->inject.dimm);
860 if (pvt->inject.bank < 0)
861 sprintf(bank, "any");
862 else
863 sprintf(bank, "%d", pvt->inject.bank);
864 if (pvt->inject.rank < 0)
865 sprintf(rank, "any");
866 else
867 sprintf(rank, "%d", pvt->inject.rank);
868 if (pvt->inject.page < 0)
869 sprintf(page, "any");
870 else
871 sprintf(page, "0x%04x", pvt->inject.page);
872 if (pvt->inject.col < 0)
873 sprintf(col, "any");
874 else
875 sprintf(col, "0x%04x", pvt->inject.col);
876
877 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
878 "rank: %s\npage: %s\ncolumn: %s\n",
879 channel, dimm, bank, rank, page, col);
880}
881
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300882static int write_and_test(struct pci_dev *dev, int where, u32 val)
883{
884 u32 read;
885 int count;
886
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300887 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
888 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
889 where, val);
890
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300891 for (count = 0; count < 10; count++) {
892 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300893 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300894 pci_write_config_dword(dev, where, val);
895 pci_read_config_dword(dev, where, &read);
896
897 if (read == val)
898 return 0;
899 }
900
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300901 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
902 "write=%08x. Read=%08x\n",
903 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
904 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300905
906 return -EINVAL;
907}
908
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300909/*
910 * This routine prepares the Memory Controller for error injection.
911 * The error will be injected when some process tries to write to the
912 * memory that matches the given criteria.
913 * The criteria can be set in terms of a mask where dimm, rank, bank, page
914 * and col can be specified.
915 * A -1 value for any of the mask items will make the MCU to ignore
916 * that matching criteria for error injection.
917 *
918 * It should be noticed that the error will only happen after a write operation
919 * on a memory that matches the condition. if REPEAT_EN is not enabled at
920 * inject mask, then it will produce just one error. Otherwise, it will repeat
921 * until the injectmask would be cleaned.
922 *
923 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
924 * is reliable enough to check if the MC is using the
925 * three channels. However, this is not clear at the datasheet.
926 */
927static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
928 const char *data, size_t count)
929{
930 struct i7core_pvt *pvt = mci->pvt_info;
931 u32 injectmask;
932 u64 mask = 0;
933 int rc;
934 long enable;
935
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300936 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300937 return 0;
938
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300939 rc = strict_strtoul(data, 10, &enable);
940 if ((rc < 0))
941 return 0;
942
943 if (enable) {
944 pvt->inject.enable = 1;
945 } else {
946 disable_inject(mci);
947 return count;
948 }
949
950 /* Sets pvt->inject.dimm mask */
951 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300952 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300953 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300954 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300955 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300956 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300957 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300958 }
959
960 /* Sets pvt->inject.rank mask */
961 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300962 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300963 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300964 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300965 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300966 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300967 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300968 }
969
970 /* Sets pvt->inject.bank mask */
971 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300972 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300973 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300974 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300975
976 /* Sets pvt->inject.page mask */
977 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300978 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300979 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300980 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300981
982 /* Sets pvt->inject.column mask */
983 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300984 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300985 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300986 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300987
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300988 /*
989 * bit 0: REPEAT_EN
990 * bits 1-2: MASK_HALF_CACHELINE
991 * bit 3: INJECT_ECC
992 * bit 4: INJECT_ADDR_PARITY
993 */
994
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300995 injectmask = (pvt->inject.type & 1) |
996 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300997 (pvt->inject.type & 0x6) << (3 - 1);
998
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300999 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001000 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001001 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001002
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001003 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001004 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001005 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001006 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1007
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001008 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001009 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1010
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001011 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001012 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001013
1014 /*
1015 * This is something undocumented, based on my tests
1016 * Without writing 8 to this register, errors aren't injected. Not sure
1017 * why.
1018 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001019 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001020 MC_CFG_CONTROL, 8);
1021
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001022 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1023 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001024 mask, pvt->inject.eccmask, injectmask);
1025
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001026
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001027 return count;
1028}
1029
1030static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1031 char *data)
1032{
1033 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001034 u32 injectmask;
1035
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001036 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001037 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001038
1039 debugf0("Inject error read: 0x%018x\n", injectmask);
1040
1041 if (injectmask & 0x0c)
1042 pvt->inject.enable = 1;
1043
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001044 return sprintf(data, "%d\n", pvt->inject.enable);
1045}
1046
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001047static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1048{
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001049 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001050 struct i7core_pvt *pvt = mci->pvt_info;
1051
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001052 if (!pvt->ce_count_available) {
1053 count = sprintf(data, "data unavailable\n");
1054 return 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001055 }
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001056 if (!pvt->is_registered) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001057 count = sprintf(data, "all channels "
1058 "UDIMM0: %lu UDIMM1: %lu UDIMM2: %lu\n",
1059 pvt->udimm_ce_count[0],
1060 pvt->udimm_ce_count[1],
1061 pvt->udimm_ce_count[2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001062 data += count;
1063 total += count;
1064 } else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001065 for (i = 0; i < NUM_CHANS; i++) {
1066 count = sprintf(data, "channel %d RDIMM0: %lu "
1067 "RDIMM1: %lu RDIMM2: %lu\n",
1068 i,
1069 pvt->rdimm_ce_count[i][0],
1070 pvt->rdimm_ce_count[i][1],
1071 pvt->rdimm_ce_count[i][2]);
Mauro Carvalho Chehabd88b8502009-09-05 05:10:31 -03001072 data += count;
1073 total += count;
1074 }
1075 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001076
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001077 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001078}
1079
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001080/*
1081 * Sysfs struct
1082 */
1083static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001084 {
1085 .attr = {
1086 .name = "inject_section",
1087 .mode = (S_IRUGO | S_IWUSR)
1088 },
1089 .show = i7core_inject_section_show,
1090 .store = i7core_inject_section_store,
1091 }, {
1092 .attr = {
1093 .name = "inject_type",
1094 .mode = (S_IRUGO | S_IWUSR)
1095 },
1096 .show = i7core_inject_type_show,
1097 .store = i7core_inject_type_store,
1098 }, {
1099 .attr = {
1100 .name = "inject_eccmask",
1101 .mode = (S_IRUGO | S_IWUSR)
1102 },
1103 .show = i7core_inject_eccmask_show,
1104 .store = i7core_inject_eccmask_store,
1105 }, {
1106 .attr = {
1107 .name = "inject_addrmatch",
1108 .mode = (S_IRUGO | S_IWUSR)
1109 },
1110 .show = i7core_inject_addrmatch_show,
1111 .store = i7core_inject_addrmatch_store,
1112 }, {
1113 .attr = {
1114 .name = "inject_enable",
1115 .mode = (S_IRUGO | S_IWUSR)
1116 },
1117 .show = i7core_inject_enable_show,
1118 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001119 }, {
1120 .attr = {
1121 .name = "corrected_error_counts",
1122 .mode = (S_IRUGO | S_IWUSR)
1123 },
1124 .show = i7core_ce_regs_show,
1125 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001126 },
1127};
1128
1129/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001130 Device initialization routines: put/get, init/exit
1131 ****************************************************************************/
1132
1133/*
1134 * i7core_put_devices 'put' all the devices that we have
1135 * reserved via 'get'
1136 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001137static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001138{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001139 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001140
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001141 debugf0(__FILE__ ": %s()\n", __func__);
1142 for (i = 0; i < N_DEVS; i++) {
1143 struct pci_dev *pdev = i7core_dev->pdev[i];
1144 if (!pdev)
1145 continue;
1146 debugf0("Removing dev %02x:%02x.%d\n",
1147 pdev->bus->number,
1148 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1149 pci_dev_put(pdev);
1150 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001151 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001152 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001153 kfree(i7core_dev);
1154}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001155
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001156static void i7core_put_all_devices(void)
1157{
1158 struct i7core_dev *i7core_dev;
1159
1160 list_for_each_entry(i7core_dev, &i7core_edac_list, list)
1161 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001162}
1163
Keith Manntheybc2d7242009-09-03 00:05:05 -03001164static void i7core_xeon_pci_fixup(void)
1165{
1166 struct pci_dev *pdev = NULL;
1167 int i;
1168 /*
1169 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1170 * aren't announced by acpi. So, we need to use a legacy scan probing
1171 * to detect them
1172 */
1173 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001174 pci_dev_descr[0].dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001175 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001176 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001177 pcibios_scan_specific_bus(255-i);
1178 }
1179}
1180
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001181/*
1182 * i7core_get_devices Find and perform 'get' operation on the MCH's
1183 * device/functions we want to reference for this driver
1184 *
1185 * Need to 'get' device 16 func 1 and func 2
1186 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001187int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001188{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001189 struct i7core_dev *i7core_dev;
1190
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001191 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001192 u8 bus = 0;
1193 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001194
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001195 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001196 pci_dev_descr[devno].dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001197
1198 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001199 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1200 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1201 * to probe for the alternate address in case of failure
1202 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001203 if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001204 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1205 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001206
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001207 if (!pdev) {
1208 if (*prev) {
1209 *prev = pdev;
1210 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001211 }
1212
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001213 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001214 * Dev 3 function 2 only exists on chips with RDIMMs
1215 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001216 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001217 if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001218 *prev = pdev;
1219 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001220 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001221
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001222 i7core_printk(KERN_ERR,
1223 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001224 pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1225 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001226
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001227 /* End of list, leave */
1228 return -ENODEV;
1229 }
1230 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001231
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001232 if (bus == 0x3f)
1233 socket = 0;
1234 else
1235 socket = 255 - bus;
1236
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001237 i7core_dev = get_i7core_dev(socket);
1238 if (!i7core_dev) {
1239 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1240 if (!i7core_dev)
1241 return -ENOMEM;
1242 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
1243 GFP_KERNEL);
1244 if (!i7core_dev->pdev)
1245 return -ENOMEM;
1246 i7core_dev->socket = socket;
1247 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001248 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001249
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001250 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001251 i7core_printk(KERN_ERR,
1252 "Duplicated device for "
1253 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001254 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1255 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001256 pci_dev_put(pdev);
1257 return -ENODEV;
1258 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001259
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001260 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001261
1262 /* Sanity check */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001263 if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
1264 PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001265 i7core_printk(KERN_ERR,
1266 "Device PCI ID %04x:%04x "
1267 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001268 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001269 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001270 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001271 return -ENODEV;
1272 }
1273
1274 /* Be sure that the device is enabled */
1275 if (unlikely(pci_enable_device(pdev) < 0)) {
1276 i7core_printk(KERN_ERR,
1277 "Couldn't enable "
1278 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001279 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1280 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001281 return -ENODEV;
1282 }
1283
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001284 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1285 socket, bus, pci_dev_descr[devno].dev,
1286 pci_dev_descr[devno].func,
1287 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001288
1289 *prev = pdev;
1290
1291 return 0;
1292}
1293
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001294static int i7core_get_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001295{
1296 int i;
1297 struct pci_dev *pdev = NULL;
1298
1299 for (i = 0; i < N_DEVS; i++) {
1300 pdev = NULL;
1301 do {
1302 if (i7core_get_onedevice(&pdev, i) < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001303 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001304 return -ENODEV;
1305 }
1306 } while (pdev);
1307 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001308
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001309 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001310}
1311
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001312static int mci_bind_devs(struct mem_ctl_info *mci,
1313 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001314{
1315 struct i7core_pvt *pvt = mci->pvt_info;
1316 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001317 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001318
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001319 /* Associates i7core_dev and mci for future usage */
1320 pvt->i7core_dev = i7core_dev;
1321 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001322
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001323 pvt->is_registered = 0;
1324 for (i = 0; i < N_DEVS; i++) {
1325 pdev = i7core_dev->pdev[i];
1326 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001327 continue;
1328
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001329 func = PCI_FUNC(pdev->devfn);
1330 slot = PCI_SLOT(pdev->devfn);
1331 if (slot == 3) {
1332 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001333 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001334 pvt->pci_mcr[func] = pdev;
1335 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1336 if (unlikely(func > MAX_CHAN_FUNC))
1337 goto error;
1338 pvt->pci_ch[slot - 4][func] = pdev;
1339 } else if (!slot && !func)
1340 pvt->pci_noncore = pdev;
1341 else
1342 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001343
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001344 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1345 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1346 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001347
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001348 if (PCI_SLOT(pdev->devfn) == 3 &&
1349 PCI_FUNC(pdev->devfn) == 2)
1350 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001351 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001352
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001353 return 0;
1354
1355error:
1356 i7core_printk(KERN_ERR, "Device %d, function %d "
1357 "is out of the expected range\n",
1358 slot, func);
1359 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001360}
1361
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001362/****************************************************************************
1363 Error check routines
1364 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001365static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001366 int chan, int dimm, int add)
1367{
1368 char *msg;
1369 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001370 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001371
1372 for (i = 0; i < add; i++) {
1373 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001374 "(Socket=%d channel=%d dimm=%d)",
1375 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001376
1377 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1378 kfree (msg);
1379 }
1380}
1381
1382static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001383 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001384{
1385 struct i7core_pvt *pvt = mci->pvt_info;
1386 int add0 = 0, add1 = 0, add2 = 0;
1387 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001388 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001389 /* Updates CE counters */
1390
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001391 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1392 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1393 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001394
1395 if (add2 < 0)
1396 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001397 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001398
1399 if (add1 < 0)
1400 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001401 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001402
1403 if (add0 < 0)
1404 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001405 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001406 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001407 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001408
1409 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001410 pvt->rdimm_last_ce_count[chan][2] = new2;
1411 pvt->rdimm_last_ce_count[chan][1] = new1;
1412 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001413
1414 /*updated the edac core */
1415 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001416 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001417 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001418 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001419 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001420 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001421
1422}
1423
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001424static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001425{
1426 struct i7core_pvt *pvt = mci->pvt_info;
1427 u32 rcv[3][2];
1428 int i, new0, new1, new2;
1429
1430 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001431 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001432 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001433 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001434 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001435 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001436 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001437 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001438 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001439 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001440 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001441 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001442 &rcv[2][1]);
1443 for (i = 0 ; i < 3; i++) {
1444 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1445 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1446 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001447 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001448 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1449 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1450 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1451 } else {
1452 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1453 DIMM_BOT_COR_ERR(rcv[i][0]);
1454 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1455 DIMM_BOT_COR_ERR(rcv[i][1]);
1456 new2 = 0;
1457 }
1458
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001459 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001460 }
1461}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001462
1463/* This function is based on the device 3 function 4 registers as described on:
1464 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1465 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1466 * also available at:
1467 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1468 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001469static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001470{
1471 struct i7core_pvt *pvt = mci->pvt_info;
1472 u32 rcv1, rcv0;
1473 int new0, new1, new2;
1474
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001475 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001476 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001477 return;
1478 }
1479
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001480 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001481 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1482 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001483
1484 /* Store the new values */
1485 new2 = DIMM2_COR_ERR(rcv1);
1486 new1 = DIMM1_COR_ERR(rcv0);
1487 new0 = DIMM0_COR_ERR(rcv0);
1488
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001489 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001490 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001491 /* Updates CE counters */
1492 int add0, add1, add2;
1493
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001494 add2 = new2 - pvt->udimm_last_ce_count[2];
1495 add1 = new1 - pvt->udimm_last_ce_count[1];
1496 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001497
1498 if (add2 < 0)
1499 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001500 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001501
1502 if (add1 < 0)
1503 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001504 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001505
1506 if (add0 < 0)
1507 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001508 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001509
1510 if (add0 | add1 | add2)
1511 i7core_printk(KERN_ERR, "New Corrected error(s): "
1512 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1513 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001514 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001515 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001516
1517 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001518 pvt->udimm_last_ce_count[2] = new2;
1519 pvt->udimm_last_ce_count[1] = new1;
1520 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001521}
1522
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001523/*
1524 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1525 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001526 * Nehalem are defined as family 0x06, model 0x1a
1527 *
1528 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001529 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001530 * m->status MSR_IA32_MC8_STATUS
1531 * m->addr MSR_IA32_MC8_ADDR
1532 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001533 * In the case of Nehalem, the error information is masked at .status and .misc
1534 * fields
1535 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001536static void i7core_mce_output_error(struct mem_ctl_info *mci,
1537 struct mce *m)
1538{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001539 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001540 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001541 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001542 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001543 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1544 u32 dimm = (m->misc >> 16) & 0x3;
1545 u32 channel = (m->misc >> 18) & 0x3;
1546 u32 syndrome = m->misc >> 32;
1547 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001548 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001549
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001550 if (m->mcgstatus & 1)
1551 type = "FATAL";
1552 else
1553 type = "NON_FATAL";
1554
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001555 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001556 case 0:
1557 optype = "generic undef request";
1558 break;
1559 case 1:
1560 optype = "read error";
1561 break;
1562 case 2:
1563 optype = "write error";
1564 break;
1565 case 3:
1566 optype = "addr/cmd error";
1567 break;
1568 case 4:
1569 optype = "scrubbing error";
1570 break;
1571 default:
1572 optype = "reserved";
1573 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001574 }
1575
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001576 switch (errnum) {
1577 case 16:
1578 err = "read ECC error";
1579 break;
1580 case 17:
1581 err = "RAS ECC error";
1582 break;
1583 case 18:
1584 err = "write parity error";
1585 break;
1586 case 19:
1587 err = "redundacy loss";
1588 break;
1589 case 20:
1590 err = "reserved";
1591 break;
1592 case 21:
1593 err = "memory range error";
1594 break;
1595 case 22:
1596 err = "RTID out of range";
1597 break;
1598 case 23:
1599 err = "address parity error";
1600 break;
1601 case 24:
1602 err = "byte enable parity error";
1603 break;
1604 default:
1605 err = "unknown";
1606 }
1607
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001608 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001609 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001610 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001611 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001612 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001613 syndrome, core_err_cnt, (long long)m->status,
1614 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001615
1616 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001617
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001618 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001619
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001620 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001621 if (m->mcgstatus & 1)
1622 edac_mc_handle_fbd_ue(mci, csrow, 0,
1623 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001624 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001625 edac_mc_handle_fbd_ce(mci, csrow,
1626 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001627
1628 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001629}
1630
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001631/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001632 * i7core_check_error Retrieve and process errors reported by the
1633 * hardware. Called by the Core module.
1634 */
1635static void i7core_check_error(struct mem_ctl_info *mci)
1636{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001637 struct i7core_pvt *pvt = mci->pvt_info;
1638 int i;
1639 unsigned count = 0;
1640 struct mce *m = NULL;
1641 unsigned long flags;
1642
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001643 /* Copy all mce errors into a temporary buffer */
1644 spin_lock_irqsave(&pvt->mce_lock, flags);
1645 if (pvt->mce_count) {
1646 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001647
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001648 if (m) {
1649 count = pvt->mce_count;
1650 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1651 }
1652 pvt->mce_count = 0;
1653 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001654
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001655 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1656
1657 /* proccess mcelog errors */
1658 for (i = 0; i < count; i++)
1659 i7core_mce_output_error(mci, &m[i]);
1660
1661 kfree(m);
1662
1663 /* check memory count errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001664 if (!pvt->is_registered)
1665 i7core_udimm_check_mc_ecc_err(mci);
1666 else
1667 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001668}
1669
1670/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001671 * i7core_mce_check_error Replicates mcelog routine to get errors
1672 * This routine simply queues mcelog errors, and
1673 * return. The error itself should be handled later
1674 * by i7core_check_error.
1675 */
1676static int i7core_mce_check_error(void *priv, struct mce *mce)
1677{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001678 struct mem_ctl_info *mci = priv;
1679 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001680 unsigned long flags;
1681
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001682 /*
1683 * Just let mcelog handle it if the error is
1684 * outside the memory controller
1685 */
1686 if (((mce->status & 0xffff) >> 7) != 1)
1687 return 0;
1688
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001689 /* Bank 8 registers are the only ones that we know how to handle */
1690 if (mce->bank != 8)
1691 return 0;
1692
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001693 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001694 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) {
1695 debugf0("mc%d: ignoring mce log for socket %d. "
1696 "Another mc should get it.\n",
1697 pvt->i7core_dev->socket,
1698 cpu_data(mce->cpu).phys_proc_id);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001699 return 0;
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001700 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001701
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001702 spin_lock_irqsave(&pvt->mce_lock, flags);
1703 if (pvt->mce_count < MCE_LOG_LEN) {
1704 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1705 pvt->mce_count++;
1706 }
1707 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1708
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001709 /* Handle fatal errors immediately */
1710 if (mce->mcgstatus & 1)
1711 i7core_check_error(mci);
1712
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001713 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001714 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001715}
1716
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001717static int i7core_register_mci(struct i7core_dev *i7core_dev,
1718 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001719{
1720 struct mem_ctl_info *mci;
1721 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001722 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001723 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001724
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001725 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001726 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1727 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001728 if (unlikely(!mci))
1729 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001730
1731 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1732
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001733 /* record ptr to the generic device */
1734 mci->dev = &i7core_dev->pdev[0]->dev;
1735
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001736 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001737 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001738
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001739 /*
1740 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1741 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1742 * memory channels
1743 */
1744 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001745 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1746 mci->edac_cap = EDAC_FLAG_NONE;
1747 mci->mod_name = "i7core_edac.c";
1748 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001749 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1750 i7core_dev->socket);
1751 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001752 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001753 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001754 /* Set the function pointer to an actual operation function */
1755 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001756
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001757 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001758 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001759 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001760 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001761
1762 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001763 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001764
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001765 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001766 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001767 debugf0("MC: " __FILE__
1768 ": %s(): failed edac_mc_add_mc()\n", __func__);
1769 /* FIXME: perhaps some code should go here that disables error
1770 * reporting if we just enabled it
1771 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001772
1773 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001774 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001775 }
1776
1777 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001778 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1779 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001780 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001781 printk(KERN_WARNING
1782 "%s(): Unable to create PCI control\n",
1783 __func__);
1784 printk(KERN_WARNING
1785 "%s(): PCI error report via EDAC not setup\n",
1786 __func__);
1787 }
1788
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001789 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001790 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001791 pvt->inject.dimm = -1;
1792 pvt->inject.rank = -1;
1793 pvt->inject.bank = -1;
1794 pvt->inject.page = -1;
1795 pvt->inject.col = -1;
1796
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001797 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001798 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001799 pvt->edac_mce.check_error = i7core_mce_check_error;
1800 spin_lock_init(&pvt->mce_lock);
1801
1802 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001803 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001804 debugf0("MC: " __FILE__
1805 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001806 }
1807
1808fail:
1809 edac_mc_free(mci);
1810 return rc;
1811}
1812
1813/*
1814 * i7core_probe Probe for ONE instance of device to see if it is
1815 * present.
1816 * return:
1817 * 0 for FOUND a device
1818 * < 0 for error code
1819 */
1820static int __devinit i7core_probe(struct pci_dev *pdev,
1821 const struct pci_device_id *id)
1822{
1823 int dev_idx = id->driver_data;
1824 int rc;
1825 struct i7core_dev *i7core_dev;
1826
1827 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001828 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001829 */
1830 if (unlikely(dev_idx >= 1))
1831 return -EINVAL;
1832
1833 /* get the pci devices we want to reserve for our use */
1834 mutex_lock(&i7core_edac_lock);
1835 rc = i7core_get_devices();
1836 if (unlikely(rc < 0))
1837 goto fail0;
1838
1839 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1840 int channels;
1841 int csrows;
1842
1843 /* Check the number of active and not disabled channels */
1844 rc = i7core_get_active_channels(i7core_dev->socket,
1845 &channels, &csrows);
1846 if (unlikely(rc < 0))
1847 goto fail1;
1848
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001849 rc = i7core_register_mci(i7core_dev, channels, csrows);
1850 if (unlikely(rc < 0))
1851 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001852 }
1853
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001854 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001855
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001856 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001857 return 0;
1858
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001859fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001860 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001861fail0:
1862 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001863 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001864}
1865
1866/*
1867 * i7core_remove destructor for one instance of device
1868 *
1869 */
1870static void __devexit i7core_remove(struct pci_dev *pdev)
1871{
1872 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001873 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001874
1875 debugf0(__FILE__ ": %s()\n", __func__);
1876
1877 if (i7core_pci)
1878 edac_pci_release_generic_ctl(i7core_pci);
1879
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001880 /*
1881 * we have a trouble here: pdev value for removal will be wrong, since
1882 * it will point to the X58 register used to detect that the machine
1883 * is a Nehalem or upper design. However, due to the way several PCI
1884 * devices are grouped together to provide MC functionality, we need
1885 * to use a different method for releasing the devices
1886 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001887
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001888 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001889 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1890 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1891 if (mci) {
1892 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001893
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001894 i7core_dev = pvt->i7core_dev;
1895 edac_mce_unregister(&pvt->edac_mce);
1896 kfree(mci->ctl_name);
1897 edac_mc_free(mci);
1898 i7core_put_devices(i7core_dev);
1899 } else {
1900 i7core_printk(KERN_ERR,
1901 "Couldn't find mci for socket %d\n",
1902 i7core_dev->socket);
1903 }
1904 }
1905 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001906}
1907
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001908MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1909
1910/*
1911 * i7core_driver pci_driver structure for this module
1912 *
1913 */
1914static struct pci_driver i7core_driver = {
1915 .name = "i7core_edac",
1916 .probe = i7core_probe,
1917 .remove = __devexit_p(i7core_remove),
1918 .id_table = i7core_pci_tbl,
1919};
1920
1921/*
1922 * i7core_init Module entry function
1923 * Try to initialize this module for its devices
1924 */
1925static int __init i7core_init(void)
1926{
1927 int pci_rc;
1928
1929 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1930
1931 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1932 opstate_init();
1933
Keith Manntheybc2d7242009-09-03 00:05:05 -03001934 i7core_xeon_pci_fixup();
1935
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001936 pci_rc = pci_register_driver(&i7core_driver);
1937
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001938 if (pci_rc >= 0)
1939 return 0;
1940
1941 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1942 pci_rc);
1943
1944 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001945}
1946
1947/*
1948 * i7core_exit() Module exit function
1949 * Unregister the driver
1950 */
1951static void __exit i7core_exit(void)
1952{
1953 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1954 pci_unregister_driver(&i7core_driver);
1955}
1956
1957module_init(i7core_init);
1958module_exit(i7core_exit);
1959
1960MODULE_LICENSE("GPL");
1961MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1962MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1963MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1964 I7CORE_REVISION);
1965
1966module_param(edac_op_state, int, 0444);
1967MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");