blob: 8d63b0046480afe981218e19a7c8bd93e18d98b8 [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
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300209struct pci_id_table {
210 struct pci_id_descr *descr;
211 int n_devs;
212};
213
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300214struct i7core_dev {
215 struct list_head list;
216 u8 socket;
217 struct pci_dev **pdev;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300218 int n_devs;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300219 struct mem_ctl_info *mci;
220};
221
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300222struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300223 struct pci_dev *pci_noncore;
224 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
225 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
226
227 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300228
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300229 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300230 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300231 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300232
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300233 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300234
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300235 int ce_count_available;
236 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300237
238 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300239 unsigned long udimm_ce_count[MAX_DIMMS];
240 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300241 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300242 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
243 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300244
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300245 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300246
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300247 /* mcelog glue */
248 struct edac_mce edac_mce;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300249
250 /* Fifo double buffers */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300251 struct mce mce_entry[MCE_LOG_LEN];
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300252 struct mce mce_outentry[MCE_LOG_LEN];
253
254 /* Fifo in/out counters */
255 unsigned mce_in, mce_out;
256
257 /* Count indicator to show errors not got */
258 unsigned mce_overrun;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300259};
260
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300261/* Static vars */
262static LIST_HEAD(i7core_edac_list);
263static DEFINE_MUTEX(i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300264
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300265#define PCI_DESCR(device, function, device_id) \
266 .dev = (device), \
267 .func = (function), \
268 .dev_id = (device_id)
269
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300270struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300271 /* Memory controller */
272 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
273 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300274 /* Exists only for RDIMM */
275 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300276 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
277
278 /* Channel 0 */
279 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
280 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
281 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
282 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
283
284 /* Channel 1 */
285 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
286 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
287 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
288 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
289
290 /* Channel 2 */
291 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
292 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
293 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
294 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300295
296 /* Generic Non-core registers */
297 /*
298 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
299 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
300 * the probing code needs to test for the other address in case of
301 * failure of this one
302 */
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -0300303 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300304
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300305};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300306
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300307struct pci_id_descr pci_dev_descr_lynnfield[] = {
308 { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) },
309 { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) },
310 { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) },
311
312 { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) },
313 { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) },
314 { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) },
315 { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) },
316
Mauro Carvalho Chehab508fa172009-10-14 13:44:37 -0300317 { PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) },
318 { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
319 { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
320 { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300321
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -0300322 /*
323 * This is the PCI device has an alternate address on some
324 * processors like Core i7 860
325 */
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300326 { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
327};
328
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300329struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
330 /* Memory controller */
331 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) },
332 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) },
333 /* Exists only for RDIMM */
334 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1 },
335 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) },
336
337 /* Channel 0 */
338 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) },
339 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) },
340 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) },
341 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2) },
342
343 /* Channel 1 */
344 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) },
345 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) },
346 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) },
347 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2) },
348
349 /* Channel 2 */
350 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) },
351 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
352 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
353 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) },
354
355 /* Generic Non-core registers */
356 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) },
357
358};
359
360#define PCI_ID_TABLE_ENTRY(A) { A, ARRAY_SIZE(A) }
361struct pci_id_table pci_dev_table[] = {
362 PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
363 PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
364 PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
365};
366
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300367/*
368 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300369 */
370static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300371 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -0300372 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300373 {0,} /* 0 terminated list. */
374};
375
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300376static struct edac_pci_ctl_info *i7core_pci;
377
378/****************************************************************************
379 Anciliary status routines
380 ****************************************************************************/
381
382 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300383#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
384#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300385
386 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300387#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300388#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300389
390 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300391static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300392{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300393 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300394}
395
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300396static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300397{
398 static int ranks[4] = { 1, 2, 4, -EINVAL };
399
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300400 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300401}
402
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300403static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300404{
405 static int banks[4] = { 4, 8, 16, -EINVAL };
406
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300407 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300408}
409
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300410static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300411{
412 static int rows[8] = {
413 1 << 12, 1 << 13, 1 << 14, 1 << 15,
414 1 << 16, -EINVAL, -EINVAL, -EINVAL,
415 };
416
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300417 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300418}
419
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300420static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300421{
422 static int cols[8] = {
423 1 << 10, 1 << 11, 1 << 12, -EINVAL,
424 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300425 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300426}
427
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300428static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300429{
430 struct i7core_dev *i7core_dev;
431
432 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
433 if (i7core_dev->socket == socket)
434 return i7core_dev;
435 }
436
437 return NULL;
438}
439
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300440/****************************************************************************
441 Memory check routines
442 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300443static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
444 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300445{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300446 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300447 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300448
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300449 if (!i7core_dev)
450 return NULL;
451
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300452 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300453 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300454 continue;
455
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300456 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
457 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
458 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300459 }
460 }
461
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300462 return NULL;
463}
464
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300465/**
466 * i7core_get_active_channels() - gets the number of channels and csrows
467 * @socket: Quick Path Interconnect socket
468 * @channels: Number of channels that will be returned
469 * @csrows: Number of csrows found
470 *
471 * Since EDAC core needs to know in advance the number of available channels
472 * and csrows, in order to allocate memory for csrows/channels, it is needed
473 * to run two similar steps. At the first step, implemented on this function,
474 * it checks the number of csrows/channels present at one socket.
475 * this is used in order to properly allocate the size of mci components.
476 *
477 * It should be noticed that none of the current available datasheets explain
478 * or even mention how csrows are seen by the memory controller. So, we need
479 * to add a fake description for csrows.
480 * So, this driver is attributing one DIMM memory for one csrow.
481 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300482static int i7core_get_active_channels(u8 socket, unsigned *channels,
483 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300484{
485 struct pci_dev *pdev = NULL;
486 int i, j;
487 u32 status, control;
488
489 *channels = 0;
490 *csrows = 0;
491
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300492 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300493 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300494 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
495 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300496 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300497 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300498
499 /* Device 3 function 0 reads */
500 pci_read_config_dword(pdev, MC_STATUS, &status);
501 pci_read_config_dword(pdev, MC_CONTROL, &control);
502
503 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300504 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300505 /* Check if the channel is active */
506 if (!(control & (1 << (8 + i))))
507 continue;
508
509 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300510 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300511 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300512
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300513 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300514 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300515 i7core_printk(KERN_ERR, "Couldn't find socket %d "
516 "fn %d.%d!!!\n",
517 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300518 return -ENODEV;
519 }
520 /* Devices 4-6 function 1 */
521 pci_read_config_dword(pdev,
522 MC_DOD_CH_DIMM0, &dimm_dod[0]);
523 pci_read_config_dword(pdev,
524 MC_DOD_CH_DIMM1, &dimm_dod[1]);
525 pci_read_config_dword(pdev,
526 MC_DOD_CH_DIMM2, &dimm_dod[2]);
527
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300528 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300529
530 for (j = 0; j < 3; j++) {
531 if (!DIMM_PRESENT(dimm_dod[j]))
532 continue;
533 (*csrows)++;
534 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300535 }
536
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300537 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300538 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300539
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300540 return 0;
541}
542
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300543static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300544{
545 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300546 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300547 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300548 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300549 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300550 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300551 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300552
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300553 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300554 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300555 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300556 return -ENODEV;
557
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300558 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300559 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
560 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
561 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
562 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300563
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300564 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300565 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300566 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300567
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300568 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300569 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300570 if (ECCx8(pvt))
571 mode = EDAC_S8ECD8ED;
572 else
573 mode = EDAC_S4ECD4ED;
574 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300575 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300576 mode = EDAC_NONE;
577 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300578
579 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300580 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
581 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300582 numdimms(pvt->info.max_dod),
583 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300584 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300585 numrow(pvt->info.max_dod >> 6),
586 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300587
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300588 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300589 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300590
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300591 if (!pvt->pci_ch[i][0])
592 continue;
593
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300594 if (!CH_ACTIVE(pvt, i)) {
595 debugf0("Channel %i is not active\n", i);
596 continue;
597 }
598 if (CH_DISABLED(pvt, i)) {
599 debugf0("Channel %i is disabled\n", i);
600 continue;
601 }
602
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300603 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300604 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300605 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
606
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300607 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300608 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300609
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300610 if (data & REGISTERED_DIMM)
611 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300612 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300613 mtype = MEM_DDR3;
614#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300615 if (data & THREE_DIMMS_PRESENT)
616 pvt->channel[i].dimms = 3;
617 else if (data & SINGLE_QUAD_RANK_PRESENT)
618 pvt->channel[i].dimms = 1;
619 else
620 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300621#endif
622
623 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300624 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300625 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300626 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300627 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300628 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300629 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300630
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300631 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300632 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300633 i,
634 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
635 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300636 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300637 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300638
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300639 for (j = 0; j < 3; j++) {
640 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300641 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300642
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300643 if (!DIMM_PRESENT(dimm_dod[j]))
644 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300645
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300646 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
647 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
648 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
649 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300650
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300651 /* DDR3 has 8 I/O banks */
652 size = (rows * cols * banks * ranks) >> (20 - 3);
653
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300654 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300655
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300656 debugf0("\tdimm %d %d Mb offset: %x, "
657 "bank: %d, rank: %d, row: %#x, col: %#x\n",
658 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300659 RANKOFFSET(dimm_dod[j]),
660 banks, ranks, rows, cols);
661
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300662#if PAGE_SHIFT > 20
663 npages = size >> (PAGE_SHIFT - 20);
664#else
665 npages = size << (20 - PAGE_SHIFT);
666#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300667
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300668 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300669 csr->first_page = last_page + 1;
670 last_page += npages;
671 csr->last_page = last_page;
672 csr->nr_pages = npages;
673
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300674 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300675 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300676 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300677 csr->nr_channels = 1;
678
679 csr->channels[0].chan_idx = i;
680 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300681
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300682 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300683
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300684 switch (banks) {
685 case 4:
686 csr->dtype = DEV_X4;
687 break;
688 case 8:
689 csr->dtype = DEV_X8;
690 break;
691 case 16:
692 csr->dtype = DEV_X16;
693 break;
694 default:
695 csr->dtype = DEV_UNKNOWN;
696 }
697
698 csr->edac_mode = mode;
699 csr->mtype = mtype;
700
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300701 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300702 }
703
704 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
705 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
706 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
707 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
708 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
709 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
710 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
711 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300712 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300713 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300714 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300715 (value[j] >> 27) & 0x1,
716 (value[j] >> 24) & 0x7,
717 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300718 }
719
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300720 return 0;
721}
722
723/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300724 Error insertion routines
725 ****************************************************************************/
726
727/* The i7core has independent error injection features per channel.
728 However, to have a simpler code, we don't allow enabling error injection
729 on more than one channel.
730 Also, since a change at an inject parameter will be applied only at enable,
731 we're disabling error injection on all write calls to the sysfs nodes that
732 controls the error code injection.
733 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300734static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300735{
736 struct i7core_pvt *pvt = mci->pvt_info;
737
738 pvt->inject.enable = 0;
739
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300740 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300741 return -ENODEV;
742
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300743 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300744 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300745
746 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300747}
748
749/*
750 * i7core inject inject.section
751 *
752 * accept and store error injection inject.section value
753 * bit 0 - refers to the lower 32-byte half cacheline
754 * bit 1 - refers to the upper 32-byte half cacheline
755 */
756static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
757 const char *data, size_t count)
758{
759 struct i7core_pvt *pvt = mci->pvt_info;
760 unsigned long value;
761 int rc;
762
763 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300764 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300765
766 rc = strict_strtoul(data, 10, &value);
767 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300768 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300769
770 pvt->inject.section = (u32) value;
771 return count;
772}
773
774static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
775 char *data)
776{
777 struct i7core_pvt *pvt = mci->pvt_info;
778 return sprintf(data, "0x%08x\n", pvt->inject.section);
779}
780
781/*
782 * i7core inject.type
783 *
784 * accept and store error injection inject.section value
785 * bit 0 - repeat enable - Enable error repetition
786 * bit 1 - inject ECC error
787 * bit 2 - inject parity error
788 */
789static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
790 const char *data, size_t count)
791{
792 struct i7core_pvt *pvt = mci->pvt_info;
793 unsigned long value;
794 int rc;
795
796 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300797 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300798
799 rc = strict_strtoul(data, 10, &value);
800 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300801 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300802
803 pvt->inject.type = (u32) value;
804 return count;
805}
806
807static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
808 char *data)
809{
810 struct i7core_pvt *pvt = mci->pvt_info;
811 return sprintf(data, "0x%08x\n", pvt->inject.type);
812}
813
814/*
815 * i7core_inject_inject.eccmask_store
816 *
817 * The type of error (UE/CE) will depend on the inject.eccmask value:
818 * Any bits set to a 1 will flip the corresponding ECC bit
819 * Correctable errors can be injected by flipping 1 bit or the bits within
820 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
821 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
822 * uncorrectable error to be injected.
823 */
824static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
825 const char *data, size_t count)
826{
827 struct i7core_pvt *pvt = mci->pvt_info;
828 unsigned long value;
829 int rc;
830
831 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300832 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300833
834 rc = strict_strtoul(data, 10, &value);
835 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300836 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300837
838 pvt->inject.eccmask = (u32) value;
839 return count;
840}
841
842static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
843 char *data)
844{
845 struct i7core_pvt *pvt = mci->pvt_info;
846 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
847}
848
849/*
850 * i7core_addrmatch
851 *
852 * The type of error (UE/CE) will depend on the inject.eccmask value:
853 * Any bits set to a 1 will flip the corresponding ECC bit
854 * Correctable errors can be injected by flipping 1 bit or the bits within
855 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
856 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
857 * uncorrectable error to be injected.
858 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300859
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300860#define DECLARE_ADDR_MATCH(param, limit) \
861static ssize_t i7core_inject_store_##param( \
862 struct mem_ctl_info *mci, \
863 const char *data, size_t count) \
864{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300865 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300866 long value; \
867 int rc; \
868 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300869 debugf1("%s()\n", __func__); \
870 pvt = mci->pvt_info; \
871 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300872 if (pvt->inject.enable) \
873 disable_inject(mci); \
874 \
Mauro Carvalho Chehab4f87fad2009-10-04 11:54:56 -0300875 if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300876 value = -1; \
877 else { \
878 rc = strict_strtoul(data, 10, &value); \
879 if ((rc < 0) || (value >= limit)) \
880 return -EIO; \
881 } \
882 \
883 pvt->inject.param = value; \
884 \
885 return count; \
886} \
887 \
888static ssize_t i7core_inject_show_##param( \
889 struct mem_ctl_info *mci, \
890 char *data) \
891{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300892 struct i7core_pvt *pvt; \
893 \
894 pvt = mci->pvt_info; \
895 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300896 if (pvt->inject.param < 0) \
897 return sprintf(data, "any\n"); \
898 else \
899 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300900}
901
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300902#define ATTR_ADDR_MATCH(param) \
903 { \
904 .attr = { \
905 .name = #param, \
906 .mode = (S_IRUGO | S_IWUSR) \
907 }, \
908 .show = i7core_inject_show_##param, \
909 .store = i7core_inject_store_##param, \
910 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300911
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300912DECLARE_ADDR_MATCH(channel, 3);
913DECLARE_ADDR_MATCH(dimm, 3);
914DECLARE_ADDR_MATCH(rank, 4);
915DECLARE_ADDR_MATCH(bank, 32);
916DECLARE_ADDR_MATCH(page, 0x10000);
917DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300918
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300919static int write_and_test(struct pci_dev *dev, int where, u32 val)
920{
921 u32 read;
922 int count;
923
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300924 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
925 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
926 where, val);
927
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300928 for (count = 0; count < 10; count++) {
929 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300930 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300931 pci_write_config_dword(dev, where, val);
932 pci_read_config_dword(dev, where, &read);
933
934 if (read == val)
935 return 0;
936 }
937
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300938 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
939 "write=%08x. Read=%08x\n",
940 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
941 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300942
943 return -EINVAL;
944}
945
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300946/*
947 * This routine prepares the Memory Controller for error injection.
948 * The error will be injected when some process tries to write to the
949 * memory that matches the given criteria.
950 * The criteria can be set in terms of a mask where dimm, rank, bank, page
951 * and col can be specified.
952 * A -1 value for any of the mask items will make the MCU to ignore
953 * that matching criteria for error injection.
954 *
955 * It should be noticed that the error will only happen after a write operation
956 * on a memory that matches the condition. if REPEAT_EN is not enabled at
957 * inject mask, then it will produce just one error. Otherwise, it will repeat
958 * until the injectmask would be cleaned.
959 *
960 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
961 * is reliable enough to check if the MC is using the
962 * three channels. However, this is not clear at the datasheet.
963 */
964static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
965 const char *data, size_t count)
966{
967 struct i7core_pvt *pvt = mci->pvt_info;
968 u32 injectmask;
969 u64 mask = 0;
970 int rc;
971 long enable;
972
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300973 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300974 return 0;
975
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300976 rc = strict_strtoul(data, 10, &enable);
977 if ((rc < 0))
978 return 0;
979
980 if (enable) {
981 pvt->inject.enable = 1;
982 } else {
983 disable_inject(mci);
984 return count;
985 }
986
987 /* Sets pvt->inject.dimm mask */
988 if (pvt->inject.dimm < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200989 mask |= 1LL << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300990 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300991 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -0200992 mask |= (pvt->inject.dimm & 0x3LL) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300993 else
Alan Cox486dd092009-11-08 01:34:27 -0200994 mask |= (pvt->inject.dimm & 0x1LL) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300995 }
996
997 /* Sets pvt->inject.rank mask */
998 if (pvt->inject.rank < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200999 mask |= 1LL << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001000 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001001 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -02001002 mask |= (pvt->inject.rank & 0x1LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001003 else
Alan Cox486dd092009-11-08 01:34:27 -02001004 mask |= (pvt->inject.rank & 0x3LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001005 }
1006
1007 /* Sets pvt->inject.bank mask */
1008 if (pvt->inject.bank < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001009 mask |= 1LL << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001010 else
Alan Cox486dd092009-11-08 01:34:27 -02001011 mask |= (pvt->inject.bank & 0x15LL) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001012
1013 /* Sets pvt->inject.page mask */
1014 if (pvt->inject.page < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001015 mask |= 1LL << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001016 else
Alan Cox486dd092009-11-08 01:34:27 -02001017 mask |= (pvt->inject.page & 0xffff) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001018
1019 /* Sets pvt->inject.column mask */
1020 if (pvt->inject.col < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001021 mask |= 1LL << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001022 else
Alan Cox486dd092009-11-08 01:34:27 -02001023 mask |= (pvt->inject.col & 0x3fff);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001024
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001025 /*
1026 * bit 0: REPEAT_EN
1027 * bits 1-2: MASK_HALF_CACHELINE
1028 * bit 3: INJECT_ECC
1029 * bit 4: INJECT_ADDR_PARITY
1030 */
1031
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001032 injectmask = (pvt->inject.type & 1) |
1033 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001034 (pvt->inject.type & 0x6) << (3 - 1);
1035
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001036 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001037 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001038 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001039
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001040 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001041 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001042 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001043 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1044
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001045 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001046 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1047
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001048 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001049 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001050
1051 /*
1052 * This is something undocumented, based on my tests
1053 * Without writing 8 to this register, errors aren't injected. Not sure
1054 * why.
1055 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001056 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001057 MC_CFG_CONTROL, 8);
1058
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001059 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1060 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001061 mask, pvt->inject.eccmask, injectmask);
1062
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001063
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001064 return count;
1065}
1066
1067static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1068 char *data)
1069{
1070 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001071 u32 injectmask;
1072
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -03001073 if (!pvt->pci_ch[pvt->inject.channel][0])
1074 return 0;
1075
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001076 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001077 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001078
1079 debugf0("Inject error read: 0x%018x\n", injectmask);
1080
1081 if (injectmask & 0x0c)
1082 pvt->inject.enable = 1;
1083
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001084 return sprintf(data, "%d\n", pvt->inject.enable);
1085}
1086
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001087#define DECLARE_COUNTER(param) \
1088static ssize_t i7core_show_counter_##param( \
1089 struct mem_ctl_info *mci, \
1090 char *data) \
1091{ \
1092 struct i7core_pvt *pvt = mci->pvt_info; \
1093 \
1094 debugf1("%s() \n", __func__); \
1095 if (!pvt->ce_count_available || (pvt->is_registered)) \
1096 return sprintf(data, "data unavailable\n"); \
1097 return sprintf(data, "%lu\n", \
1098 pvt->udimm_ce_count[param]); \
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001099}
1100
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001101#define ATTR_COUNTER(param) \
1102 { \
1103 .attr = { \
1104 .name = __stringify(udimm##param), \
1105 .mode = (S_IRUGO | S_IWUSR) \
1106 }, \
1107 .show = i7core_show_counter_##param \
1108 }
1109
1110DECLARE_COUNTER(0);
1111DECLARE_COUNTER(1);
1112DECLARE_COUNTER(2);
1113
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001114/*
1115 * Sysfs struct
1116 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001117
1118
1119static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
1120 ATTR_ADDR_MATCH(channel),
1121 ATTR_ADDR_MATCH(dimm),
1122 ATTR_ADDR_MATCH(rank),
1123 ATTR_ADDR_MATCH(bank),
1124 ATTR_ADDR_MATCH(page),
1125 ATTR_ADDR_MATCH(col),
1126 { .attr = { .name = NULL } }
1127};
1128
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001129static struct mcidev_sysfs_group i7core_inject_addrmatch = {
1130 .name = "inject_addrmatch",
1131 .mcidev_attr = i7core_addrmatch_attrs,
1132};
1133
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001134static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
1135 ATTR_COUNTER(0),
1136 ATTR_COUNTER(1),
1137 ATTR_COUNTER(2),
1138};
1139
1140static struct mcidev_sysfs_group i7core_udimm_counters = {
1141 .name = "all_channel_counts",
1142 .mcidev_attr = i7core_udimm_counters_attrs,
1143};
1144
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001145static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001146 {
1147 .attr = {
1148 .name = "inject_section",
1149 .mode = (S_IRUGO | S_IWUSR)
1150 },
1151 .show = i7core_inject_section_show,
1152 .store = i7core_inject_section_store,
1153 }, {
1154 .attr = {
1155 .name = "inject_type",
1156 .mode = (S_IRUGO | S_IWUSR)
1157 },
1158 .show = i7core_inject_type_show,
1159 .store = i7core_inject_type_store,
1160 }, {
1161 .attr = {
1162 .name = "inject_eccmask",
1163 .mode = (S_IRUGO | S_IWUSR)
1164 },
1165 .show = i7core_inject_eccmask_show,
1166 .store = i7core_inject_eccmask_store,
1167 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001168 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001169 }, {
1170 .attr = {
1171 .name = "inject_enable",
1172 .mode = (S_IRUGO | S_IWUSR)
1173 },
1174 .show = i7core_inject_enable_show,
1175 .store = i7core_inject_enable_store,
1176 },
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001177 { .attr = { .name = NULL } }, /* Reserved for udimm counters */
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001178 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001179};
1180
1181/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001182 Device initialization routines: put/get, init/exit
1183 ****************************************************************************/
1184
1185/*
1186 * i7core_put_devices 'put' all the devices that we have
1187 * reserved via 'get'
1188 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001189static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001190{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001191 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001192
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001193 debugf0(__FILE__ ": %s()\n", __func__);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001194 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001195 struct pci_dev *pdev = i7core_dev->pdev[i];
1196 if (!pdev)
1197 continue;
1198 debugf0("Removing dev %02x:%02x.%d\n",
1199 pdev->bus->number,
1200 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1201 pci_dev_put(pdev);
1202 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001203 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001204 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001205 kfree(i7core_dev);
1206}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001207
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001208static void i7core_put_all_devices(void)
1209{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001210 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001211
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001212 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001213 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001214}
1215
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001216static void __init i7core_xeon_pci_fixup(struct pci_id_table *table)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001217{
1218 struct pci_dev *pdev = NULL;
1219 int i;
1220 /*
1221 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1222 * aren't announced by acpi. So, we need to use a legacy scan probing
1223 * to detect them
1224 */
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001225 while (table && table->descr) {
1226 pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL);
1227 if (unlikely(!pdev)) {
1228 for (i = 0; i < MAX_SOCKET_BUSES; i++)
1229 pcibios_scan_specific_bus(255-i);
1230 }
1231 table++;
Keith Manntheybc2d7242009-09-03 00:05:05 -03001232 }
1233}
1234
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001235/*
1236 * i7core_get_devices Find and perform 'get' operation on the MCH's
1237 * device/functions we want to reference for this driver
1238 *
1239 * Need to 'get' device 16 func 1 and func 2
1240 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001241int i7core_get_onedevice(struct pci_dev **prev, int devno,
1242 struct pci_id_descr *dev_descr, unsigned n_devs)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001243{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001244 struct i7core_dev *i7core_dev;
1245
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001246 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001247 u8 bus = 0;
1248 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001249
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001250 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001251 dev_descr->dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001252
1253 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001254 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1255 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1256 * to probe for the alternate address in case of failure
1257 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001258 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001259 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -03001260 PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001261
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001262 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -03001263 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1264 PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
1265 *prev);
1266
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001267 if (!pdev) {
1268 if (*prev) {
1269 *prev = pdev;
1270 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001271 }
1272
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001273 if (dev_descr->optional)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001274 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001275
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001276 if (devno == 0)
1277 return -ENODEV;
1278
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001279 i7core_printk(KERN_ERR,
1280 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001281 dev_descr->dev, dev_descr->func,
1282 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001283
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001284 /* End of list, leave */
1285 return -ENODEV;
1286 }
1287 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001288
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001289 if (bus == 0x3f)
1290 socket = 0;
1291 else
1292 socket = 255 - bus;
1293
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001294 i7core_dev = get_i7core_dev(socket);
1295 if (!i7core_dev) {
1296 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1297 if (!i7core_dev)
1298 return -ENOMEM;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001299 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001300 GFP_KERNEL);
Alexander Beregalov2a6fae32010-01-07 23:27:30 -03001301 if (!i7core_dev->pdev) {
1302 kfree(i7core_dev);
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001303 return -ENOMEM;
Alexander Beregalov2a6fae32010-01-07 23:27:30 -03001304 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001305 i7core_dev->socket = socket;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001306 i7core_dev->n_devs = n_devs;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001307 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001308 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001309
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001310 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001311 i7core_printk(KERN_ERR,
1312 "Duplicated device for "
1313 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001314 bus, dev_descr->dev, dev_descr->func,
1315 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001316 pci_dev_put(pdev);
1317 return -ENODEV;
1318 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001319
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001320 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001321
1322 /* Sanity check */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001323 if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1324 PCI_FUNC(pdev->devfn) != dev_descr->func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001325 i7core_printk(KERN_ERR,
1326 "Device PCI ID %04x:%04x "
1327 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001328 PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001329 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001330 bus, dev_descr->dev, dev_descr->func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001331 return -ENODEV;
1332 }
1333
1334 /* Be sure that the device is enabled */
1335 if (unlikely(pci_enable_device(pdev) < 0)) {
1336 i7core_printk(KERN_ERR,
1337 "Couldn't enable "
1338 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001339 bus, dev_descr->dev, dev_descr->func,
1340 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001341 return -ENODEV;
1342 }
1343
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001344 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001345 socket, bus, dev_descr->dev,
1346 dev_descr->func,
1347 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001348
1349 *prev = pdev;
1350
1351 return 0;
1352}
1353
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001354static int i7core_get_devices(struct pci_id_table *table)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001355{
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001356 int i, rc;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001357 struct pci_dev *pdev = NULL;
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001358 struct pci_id_descr *dev_descr;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001359
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001360 while (table && table->descr) {
1361 dev_descr = table->descr;
1362 for (i = 0; i < table->n_devs; i++) {
1363 pdev = NULL;
1364 do {
1365 rc = i7core_get_onedevice(&pdev, i, &dev_descr[i],
1366 table->n_devs);
1367 if (rc < 0) {
1368 if (i == 0) {
1369 i = table->n_devs;
1370 break;
1371 }
1372 i7core_put_all_devices();
1373 return -ENODEV;
1374 }
1375 } while (pdev);
1376 }
1377 table++;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001378 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001379
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001380 return 0;
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001381 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001382}
1383
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001384static int mci_bind_devs(struct mem_ctl_info *mci,
1385 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001386{
1387 struct i7core_pvt *pvt = mci->pvt_info;
1388 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001389 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001390
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001391 /* Associates i7core_dev and mci for future usage */
1392 pvt->i7core_dev = i7core_dev;
1393 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001394
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001395 pvt->is_registered = 0;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001396 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001397 pdev = i7core_dev->pdev[i];
1398 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001399 continue;
1400
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001401 func = PCI_FUNC(pdev->devfn);
1402 slot = PCI_SLOT(pdev->devfn);
1403 if (slot == 3) {
1404 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001405 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001406 pvt->pci_mcr[func] = pdev;
1407 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1408 if (unlikely(func > MAX_CHAN_FUNC))
1409 goto error;
1410 pvt->pci_ch[slot - 4][func] = pdev;
1411 } else if (!slot && !func)
1412 pvt->pci_noncore = pdev;
1413 else
1414 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001415
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001416 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1417 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1418 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001419
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001420 if (PCI_SLOT(pdev->devfn) == 3 &&
1421 PCI_FUNC(pdev->devfn) == 2)
1422 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001423 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001424
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001425 /*
1426 * Add extra nodes to count errors on udimm
1427 * For registered memory, this is not needed, since the counters
1428 * are already displayed at the standard locations
1429 */
1430 if (!pvt->is_registered)
1431 i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp =
1432 &i7core_udimm_counters;
1433
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001434 return 0;
1435
1436error:
1437 i7core_printk(KERN_ERR, "Device %d, function %d "
1438 "is out of the expected range\n",
1439 slot, func);
1440 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001441}
1442
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001443/****************************************************************************
1444 Error check routines
1445 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001446static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001447 int chan, int dimm, int add)
1448{
1449 char *msg;
1450 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001451 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001452
1453 for (i = 0; i < add; i++) {
1454 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001455 "(Socket=%d channel=%d dimm=%d)",
1456 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001457
1458 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1459 kfree (msg);
1460 }
1461}
1462
1463static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001464 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001465{
1466 struct i7core_pvt *pvt = mci->pvt_info;
1467 int add0 = 0, add1 = 0, add2 = 0;
1468 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001469 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001470 /* Updates CE counters */
1471
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001472 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1473 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1474 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001475
1476 if (add2 < 0)
1477 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001478 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001479
1480 if (add1 < 0)
1481 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001482 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001483
1484 if (add0 < 0)
1485 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001486 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001487 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001488 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001489
1490 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001491 pvt->rdimm_last_ce_count[chan][2] = new2;
1492 pvt->rdimm_last_ce_count[chan][1] = new1;
1493 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001494
1495 /*updated the edac core */
1496 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001497 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001498 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001499 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001500 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001501 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001502
1503}
1504
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001505static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001506{
1507 struct i7core_pvt *pvt = mci->pvt_info;
1508 u32 rcv[3][2];
1509 int i, new0, new1, new2;
1510
1511 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001512 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001513 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001514 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001515 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001516 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001517 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001518 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001519 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001520 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001521 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001522 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001523 &rcv[2][1]);
1524 for (i = 0 ; i < 3; i++) {
1525 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1526 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1527 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001528 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001529 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1530 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1531 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1532 } else {
1533 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1534 DIMM_BOT_COR_ERR(rcv[i][0]);
1535 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1536 DIMM_BOT_COR_ERR(rcv[i][1]);
1537 new2 = 0;
1538 }
1539
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001540 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001541 }
1542}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001543
1544/* This function is based on the device 3 function 4 registers as described on:
1545 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1546 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1547 * also available at:
1548 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1549 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001550static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001551{
1552 struct i7core_pvt *pvt = mci->pvt_info;
1553 u32 rcv1, rcv0;
1554 int new0, new1, new2;
1555
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001556 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001557 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001558 return;
1559 }
1560
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001561 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001562 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1563 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001564
1565 /* Store the new values */
1566 new2 = DIMM2_COR_ERR(rcv1);
1567 new1 = DIMM1_COR_ERR(rcv0);
1568 new0 = DIMM0_COR_ERR(rcv0);
1569
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001570 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001571 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001572 /* Updates CE counters */
1573 int add0, add1, add2;
1574
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001575 add2 = new2 - pvt->udimm_last_ce_count[2];
1576 add1 = new1 - pvt->udimm_last_ce_count[1];
1577 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001578
1579 if (add2 < 0)
1580 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001581 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001582
1583 if (add1 < 0)
1584 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001585 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001586
1587 if (add0 < 0)
1588 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001589 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001590
1591 if (add0 | add1 | add2)
1592 i7core_printk(KERN_ERR, "New Corrected error(s): "
1593 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1594 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001595 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001596 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001597
1598 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001599 pvt->udimm_last_ce_count[2] = new2;
1600 pvt->udimm_last_ce_count[1] = new1;
1601 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001602}
1603
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001604/*
1605 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1606 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001607 * Nehalem are defined as family 0x06, model 0x1a
1608 *
1609 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001610 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001611 * m->status MSR_IA32_MC8_STATUS
1612 * m->addr MSR_IA32_MC8_ADDR
1613 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001614 * In the case of Nehalem, the error information is masked at .status and .misc
1615 * fields
1616 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001617static void i7core_mce_output_error(struct mem_ctl_info *mci,
1618 struct mce *m)
1619{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001620 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001621 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001622 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001623 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001624 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1625 u32 dimm = (m->misc >> 16) & 0x3;
1626 u32 channel = (m->misc >> 18) & 0x3;
1627 u32 syndrome = m->misc >> 32;
1628 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001629 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001630
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001631 if (m->mcgstatus & 1)
1632 type = "FATAL";
1633 else
1634 type = "NON_FATAL";
1635
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001636 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001637 case 0:
1638 optype = "generic undef request";
1639 break;
1640 case 1:
1641 optype = "read error";
1642 break;
1643 case 2:
1644 optype = "write error";
1645 break;
1646 case 3:
1647 optype = "addr/cmd error";
1648 break;
1649 case 4:
1650 optype = "scrubbing error";
1651 break;
1652 default:
1653 optype = "reserved";
1654 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001655 }
1656
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001657 switch (errnum) {
1658 case 16:
1659 err = "read ECC error";
1660 break;
1661 case 17:
1662 err = "RAS ECC error";
1663 break;
1664 case 18:
1665 err = "write parity error";
1666 break;
1667 case 19:
1668 err = "redundacy loss";
1669 break;
1670 case 20:
1671 err = "reserved";
1672 break;
1673 case 21:
1674 err = "memory range error";
1675 break;
1676 case 22:
1677 err = "RTID out of range";
1678 break;
1679 case 23:
1680 err = "address parity error";
1681 break;
1682 case 24:
1683 err = "byte enable parity error";
1684 break;
1685 default:
1686 err = "unknown";
1687 }
1688
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001689 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001690 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001691 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001692 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001693 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001694 syndrome, core_err_cnt, (long long)m->status,
1695 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001696
1697 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001698
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001699 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001700
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001701 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001702 if (m->mcgstatus & 1)
1703 edac_mc_handle_fbd_ue(mci, csrow, 0,
1704 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001705 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001706 edac_mc_handle_fbd_ce(mci, csrow,
1707 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001708
1709 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001710}
1711
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001712/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001713 * i7core_check_error Retrieve and process errors reported by the
1714 * hardware. Called by the Core module.
1715 */
1716static void i7core_check_error(struct mem_ctl_info *mci)
1717{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001718 struct i7core_pvt *pvt = mci->pvt_info;
1719 int i;
1720 unsigned count = 0;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001721 struct mce *m;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001722
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001723 /*
1724 * MCE first step: Copy all mce errors into a temporary buffer
1725 * We use a double buffering here, to reduce the risk of
1726 * loosing an error.
1727 */
1728 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001729 count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
1730 % MCE_LOG_LEN;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001731 if (!count)
Vernon Mauery8a311e12010-04-16 19:40:19 -03001732 goto check_ce_error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001733
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001734 m = pvt->mce_outentry;
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001735 if (pvt->mce_in + count > MCE_LOG_LEN) {
1736 unsigned l = MCE_LOG_LEN - pvt->mce_in;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001737
1738 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
1739 smp_wmb();
1740 pvt->mce_in = 0;
1741 count -= l;
1742 m += l;
1743 }
1744 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
1745 smp_wmb();
1746 pvt->mce_in += count;
1747
1748 smp_rmb();
1749 if (pvt->mce_overrun) {
1750 i7core_printk(KERN_ERR, "Lost %d memory errors\n",
1751 pvt->mce_overrun);
1752 smp_wmb();
1753 pvt->mce_overrun = 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001754 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001755
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001756 /*
1757 * MCE second step: parse errors and display
1758 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001759 for (i = 0; i < count; i++)
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001760 i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001761
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001762 /*
1763 * Now, let's increment CE error counts
1764 */
Vernon Mauery8a311e12010-04-16 19:40:19 -03001765check_ce_error:
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001766 if (!pvt->is_registered)
1767 i7core_udimm_check_mc_ecc_err(mci);
1768 else
1769 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001770}
1771
1772/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001773 * i7core_mce_check_error Replicates mcelog routine to get errors
1774 * This routine simply queues mcelog errors, and
1775 * return. The error itself should be handled later
1776 * by i7core_check_error.
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001777 * WARNING: As this routine should be called at NMI time, extra care should
1778 * be taken to avoid deadlocks, and to be as fast as possible.
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001779 */
1780static int i7core_mce_check_error(void *priv, struct mce *mce)
1781{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001782 struct mem_ctl_info *mci = priv;
1783 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001784
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001785 /*
1786 * Just let mcelog handle it if the error is
1787 * outside the memory controller
1788 */
1789 if (((mce->status & 0xffff) >> 7) != 1)
1790 return 0;
1791
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001792 /* Bank 8 registers are the only ones that we know how to handle */
1793 if (mce->bank != 8)
1794 return 0;
1795
Randy Dunlap3b918c12009-11-08 01:36:40 -02001796#ifdef CONFIG_SMP
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001797 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001798 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001799 return 0;
Randy Dunlap3b918c12009-11-08 01:36:40 -02001800#endif
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001801
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001802 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001803 if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001804 smp_wmb();
1805 pvt->mce_overrun++;
1806 return 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001807 }
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001808
1809 /* Copy memory error at the ringbuffer */
1810 memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001811 smp_wmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001812 pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001813
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001814 /* Handle fatal errors immediately */
1815 if (mce->mcgstatus & 1)
1816 i7core_check_error(mci);
1817
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001818 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001819 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001820}
1821
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001822static int i7core_register_mci(struct i7core_dev *i7core_dev,
1823 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001824{
1825 struct mem_ctl_info *mci;
1826 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001827 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001828 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001829
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001830 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001831 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1832 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001833 if (unlikely(!mci))
1834 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001835
1836 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1837
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001838 /* record ptr to the generic device */
1839 mci->dev = &i7core_dev->pdev[0]->dev;
1840
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001841 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001842 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001843
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001844 /*
1845 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1846 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1847 * memory channels
1848 */
1849 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001850 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1851 mci->edac_cap = EDAC_FLAG_NONE;
1852 mci->mod_name = "i7core_edac.c";
1853 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001854 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1855 i7core_dev->socket);
1856 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001857 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001858 mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001859 /* Set the function pointer to an actual operation function */
1860 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001861
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001862 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001863 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001864 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001865 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001866
1867 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001868 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001869
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001870 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001871 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001872 debugf0("MC: " __FILE__
1873 ": %s(): failed edac_mc_add_mc()\n", __func__);
1874 /* FIXME: perhaps some code should go here that disables error
1875 * reporting if we just enabled it
1876 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001877
1878 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001879 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001880 }
1881
1882 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001883 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1884 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001885 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001886 printk(KERN_WARNING
1887 "%s(): Unable to create PCI control\n",
1888 __func__);
1889 printk(KERN_WARNING
1890 "%s(): PCI error report via EDAC not setup\n",
1891 __func__);
1892 }
1893
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001894 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001895 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001896 pvt->inject.dimm = -1;
1897 pvt->inject.rank = -1;
1898 pvt->inject.bank = -1;
1899 pvt->inject.page = -1;
1900 pvt->inject.col = -1;
1901
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001902 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001903 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001904 pvt->edac_mce.check_error = i7core_mce_check_error;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001905
1906 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001907 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001908 debugf0("MC: " __FILE__
1909 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001910 }
1911
1912fail:
Tony Luckd4d1ef42010-05-18 10:53:25 -03001913 if (rc < 0)
1914 edac_mc_free(mci);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001915 return rc;
1916}
1917
1918/*
1919 * i7core_probe Probe for ONE instance of device to see if it is
1920 * present.
1921 * return:
1922 * 0 for FOUND a device
1923 * < 0 for error code
1924 */
1925static int __devinit i7core_probe(struct pci_dev *pdev,
1926 const struct pci_device_id *id)
1927{
1928 int dev_idx = id->driver_data;
1929 int rc;
1930 struct i7core_dev *i7core_dev;
1931
1932 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001933 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001934 */
1935 if (unlikely(dev_idx >= 1))
1936 return -EINVAL;
1937
1938 /* get the pci devices we want to reserve for our use */
1939 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001940
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001941 rc = i7core_get_devices(pci_dev_table);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001942 if (unlikely(rc < 0))
1943 goto fail0;
1944
1945 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1946 int channels;
1947 int csrows;
1948
1949 /* Check the number of active and not disabled channels */
1950 rc = i7core_get_active_channels(i7core_dev->socket,
1951 &channels, &csrows);
1952 if (unlikely(rc < 0))
1953 goto fail1;
1954
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001955 rc = i7core_register_mci(i7core_dev, channels, csrows);
1956 if (unlikely(rc < 0))
1957 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001958 }
1959
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001960 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001961
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001962 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001963 return 0;
1964
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001965fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001966 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001967fail0:
1968 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001969 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001970}
1971
1972/*
1973 * i7core_remove destructor for one instance of device
1974 *
1975 */
1976static void __devexit i7core_remove(struct pci_dev *pdev)
1977{
1978 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001979 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001980
1981 debugf0(__FILE__ ": %s()\n", __func__);
1982
1983 if (i7core_pci)
1984 edac_pci_release_generic_ctl(i7core_pci);
1985
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001986 /*
1987 * we have a trouble here: pdev value for removal will be wrong, since
1988 * it will point to the X58 register used to detect that the machine
1989 * is a Nehalem or upper design. However, due to the way several PCI
1990 * devices are grouped together to provide MC functionality, we need
1991 * to use a different method for releasing the devices
1992 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001993
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001994 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001995 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1996 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1997 if (mci) {
1998 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001999
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002000 i7core_dev = pvt->i7core_dev;
2001 edac_mce_unregister(&pvt->edac_mce);
2002 kfree(mci->ctl_name);
2003 edac_mc_free(mci);
2004 i7core_put_devices(i7core_dev);
2005 } else {
2006 i7core_printk(KERN_ERR,
2007 "Couldn't find mci for socket %d\n",
2008 i7core_dev->socket);
2009 }
2010 }
2011 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002012}
2013
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002014MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
2015
2016/*
2017 * i7core_driver pci_driver structure for this module
2018 *
2019 */
2020static struct pci_driver i7core_driver = {
2021 .name = "i7core_edac",
2022 .probe = i7core_probe,
2023 .remove = __devexit_p(i7core_remove),
2024 .id_table = i7core_pci_tbl,
2025};
2026
2027/*
2028 * i7core_init Module entry function
2029 * Try to initialize this module for its devices
2030 */
2031static int __init i7core_init(void)
2032{
2033 int pci_rc;
2034
2035 debugf2("MC: " __FILE__ ": %s()\n", __func__);
2036
2037 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
2038 opstate_init();
2039
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03002040 i7core_xeon_pci_fixup(pci_dev_table);
Keith Manntheybc2d7242009-09-03 00:05:05 -03002041
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002042 pci_rc = pci_register_driver(&i7core_driver);
2043
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03002044 if (pci_rc >= 0)
2045 return 0;
2046
2047 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
2048 pci_rc);
2049
2050 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002051}
2052
2053/*
2054 * i7core_exit() Module exit function
2055 * Unregister the driver
2056 */
2057static void __exit i7core_exit(void)
2058{
2059 debugf2("MC: " __FILE__ ": %s()\n", __func__);
2060 pci_unregister_driver(&i7core_driver);
2061}
2062
2063module_init(i7core_init);
2064module_exit(i7core_exit);
2065
2066MODULE_LICENSE("GPL");
2067MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
2068MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
2069MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
2070 I7CORE_REVISION);
2071
2072module_param(edac_op_state, int, 0444);
2073MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");