blob: e944b63d9f062fe510ba234292b6622ec8bdbd07 [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>
Randy Dunlap3b918c12009-11-08 01:36:40 -020028#include <linux/delay.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030029#include <linux/edac.h>
30#include <linux/mmzone.h>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030031#include <linux/edac_mce.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 Chehabde06eee2009-10-14 08:02:40 -0300206 int optional;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300207};
208
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300209struct i7core_dev {
210 struct list_head list;
211 u8 socket;
212 struct pci_dev **pdev;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300213 int n_devs;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300214 struct mem_ctl_info *mci;
215};
216
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300217struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300218 struct pci_dev *pci_noncore;
219 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
220 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
221
222 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300223
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300224 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300225 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300226 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300227
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300228 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300229
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300230 int ce_count_available;
231 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300232
233 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300234 unsigned long udimm_ce_count[MAX_DIMMS];
235 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300236 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300237 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
238 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300239
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300240 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300241
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300242 /* mcelog glue */
243 struct edac_mce edac_mce;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300244
245 /* Fifo double buffers */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300246 struct mce mce_entry[MCE_LOG_LEN];
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300247 struct mce mce_outentry[MCE_LOG_LEN];
248
249 /* Fifo in/out counters */
250 unsigned mce_in, mce_out;
251
252 /* Count indicator to show errors not got */
253 unsigned mce_overrun;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300254};
255
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300256/* Static vars */
257static LIST_HEAD(i7core_edac_list);
258static DEFINE_MUTEX(i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300259
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300260#define PCI_DESCR(device, function, device_id) \
261 .dev = (device), \
262 .func = (function), \
263 .dev_id = (device_id)
264
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300265struct pci_id_descr pci_dev_descr_i7core[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300266 /* Memory controller */
267 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
268 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300269 /* Exists only for RDIMM */
270 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300271 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
272
273 /* Channel 0 */
274 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
275 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
276 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
277 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
278
279 /* Channel 1 */
280 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
281 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
282 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
283 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
284
285 /* Channel 2 */
286 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
287 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
288 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
289 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300290
291 /* Generic Non-core registers */
292 /*
293 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
294 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
295 * the probing code needs to test for the other address in case of
296 * failure of this one
297 */
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -0300298 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300299
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300300};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300301
302/*
303 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300304 */
305static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300306 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300307 {0,} /* 0 terminated list. */
308};
309
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300310static struct edac_pci_ctl_info *i7core_pci;
311
312/****************************************************************************
313 Anciliary status routines
314 ****************************************************************************/
315
316 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300317#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
318#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300319
320 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300321#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300322#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300323
324 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300325static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300326{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300327 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300328}
329
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300330static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300331{
332 static int ranks[4] = { 1, 2, 4, -EINVAL };
333
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300334 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300335}
336
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300337static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300338{
339 static int banks[4] = { 4, 8, 16, -EINVAL };
340
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300341 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300342}
343
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300344static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300345{
346 static int rows[8] = {
347 1 << 12, 1 << 13, 1 << 14, 1 << 15,
348 1 << 16, -EINVAL, -EINVAL, -EINVAL,
349 };
350
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300351 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300352}
353
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300354static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300355{
356 static int cols[8] = {
357 1 << 10, 1 << 11, 1 << 12, -EINVAL,
358 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300359 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300360}
361
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300362static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300363{
364 struct i7core_dev *i7core_dev;
365
366 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
367 if (i7core_dev->socket == socket)
368 return i7core_dev;
369 }
370
371 return NULL;
372}
373
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300374/****************************************************************************
375 Memory check routines
376 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300377static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
378 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300379{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300380 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300381 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300382
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300383 if (!i7core_dev)
384 return NULL;
385
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300386 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300387 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300388 continue;
389
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300390 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
391 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
392 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300393 }
394 }
395
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300396 return NULL;
397}
398
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300399/**
400 * i7core_get_active_channels() - gets the number of channels and csrows
401 * @socket: Quick Path Interconnect socket
402 * @channels: Number of channels that will be returned
403 * @csrows: Number of csrows found
404 *
405 * Since EDAC core needs to know in advance the number of available channels
406 * and csrows, in order to allocate memory for csrows/channels, it is needed
407 * to run two similar steps. At the first step, implemented on this function,
408 * it checks the number of csrows/channels present at one socket.
409 * this is used in order to properly allocate the size of mci components.
410 *
411 * It should be noticed that none of the current available datasheets explain
412 * or even mention how csrows are seen by the memory controller. So, we need
413 * to add a fake description for csrows.
414 * So, this driver is attributing one DIMM memory for one csrow.
415 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300416static int i7core_get_active_channels(u8 socket, unsigned *channels,
417 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300418{
419 struct pci_dev *pdev = NULL;
420 int i, j;
421 u32 status, control;
422
423 *channels = 0;
424 *csrows = 0;
425
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300426 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300427 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300428 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
429 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300430 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300431 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300432
433 /* Device 3 function 0 reads */
434 pci_read_config_dword(pdev, MC_STATUS, &status);
435 pci_read_config_dword(pdev, MC_CONTROL, &control);
436
437 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300438 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300439 /* Check if the channel is active */
440 if (!(control & (1 << (8 + i))))
441 continue;
442
443 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300444 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300445 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300446
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300447 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300448 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300449 i7core_printk(KERN_ERR, "Couldn't find socket %d "
450 "fn %d.%d!!!\n",
451 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300452 return -ENODEV;
453 }
454 /* Devices 4-6 function 1 */
455 pci_read_config_dword(pdev,
456 MC_DOD_CH_DIMM0, &dimm_dod[0]);
457 pci_read_config_dword(pdev,
458 MC_DOD_CH_DIMM1, &dimm_dod[1]);
459 pci_read_config_dword(pdev,
460 MC_DOD_CH_DIMM2, &dimm_dod[2]);
461
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300462 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300463
464 for (j = 0; j < 3; j++) {
465 if (!DIMM_PRESENT(dimm_dod[j]))
466 continue;
467 (*csrows)++;
468 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300469 }
470
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300471 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300472 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300473
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300474 return 0;
475}
476
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300477static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300478{
479 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300480 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300481 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300482 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300483 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300484 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300485 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300486
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300487 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300488 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300489 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300490 return -ENODEV;
491
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300492 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300493 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
494 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
495 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
496 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300497
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300498 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300499 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300500 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300501
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300502 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300503 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300504 if (ECCx8(pvt))
505 mode = EDAC_S8ECD8ED;
506 else
507 mode = EDAC_S4ECD4ED;
508 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300509 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300510 mode = EDAC_NONE;
511 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300512
513 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300514 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
515 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300516 numdimms(pvt->info.max_dod),
517 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300518 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300519 numrow(pvt->info.max_dod >> 6),
520 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300521
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300522 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300523 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300524
525 if (!CH_ACTIVE(pvt, i)) {
526 debugf0("Channel %i is not active\n", i);
527 continue;
528 }
529 if (CH_DISABLED(pvt, i)) {
530 debugf0("Channel %i is disabled\n", i);
531 continue;
532 }
533
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300534 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300535 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300536 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
537
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300538 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300539 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300540
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300541 if (data & REGISTERED_DIMM)
542 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300543 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300544 mtype = MEM_DDR3;
545#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300546 if (data & THREE_DIMMS_PRESENT)
547 pvt->channel[i].dimms = 3;
548 else if (data & SINGLE_QUAD_RANK_PRESENT)
549 pvt->channel[i].dimms = 1;
550 else
551 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300552#endif
553
554 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300555 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300556 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300557 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300558 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300559 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300560 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300561
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300562 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300563 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300564 i,
565 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
566 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300567 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300568 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300569
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300570 for (j = 0; j < 3; j++) {
571 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300572 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300573
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300574 if (!DIMM_PRESENT(dimm_dod[j]))
575 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300576
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300577 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
578 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
579 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
580 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300581
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300582 /* DDR3 has 8 I/O banks */
583 size = (rows * cols * banks * ranks) >> (20 - 3);
584
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300585 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300586
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300587 debugf0("\tdimm %d %d Mb offset: %x, "
588 "bank: %d, rank: %d, row: %#x, col: %#x\n",
589 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300590 RANKOFFSET(dimm_dod[j]),
591 banks, ranks, rows, cols);
592
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300593#if PAGE_SHIFT > 20
594 npages = size >> (PAGE_SHIFT - 20);
595#else
596 npages = size << (20 - PAGE_SHIFT);
597#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300598
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300599 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300600 csr->first_page = last_page + 1;
601 last_page += npages;
602 csr->last_page = last_page;
603 csr->nr_pages = npages;
604
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300605 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300606 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300607 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300608 csr->nr_channels = 1;
609
610 csr->channels[0].chan_idx = i;
611 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300612
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300613 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300614
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300615 switch (banks) {
616 case 4:
617 csr->dtype = DEV_X4;
618 break;
619 case 8:
620 csr->dtype = DEV_X8;
621 break;
622 case 16:
623 csr->dtype = DEV_X16;
624 break;
625 default:
626 csr->dtype = DEV_UNKNOWN;
627 }
628
629 csr->edac_mode = mode;
630 csr->mtype = mtype;
631
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300632 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300633 }
634
635 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
636 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
637 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
638 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
639 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
640 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
641 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
642 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300643 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300644 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300645 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300646 (value[j] >> 27) & 0x1,
647 (value[j] >> 24) & 0x7,
648 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300649 }
650
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300651 return 0;
652}
653
654/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300655 Error insertion routines
656 ****************************************************************************/
657
658/* The i7core has independent error injection features per channel.
659 However, to have a simpler code, we don't allow enabling error injection
660 on more than one channel.
661 Also, since a change at an inject parameter will be applied only at enable,
662 we're disabling error injection on all write calls to the sysfs nodes that
663 controls the error code injection.
664 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300665static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300666{
667 struct i7core_pvt *pvt = mci->pvt_info;
668
669 pvt->inject.enable = 0;
670
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300671 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300672 return -ENODEV;
673
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300674 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300675 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300676
677 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300678}
679
680/*
681 * i7core inject inject.section
682 *
683 * accept and store error injection inject.section value
684 * bit 0 - refers to the lower 32-byte half cacheline
685 * bit 1 - refers to the upper 32-byte half cacheline
686 */
687static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
688 const char *data, size_t count)
689{
690 struct i7core_pvt *pvt = mci->pvt_info;
691 unsigned long value;
692 int rc;
693
694 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300695 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300696
697 rc = strict_strtoul(data, 10, &value);
698 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300699 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300700
701 pvt->inject.section = (u32) value;
702 return count;
703}
704
705static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
706 char *data)
707{
708 struct i7core_pvt *pvt = mci->pvt_info;
709 return sprintf(data, "0x%08x\n", pvt->inject.section);
710}
711
712/*
713 * i7core inject.type
714 *
715 * accept and store error injection inject.section value
716 * bit 0 - repeat enable - Enable error repetition
717 * bit 1 - inject ECC error
718 * bit 2 - inject parity error
719 */
720static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
721 const char *data, size_t count)
722{
723 struct i7core_pvt *pvt = mci->pvt_info;
724 unsigned long value;
725 int rc;
726
727 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300728 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300729
730 rc = strict_strtoul(data, 10, &value);
731 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300732 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300733
734 pvt->inject.type = (u32) value;
735 return count;
736}
737
738static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
739 char *data)
740{
741 struct i7core_pvt *pvt = mci->pvt_info;
742 return sprintf(data, "0x%08x\n", pvt->inject.type);
743}
744
745/*
746 * i7core_inject_inject.eccmask_store
747 *
748 * The type of error (UE/CE) will depend on the inject.eccmask value:
749 * Any bits set to a 1 will flip the corresponding ECC bit
750 * Correctable errors can be injected by flipping 1 bit or the bits within
751 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
752 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
753 * uncorrectable error to be injected.
754 */
755static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
756 const char *data, size_t count)
757{
758 struct i7core_pvt *pvt = mci->pvt_info;
759 unsigned long value;
760 int rc;
761
762 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300763 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300764
765 rc = strict_strtoul(data, 10, &value);
766 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300767 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300768
769 pvt->inject.eccmask = (u32) value;
770 return count;
771}
772
773static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
774 char *data)
775{
776 struct i7core_pvt *pvt = mci->pvt_info;
777 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
778}
779
780/*
781 * i7core_addrmatch
782 *
783 * The type of error (UE/CE) will depend on the inject.eccmask value:
784 * Any bits set to a 1 will flip the corresponding ECC bit
785 * Correctable errors can be injected by flipping 1 bit or the bits within
786 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
787 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
788 * uncorrectable error to be injected.
789 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300790
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300791#define DECLARE_ADDR_MATCH(param, limit) \
792static ssize_t i7core_inject_store_##param( \
793 struct mem_ctl_info *mci, \
794 const char *data, size_t count) \
795{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300796 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300797 long value; \
798 int rc; \
799 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300800 debugf1("%s()\n", __func__); \
801 pvt = mci->pvt_info; \
802 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300803 if (pvt->inject.enable) \
804 disable_inject(mci); \
805 \
Mauro Carvalho Chehab4f87fad2009-10-04 11:54:56 -0300806 if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300807 value = -1; \
808 else { \
809 rc = strict_strtoul(data, 10, &value); \
810 if ((rc < 0) || (value >= limit)) \
811 return -EIO; \
812 } \
813 \
814 pvt->inject.param = value; \
815 \
816 return count; \
817} \
818 \
819static ssize_t i7core_inject_show_##param( \
820 struct mem_ctl_info *mci, \
821 char *data) \
822{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300823 struct i7core_pvt *pvt; \
824 \
825 pvt = mci->pvt_info; \
826 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300827 if (pvt->inject.param < 0) \
828 return sprintf(data, "any\n"); \
829 else \
830 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300831}
832
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300833#define ATTR_ADDR_MATCH(param) \
834 { \
835 .attr = { \
836 .name = #param, \
837 .mode = (S_IRUGO | S_IWUSR) \
838 }, \
839 .show = i7core_inject_show_##param, \
840 .store = i7core_inject_store_##param, \
841 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300842
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300843DECLARE_ADDR_MATCH(channel, 3);
844DECLARE_ADDR_MATCH(dimm, 3);
845DECLARE_ADDR_MATCH(rank, 4);
846DECLARE_ADDR_MATCH(bank, 32);
847DECLARE_ADDR_MATCH(page, 0x10000);
848DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300849
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300850static int write_and_test(struct pci_dev *dev, int where, u32 val)
851{
852 u32 read;
853 int count;
854
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300855 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
856 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
857 where, val);
858
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300859 for (count = 0; count < 10; count++) {
860 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300861 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300862 pci_write_config_dword(dev, where, val);
863 pci_read_config_dword(dev, where, &read);
864
865 if (read == val)
866 return 0;
867 }
868
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300869 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
870 "write=%08x. Read=%08x\n",
871 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
872 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300873
874 return -EINVAL;
875}
876
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300877/*
878 * This routine prepares the Memory Controller for error injection.
879 * The error will be injected when some process tries to write to the
880 * memory that matches the given criteria.
881 * The criteria can be set in terms of a mask where dimm, rank, bank, page
882 * and col can be specified.
883 * A -1 value for any of the mask items will make the MCU to ignore
884 * that matching criteria for error injection.
885 *
886 * It should be noticed that the error will only happen after a write operation
887 * on a memory that matches the condition. if REPEAT_EN is not enabled at
888 * inject mask, then it will produce just one error. Otherwise, it will repeat
889 * until the injectmask would be cleaned.
890 *
891 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
892 * is reliable enough to check if the MC is using the
893 * three channels. However, this is not clear at the datasheet.
894 */
895static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
896 const char *data, size_t count)
897{
898 struct i7core_pvt *pvt = mci->pvt_info;
899 u32 injectmask;
900 u64 mask = 0;
901 int rc;
902 long enable;
903
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300904 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300905 return 0;
906
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300907 rc = strict_strtoul(data, 10, &enable);
908 if ((rc < 0))
909 return 0;
910
911 if (enable) {
912 pvt->inject.enable = 1;
913 } else {
914 disable_inject(mci);
915 return count;
916 }
917
918 /* Sets pvt->inject.dimm mask */
919 if (pvt->inject.dimm < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200920 mask |= 1LL << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300921 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300922 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -0200923 mask |= (pvt->inject.dimm & 0x3LL) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300924 else
Alan Cox486dd092009-11-08 01:34:27 -0200925 mask |= (pvt->inject.dimm & 0x1LL) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300926 }
927
928 /* Sets pvt->inject.rank mask */
929 if (pvt->inject.rank < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200930 mask |= 1LL << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300931 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300932 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -0200933 mask |= (pvt->inject.rank & 0x1LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300934 else
Alan Cox486dd092009-11-08 01:34:27 -0200935 mask |= (pvt->inject.rank & 0x3LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300936 }
937
938 /* Sets pvt->inject.bank mask */
939 if (pvt->inject.bank < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200940 mask |= 1LL << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300941 else
Alan Cox486dd092009-11-08 01:34:27 -0200942 mask |= (pvt->inject.bank & 0x15LL) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300943
944 /* Sets pvt->inject.page mask */
945 if (pvt->inject.page < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200946 mask |= 1LL << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300947 else
Alan Cox486dd092009-11-08 01:34:27 -0200948 mask |= (pvt->inject.page & 0xffff) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300949
950 /* Sets pvt->inject.column mask */
951 if (pvt->inject.col < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200952 mask |= 1LL << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300953 else
Alan Cox486dd092009-11-08 01:34:27 -0200954 mask |= (pvt->inject.col & 0x3fff);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300955
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300956 /*
957 * bit 0: REPEAT_EN
958 * bits 1-2: MASK_HALF_CACHELINE
959 * bit 3: INJECT_ECC
960 * bit 4: INJECT_ADDR_PARITY
961 */
962
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300963 injectmask = (pvt->inject.type & 1) |
964 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300965 (pvt->inject.type & 0x6) << (3 - 1);
966
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300967 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300968 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300969 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300970
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300971 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300972 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300973 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300974 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
975
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300976 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300977 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
978
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300979 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300980 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300981
982 /*
983 * This is something undocumented, based on my tests
984 * Without writing 8 to this register, errors aren't injected. Not sure
985 * why.
986 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300987 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300988 MC_CFG_CONTROL, 8);
989
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300990 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
991 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300992 mask, pvt->inject.eccmask, injectmask);
993
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300994
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300995 return count;
996}
997
998static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
999 char *data)
1000{
1001 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001002 u32 injectmask;
1003
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001004 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001005 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001006
1007 debugf0("Inject error read: 0x%018x\n", injectmask);
1008
1009 if (injectmask & 0x0c)
1010 pvt->inject.enable = 1;
1011
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001012 return sprintf(data, "%d\n", pvt->inject.enable);
1013}
1014
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001015#define DECLARE_COUNTER(param) \
1016static ssize_t i7core_show_counter_##param( \
1017 struct mem_ctl_info *mci, \
1018 char *data) \
1019{ \
1020 struct i7core_pvt *pvt = mci->pvt_info; \
1021 \
1022 debugf1("%s() \n", __func__); \
1023 if (!pvt->ce_count_available || (pvt->is_registered)) \
1024 return sprintf(data, "data unavailable\n"); \
1025 return sprintf(data, "%lu\n", \
1026 pvt->udimm_ce_count[param]); \
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001027}
1028
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001029#define ATTR_COUNTER(param) \
1030 { \
1031 .attr = { \
1032 .name = __stringify(udimm##param), \
1033 .mode = (S_IRUGO | S_IWUSR) \
1034 }, \
1035 .show = i7core_show_counter_##param \
1036 }
1037
1038DECLARE_COUNTER(0);
1039DECLARE_COUNTER(1);
1040DECLARE_COUNTER(2);
1041
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001042/*
1043 * Sysfs struct
1044 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001045
1046
1047static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
1048 ATTR_ADDR_MATCH(channel),
1049 ATTR_ADDR_MATCH(dimm),
1050 ATTR_ADDR_MATCH(rank),
1051 ATTR_ADDR_MATCH(bank),
1052 ATTR_ADDR_MATCH(page),
1053 ATTR_ADDR_MATCH(col),
1054 { .attr = { .name = NULL } }
1055};
1056
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001057static struct mcidev_sysfs_group i7core_inject_addrmatch = {
1058 .name = "inject_addrmatch",
1059 .mcidev_attr = i7core_addrmatch_attrs,
1060};
1061
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001062static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
1063 ATTR_COUNTER(0),
1064 ATTR_COUNTER(1),
1065 ATTR_COUNTER(2),
1066};
1067
1068static struct mcidev_sysfs_group i7core_udimm_counters = {
1069 .name = "all_channel_counts",
1070 .mcidev_attr = i7core_udimm_counters_attrs,
1071};
1072
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001073static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001074 {
1075 .attr = {
1076 .name = "inject_section",
1077 .mode = (S_IRUGO | S_IWUSR)
1078 },
1079 .show = i7core_inject_section_show,
1080 .store = i7core_inject_section_store,
1081 }, {
1082 .attr = {
1083 .name = "inject_type",
1084 .mode = (S_IRUGO | S_IWUSR)
1085 },
1086 .show = i7core_inject_type_show,
1087 .store = i7core_inject_type_store,
1088 }, {
1089 .attr = {
1090 .name = "inject_eccmask",
1091 .mode = (S_IRUGO | S_IWUSR)
1092 },
1093 .show = i7core_inject_eccmask_show,
1094 .store = i7core_inject_eccmask_store,
1095 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001096 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001097 }, {
1098 .attr = {
1099 .name = "inject_enable",
1100 .mode = (S_IRUGO | S_IWUSR)
1101 },
1102 .show = i7core_inject_enable_show,
1103 .store = i7core_inject_enable_store,
1104 },
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001105 { .attr = { .name = NULL } }, /* Reserved for udimm counters */
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001106 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001107};
1108
1109/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001110 Device initialization routines: put/get, init/exit
1111 ****************************************************************************/
1112
1113/*
1114 * i7core_put_devices 'put' all the devices that we have
1115 * reserved via 'get'
1116 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001117static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001118{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001119 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001120
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001121 debugf0(__FILE__ ": %s()\n", __func__);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001122 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001123 struct pci_dev *pdev = i7core_dev->pdev[i];
1124 if (!pdev)
1125 continue;
1126 debugf0("Removing dev %02x:%02x.%d\n",
1127 pdev->bus->number,
1128 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1129 pci_dev_put(pdev);
1130 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001131 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001132 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001133 kfree(i7core_dev);
1134}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001135
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001136static void i7core_put_all_devices(void)
1137{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001138 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001139
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001140 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001141 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001142}
1143
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001144static void i7core_xeon_pci_fixup(int dev_id)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001145{
1146 struct pci_dev *pdev = NULL;
1147 int i;
1148 /*
1149 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1150 * aren't announced by acpi. So, we need to use a legacy scan probing
1151 * to detect them
1152 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001153 pdev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001154 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001155 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001156 pcibios_scan_specific_bus(255-i);
1157 }
1158}
1159
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001160/*
1161 * i7core_get_devices Find and perform 'get' operation on the MCH's
1162 * device/functions we want to reference for this driver
1163 *
1164 * Need to 'get' device 16 func 1 and func 2
1165 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001166int i7core_get_onedevice(struct pci_dev **prev, int devno,
1167 struct pci_id_descr *dev_descr, unsigned n_devs)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001168{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001169 struct i7core_dev *i7core_dev;
1170
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001171 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001172 u8 bus = 0;
1173 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001174
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001175 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001176 dev_descr->dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001177
1178 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001179 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1180 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1181 * to probe for the alternate address in case of failure
1182 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001183 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001184 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -03001185 PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001186
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001187 if (!pdev) {
1188 if (*prev) {
1189 *prev = pdev;
1190 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001191 }
1192
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001193 if (dev_descr->optional)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001194 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001195
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001196 i7core_printk(KERN_ERR,
1197 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001198 dev_descr->dev, dev_descr->func,
1199 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001200
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001201 /* End of list, leave */
1202 return -ENODEV;
1203 }
1204 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001205
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001206 if (bus == 0x3f)
1207 socket = 0;
1208 else
1209 socket = 255 - bus;
1210
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001211 i7core_dev = get_i7core_dev(socket);
1212 if (!i7core_dev) {
1213 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1214 if (!i7core_dev)
1215 return -ENOMEM;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001216 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001217 GFP_KERNEL);
1218 if (!i7core_dev->pdev)
1219 return -ENOMEM;
1220 i7core_dev->socket = socket;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001221 i7core_dev->n_devs = n_devs;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001222 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001223 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001224
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001225 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001226 i7core_printk(KERN_ERR,
1227 "Duplicated device for "
1228 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001229 bus, dev_descr->dev, dev_descr->func,
1230 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001231 pci_dev_put(pdev);
1232 return -ENODEV;
1233 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001234
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001235 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001236
1237 /* Sanity check */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001238 if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1239 PCI_FUNC(pdev->devfn) != dev_descr->func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001240 i7core_printk(KERN_ERR,
1241 "Device PCI ID %04x:%04x "
1242 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001243 PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001244 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001245 bus, dev_descr->dev, dev_descr->func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001246 return -ENODEV;
1247 }
1248
1249 /* Be sure that the device is enabled */
1250 if (unlikely(pci_enable_device(pdev) < 0)) {
1251 i7core_printk(KERN_ERR,
1252 "Couldn't enable "
1253 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001254 bus, dev_descr->dev, dev_descr->func,
1255 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001256 return -ENODEV;
1257 }
1258
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001259 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001260 socket, bus, dev_descr->dev,
1261 dev_descr->func,
1262 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001263
1264 *prev = pdev;
1265
1266 return 0;
1267}
1268
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001269static int i7core_get_devices(struct pci_id_descr dev_descr[], unsigned n_devs)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001270{
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001271 int i, rc;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001272 struct pci_dev *pdev = NULL;
1273
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001274 for (i = 0; i < n_devs; i++) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001275 pdev = NULL;
1276 do {
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001277 rc = i7core_get_onedevice(&pdev, i, &dev_descr[i],
1278 n_devs);
1279 if (rc < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001280 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001281 return -ENODEV;
1282 }
1283 } while (pdev);
1284 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001285
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001286 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001287}
1288
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001289static int mci_bind_devs(struct mem_ctl_info *mci,
1290 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001291{
1292 struct i7core_pvt *pvt = mci->pvt_info;
1293 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001294 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001295
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001296 /* Associates i7core_dev and mci for future usage */
1297 pvt->i7core_dev = i7core_dev;
1298 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001299
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001300 pvt->is_registered = 0;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001301 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001302 pdev = i7core_dev->pdev[i];
1303 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001304 continue;
1305
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001306 func = PCI_FUNC(pdev->devfn);
1307 slot = PCI_SLOT(pdev->devfn);
1308 if (slot == 3) {
1309 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001310 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001311 pvt->pci_mcr[func] = pdev;
1312 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1313 if (unlikely(func > MAX_CHAN_FUNC))
1314 goto error;
1315 pvt->pci_ch[slot - 4][func] = pdev;
1316 } else if (!slot && !func)
1317 pvt->pci_noncore = pdev;
1318 else
1319 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001320
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001321 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1322 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1323 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001324
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001325 if (PCI_SLOT(pdev->devfn) == 3 &&
1326 PCI_FUNC(pdev->devfn) == 2)
1327 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001328 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001329
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001330 /*
1331 * Add extra nodes to count errors on udimm
1332 * For registered memory, this is not needed, since the counters
1333 * are already displayed at the standard locations
1334 */
1335 if (!pvt->is_registered)
1336 i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp =
1337 &i7core_udimm_counters;
1338
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001339 return 0;
1340
1341error:
1342 i7core_printk(KERN_ERR, "Device %d, function %d "
1343 "is out of the expected range\n",
1344 slot, func);
1345 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001346}
1347
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001348/****************************************************************************
1349 Error check routines
1350 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001351static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001352 int chan, int dimm, int add)
1353{
1354 char *msg;
1355 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001356 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001357
1358 for (i = 0; i < add; i++) {
1359 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001360 "(Socket=%d channel=%d dimm=%d)",
1361 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001362
1363 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1364 kfree (msg);
1365 }
1366}
1367
1368static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001369 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001370{
1371 struct i7core_pvt *pvt = mci->pvt_info;
1372 int add0 = 0, add1 = 0, add2 = 0;
1373 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001374 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001375 /* Updates CE counters */
1376
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001377 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1378 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1379 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001380
1381 if (add2 < 0)
1382 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001383 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001384
1385 if (add1 < 0)
1386 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001387 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001388
1389 if (add0 < 0)
1390 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001391 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001392 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001393 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001394
1395 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001396 pvt->rdimm_last_ce_count[chan][2] = new2;
1397 pvt->rdimm_last_ce_count[chan][1] = new1;
1398 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001399
1400 /*updated the edac core */
1401 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001402 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001403 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001404 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001405 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001406 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001407
1408}
1409
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001410static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001411{
1412 struct i7core_pvt *pvt = mci->pvt_info;
1413 u32 rcv[3][2];
1414 int i, new0, new1, new2;
1415
1416 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001417 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001418 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001419 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001420 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001421 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001422 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001423 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001424 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001425 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001426 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001427 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001428 &rcv[2][1]);
1429 for (i = 0 ; i < 3; i++) {
1430 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1431 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1432 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001433 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001434 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1435 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1436 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1437 } else {
1438 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1439 DIMM_BOT_COR_ERR(rcv[i][0]);
1440 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1441 DIMM_BOT_COR_ERR(rcv[i][1]);
1442 new2 = 0;
1443 }
1444
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001445 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001446 }
1447}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001448
1449/* This function is based on the device 3 function 4 registers as described on:
1450 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1451 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1452 * also available at:
1453 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1454 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001455static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001456{
1457 struct i7core_pvt *pvt = mci->pvt_info;
1458 u32 rcv1, rcv0;
1459 int new0, new1, new2;
1460
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001461 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001462 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001463 return;
1464 }
1465
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001466 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001467 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1468 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001469
1470 /* Store the new values */
1471 new2 = DIMM2_COR_ERR(rcv1);
1472 new1 = DIMM1_COR_ERR(rcv0);
1473 new0 = DIMM0_COR_ERR(rcv0);
1474
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001475 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001476 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001477 /* Updates CE counters */
1478 int add0, add1, add2;
1479
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001480 add2 = new2 - pvt->udimm_last_ce_count[2];
1481 add1 = new1 - pvt->udimm_last_ce_count[1];
1482 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001483
1484 if (add2 < 0)
1485 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001486 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001487
1488 if (add1 < 0)
1489 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001490 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001491
1492 if (add0 < 0)
1493 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001494 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001495
1496 if (add0 | add1 | add2)
1497 i7core_printk(KERN_ERR, "New Corrected error(s): "
1498 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1499 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001500 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001501 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001502
1503 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001504 pvt->udimm_last_ce_count[2] = new2;
1505 pvt->udimm_last_ce_count[1] = new1;
1506 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001507}
1508
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001509/*
1510 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1511 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001512 * Nehalem are defined as family 0x06, model 0x1a
1513 *
1514 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001515 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001516 * m->status MSR_IA32_MC8_STATUS
1517 * m->addr MSR_IA32_MC8_ADDR
1518 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001519 * In the case of Nehalem, the error information is masked at .status and .misc
1520 * fields
1521 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001522static void i7core_mce_output_error(struct mem_ctl_info *mci,
1523 struct mce *m)
1524{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001525 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001526 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001527 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001528 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001529 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1530 u32 dimm = (m->misc >> 16) & 0x3;
1531 u32 channel = (m->misc >> 18) & 0x3;
1532 u32 syndrome = m->misc >> 32;
1533 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001534 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001535
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001536 if (m->mcgstatus & 1)
1537 type = "FATAL";
1538 else
1539 type = "NON_FATAL";
1540
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001541 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001542 case 0:
1543 optype = "generic undef request";
1544 break;
1545 case 1:
1546 optype = "read error";
1547 break;
1548 case 2:
1549 optype = "write error";
1550 break;
1551 case 3:
1552 optype = "addr/cmd error";
1553 break;
1554 case 4:
1555 optype = "scrubbing error";
1556 break;
1557 default:
1558 optype = "reserved";
1559 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001560 }
1561
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001562 switch (errnum) {
1563 case 16:
1564 err = "read ECC error";
1565 break;
1566 case 17:
1567 err = "RAS ECC error";
1568 break;
1569 case 18:
1570 err = "write parity error";
1571 break;
1572 case 19:
1573 err = "redundacy loss";
1574 break;
1575 case 20:
1576 err = "reserved";
1577 break;
1578 case 21:
1579 err = "memory range error";
1580 break;
1581 case 22:
1582 err = "RTID out of range";
1583 break;
1584 case 23:
1585 err = "address parity error";
1586 break;
1587 case 24:
1588 err = "byte enable parity error";
1589 break;
1590 default:
1591 err = "unknown";
1592 }
1593
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001594 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001595 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001596 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001597 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001598 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001599 syndrome, core_err_cnt, (long long)m->status,
1600 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001601
1602 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001603
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001604 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001605
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001606 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001607 if (m->mcgstatus & 1)
1608 edac_mc_handle_fbd_ue(mci, csrow, 0,
1609 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001610 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001611 edac_mc_handle_fbd_ce(mci, csrow,
1612 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001613
1614 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001615}
1616
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001617/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001618 * i7core_check_error Retrieve and process errors reported by the
1619 * hardware. Called by the Core module.
1620 */
1621static void i7core_check_error(struct mem_ctl_info *mci)
1622{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001623 struct i7core_pvt *pvt = mci->pvt_info;
1624 int i;
1625 unsigned count = 0;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001626 struct mce *m;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001627
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001628 /*
1629 * MCE first step: Copy all mce errors into a temporary buffer
1630 * We use a double buffering here, to reduce the risk of
1631 * loosing an error.
1632 */
1633 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001634 count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
1635 % MCE_LOG_LEN;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001636 if (!count)
1637 return;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001638
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001639 m = pvt->mce_outentry;
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001640 if (pvt->mce_in + count > MCE_LOG_LEN) {
1641 unsigned l = MCE_LOG_LEN - pvt->mce_in;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001642
1643 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
1644 smp_wmb();
1645 pvt->mce_in = 0;
1646 count -= l;
1647 m += l;
1648 }
1649 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
1650 smp_wmb();
1651 pvt->mce_in += count;
1652
1653 smp_rmb();
1654 if (pvt->mce_overrun) {
1655 i7core_printk(KERN_ERR, "Lost %d memory errors\n",
1656 pvt->mce_overrun);
1657 smp_wmb();
1658 pvt->mce_overrun = 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001659 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001660
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001661 /*
1662 * MCE second step: parse errors and display
1663 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001664 for (i = 0; i < count; i++)
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001665 i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001666
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001667 /*
1668 * Now, let's increment CE error counts
1669 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001670 if (!pvt->is_registered)
1671 i7core_udimm_check_mc_ecc_err(mci);
1672 else
1673 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001674}
1675
1676/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001677 * i7core_mce_check_error Replicates mcelog routine to get errors
1678 * This routine simply queues mcelog errors, and
1679 * return. The error itself should be handled later
1680 * by i7core_check_error.
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001681 * WARNING: As this routine should be called at NMI time, extra care should
1682 * be taken to avoid deadlocks, and to be as fast as possible.
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001683 */
1684static int i7core_mce_check_error(void *priv, struct mce *mce)
1685{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001686 struct mem_ctl_info *mci = priv;
1687 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001688
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001689 /*
1690 * Just let mcelog handle it if the error is
1691 * outside the memory controller
1692 */
1693 if (((mce->status & 0xffff) >> 7) != 1)
1694 return 0;
1695
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001696 /* Bank 8 registers are the only ones that we know how to handle */
1697 if (mce->bank != 8)
1698 return 0;
1699
Randy Dunlap3b918c12009-11-08 01:36:40 -02001700#ifdef CONFIG_SMP
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001701 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001702 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001703 return 0;
Randy Dunlap3b918c12009-11-08 01:36:40 -02001704#endif
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001705
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001706 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001707 if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001708 smp_wmb();
1709 pvt->mce_overrun++;
1710 return 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001711 }
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001712
1713 /* Copy memory error at the ringbuffer */
1714 memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001715 smp_wmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001716 pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001717
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001718 /* Handle fatal errors immediately */
1719 if (mce->mcgstatus & 1)
1720 i7core_check_error(mci);
1721
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001722 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001723 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001724}
1725
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001726static int i7core_register_mci(struct i7core_dev *i7core_dev,
1727 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001728{
1729 struct mem_ctl_info *mci;
1730 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001731 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001732 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001733
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001734 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001735 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1736 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001737 if (unlikely(!mci))
1738 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001739
1740 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1741
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001742 /* record ptr to the generic device */
1743 mci->dev = &i7core_dev->pdev[0]->dev;
1744
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001745 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001746 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001747
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001748 /*
1749 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1750 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1751 * memory channels
1752 */
1753 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001754 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1755 mci->edac_cap = EDAC_FLAG_NONE;
1756 mci->mod_name = "i7core_edac.c";
1757 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001758 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1759 i7core_dev->socket);
1760 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001761 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001762 mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001763 /* Set the function pointer to an actual operation function */
1764 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001765
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001766 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001767 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001768 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001769 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001770
1771 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001772 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001773
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001774 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001775 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001776 debugf0("MC: " __FILE__
1777 ": %s(): failed edac_mc_add_mc()\n", __func__);
1778 /* FIXME: perhaps some code should go here that disables error
1779 * reporting if we just enabled it
1780 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001781
1782 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001783 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001784 }
1785
1786 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001787 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1788 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001789 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001790 printk(KERN_WARNING
1791 "%s(): Unable to create PCI control\n",
1792 __func__);
1793 printk(KERN_WARNING
1794 "%s(): PCI error report via EDAC not setup\n",
1795 __func__);
1796 }
1797
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001798 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001799 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001800 pvt->inject.dimm = -1;
1801 pvt->inject.rank = -1;
1802 pvt->inject.bank = -1;
1803 pvt->inject.page = -1;
1804 pvt->inject.col = -1;
1805
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001806 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001807 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001808 pvt->edac_mce.check_error = i7core_mce_check_error;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001809
1810 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001811 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001812 debugf0("MC: " __FILE__
1813 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001814 }
1815
1816fail:
1817 edac_mc_free(mci);
1818 return rc;
1819}
1820
1821/*
1822 * i7core_probe Probe for ONE instance of device to see if it is
1823 * present.
1824 * return:
1825 * 0 for FOUND a device
1826 * < 0 for error code
1827 */
1828static int __devinit i7core_probe(struct pci_dev *pdev,
1829 const struct pci_device_id *id)
1830{
1831 int dev_idx = id->driver_data;
1832 int rc;
1833 struct i7core_dev *i7core_dev;
1834
1835 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001836 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001837 */
1838 if (unlikely(dev_idx >= 1))
1839 return -EINVAL;
1840
1841 /* get the pci devices we want to reserve for our use */
1842 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001843
1844 rc = i7core_get_devices(pci_dev_descr_i7core,
1845 ARRAY_SIZE(pci_dev_descr_i7core));
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001846 if (unlikely(rc < 0))
1847 goto fail0;
1848
1849 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1850 int channels;
1851 int csrows;
1852
1853 /* Check the number of active and not disabled channels */
1854 rc = i7core_get_active_channels(i7core_dev->socket,
1855 &channels, &csrows);
1856 if (unlikely(rc < 0))
1857 goto fail1;
1858
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001859 rc = i7core_register_mci(i7core_dev, channels, csrows);
1860 if (unlikely(rc < 0))
1861 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001862 }
1863
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001864 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001865
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001866 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001867 return 0;
1868
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001869fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001870 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001871fail0:
1872 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001873 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001874}
1875
1876/*
1877 * i7core_remove destructor for one instance of device
1878 *
1879 */
1880static void __devexit i7core_remove(struct pci_dev *pdev)
1881{
1882 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001883 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001884
1885 debugf0(__FILE__ ": %s()\n", __func__);
1886
1887 if (i7core_pci)
1888 edac_pci_release_generic_ctl(i7core_pci);
1889
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001890 /*
1891 * we have a trouble here: pdev value for removal will be wrong, since
1892 * it will point to the X58 register used to detect that the machine
1893 * is a Nehalem or upper design. However, due to the way several PCI
1894 * devices are grouped together to provide MC functionality, we need
1895 * to use a different method for releasing the devices
1896 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001897
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001898 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001899 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1900 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1901 if (mci) {
1902 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001903
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001904 i7core_dev = pvt->i7core_dev;
1905 edac_mce_unregister(&pvt->edac_mce);
1906 kfree(mci->ctl_name);
1907 edac_mc_free(mci);
1908 i7core_put_devices(i7core_dev);
1909 } else {
1910 i7core_printk(KERN_ERR,
1911 "Couldn't find mci for socket %d\n",
1912 i7core_dev->socket);
1913 }
1914 }
1915 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001916}
1917
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001918MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1919
1920/*
1921 * i7core_driver pci_driver structure for this module
1922 *
1923 */
1924static struct pci_driver i7core_driver = {
1925 .name = "i7core_edac",
1926 .probe = i7core_probe,
1927 .remove = __devexit_p(i7core_remove),
1928 .id_table = i7core_pci_tbl,
1929};
1930
1931/*
1932 * i7core_init Module entry function
1933 * Try to initialize this module for its devices
1934 */
1935static int __init i7core_init(void)
1936{
1937 int pci_rc;
1938
1939 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1940
1941 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1942 opstate_init();
1943
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001944 i7core_xeon_pci_fixup(pci_dev_descr_i7core[0].dev_id);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001945
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001946 pci_rc = pci_register_driver(&i7core_driver);
1947
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001948 if (pci_rc >= 0)
1949 return 0;
1950
1951 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1952 pci_rc);
1953
1954 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001955}
1956
1957/*
1958 * i7core_exit() Module exit function
1959 * Unregister the driver
1960 */
1961static void __exit i7core_exit(void)
1962{
1963 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1964 pci_unregister_driver(&i7core_driver);
1965}
1966
1967module_init(i7core_init);
1968module_exit(i7core_exit);
1969
1970MODULE_LICENSE("GPL");
1971MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1972MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1973MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1974 I7CORE_REVISION);
1975
1976module_param(edac_op_state, int, 0444);
1977MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");