blob: 4c1dcc1f562e86718195d947aa7d9e8582a04578 [file] [log] [blame]
Mauro Carvalho Chehab52707f92010-05-18 20:43:52 -03001/* Intel i7 core/Nehalem Memory Controller kernel module
2 *
3 * This driver supports yhe memory controllers found on the Intel
4 * processor families i7core, i7core 7xx/8xx, i5core, Xeon 35xx,
5 * Xeon 55xx and Xeon 56xx also known as Nehalem, Nehalem-EP, Lynnfield
6 * and Westmere-EP.
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03007 *
8 * This file may be distributed under the terms of the
9 * GNU General Public License version 2 only.
10 *
Mauro Carvalho Chehab52707f92010-05-18 20:43:52 -030011 * Copyright (c) 2009-2010 by:
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030012 * Mauro Carvalho Chehab <mchehab@redhat.com>
13 *
14 * Red Hat Inc. http://www.redhat.com
15 *
16 * Forked and adapted from the i5400_edac driver
17 *
18 * Based on the following public Intel datasheets:
19 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
20 * Datasheet, Volume 2:
21 * http://download.intel.com/design/processor/datashts/320835.pdf
22 * Intel Xeon Processor 5500 Series Datasheet Volume 2
23 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
24 * also available at:
25 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
26 */
27
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030028#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/pci.h>
31#include <linux/pci_ids.h>
32#include <linux/slab.h>
Randy Dunlap3b918c12009-11-08 01:36:40 -020033#include <linux/delay.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030034#include <linux/edac.h>
35#include <linux/mmzone.h>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030036#include <linux/edac_mce.h>
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030037#include <linux/smp.h>
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -030038#include <asm/processor.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030039
40#include "edac_core.h"
41
Mauro Carvalho Chehab18c29002010-08-10 18:33:27 -030042/* Static vars */
43static LIST_HEAD(i7core_edac_list);
44static DEFINE_MUTEX(i7core_edac_lock);
45static int probed;
46
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030047/*
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030048 * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
49 * registers start at bus 255, and are not reported by BIOS.
50 * We currently find devices with only 2 sockets. In order to support more QPI
51 * Quick Path Interconnect, just increment this number.
52 */
53#define MAX_SOCKET_BUSES 2
54
55
56/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030057 * Alter this version for the module when modifications are made
58 */
59#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
60#define EDAC_MOD_STR "i7core_edac"
61
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030062/*
63 * Debug macros
64 */
65#define i7core_printk(level, fmt, arg...) \
66 edac_printk(level, "i7core", fmt, ##arg)
67
68#define i7core_mc_printk(mci, level, fmt, arg...) \
69 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
70
71/*
72 * i7core Memory Controller Registers
73 */
74
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030075 /* OFFSETS for Device 0 Function 0 */
76
77#define MC_CFG_CONTROL 0x90
78
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030079 /* OFFSETS for Device 3 Function 0 */
80
81#define MC_CONTROL 0x48
82#define MC_STATUS 0x4c
83#define MC_MAX_DOD 0x64
84
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030085/*
86 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
87 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
88 */
89
90#define MC_TEST_ERR_RCV1 0x60
91 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
92
93#define MC_TEST_ERR_RCV0 0x64
94 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
95 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
96
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -030097/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
98#define MC_COR_ECC_CNT_0 0x80
99#define MC_COR_ECC_CNT_1 0x84
100#define MC_COR_ECC_CNT_2 0x88
101#define MC_COR_ECC_CNT_3 0x8c
102#define MC_COR_ECC_CNT_4 0x90
103#define MC_COR_ECC_CNT_5 0x94
104
105#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
106#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
107
108
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300109 /* OFFSETS for Devices 4,5 and 6 Function 0 */
110
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300111#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
112 #define THREE_DIMMS_PRESENT (1 << 24)
113 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
114 #define QUAD_RANK_PRESENT (1 << 22)
115 #define REGISTERED_DIMM (1 << 15)
116
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300117#define MC_CHANNEL_MAPPER 0x60
118 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
119 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
120
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300121#define MC_CHANNEL_RANK_PRESENT 0x7c
122 #define RANK_PRESENT_MASK 0xffff
123
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300124#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300125#define MC_CHANNEL_ERROR_MASK 0xf8
126#define MC_CHANNEL_ERROR_INJECT 0xfc
127 #define INJECT_ADDR_PARITY 0x10
128 #define INJECT_ECC 0x08
129 #define MASK_CACHELINE 0x06
130 #define MASK_FULL_CACHELINE 0x06
131 #define MASK_MSB32_CACHELINE 0x04
132 #define MASK_LSB32_CACHELINE 0x02
133 #define NO_MASK_CACHELINE 0x00
134 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300135
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300136 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300137
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300138#define MC_DOD_CH_DIMM0 0x48
139#define MC_DOD_CH_DIMM1 0x4c
140#define MC_DOD_CH_DIMM2 0x50
141 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
142 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
143 #define DIMM_PRESENT_MASK (1 << 9)
144 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300145 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
146 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
147 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
148 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300149 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300150 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300151 #define MC_DOD_NUMCOL_MASK 3
152 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300153
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300154#define MC_RANK_PRESENT 0x7c
155
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300156#define MC_SAG_CH_0 0x80
157#define MC_SAG_CH_1 0x84
158#define MC_SAG_CH_2 0x88
159#define MC_SAG_CH_3 0x8c
160#define MC_SAG_CH_4 0x90
161#define MC_SAG_CH_5 0x94
162#define MC_SAG_CH_6 0x98
163#define MC_SAG_CH_7 0x9c
164
165#define MC_RIR_LIMIT_CH_0 0x40
166#define MC_RIR_LIMIT_CH_1 0x44
167#define MC_RIR_LIMIT_CH_2 0x48
168#define MC_RIR_LIMIT_CH_3 0x4C
169#define MC_RIR_LIMIT_CH_4 0x50
170#define MC_RIR_LIMIT_CH_5 0x54
171#define MC_RIR_LIMIT_CH_6 0x58
172#define MC_RIR_LIMIT_CH_7 0x5C
173#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
174
175#define MC_RIR_WAY_CH 0x80
176 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
177 #define MC_RIR_WAY_RANK_MASK 0x7
178
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300179/*
180 * i7core structs
181 */
182
183#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300184#define MAX_DIMMS 3 /* Max DIMMS per channel */
185#define MAX_MCR_FUNC 4
186#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300187
188struct i7core_info {
189 u32 mc_control;
190 u32 mc_status;
191 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300192 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300193};
194
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300195
196struct i7core_inject {
197 int enable;
198
199 u32 section;
200 u32 type;
201 u32 eccmask;
202
203 /* Error address mask */
204 int channel, dimm, rank, bank, page, col;
205};
206
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300207struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300208 u32 ranks;
209 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300210};
211
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300212struct pci_id_descr {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300213 int dev;
214 int func;
215 int dev_id;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300216 int optional;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300217};
218
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300219struct pci_id_table {
220 struct pci_id_descr *descr;
221 int n_devs;
222};
223
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300224struct i7core_dev {
225 struct list_head list;
226 u8 socket;
227 struct pci_dev **pdev;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300228 int n_devs;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300229 struct mem_ctl_info *mci;
230};
231
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300232struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300233 struct pci_dev *pci_noncore;
234 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
235 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
236
237 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300238
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300239 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300240 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300241 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300242
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300243 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300244
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300245 int ce_count_available;
246 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300247
248 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300249 unsigned long udimm_ce_count[MAX_DIMMS];
250 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300251 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300252 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
253 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300254
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300255 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300256
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300257 /* mcelog glue */
258 struct edac_mce edac_mce;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300259
260 /* Fifo double buffers */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300261 struct mce mce_entry[MCE_LOG_LEN];
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300262 struct mce mce_outentry[MCE_LOG_LEN];
263
264 /* Fifo in/out counters */
265 unsigned mce_in, mce_out;
266
267 /* Count indicator to show errors not got */
268 unsigned mce_overrun;
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300269
270 /* Struct to control EDAC polling */
271 struct edac_pci_ctl_info *i7core_pci;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300272};
273
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300274#define PCI_DESCR(device, function, device_id) \
275 .dev = (device), \
276 .func = (function), \
277 .dev_id = (device_id)
278
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300279struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300280 /* Memory controller */
281 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
282 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300283 /* Exists only for RDIMM */
284 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300285 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
286
287 /* Channel 0 */
288 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
289 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
290 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
291 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
292
293 /* Channel 1 */
294 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
295 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
296 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
297 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
298
299 /* Channel 2 */
300 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
301 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
302 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
303 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300304
305 /* Generic Non-core registers */
306 /*
307 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
308 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
309 * the probing code needs to test for the other address in case of
310 * failure of this one
311 */
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -0300312 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300313
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300314};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300315
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300316struct pci_id_descr pci_dev_descr_lynnfield[] = {
317 { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) },
318 { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) },
319 { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) },
320
321 { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) },
322 { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) },
323 { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) },
324 { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) },
325
Mauro Carvalho Chehab508fa172009-10-14 13:44:37 -0300326 { PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) },
327 { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
328 { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
329 { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300330
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -0300331 /*
332 * This is the PCI device has an alternate address on some
333 * processors like Core i7 860
334 */
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300335 { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
336};
337
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300338struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
339 /* Memory controller */
340 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) },
341 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) },
342 /* Exists only for RDIMM */
343 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1 },
344 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) },
345
346 /* Channel 0 */
347 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) },
348 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) },
349 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) },
350 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2) },
351
352 /* Channel 1 */
353 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) },
354 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) },
355 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) },
356 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2) },
357
358 /* Channel 2 */
359 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) },
360 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
361 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
362 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) },
363
364 /* Generic Non-core registers */
365 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) },
366
367};
368
369#define PCI_ID_TABLE_ENTRY(A) { A, ARRAY_SIZE(A) }
370struct pci_id_table pci_dev_table[] = {
371 PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
372 PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
373 PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
374};
375
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300376/*
377 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300378 */
379static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300380 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -0300381 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300382 {0,} /* 0 terminated list. */
383};
384
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300385/****************************************************************************
386 Anciliary status routines
387 ****************************************************************************/
388
389 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300390#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
391#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300392
393 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300394#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300395#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300396
397 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300398static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300399{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300400 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300401}
402
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300403static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300404{
405 static int ranks[4] = { 1, 2, 4, -EINVAL };
406
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300407 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300408}
409
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300410static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300411{
412 static int banks[4] = { 4, 8, 16, -EINVAL };
413
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300414 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300415}
416
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300417static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300418{
419 static int rows[8] = {
420 1 << 12, 1 << 13, 1 << 14, 1 << 15,
421 1 << 16, -EINVAL, -EINVAL, -EINVAL,
422 };
423
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300424 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300425}
426
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300427static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300428{
429 static int cols[8] = {
430 1 << 10, 1 << 11, 1 << 12, -EINVAL,
431 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300432 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300433}
434
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300435static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300436{
437 struct i7core_dev *i7core_dev;
438
439 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
440 if (i7core_dev->socket == socket)
441 return i7core_dev;
442 }
443
444 return NULL;
445}
446
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300447/****************************************************************************
448 Memory check routines
449 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300450static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
451 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300452{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300453 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300454 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300455
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300456 if (!i7core_dev)
457 return NULL;
458
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300459 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300460 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300461 continue;
462
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300463 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
464 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
465 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300466 }
467 }
468
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300469 return NULL;
470}
471
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300472/**
473 * i7core_get_active_channels() - gets the number of channels and csrows
474 * @socket: Quick Path Interconnect socket
475 * @channels: Number of channels that will be returned
476 * @csrows: Number of csrows found
477 *
478 * Since EDAC core needs to know in advance the number of available channels
479 * and csrows, in order to allocate memory for csrows/channels, it is needed
480 * to run two similar steps. At the first step, implemented on this function,
481 * it checks the number of csrows/channels present at one socket.
482 * this is used in order to properly allocate the size of mci components.
483 *
484 * It should be noticed that none of the current available datasheets explain
485 * or even mention how csrows are seen by the memory controller. So, we need
486 * to add a fake description for csrows.
487 * So, this driver is attributing one DIMM memory for one csrow.
488 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300489static int i7core_get_active_channels(u8 socket, unsigned *channels,
490 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300491{
492 struct pci_dev *pdev = NULL;
493 int i, j;
494 u32 status, control;
495
496 *channels = 0;
497 *csrows = 0;
498
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300499 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300500 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300501 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
502 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300503 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300504 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300505
506 /* Device 3 function 0 reads */
507 pci_read_config_dword(pdev, MC_STATUS, &status);
508 pci_read_config_dword(pdev, MC_CONTROL, &control);
509
510 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300511 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300512 /* Check if the channel is active */
513 if (!(control & (1 << (8 + i))))
514 continue;
515
516 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300517 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300518 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300519
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300520 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300521 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300522 i7core_printk(KERN_ERR, "Couldn't find socket %d "
523 "fn %d.%d!!!\n",
524 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300525 return -ENODEV;
526 }
527 /* Devices 4-6 function 1 */
528 pci_read_config_dword(pdev,
529 MC_DOD_CH_DIMM0, &dimm_dod[0]);
530 pci_read_config_dword(pdev,
531 MC_DOD_CH_DIMM1, &dimm_dod[1]);
532 pci_read_config_dword(pdev,
533 MC_DOD_CH_DIMM2, &dimm_dod[2]);
534
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300535 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300536
537 for (j = 0; j < 3; j++) {
538 if (!DIMM_PRESENT(dimm_dod[j]))
539 continue;
540 (*csrows)++;
541 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300542 }
543
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300544 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300545 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300546
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300547 return 0;
548}
549
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300550static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300551{
552 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300553 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300554 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300555 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300556 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300557 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300558 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300559
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300560 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300561 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300562 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300563 return -ENODEV;
564
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300565 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300566 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
567 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
568 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
569 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300570
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300571 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300572 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300573 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300574
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300575 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300576 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300577 if (ECCx8(pvt))
578 mode = EDAC_S8ECD8ED;
579 else
580 mode = EDAC_S4ECD4ED;
581 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300582 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300583 mode = EDAC_NONE;
584 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300585
586 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300587 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
588 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300589 numdimms(pvt->info.max_dod),
590 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300591 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300592 numrow(pvt->info.max_dod >> 6),
593 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300594
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300595 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300596 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300597
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300598 if (!pvt->pci_ch[i][0])
599 continue;
600
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300601 if (!CH_ACTIVE(pvt, i)) {
602 debugf0("Channel %i is not active\n", i);
603 continue;
604 }
605 if (CH_DISABLED(pvt, i)) {
606 debugf0("Channel %i is disabled\n", i);
607 continue;
608 }
609
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300610 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300611 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300612 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
613
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300614 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300615 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300616
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300617 if (data & REGISTERED_DIMM)
618 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300619 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300620 mtype = MEM_DDR3;
621#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300622 if (data & THREE_DIMMS_PRESENT)
623 pvt->channel[i].dimms = 3;
624 else if (data & SINGLE_QUAD_RANK_PRESENT)
625 pvt->channel[i].dimms = 1;
626 else
627 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300628#endif
629
630 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300631 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300632 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300633 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300634 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300635 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300636 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300637
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300638 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300639 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300640 i,
641 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
642 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300643 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300644 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300645
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300646 for (j = 0; j < 3; j++) {
647 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300648 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300649
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300650 if (!DIMM_PRESENT(dimm_dod[j]))
651 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300652
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300653 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
654 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
655 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
656 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300657
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300658 /* DDR3 has 8 I/O banks */
659 size = (rows * cols * banks * ranks) >> (20 - 3);
660
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300661 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300662
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300663 debugf0("\tdimm %d %d Mb offset: %x, "
664 "bank: %d, rank: %d, row: %#x, col: %#x\n",
665 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300666 RANKOFFSET(dimm_dod[j]),
667 banks, ranks, rows, cols);
668
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300669#if PAGE_SHIFT > 20
670 npages = size >> (PAGE_SHIFT - 20);
671#else
672 npages = size << (20 - PAGE_SHIFT);
673#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300674
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300675 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300676 csr->first_page = last_page + 1;
677 last_page += npages;
678 csr->last_page = last_page;
679 csr->nr_pages = npages;
680
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300681 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300682 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300683 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300684 csr->nr_channels = 1;
685
686 csr->channels[0].chan_idx = i;
687 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300688
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300689 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300690
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300691 switch (banks) {
692 case 4:
693 csr->dtype = DEV_X4;
694 break;
695 case 8:
696 csr->dtype = DEV_X8;
697 break;
698 case 16:
699 csr->dtype = DEV_X16;
700 break;
701 default:
702 csr->dtype = DEV_UNKNOWN;
703 }
704
705 csr->edac_mode = mode;
706 csr->mtype = mtype;
707
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300708 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300709 }
710
711 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
712 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
713 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
714 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
715 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
716 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
717 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
718 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300719 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300720 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300721 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300722 (value[j] >> 27) & 0x1,
723 (value[j] >> 24) & 0x7,
724 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300725 }
726
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300727 return 0;
728}
729
730/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300731 Error insertion routines
732 ****************************************************************************/
733
734/* The i7core has independent error injection features per channel.
735 However, to have a simpler code, we don't allow enabling error injection
736 on more than one channel.
737 Also, since a change at an inject parameter will be applied only at enable,
738 we're disabling error injection on all write calls to the sysfs nodes that
739 controls the error code injection.
740 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300741static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300742{
743 struct i7core_pvt *pvt = mci->pvt_info;
744
745 pvt->inject.enable = 0;
746
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300747 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300748 return -ENODEV;
749
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300750 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300751 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300752
753 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300754}
755
756/*
757 * i7core inject inject.section
758 *
759 * accept and store error injection inject.section value
760 * bit 0 - refers to the lower 32-byte half cacheline
761 * bit 1 - refers to the upper 32-byte half cacheline
762 */
763static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
764 const char *data, size_t count)
765{
766 struct i7core_pvt *pvt = mci->pvt_info;
767 unsigned long value;
768 int rc;
769
770 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300771 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300772
773 rc = strict_strtoul(data, 10, &value);
774 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300775 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300776
777 pvt->inject.section = (u32) value;
778 return count;
779}
780
781static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
782 char *data)
783{
784 struct i7core_pvt *pvt = mci->pvt_info;
785 return sprintf(data, "0x%08x\n", pvt->inject.section);
786}
787
788/*
789 * i7core inject.type
790 *
791 * accept and store error injection inject.section value
792 * bit 0 - repeat enable - Enable error repetition
793 * bit 1 - inject ECC error
794 * bit 2 - inject parity error
795 */
796static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
797 const char *data, size_t count)
798{
799 struct i7core_pvt *pvt = mci->pvt_info;
800 unsigned long value;
801 int rc;
802
803 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300804 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300805
806 rc = strict_strtoul(data, 10, &value);
807 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300808 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300809
810 pvt->inject.type = (u32) value;
811 return count;
812}
813
814static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
815 char *data)
816{
817 struct i7core_pvt *pvt = mci->pvt_info;
818 return sprintf(data, "0x%08x\n", pvt->inject.type);
819}
820
821/*
822 * i7core_inject_inject.eccmask_store
823 *
824 * The type of error (UE/CE) will depend on the inject.eccmask value:
825 * Any bits set to a 1 will flip the corresponding ECC bit
826 * Correctable errors can be injected by flipping 1 bit or the bits within
827 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
828 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
829 * uncorrectable error to be injected.
830 */
831static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
832 const char *data, size_t count)
833{
834 struct i7core_pvt *pvt = mci->pvt_info;
835 unsigned long value;
836 int rc;
837
838 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300839 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300840
841 rc = strict_strtoul(data, 10, &value);
842 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300843 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300844
845 pvt->inject.eccmask = (u32) value;
846 return count;
847}
848
849static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
850 char *data)
851{
852 struct i7core_pvt *pvt = mci->pvt_info;
853 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
854}
855
856/*
857 * i7core_addrmatch
858 *
859 * The type of error (UE/CE) will depend on the inject.eccmask value:
860 * Any bits set to a 1 will flip the corresponding ECC bit
861 * Correctable errors can be injected by flipping 1 bit or the bits within
862 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
863 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
864 * uncorrectable error to be injected.
865 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300866
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300867#define DECLARE_ADDR_MATCH(param, limit) \
868static ssize_t i7core_inject_store_##param( \
869 struct mem_ctl_info *mci, \
870 const char *data, size_t count) \
871{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300872 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300873 long value; \
874 int rc; \
875 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300876 debugf1("%s()\n", __func__); \
877 pvt = mci->pvt_info; \
878 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300879 if (pvt->inject.enable) \
880 disable_inject(mci); \
881 \
Mauro Carvalho Chehab4f87fad2009-10-04 11:54:56 -0300882 if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300883 value = -1; \
884 else { \
885 rc = strict_strtoul(data, 10, &value); \
886 if ((rc < 0) || (value >= limit)) \
887 return -EIO; \
888 } \
889 \
890 pvt->inject.param = value; \
891 \
892 return count; \
893} \
894 \
895static ssize_t i7core_inject_show_##param( \
896 struct mem_ctl_info *mci, \
897 char *data) \
898{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300899 struct i7core_pvt *pvt; \
900 \
901 pvt = mci->pvt_info; \
902 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300903 if (pvt->inject.param < 0) \
904 return sprintf(data, "any\n"); \
905 else \
906 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300907}
908
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300909#define ATTR_ADDR_MATCH(param) \
910 { \
911 .attr = { \
912 .name = #param, \
913 .mode = (S_IRUGO | S_IWUSR) \
914 }, \
915 .show = i7core_inject_show_##param, \
916 .store = i7core_inject_store_##param, \
917 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300918
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300919DECLARE_ADDR_MATCH(channel, 3);
920DECLARE_ADDR_MATCH(dimm, 3);
921DECLARE_ADDR_MATCH(rank, 4);
922DECLARE_ADDR_MATCH(bank, 32);
923DECLARE_ADDR_MATCH(page, 0x10000);
924DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300925
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300926static int write_and_test(struct pci_dev *dev, int where, u32 val)
927{
928 u32 read;
929 int count;
930
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300931 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
932 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
933 where, val);
934
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300935 for (count = 0; count < 10; count++) {
936 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300937 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300938 pci_write_config_dword(dev, where, val);
939 pci_read_config_dword(dev, where, &read);
940
941 if (read == val)
942 return 0;
943 }
944
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300945 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
946 "write=%08x. Read=%08x\n",
947 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
948 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300949
950 return -EINVAL;
951}
952
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300953/*
954 * This routine prepares the Memory Controller for error injection.
955 * The error will be injected when some process tries to write to the
956 * memory that matches the given criteria.
957 * The criteria can be set in terms of a mask where dimm, rank, bank, page
958 * and col can be specified.
959 * A -1 value for any of the mask items will make the MCU to ignore
960 * that matching criteria for error injection.
961 *
962 * It should be noticed that the error will only happen after a write operation
963 * on a memory that matches the condition. if REPEAT_EN is not enabled at
964 * inject mask, then it will produce just one error. Otherwise, it will repeat
965 * until the injectmask would be cleaned.
966 *
967 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
968 * is reliable enough to check if the MC is using the
969 * three channels. However, this is not clear at the datasheet.
970 */
971static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
972 const char *data, size_t count)
973{
974 struct i7core_pvt *pvt = mci->pvt_info;
975 u32 injectmask;
976 u64 mask = 0;
977 int rc;
978 long enable;
979
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300980 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300981 return 0;
982
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300983 rc = strict_strtoul(data, 10, &enable);
984 if ((rc < 0))
985 return 0;
986
987 if (enable) {
988 pvt->inject.enable = 1;
989 } else {
990 disable_inject(mci);
991 return count;
992 }
993
994 /* Sets pvt->inject.dimm mask */
995 if (pvt->inject.dimm < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200996 mask |= 1LL << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300997 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300998 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -0200999 mask |= (pvt->inject.dimm & 0x3LL) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001000 else
Alan Cox486dd092009-11-08 01:34:27 -02001001 mask |= (pvt->inject.dimm & 0x1LL) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001002 }
1003
1004 /* Sets pvt->inject.rank mask */
1005 if (pvt->inject.rank < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001006 mask |= 1LL << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001007 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001008 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -02001009 mask |= (pvt->inject.rank & 0x1LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001010 else
Alan Cox486dd092009-11-08 01:34:27 -02001011 mask |= (pvt->inject.rank & 0x3LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001012 }
1013
1014 /* Sets pvt->inject.bank mask */
1015 if (pvt->inject.bank < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001016 mask |= 1LL << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001017 else
Alan Cox486dd092009-11-08 01:34:27 -02001018 mask |= (pvt->inject.bank & 0x15LL) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001019
1020 /* Sets pvt->inject.page mask */
1021 if (pvt->inject.page < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001022 mask |= 1LL << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001023 else
Alan Cox486dd092009-11-08 01:34:27 -02001024 mask |= (pvt->inject.page & 0xffff) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001025
1026 /* Sets pvt->inject.column mask */
1027 if (pvt->inject.col < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001028 mask |= 1LL << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001029 else
Alan Cox486dd092009-11-08 01:34:27 -02001030 mask |= (pvt->inject.col & 0x3fff);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001031
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001032 /*
1033 * bit 0: REPEAT_EN
1034 * bits 1-2: MASK_HALF_CACHELINE
1035 * bit 3: INJECT_ECC
1036 * bit 4: INJECT_ADDR_PARITY
1037 */
1038
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001039 injectmask = (pvt->inject.type & 1) |
1040 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001041 (pvt->inject.type & 0x6) << (3 - 1);
1042
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001043 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001044 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001045 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001046
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001047 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001048 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001049 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001050 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1051
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001052 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001053 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1054
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001055 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001056 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001057
1058 /*
1059 * This is something undocumented, based on my tests
1060 * Without writing 8 to this register, errors aren't injected. Not sure
1061 * why.
1062 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001063 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001064 MC_CFG_CONTROL, 8);
1065
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001066 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1067 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001068 mask, pvt->inject.eccmask, injectmask);
1069
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001070
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001071 return count;
1072}
1073
1074static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1075 char *data)
1076{
1077 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001078 u32 injectmask;
1079
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -03001080 if (!pvt->pci_ch[pvt->inject.channel][0])
1081 return 0;
1082
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001083 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001084 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001085
1086 debugf0("Inject error read: 0x%018x\n", injectmask);
1087
1088 if (injectmask & 0x0c)
1089 pvt->inject.enable = 1;
1090
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001091 return sprintf(data, "%d\n", pvt->inject.enable);
1092}
1093
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001094#define DECLARE_COUNTER(param) \
1095static ssize_t i7core_show_counter_##param( \
1096 struct mem_ctl_info *mci, \
1097 char *data) \
1098{ \
1099 struct i7core_pvt *pvt = mci->pvt_info; \
1100 \
1101 debugf1("%s() \n", __func__); \
1102 if (!pvt->ce_count_available || (pvt->is_registered)) \
1103 return sprintf(data, "data unavailable\n"); \
1104 return sprintf(data, "%lu\n", \
1105 pvt->udimm_ce_count[param]); \
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001106}
1107
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001108#define ATTR_COUNTER(param) \
1109 { \
1110 .attr = { \
1111 .name = __stringify(udimm##param), \
1112 .mode = (S_IRUGO | S_IWUSR) \
1113 }, \
1114 .show = i7core_show_counter_##param \
1115 }
1116
1117DECLARE_COUNTER(0);
1118DECLARE_COUNTER(1);
1119DECLARE_COUNTER(2);
1120
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001121/*
1122 * Sysfs struct
1123 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001124
1125
1126static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
1127 ATTR_ADDR_MATCH(channel),
1128 ATTR_ADDR_MATCH(dimm),
1129 ATTR_ADDR_MATCH(rank),
1130 ATTR_ADDR_MATCH(bank),
1131 ATTR_ADDR_MATCH(page),
1132 ATTR_ADDR_MATCH(col),
1133 { .attr = { .name = NULL } }
1134};
1135
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001136static struct mcidev_sysfs_group i7core_inject_addrmatch = {
1137 .name = "inject_addrmatch",
1138 .mcidev_attr = i7core_addrmatch_attrs,
1139};
1140
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001141static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
1142 ATTR_COUNTER(0),
1143 ATTR_COUNTER(1),
1144 ATTR_COUNTER(2),
Marcin Slusarz64aab722010-09-30 15:15:30 -07001145 { .attr = { .name = NULL } }
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001146};
1147
1148static struct mcidev_sysfs_group i7core_udimm_counters = {
1149 .name = "all_channel_counts",
1150 .mcidev_attr = i7core_udimm_counters_attrs,
1151};
1152
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001153static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001154 {
1155 .attr = {
1156 .name = "inject_section",
1157 .mode = (S_IRUGO | S_IWUSR)
1158 },
1159 .show = i7core_inject_section_show,
1160 .store = i7core_inject_section_store,
1161 }, {
1162 .attr = {
1163 .name = "inject_type",
1164 .mode = (S_IRUGO | S_IWUSR)
1165 },
1166 .show = i7core_inject_type_show,
1167 .store = i7core_inject_type_store,
1168 }, {
1169 .attr = {
1170 .name = "inject_eccmask",
1171 .mode = (S_IRUGO | S_IWUSR)
1172 },
1173 .show = i7core_inject_eccmask_show,
1174 .store = i7core_inject_eccmask_store,
1175 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001176 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001177 }, {
1178 .attr = {
1179 .name = "inject_enable",
1180 .mode = (S_IRUGO | S_IWUSR)
1181 },
1182 .show = i7core_inject_enable_show,
1183 .store = i7core_inject_enable_store,
1184 },
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001185 { .attr = { .name = NULL } }, /* Reserved for udimm counters */
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001186 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001187};
1188
1189/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001190 Device initialization routines: put/get, init/exit
1191 ****************************************************************************/
1192
1193/*
1194 * i7core_put_devices 'put' all the devices that we have
1195 * reserved via 'get'
1196 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001197static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001198{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001199 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001200
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001201 debugf0(__FILE__ ": %s()\n", __func__);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001202 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001203 struct pci_dev *pdev = i7core_dev->pdev[i];
1204 if (!pdev)
1205 continue;
1206 debugf0("Removing dev %02x:%02x.%d\n",
1207 pdev->bus->number,
1208 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1209 pci_dev_put(pdev);
1210 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001211 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001212 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001213 kfree(i7core_dev);
1214}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001215
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001216static void i7core_put_all_devices(void)
1217{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001218 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001219
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001220 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001221 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001222}
1223
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001224static void __init i7core_xeon_pci_fixup(struct pci_id_table *table)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001225{
1226 struct pci_dev *pdev = NULL;
1227 int i;
1228 /*
1229 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1230 * aren't announced by acpi. So, we need to use a legacy scan probing
1231 * to detect them
1232 */
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001233 while (table && table->descr) {
1234 pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL);
1235 if (unlikely(!pdev)) {
1236 for (i = 0; i < MAX_SOCKET_BUSES; i++)
1237 pcibios_scan_specific_bus(255-i);
1238 }
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001239 pci_dev_put(pdev);
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001240 table++;
Keith Manntheybc2d7242009-09-03 00:05:05 -03001241 }
1242}
1243
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001244static unsigned i7core_pci_lastbus(void)
1245{
1246 int last_bus = 0, bus;
1247 struct pci_bus *b = NULL;
1248
1249 while ((b = pci_find_next_bus(b)) != NULL) {
1250 bus = b->number;
1251 debugf0("Found bus %d\n", bus);
1252 if (bus > last_bus)
1253 last_bus = bus;
1254 }
1255
1256 debugf0("Last bus %d\n", last_bus);
1257
1258 return last_bus;
1259}
1260
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001261/*
1262 * i7core_get_devices Find and perform 'get' operation on the MCH's
1263 * device/functions we want to reference for this driver
1264 *
1265 * Need to 'get' device 16 func 1 and func 2
1266 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001267int i7core_get_onedevice(struct pci_dev **prev, int devno,
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001268 struct pci_id_descr *dev_descr, unsigned n_devs,
1269 unsigned last_bus)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001270{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001271 struct i7core_dev *i7core_dev;
1272
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001273 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001274 u8 bus = 0;
1275 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001276
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001277 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001278 dev_descr->dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001279
1280 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001281 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1282 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1283 * to probe for the alternate address in case of failure
1284 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001285 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001286 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -03001287 PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001288
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001289 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -03001290 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1291 PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
1292 *prev);
1293
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001294 if (!pdev) {
1295 if (*prev) {
1296 *prev = pdev;
1297 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001298 }
1299
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001300 if (dev_descr->optional)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001301 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001302
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001303 if (devno == 0)
1304 return -ENODEV;
1305
Daniel J Bluemanab089372010-07-23 23:16:52 +01001306 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001307 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001308 dev_descr->dev, dev_descr->func,
1309 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001310
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001311 /* End of list, leave */
1312 return -ENODEV;
1313 }
1314 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001315
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001316 socket = last_bus - bus;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001317
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001318 i7core_dev = get_i7core_dev(socket);
1319 if (!i7core_dev) {
1320 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1321 if (!i7core_dev)
1322 return -ENOMEM;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001323 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001324 GFP_KERNEL);
Alexander Beregalov2a6fae32010-01-07 23:27:30 -03001325 if (!i7core_dev->pdev) {
1326 kfree(i7core_dev);
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001327 return -ENOMEM;
Alexander Beregalov2a6fae32010-01-07 23:27:30 -03001328 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001329 i7core_dev->socket = socket;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001330 i7core_dev->n_devs = n_devs;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001331 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001332 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001333
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001334 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001335 i7core_printk(KERN_ERR,
1336 "Duplicated device for "
1337 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001338 bus, dev_descr->dev, dev_descr->func,
1339 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001340 pci_dev_put(pdev);
1341 return -ENODEV;
1342 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001343
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001344 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001345
1346 /* Sanity check */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001347 if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1348 PCI_FUNC(pdev->devfn) != dev_descr->func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001349 i7core_printk(KERN_ERR,
1350 "Device PCI ID %04x:%04x "
1351 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001352 PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001353 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001354 bus, dev_descr->dev, dev_descr->func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001355 return -ENODEV;
1356 }
1357
1358 /* Be sure that the device is enabled */
1359 if (unlikely(pci_enable_device(pdev) < 0)) {
1360 i7core_printk(KERN_ERR,
1361 "Couldn't enable "
1362 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001363 bus, dev_descr->dev, dev_descr->func,
1364 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001365 return -ENODEV;
1366 }
1367
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001368 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001369 socket, bus, dev_descr->dev,
1370 dev_descr->func,
1371 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001372
1373 *prev = pdev;
1374
1375 return 0;
1376}
1377
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001378static int i7core_get_devices(struct pci_id_table *table)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001379{
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001380 int i, rc, last_bus;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001381 struct pci_dev *pdev = NULL;
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001382 struct pci_id_descr *dev_descr;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001383
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001384 last_bus = i7core_pci_lastbus();
1385
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001386 while (table && table->descr) {
1387 dev_descr = table->descr;
1388 for (i = 0; i < table->n_devs; i++) {
1389 pdev = NULL;
1390 do {
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001391 rc = i7core_get_onedevice(&pdev, i,
1392 &dev_descr[i],
1393 table->n_devs,
1394 last_bus);
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001395 if (rc < 0) {
1396 if (i == 0) {
1397 i = table->n_devs;
1398 break;
1399 }
1400 i7core_put_all_devices();
1401 return -ENODEV;
1402 }
1403 } while (pdev);
1404 }
1405 table++;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001406 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001407
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001408 return 0;
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001409 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001410}
1411
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001412static int mci_bind_devs(struct mem_ctl_info *mci,
1413 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001414{
1415 struct i7core_pvt *pvt = mci->pvt_info;
1416 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001417 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001418
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001419 /* Associates i7core_dev and mci for future usage */
1420 pvt->i7core_dev = i7core_dev;
1421 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001422
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001423 pvt->is_registered = 0;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001424 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001425 pdev = i7core_dev->pdev[i];
1426 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001427 continue;
1428
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001429 func = PCI_FUNC(pdev->devfn);
1430 slot = PCI_SLOT(pdev->devfn);
1431 if (slot == 3) {
1432 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001433 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001434 pvt->pci_mcr[func] = pdev;
1435 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1436 if (unlikely(func > MAX_CHAN_FUNC))
1437 goto error;
1438 pvt->pci_ch[slot - 4][func] = pdev;
1439 } else if (!slot && !func)
1440 pvt->pci_noncore = pdev;
1441 else
1442 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001443
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001444 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1445 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1446 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001447
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001448 if (PCI_SLOT(pdev->devfn) == 3 &&
1449 PCI_FUNC(pdev->devfn) == 2)
1450 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001451 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001452
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001453 /*
1454 * Add extra nodes to count errors on udimm
1455 * For registered memory, this is not needed, since the counters
1456 * are already displayed at the standard locations
1457 */
1458 if (!pvt->is_registered)
1459 i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp =
1460 &i7core_udimm_counters;
1461
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001462 return 0;
1463
1464error:
1465 i7core_printk(KERN_ERR, "Device %d, function %d "
1466 "is out of the expected range\n",
1467 slot, func);
1468 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001469}
1470
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001471/****************************************************************************
1472 Error check routines
1473 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001474static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001475 int chan, int dimm, int add)
1476{
1477 char *msg;
1478 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001479 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001480
1481 for (i = 0; i < add; i++) {
1482 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001483 "(Socket=%d channel=%d dimm=%d)",
1484 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001485
1486 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1487 kfree (msg);
1488 }
1489}
1490
1491static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001492 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001493{
1494 struct i7core_pvt *pvt = mci->pvt_info;
1495 int add0 = 0, add1 = 0, add2 = 0;
1496 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001497 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001498 /* Updates CE counters */
1499
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001500 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1501 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1502 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001503
1504 if (add2 < 0)
1505 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001506 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001507
1508 if (add1 < 0)
1509 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001510 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001511
1512 if (add0 < 0)
1513 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001514 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001515 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001516 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001517
1518 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001519 pvt->rdimm_last_ce_count[chan][2] = new2;
1520 pvt->rdimm_last_ce_count[chan][1] = new1;
1521 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001522
1523 /*updated the edac core */
1524 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001525 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001526 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001527 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001528 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001529 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001530
1531}
1532
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001533static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001534{
1535 struct i7core_pvt *pvt = mci->pvt_info;
1536 u32 rcv[3][2];
1537 int i, new0, new1, new2;
1538
1539 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001540 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001541 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001542 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001543 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001544 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001545 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001546 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001547 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001548 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001549 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001550 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001551 &rcv[2][1]);
1552 for (i = 0 ; i < 3; i++) {
1553 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1554 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1555 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001556 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001557 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1558 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1559 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1560 } else {
1561 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1562 DIMM_BOT_COR_ERR(rcv[i][0]);
1563 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1564 DIMM_BOT_COR_ERR(rcv[i][1]);
1565 new2 = 0;
1566 }
1567
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001568 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001569 }
1570}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001571
1572/* This function is based on the device 3 function 4 registers as described on:
1573 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1574 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1575 * also available at:
1576 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1577 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001578static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001579{
1580 struct i7core_pvt *pvt = mci->pvt_info;
1581 u32 rcv1, rcv0;
1582 int new0, new1, new2;
1583
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001584 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001585 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001586 return;
1587 }
1588
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001589 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001590 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1591 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001592
1593 /* Store the new values */
1594 new2 = DIMM2_COR_ERR(rcv1);
1595 new1 = DIMM1_COR_ERR(rcv0);
1596 new0 = DIMM0_COR_ERR(rcv0);
1597
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001598 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001599 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001600 /* Updates CE counters */
1601 int add0, add1, add2;
1602
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001603 add2 = new2 - pvt->udimm_last_ce_count[2];
1604 add1 = new1 - pvt->udimm_last_ce_count[1];
1605 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001606
1607 if (add2 < 0)
1608 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001609 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001610
1611 if (add1 < 0)
1612 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001613 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001614
1615 if (add0 < 0)
1616 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001617 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001618
1619 if (add0 | add1 | add2)
1620 i7core_printk(KERN_ERR, "New Corrected error(s): "
1621 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1622 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001623 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001624 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001625
1626 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001627 pvt->udimm_last_ce_count[2] = new2;
1628 pvt->udimm_last_ce_count[1] = new1;
1629 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001630}
1631
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001632/*
1633 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1634 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001635 * Nehalem are defined as family 0x06, model 0x1a
1636 *
1637 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001638 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001639 * m->status MSR_IA32_MC8_STATUS
1640 * m->addr MSR_IA32_MC8_ADDR
1641 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001642 * In the case of Nehalem, the error information is masked at .status and .misc
1643 * fields
1644 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001645static void i7core_mce_output_error(struct mem_ctl_info *mci,
1646 struct mce *m)
1647{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001648 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001649 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001650 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001651 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001652 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1653 u32 dimm = (m->misc >> 16) & 0x3;
1654 u32 channel = (m->misc >> 18) & 0x3;
1655 u32 syndrome = m->misc >> 32;
1656 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001657 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001658
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001659 if (m->mcgstatus & 1)
1660 type = "FATAL";
1661 else
1662 type = "NON_FATAL";
1663
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001664 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001665 case 0:
1666 optype = "generic undef request";
1667 break;
1668 case 1:
1669 optype = "read error";
1670 break;
1671 case 2:
1672 optype = "write error";
1673 break;
1674 case 3:
1675 optype = "addr/cmd error";
1676 break;
1677 case 4:
1678 optype = "scrubbing error";
1679 break;
1680 default:
1681 optype = "reserved";
1682 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001683 }
1684
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001685 switch (errnum) {
1686 case 16:
1687 err = "read ECC error";
1688 break;
1689 case 17:
1690 err = "RAS ECC error";
1691 break;
1692 case 18:
1693 err = "write parity error";
1694 break;
1695 case 19:
1696 err = "redundacy loss";
1697 break;
1698 case 20:
1699 err = "reserved";
1700 break;
1701 case 21:
1702 err = "memory range error";
1703 break;
1704 case 22:
1705 err = "RTID out of range";
1706 break;
1707 case 23:
1708 err = "address parity error";
1709 break;
1710 case 24:
1711 err = "byte enable parity error";
1712 break;
1713 default:
1714 err = "unknown";
1715 }
1716
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001717 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001718 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001719 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001720 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001721 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001722 syndrome, core_err_cnt, (long long)m->status,
1723 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001724
1725 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001726
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001727 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001728
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001729 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001730 if (m->mcgstatus & 1)
1731 edac_mc_handle_fbd_ue(mci, csrow, 0,
1732 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001733 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001734 edac_mc_handle_fbd_ce(mci, csrow,
1735 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001736
1737 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001738}
1739
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001740/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001741 * i7core_check_error Retrieve and process errors reported by the
1742 * hardware. Called by the Core module.
1743 */
1744static void i7core_check_error(struct mem_ctl_info *mci)
1745{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001746 struct i7core_pvt *pvt = mci->pvt_info;
1747 int i;
1748 unsigned count = 0;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001749 struct mce *m;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001750
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001751 /*
1752 * MCE first step: Copy all mce errors into a temporary buffer
1753 * We use a double buffering here, to reduce the risk of
1754 * loosing an error.
1755 */
1756 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001757 count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
1758 % MCE_LOG_LEN;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001759 if (!count)
Vernon Mauery8a311e12010-04-16 19:40:19 -03001760 goto check_ce_error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001761
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001762 m = pvt->mce_outentry;
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001763 if (pvt->mce_in + count > MCE_LOG_LEN) {
1764 unsigned l = MCE_LOG_LEN - pvt->mce_in;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001765
1766 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
1767 smp_wmb();
1768 pvt->mce_in = 0;
1769 count -= l;
1770 m += l;
1771 }
1772 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
1773 smp_wmb();
1774 pvt->mce_in += count;
1775
1776 smp_rmb();
1777 if (pvt->mce_overrun) {
1778 i7core_printk(KERN_ERR, "Lost %d memory errors\n",
1779 pvt->mce_overrun);
1780 smp_wmb();
1781 pvt->mce_overrun = 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001782 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001783
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001784 /*
1785 * MCE second step: parse errors and display
1786 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001787 for (i = 0; i < count; i++)
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001788 i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001789
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001790 /*
1791 * Now, let's increment CE error counts
1792 */
Vernon Mauery8a311e12010-04-16 19:40:19 -03001793check_ce_error:
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001794 if (!pvt->is_registered)
1795 i7core_udimm_check_mc_ecc_err(mci);
1796 else
1797 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001798}
1799
1800/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001801 * i7core_mce_check_error Replicates mcelog routine to get errors
1802 * This routine simply queues mcelog errors, and
1803 * return. The error itself should be handled later
1804 * by i7core_check_error.
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001805 * WARNING: As this routine should be called at NMI time, extra care should
1806 * be taken to avoid deadlocks, and to be as fast as possible.
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001807 */
1808static int i7core_mce_check_error(void *priv, struct mce *mce)
1809{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001810 struct mem_ctl_info *mci = priv;
1811 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001812
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001813 /*
1814 * Just let mcelog handle it if the error is
1815 * outside the memory controller
1816 */
1817 if (((mce->status & 0xffff) >> 7) != 1)
1818 return 0;
1819
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001820 /* Bank 8 registers are the only ones that we know how to handle */
1821 if (mce->bank != 8)
1822 return 0;
1823
Randy Dunlap3b918c12009-11-08 01:36:40 -02001824#ifdef CONFIG_SMP
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001825 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001826 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001827 return 0;
Randy Dunlap3b918c12009-11-08 01:36:40 -02001828#endif
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001829
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001830 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001831 if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001832 smp_wmb();
1833 pvt->mce_overrun++;
1834 return 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001835 }
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001836
1837 /* Copy memory error at the ringbuffer */
1838 memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001839 smp_wmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001840 pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001841
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001842 /* Handle fatal errors immediately */
1843 if (mce->mcgstatus & 1)
1844 i7core_check_error(mci);
1845
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001846 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001847 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001848}
1849
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001850static int i7core_register_mci(struct i7core_dev *i7core_dev,
1851 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001852{
1853 struct mem_ctl_info *mci;
1854 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001855 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001856 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001857
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001858 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001859 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1860 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001861 if (unlikely(!mci))
1862 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001863
1864 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1865
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001866 /* record ptr to the generic device */
1867 mci->dev = &i7core_dev->pdev[0]->dev;
1868
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001869 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001870 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001871
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001872 /*
1873 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1874 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1875 * memory channels
1876 */
1877 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001878 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1879 mci->edac_cap = EDAC_FLAG_NONE;
1880 mci->mod_name = "i7core_edac.c";
1881 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001882 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1883 i7core_dev->socket);
1884 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001885 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001886 mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001887 /* Set the function pointer to an actual operation function */
1888 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001889
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001890 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001891 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001892 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001893 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001894
1895 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001896 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001897
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001898 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001899 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001900 debugf0("MC: " __FILE__
1901 ": %s(): failed edac_mc_add_mc()\n", __func__);
1902 /* FIXME: perhaps some code should go here that disables error
1903 * reporting if we just enabled it
1904 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001905
1906 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001907 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001908 }
1909
1910 /* allocating generic PCI control info */
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -03001911 pvt->i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001912 EDAC_MOD_STR);
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -03001913 if (unlikely(!pvt->i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001914 printk(KERN_WARNING
1915 "%s(): Unable to create PCI control\n",
1916 __func__);
1917 printk(KERN_WARNING
1918 "%s(): PCI error report via EDAC not setup\n",
1919 __func__);
1920 }
1921
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001922 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001923 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001924 pvt->inject.dimm = -1;
1925 pvt->inject.rank = -1;
1926 pvt->inject.bank = -1;
1927 pvt->inject.page = -1;
1928 pvt->inject.col = -1;
1929
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001930 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001931 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001932 pvt->edac_mce.check_error = i7core_mce_check_error;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001933
1934 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001935 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001936 debugf0("MC: " __FILE__
1937 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001938 }
1939
1940fail:
Tony Luckd4d1ef42010-05-18 10:53:25 -03001941 if (rc < 0)
1942 edac_mc_free(mci);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001943 return rc;
1944}
1945
1946/*
1947 * i7core_probe Probe for ONE instance of device to see if it is
1948 * present.
1949 * return:
1950 * 0 for FOUND a device
1951 * < 0 for error code
1952 */
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03001953
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001954static int __devinit i7core_probe(struct pci_dev *pdev,
1955 const struct pci_device_id *id)
1956{
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001957 int rc;
1958 struct i7core_dev *i7core_dev;
1959
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03001960 /* get the pci devices we want to reserve for our use */
1961 mutex_lock(&i7core_edac_lock);
1962
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001963 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001964 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001965 */
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03001966 if (unlikely(probed >= 1)) {
1967 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001968 return -EINVAL;
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03001969 }
1970 probed++;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001971
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001972 rc = i7core_get_devices(pci_dev_table);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001973 if (unlikely(rc < 0))
1974 goto fail0;
1975
1976 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1977 int channels;
1978 int csrows;
1979
1980 /* Check the number of active and not disabled channels */
1981 rc = i7core_get_active_channels(i7core_dev->socket,
1982 &channels, &csrows);
1983 if (unlikely(rc < 0))
1984 goto fail1;
1985
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001986 rc = i7core_register_mci(i7core_dev, channels, csrows);
1987 if (unlikely(rc < 0))
1988 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001989 }
1990
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001991 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001992
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001993 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001994 return 0;
1995
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001996fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001997 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001998fail0:
1999 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03002000 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002001}
2002
2003/*
2004 * i7core_remove destructor for one instance of device
2005 *
2006 */
2007static void __devexit i7core_remove(struct pci_dev *pdev)
2008{
2009 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002010 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -03002011 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002012
2013 debugf0(__FILE__ ": %s()\n", __func__);
2014
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002015 /*
2016 * we have a trouble here: pdev value for removal will be wrong, since
2017 * it will point to the X58 register used to detect that the machine
2018 * is a Nehalem or upper design. However, due to the way several PCI
2019 * devices are grouped together to provide MC functionality, we need
2020 * to use a different method for releasing the devices
2021 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03002022
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03002023 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002024 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -03002025 mci = find_mci_by_dev(&i7core_dev->pdev[0]->dev);
2026 if (unlikely(!mci || !mci->pvt_info)) {
2027 i7core_printk(KERN_ERR,
2028 "Couldn't find mci hanler\n");
2029 } else {
2030 pvt = mci->pvt_info;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002031 i7core_dev = pvt->i7core_dev;
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -03002032
2033 if (likely(pvt->i7core_pci))
2034 edac_pci_release_generic_ctl(pvt->i7core_pci);
2035 else
2036 i7core_printk(KERN_ERR,
2037 "Couldn't find mem_ctl_info for socket %d\n",
2038 i7core_dev->socket);
2039 pvt->i7core_pci = NULL;
2040
2041 edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
2042
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002043 edac_mce_unregister(&pvt->edac_mce);
2044 kfree(mci->ctl_name);
2045 edac_mc_free(mci);
2046 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002047 }
2048 }
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002049 probed--;
2050
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002051 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002052}
2053
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002054MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
2055
2056/*
2057 * i7core_driver pci_driver structure for this module
2058 *
2059 */
2060static struct pci_driver i7core_driver = {
2061 .name = "i7core_edac",
2062 .probe = i7core_probe,
2063 .remove = __devexit_p(i7core_remove),
2064 .id_table = i7core_pci_tbl,
2065};
2066
2067/*
2068 * i7core_init Module entry function
2069 * Try to initialize this module for its devices
2070 */
2071static int __init i7core_init(void)
2072{
2073 int pci_rc;
2074
2075 debugf2("MC: " __FILE__ ": %s()\n", __func__);
2076
2077 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
2078 opstate_init();
2079
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03002080 i7core_xeon_pci_fixup(pci_dev_table);
Keith Manntheybc2d7242009-09-03 00:05:05 -03002081
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002082 pci_rc = pci_register_driver(&i7core_driver);
2083
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03002084 if (pci_rc >= 0)
2085 return 0;
2086
2087 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
2088 pci_rc);
2089
2090 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002091}
2092
2093/*
2094 * i7core_exit() Module exit function
2095 * Unregister the driver
2096 */
2097static void __exit i7core_exit(void)
2098{
2099 debugf2("MC: " __FILE__ ": %s()\n", __func__);
2100 pci_unregister_driver(&i7core_driver);
2101}
2102
2103module_init(i7core_init);
2104module_exit(i7core_exit);
2105
2106MODULE_LICENSE("GPL");
2107MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
2108MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
2109MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
2110 I7CORE_REVISION);
2111
2112module_param(edac_op_state, int, 0444);
2113MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");