blob: 915835339d7cc3d62590c29638f2436d44874bc4 [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 Chehab54a08ab2010-08-19 15:51:00 -030047static int use_pci_fixup;
48module_param(use_pci_fixup, int, 0444);
49MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030050/*
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030051 * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
52 * registers start at bus 255, and are not reported by BIOS.
53 * We currently find devices with only 2 sockets. In order to support more QPI
54 * Quick Path Interconnect, just increment this number.
55 */
56#define MAX_SOCKET_BUSES 2
57
58
59/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030060 * Alter this version for the module when modifications are made
61 */
62#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
63#define EDAC_MOD_STR "i7core_edac"
64
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030065/*
66 * Debug macros
67 */
68#define i7core_printk(level, fmt, arg...) \
69 edac_printk(level, "i7core", fmt, ##arg)
70
71#define i7core_mc_printk(mci, level, fmt, arg...) \
72 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
73
74/*
75 * i7core Memory Controller Registers
76 */
77
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030078 /* OFFSETS for Device 0 Function 0 */
79
80#define MC_CFG_CONTROL 0x90
81
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030082 /* OFFSETS for Device 3 Function 0 */
83
84#define MC_CONTROL 0x48
85#define MC_STATUS 0x4c
86#define MC_MAX_DOD 0x64
87
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030088/*
89 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
90 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
91 */
92
93#define MC_TEST_ERR_RCV1 0x60
94 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
95
96#define MC_TEST_ERR_RCV0 0x64
97 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
98 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
99
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300100/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
101#define MC_COR_ECC_CNT_0 0x80
102#define MC_COR_ECC_CNT_1 0x84
103#define MC_COR_ECC_CNT_2 0x88
104#define MC_COR_ECC_CNT_3 0x8c
105#define MC_COR_ECC_CNT_4 0x90
106#define MC_COR_ECC_CNT_5 0x94
107
108#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
109#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
110
111
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300112 /* OFFSETS for Devices 4,5 and 6 Function 0 */
113
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300114#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
115 #define THREE_DIMMS_PRESENT (1 << 24)
116 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
117 #define QUAD_RANK_PRESENT (1 << 22)
118 #define REGISTERED_DIMM (1 << 15)
119
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300120#define MC_CHANNEL_MAPPER 0x60
121 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
122 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
123
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300124#define MC_CHANNEL_RANK_PRESENT 0x7c
125 #define RANK_PRESENT_MASK 0xffff
126
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300127#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300128#define MC_CHANNEL_ERROR_MASK 0xf8
129#define MC_CHANNEL_ERROR_INJECT 0xfc
130 #define INJECT_ADDR_PARITY 0x10
131 #define INJECT_ECC 0x08
132 #define MASK_CACHELINE 0x06
133 #define MASK_FULL_CACHELINE 0x06
134 #define MASK_MSB32_CACHELINE 0x04
135 #define MASK_LSB32_CACHELINE 0x02
136 #define NO_MASK_CACHELINE 0x00
137 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300138
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300139 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300140
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300141#define MC_DOD_CH_DIMM0 0x48
142#define MC_DOD_CH_DIMM1 0x4c
143#define MC_DOD_CH_DIMM2 0x50
144 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
145 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
146 #define DIMM_PRESENT_MASK (1 << 9)
147 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300148 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
149 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
150 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
151 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300152 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300153 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300154 #define MC_DOD_NUMCOL_MASK 3
155 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300156
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300157#define MC_RANK_PRESENT 0x7c
158
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300159#define MC_SAG_CH_0 0x80
160#define MC_SAG_CH_1 0x84
161#define MC_SAG_CH_2 0x88
162#define MC_SAG_CH_3 0x8c
163#define MC_SAG_CH_4 0x90
164#define MC_SAG_CH_5 0x94
165#define MC_SAG_CH_6 0x98
166#define MC_SAG_CH_7 0x9c
167
168#define MC_RIR_LIMIT_CH_0 0x40
169#define MC_RIR_LIMIT_CH_1 0x44
170#define MC_RIR_LIMIT_CH_2 0x48
171#define MC_RIR_LIMIT_CH_3 0x4C
172#define MC_RIR_LIMIT_CH_4 0x50
173#define MC_RIR_LIMIT_CH_5 0x54
174#define MC_RIR_LIMIT_CH_6 0x58
175#define MC_RIR_LIMIT_CH_7 0x5C
176#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
177
178#define MC_RIR_WAY_CH 0x80
179 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
180 #define MC_RIR_WAY_RANK_MASK 0x7
181
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300182/*
183 * i7core structs
184 */
185
186#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300187#define MAX_DIMMS 3 /* Max DIMMS per channel */
188#define MAX_MCR_FUNC 4
189#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300190
191struct i7core_info {
192 u32 mc_control;
193 u32 mc_status;
194 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300195 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300196};
197
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300198
199struct i7core_inject {
200 int enable;
201
202 u32 section;
203 u32 type;
204 u32 eccmask;
205
206 /* Error address mask */
207 int channel, dimm, rank, bank, page, col;
208};
209
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300210struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300211 u32 ranks;
212 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300213};
214
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300215struct pci_id_descr {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300216 int dev;
217 int func;
218 int dev_id;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300219 int optional;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300220};
221
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300222struct pci_id_table {
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300223 const struct pci_id_descr *descr;
224 int n_devs;
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300225};
226
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300227struct i7core_dev {
228 struct list_head list;
229 u8 socket;
230 struct pci_dev **pdev;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300231 int n_devs;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300232 struct mem_ctl_info *mci;
233};
234
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300235struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300236 struct pci_dev *pci_noncore;
237 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
238 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
239
240 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300241
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300242 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300243 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300244 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300245
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300246 int ce_count_available;
247 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300248
249 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300250 unsigned long udimm_ce_count[MAX_DIMMS];
251 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300252 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300253 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
254 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300255
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300256 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300257
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300258 /* mcelog glue */
259 struct edac_mce edac_mce;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300260
261 /* Fifo double buffers */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300262 struct mce mce_entry[MCE_LOG_LEN];
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300263 struct mce mce_outentry[MCE_LOG_LEN];
264
265 /* Fifo in/out counters */
266 unsigned mce_in, mce_out;
267
268 /* Count indicator to show errors not got */
269 unsigned mce_overrun;
Mauro Carvalho Chehab939747bd2010-08-10 11:22:01 -0300270
271 /* Struct to control EDAC polling */
272 struct edac_pci_ctl_info *i7core_pci;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300273};
274
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300275#define PCI_DESCR(device, function, device_id) \
276 .dev = (device), \
277 .func = (function), \
278 .dev_id = (device_id)
279
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300280static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300281 /* Memory controller */
282 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
283 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300284 /* Exists only for RDIMM */
285 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300286 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
287
288 /* Channel 0 */
289 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
290 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
291 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
292 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
293
294 /* Channel 1 */
295 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
296 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
297 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
298 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
299
300 /* Channel 2 */
301 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
302 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
303 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
304 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300305
306 /* Generic Non-core registers */
307 /*
308 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
309 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
310 * the probing code needs to test for the other address in case of
311 * failure of this one
312 */
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -0300313 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300314
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300315};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300316
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300317static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300318 { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) },
319 { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) },
320 { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) },
321
322 { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) },
323 { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) },
324 { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) },
325 { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) },
326
Mauro Carvalho Chehab508fa172009-10-14 13:44:37 -0300327 { PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) },
328 { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
329 { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
330 { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300331
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -0300332 /*
333 * This is the PCI device has an alternate address on some
334 * processors like Core i7 860
335 */
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300336 { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
337};
338
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300339static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300340 /* Memory controller */
341 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) },
342 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) },
343 /* Exists only for RDIMM */
344 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1 },
345 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) },
346
347 /* Channel 0 */
348 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) },
349 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) },
350 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) },
351 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2) },
352
353 /* Channel 1 */
354 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) },
355 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) },
356 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) },
357 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2) },
358
359 /* Channel 2 */
360 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) },
361 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
362 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
363 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) },
364
365 /* Generic Non-core registers */
366 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) },
367
368};
369
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300370#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
371static const struct pci_id_table pci_dev_table[] = {
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300372 PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
373 PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
374 PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
375};
376
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300377/*
378 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300379 */
380static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300381 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -0300382 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300383 {0,} /* 0 terminated list. */
384};
385
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300386/****************************************************************************
387 Anciliary status routines
388 ****************************************************************************/
389
390 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300391#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
392#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300393
394 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300395#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300396#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300397
398 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300399static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300400{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300401 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300402}
403
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300404static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300405{
406 static int ranks[4] = { 1, 2, 4, -EINVAL };
407
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300408 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300409}
410
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300411static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300412{
413 static int banks[4] = { 4, 8, 16, -EINVAL };
414
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300415 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300416}
417
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300418static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300419{
420 static int rows[8] = {
421 1 << 12, 1 << 13, 1 << 14, 1 << 15,
422 1 << 16, -EINVAL, -EINVAL, -EINVAL,
423 };
424
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300425 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300426}
427
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300428static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300429{
430 static int cols[8] = {
431 1 << 10, 1 << 11, 1 << 12, -EINVAL,
432 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300433 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300434}
435
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300436static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300437{
438 struct i7core_dev *i7core_dev;
439
440 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
441 if (i7core_dev->socket == socket)
442 return i7core_dev;
443 }
444
445 return NULL;
446}
447
Hidetoshi Seto848b2f72010-08-20 04:24:44 -0300448static struct i7core_dev *alloc_i7core_dev(u8 socket,
449 const struct pci_id_table *table)
450{
451 struct i7core_dev *i7core_dev;
452
453 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
454 if (!i7core_dev)
455 return NULL;
456
457 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs,
458 GFP_KERNEL);
459 if (!i7core_dev->pdev) {
460 kfree(i7core_dev);
461 return NULL;
462 }
463
464 i7core_dev->socket = socket;
465 i7core_dev->n_devs = table->n_devs;
466 list_add_tail(&i7core_dev->list, &i7core_edac_list);
467
468 return i7core_dev;
469}
470
Hidetoshi Seto2aa9be42010-08-20 04:25:00 -0300471static void free_i7core_dev(struct i7core_dev *i7core_dev)
472{
473 list_del(&i7core_dev->list);
474 kfree(i7core_dev->pdev);
475 kfree(i7core_dev);
476}
477
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300478/****************************************************************************
479 Memory check routines
480 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300481static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
482 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300483{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300484 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300485 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300486
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300487 if (!i7core_dev)
488 return NULL;
489
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300490 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300491 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300492 continue;
493
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300494 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
495 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
496 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300497 }
498 }
499
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300500 return NULL;
501}
502
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300503/**
504 * i7core_get_active_channels() - gets the number of channels and csrows
505 * @socket: Quick Path Interconnect socket
506 * @channels: Number of channels that will be returned
507 * @csrows: Number of csrows found
508 *
509 * Since EDAC core needs to know in advance the number of available channels
510 * and csrows, in order to allocate memory for csrows/channels, it is needed
511 * to run two similar steps. At the first step, implemented on this function,
512 * it checks the number of csrows/channels present at one socket.
513 * this is used in order to properly allocate the size of mci components.
514 *
515 * It should be noticed that none of the current available datasheets explain
516 * or even mention how csrows are seen by the memory controller. So, we need
517 * to add a fake description for csrows.
518 * So, this driver is attributing one DIMM memory for one csrow.
519 */
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300520static int i7core_get_active_channels(const u8 socket, unsigned *channels,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300521 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300522{
523 struct pci_dev *pdev = NULL;
524 int i, j;
525 u32 status, control;
526
527 *channels = 0;
528 *csrows = 0;
529
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300530 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300531 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300532 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
533 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300534 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300535 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300536
537 /* Device 3 function 0 reads */
538 pci_read_config_dword(pdev, MC_STATUS, &status);
539 pci_read_config_dword(pdev, MC_CONTROL, &control);
540
541 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300542 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300543 /* Check if the channel is active */
544 if (!(control & (1 << (8 + i))))
545 continue;
546
547 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300548 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300549 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300550
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300551 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300552 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300553 i7core_printk(KERN_ERR, "Couldn't find socket %d "
554 "fn %d.%d!!!\n",
555 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300556 return -ENODEV;
557 }
558 /* Devices 4-6 function 1 */
559 pci_read_config_dword(pdev,
560 MC_DOD_CH_DIMM0, &dimm_dod[0]);
561 pci_read_config_dword(pdev,
562 MC_DOD_CH_DIMM1, &dimm_dod[1]);
563 pci_read_config_dword(pdev,
564 MC_DOD_CH_DIMM2, &dimm_dod[2]);
565
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300566 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300567
568 for (j = 0; j < 3; j++) {
569 if (!DIMM_PRESENT(dimm_dod[j]))
570 continue;
571 (*csrows)++;
572 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300573 }
574
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300575 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300576 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300577
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300578 return 0;
579}
580
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300581static int get_dimm_config(const struct mem_ctl_info *mci)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300582{
583 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300584 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300585 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300586 int i, j;
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300587 int csrow = 0;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300588 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300589 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300590 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300591
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300592 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300593 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300594 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300595 return -ENODEV;
596
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300597 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300598 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
599 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
600 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
601 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300602
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300603 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300604 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300605 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300606
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300607 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300608 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300609 if (ECCx8(pvt))
610 mode = EDAC_S8ECD8ED;
611 else
612 mode = EDAC_S4ECD4ED;
613 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300614 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300615 mode = EDAC_NONE;
616 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300617
618 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300619 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
620 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300621 numdimms(pvt->info.max_dod),
622 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300623 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300624 numrow(pvt->info.max_dod >> 6),
625 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300626
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300627 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300628 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300629
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300630 if (!pvt->pci_ch[i][0])
631 continue;
632
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300633 if (!CH_ACTIVE(pvt, i)) {
634 debugf0("Channel %i is not active\n", i);
635 continue;
636 }
637 if (CH_DISABLED(pvt, i)) {
638 debugf0("Channel %i is disabled\n", i);
639 continue;
640 }
641
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300642 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300643 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300644 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
645
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300646 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300647 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300648
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300649 if (data & REGISTERED_DIMM)
650 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300651 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300652 mtype = MEM_DDR3;
653#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300654 if (data & THREE_DIMMS_PRESENT)
655 pvt->channel[i].dimms = 3;
656 else if (data & SINGLE_QUAD_RANK_PRESENT)
657 pvt->channel[i].dimms = 1;
658 else
659 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300660#endif
661
662 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300663 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300664 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300665 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300666 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300667 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300668 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300669
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300670 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300671 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300672 i,
673 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
674 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300675 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300676 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300677
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300678 for (j = 0; j < 3; j++) {
679 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300680 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300681
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300682 if (!DIMM_PRESENT(dimm_dod[j]))
683 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300684
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300685 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
686 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
687 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
688 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300689
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300690 /* DDR3 has 8 I/O banks */
691 size = (rows * cols * banks * ranks) >> (20 - 3);
692
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300693 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300694
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300695 debugf0("\tdimm %d %d Mb offset: %x, "
696 "bank: %d, rank: %d, row: %#x, col: %#x\n",
697 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300698 RANKOFFSET(dimm_dod[j]),
699 banks, ranks, rows, cols);
700
Mauro Carvalho Chehabe9144602010-08-10 20:26:35 -0300701 npages = MiB_TO_PAGES(size);
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300702
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300703 csr = &mci->csrows[csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300704 csr->first_page = last_page + 1;
705 last_page += npages;
706 csr->last_page = last_page;
707 csr->nr_pages = npages;
708
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300709 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300710 csr->grain = 8;
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300711 csr->csrow_idx = csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300712 csr->nr_channels = 1;
713
714 csr->channels[0].chan_idx = i;
715 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300716
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300717 pvt->csrow_map[i][j] = csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300718
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300719 switch (banks) {
720 case 4:
721 csr->dtype = DEV_X4;
722 break;
723 case 8:
724 csr->dtype = DEV_X8;
725 break;
726 case 16:
727 csr->dtype = DEV_X16;
728 break;
729 default:
730 csr->dtype = DEV_UNKNOWN;
731 }
732
733 csr->edac_mode = mode;
734 csr->mtype = mtype;
735
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300736 csrow++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300737 }
738
739 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
740 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
741 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
742 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
743 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
744 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
745 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
746 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300747 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300748 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300749 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300750 (value[j] >> 27) & 0x1,
751 (value[j] >> 24) & 0x7,
752 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300753 }
754
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300755 return 0;
756}
757
758/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300759 Error insertion routines
760 ****************************************************************************/
761
762/* The i7core has independent error injection features per channel.
763 However, to have a simpler code, we don't allow enabling error injection
764 on more than one channel.
765 Also, since a change at an inject parameter will be applied only at enable,
766 we're disabling error injection on all write calls to the sysfs nodes that
767 controls the error code injection.
768 */
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300769static int disable_inject(const struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300770{
771 struct i7core_pvt *pvt = mci->pvt_info;
772
773 pvt->inject.enable = 0;
774
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300775 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300776 return -ENODEV;
777
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300778 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300779 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300780
781 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300782}
783
784/*
785 * i7core inject inject.section
786 *
787 * accept and store error injection inject.section value
788 * bit 0 - refers to the lower 32-byte half cacheline
789 * bit 1 - refers to the upper 32-byte half cacheline
790 */
791static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
792 const char *data, size_t count)
793{
794 struct i7core_pvt *pvt = mci->pvt_info;
795 unsigned long value;
796 int rc;
797
798 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300799 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300800
801 rc = strict_strtoul(data, 10, &value);
802 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300803 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300804
805 pvt->inject.section = (u32) value;
806 return count;
807}
808
809static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
810 char *data)
811{
812 struct i7core_pvt *pvt = mci->pvt_info;
813 return sprintf(data, "0x%08x\n", pvt->inject.section);
814}
815
816/*
817 * i7core inject.type
818 *
819 * accept and store error injection inject.section value
820 * bit 0 - repeat enable - Enable error repetition
821 * bit 1 - inject ECC error
822 * bit 2 - inject parity error
823 */
824static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
825 const char *data, size_t count)
826{
827 struct i7core_pvt *pvt = mci->pvt_info;
828 unsigned long value;
829 int rc;
830
831 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300832 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300833
834 rc = strict_strtoul(data, 10, &value);
835 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300836 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300837
838 pvt->inject.type = (u32) value;
839 return count;
840}
841
842static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
843 char *data)
844{
845 struct i7core_pvt *pvt = mci->pvt_info;
846 return sprintf(data, "0x%08x\n", pvt->inject.type);
847}
848
849/*
850 * i7core_inject_inject.eccmask_store
851 *
852 * The type of error (UE/CE) will depend on the inject.eccmask value:
853 * Any bits set to a 1 will flip the corresponding ECC bit
854 * Correctable errors can be injected by flipping 1 bit or the bits within
855 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
856 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
857 * uncorrectable error to be injected.
858 */
859static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
860 const char *data, size_t count)
861{
862 struct i7core_pvt *pvt = mci->pvt_info;
863 unsigned long value;
864 int rc;
865
866 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300867 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300868
869 rc = strict_strtoul(data, 10, &value);
870 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300871 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300872
873 pvt->inject.eccmask = (u32) value;
874 return count;
875}
876
877static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
878 char *data)
879{
880 struct i7core_pvt *pvt = mci->pvt_info;
881 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
882}
883
884/*
885 * i7core_addrmatch
886 *
887 * The type of error (UE/CE) will depend on the inject.eccmask value:
888 * Any bits set to a 1 will flip the corresponding ECC bit
889 * Correctable errors can be injected by flipping 1 bit or the bits within
890 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
891 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
892 * uncorrectable error to be injected.
893 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300894
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300895#define DECLARE_ADDR_MATCH(param, limit) \
896static ssize_t i7core_inject_store_##param( \
897 struct mem_ctl_info *mci, \
898 const char *data, size_t count) \
899{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300900 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300901 long value; \
902 int rc; \
903 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300904 debugf1("%s()\n", __func__); \
905 pvt = mci->pvt_info; \
906 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300907 if (pvt->inject.enable) \
908 disable_inject(mci); \
909 \
Mauro Carvalho Chehab4f87fad2009-10-04 11:54:56 -0300910 if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300911 value = -1; \
912 else { \
913 rc = strict_strtoul(data, 10, &value); \
914 if ((rc < 0) || (value >= limit)) \
915 return -EIO; \
916 } \
917 \
918 pvt->inject.param = value; \
919 \
920 return count; \
921} \
922 \
923static ssize_t i7core_inject_show_##param( \
924 struct mem_ctl_info *mci, \
925 char *data) \
926{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300927 struct i7core_pvt *pvt; \
928 \
929 pvt = mci->pvt_info; \
930 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300931 if (pvt->inject.param < 0) \
932 return sprintf(data, "any\n"); \
933 else \
934 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300935}
936
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300937#define ATTR_ADDR_MATCH(param) \
938 { \
939 .attr = { \
940 .name = #param, \
941 .mode = (S_IRUGO | S_IWUSR) \
942 }, \
943 .show = i7core_inject_show_##param, \
944 .store = i7core_inject_store_##param, \
945 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300946
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300947DECLARE_ADDR_MATCH(channel, 3);
948DECLARE_ADDR_MATCH(dimm, 3);
949DECLARE_ADDR_MATCH(rank, 4);
950DECLARE_ADDR_MATCH(bank, 32);
951DECLARE_ADDR_MATCH(page, 0x10000);
952DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300953
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300954static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300955{
956 u32 read;
957 int count;
958
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300959 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
960 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
961 where, val);
962
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300963 for (count = 0; count < 10; count++) {
964 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300965 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300966 pci_write_config_dword(dev, where, val);
967 pci_read_config_dword(dev, where, &read);
968
969 if (read == val)
970 return 0;
971 }
972
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300973 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
974 "write=%08x. Read=%08x\n",
975 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
976 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300977
978 return -EINVAL;
979}
980
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300981/*
982 * This routine prepares the Memory Controller for error injection.
983 * The error will be injected when some process tries to write to the
984 * memory that matches the given criteria.
985 * The criteria can be set in terms of a mask where dimm, rank, bank, page
986 * and col can be specified.
987 * A -1 value for any of the mask items will make the MCU to ignore
988 * that matching criteria for error injection.
989 *
990 * It should be noticed that the error will only happen after a write operation
991 * on a memory that matches the condition. if REPEAT_EN is not enabled at
992 * inject mask, then it will produce just one error. Otherwise, it will repeat
993 * until the injectmask would be cleaned.
994 *
995 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
996 * is reliable enough to check if the MC is using the
997 * three channels. However, this is not clear at the datasheet.
998 */
999static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
1000 const char *data, size_t count)
1001{
1002 struct i7core_pvt *pvt = mci->pvt_info;
1003 u32 injectmask;
1004 u64 mask = 0;
1005 int rc;
1006 long enable;
1007
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001008 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001009 return 0;
1010
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001011 rc = strict_strtoul(data, 10, &enable);
1012 if ((rc < 0))
1013 return 0;
1014
1015 if (enable) {
1016 pvt->inject.enable = 1;
1017 } else {
1018 disable_inject(mci);
1019 return count;
1020 }
1021
1022 /* Sets pvt->inject.dimm mask */
1023 if (pvt->inject.dimm < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001024 mask |= 1LL << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001025 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001026 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -02001027 mask |= (pvt->inject.dimm & 0x3LL) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001028 else
Alan Cox486dd092009-11-08 01:34:27 -02001029 mask |= (pvt->inject.dimm & 0x1LL) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001030 }
1031
1032 /* Sets pvt->inject.rank mask */
1033 if (pvt->inject.rank < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001034 mask |= 1LL << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001035 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001036 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -02001037 mask |= (pvt->inject.rank & 0x1LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001038 else
Alan Cox486dd092009-11-08 01:34:27 -02001039 mask |= (pvt->inject.rank & 0x3LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001040 }
1041
1042 /* Sets pvt->inject.bank mask */
1043 if (pvt->inject.bank < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001044 mask |= 1LL << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001045 else
Alan Cox486dd092009-11-08 01:34:27 -02001046 mask |= (pvt->inject.bank & 0x15LL) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001047
1048 /* Sets pvt->inject.page mask */
1049 if (pvt->inject.page < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001050 mask |= 1LL << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001051 else
Alan Cox486dd092009-11-08 01:34:27 -02001052 mask |= (pvt->inject.page & 0xffff) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001053
1054 /* Sets pvt->inject.column mask */
1055 if (pvt->inject.col < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001056 mask |= 1LL << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001057 else
Alan Cox486dd092009-11-08 01:34:27 -02001058 mask |= (pvt->inject.col & 0x3fff);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001059
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001060 /*
1061 * bit 0: REPEAT_EN
1062 * bits 1-2: MASK_HALF_CACHELINE
1063 * bit 3: INJECT_ECC
1064 * bit 4: INJECT_ADDR_PARITY
1065 */
1066
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001067 injectmask = (pvt->inject.type & 1) |
1068 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001069 (pvt->inject.type & 0x6) << (3 - 1);
1070
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001071 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001072 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001073 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001074
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001075 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001076 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001077 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001078 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1079
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001080 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001081 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1082
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001083 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001084 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001085
1086 /*
1087 * This is something undocumented, based on my tests
1088 * Without writing 8 to this register, errors aren't injected. Not sure
1089 * why.
1090 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001091 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001092 MC_CFG_CONTROL, 8);
1093
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001094 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1095 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001096 mask, pvt->inject.eccmask, injectmask);
1097
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001098
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001099 return count;
1100}
1101
1102static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1103 char *data)
1104{
1105 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001106 u32 injectmask;
1107
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -03001108 if (!pvt->pci_ch[pvt->inject.channel][0])
1109 return 0;
1110
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001111 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001112 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001113
1114 debugf0("Inject error read: 0x%018x\n", injectmask);
1115
1116 if (injectmask & 0x0c)
1117 pvt->inject.enable = 1;
1118
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001119 return sprintf(data, "%d\n", pvt->inject.enable);
1120}
1121
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001122#define DECLARE_COUNTER(param) \
1123static ssize_t i7core_show_counter_##param( \
1124 struct mem_ctl_info *mci, \
1125 char *data) \
1126{ \
1127 struct i7core_pvt *pvt = mci->pvt_info; \
1128 \
1129 debugf1("%s() \n", __func__); \
1130 if (!pvt->ce_count_available || (pvt->is_registered)) \
1131 return sprintf(data, "data unavailable\n"); \
1132 return sprintf(data, "%lu\n", \
1133 pvt->udimm_ce_count[param]); \
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001134}
1135
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001136#define ATTR_COUNTER(param) \
1137 { \
1138 .attr = { \
1139 .name = __stringify(udimm##param), \
1140 .mode = (S_IRUGO | S_IWUSR) \
1141 }, \
1142 .show = i7core_show_counter_##param \
1143 }
1144
1145DECLARE_COUNTER(0);
1146DECLARE_COUNTER(1);
1147DECLARE_COUNTER(2);
1148
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001149/*
1150 * Sysfs struct
1151 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001152
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001153static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001154 ATTR_ADDR_MATCH(channel),
1155 ATTR_ADDR_MATCH(dimm),
1156 ATTR_ADDR_MATCH(rank),
1157 ATTR_ADDR_MATCH(bank),
1158 ATTR_ADDR_MATCH(page),
1159 ATTR_ADDR_MATCH(col),
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001160 { } /* End of list */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001161};
1162
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001163static const struct mcidev_sysfs_group i7core_inject_addrmatch = {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001164 .name = "inject_addrmatch",
1165 .mcidev_attr = i7core_addrmatch_attrs,
1166};
1167
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001168static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001169 ATTR_COUNTER(0),
1170 ATTR_COUNTER(1),
1171 ATTR_COUNTER(2),
Marcin Slusarz64aab722010-09-30 15:15:30 -07001172 { .attr = { .name = NULL } }
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001173};
1174
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001175static const struct mcidev_sysfs_group i7core_udimm_counters = {
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001176 .name = "all_channel_counts",
1177 .mcidev_attr = i7core_udimm_counters_attrs,
1178};
1179
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001180static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001181 {
1182 .attr = {
1183 .name = "inject_section",
1184 .mode = (S_IRUGO | S_IWUSR)
1185 },
1186 .show = i7core_inject_section_show,
1187 .store = i7core_inject_section_store,
1188 }, {
1189 .attr = {
1190 .name = "inject_type",
1191 .mode = (S_IRUGO | S_IWUSR)
1192 },
1193 .show = i7core_inject_type_show,
1194 .store = i7core_inject_type_store,
1195 }, {
1196 .attr = {
1197 .name = "inject_eccmask",
1198 .mode = (S_IRUGO | S_IWUSR)
1199 },
1200 .show = i7core_inject_eccmask_show,
1201 .store = i7core_inject_eccmask_store,
1202 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001203 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001204 }, {
1205 .attr = {
1206 .name = "inject_enable",
1207 .mode = (S_IRUGO | S_IWUSR)
1208 },
1209 .show = i7core_inject_enable_show,
1210 .store = i7core_inject_enable_store,
1211 },
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001212 { } /* End of list */
1213};
1214
1215static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = {
1216 {
1217 .attr = {
1218 .name = "inject_section",
1219 .mode = (S_IRUGO | S_IWUSR)
1220 },
1221 .show = i7core_inject_section_show,
1222 .store = i7core_inject_section_store,
1223 }, {
1224 .attr = {
1225 .name = "inject_type",
1226 .mode = (S_IRUGO | S_IWUSR)
1227 },
1228 .show = i7core_inject_type_show,
1229 .store = i7core_inject_type_store,
1230 }, {
1231 .attr = {
1232 .name = "inject_eccmask",
1233 .mode = (S_IRUGO | S_IWUSR)
1234 },
1235 .show = i7core_inject_eccmask_show,
1236 .store = i7core_inject_eccmask_store,
1237 }, {
1238 .grp = &i7core_inject_addrmatch,
1239 }, {
1240 .attr = {
1241 .name = "inject_enable",
1242 .mode = (S_IRUGO | S_IWUSR)
1243 },
1244 .show = i7core_inject_enable_show,
1245 .store = i7core_inject_enable_store,
1246 }, {
1247 .grp = &i7core_udimm_counters,
1248 },
1249 { } /* End of list */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001250};
1251
1252/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001253 Device initialization routines: put/get, init/exit
1254 ****************************************************************************/
1255
1256/*
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001257 * i7core_put_all_devices 'put' all the devices that we have
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001258 * reserved via 'get'
1259 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001260static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001261{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001262 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001263
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001264 debugf0(__FILE__ ": %s()\n", __func__);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001265 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001266 struct pci_dev *pdev = i7core_dev->pdev[i];
1267 if (!pdev)
1268 continue;
1269 debugf0("Removing dev %02x:%02x.%d\n",
1270 pdev->bus->number,
1271 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1272 pci_dev_put(pdev);
1273 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001274}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001275
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001276static void i7core_put_all_devices(void)
1277{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001278 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001279
Mauro Carvalho Chehab39300e72010-08-11 23:40:15 -03001280 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001281 i7core_put_devices(i7core_dev);
Hidetoshi Seto2aa9be42010-08-20 04:25:00 -03001282 free_i7core_dev(i7core_dev);
Mauro Carvalho Chehab39300e72010-08-11 23:40:15 -03001283 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001284}
1285
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001286static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001287{
1288 struct pci_dev *pdev = NULL;
1289 int i;
Mauro Carvalho Chehab54a08ab2010-08-19 15:51:00 -03001290
Keith Manntheybc2d7242009-09-03 00:05:05 -03001291 /*
1292 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1293 * aren't announced by acpi. So, we need to use a legacy scan probing
1294 * to detect them
1295 */
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001296 while (table && table->descr) {
1297 pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL);
1298 if (unlikely(!pdev)) {
1299 for (i = 0; i < MAX_SOCKET_BUSES; i++)
1300 pcibios_scan_specific_bus(255-i);
1301 }
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001302 pci_dev_put(pdev);
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001303 table++;
Keith Manntheybc2d7242009-09-03 00:05:05 -03001304 }
1305}
1306
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001307static unsigned i7core_pci_lastbus(void)
1308{
1309 int last_bus = 0, bus;
1310 struct pci_bus *b = NULL;
1311
1312 while ((b = pci_find_next_bus(b)) != NULL) {
1313 bus = b->number;
1314 debugf0("Found bus %d\n", bus);
1315 if (bus > last_bus)
1316 last_bus = bus;
1317 }
1318
1319 debugf0("Last bus %d\n", last_bus);
1320
1321 return last_bus;
1322}
1323
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001324/*
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001325 * i7core_get_all_devices Find and perform 'get' operation on the MCH's
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001326 * device/functions we want to reference for this driver
1327 *
1328 * Need to 'get' device 16 func 1 and func 2
1329 */
Hidetoshi Setob197cba2010-08-20 04:24:31 -03001330static int i7core_get_onedevice(struct pci_dev **prev,
1331 const struct pci_id_table *table,
1332 const unsigned devno,
1333 const unsigned last_bus)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001334{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001335 struct i7core_dev *i7core_dev;
Hidetoshi Setob197cba2010-08-20 04:24:31 -03001336 const struct pci_id_descr *dev_descr = &table->descr[devno];
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001337
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001338 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001339 u8 bus = 0;
1340 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001341
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001342 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001343 dev_descr->dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001344
1345 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001346 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1347 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1348 * to probe for the alternate address in case of failure
1349 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001350 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001351 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -03001352 PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001353
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001354 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -03001355 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1356 PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
1357 *prev);
1358
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001359 if (!pdev) {
1360 if (*prev) {
1361 *prev = pdev;
1362 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001363 }
1364
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001365 if (dev_descr->optional)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001366 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001367
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001368 if (devno == 0)
1369 return -ENODEV;
1370
Daniel J Bluemanab089372010-07-23 23:16:52 +01001371 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001372 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001373 dev_descr->dev, dev_descr->func,
1374 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001375
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001376 /* End of list, leave */
1377 return -ENODEV;
1378 }
1379 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001380
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001381 socket = last_bus - bus;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001382
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001383 i7core_dev = get_i7core_dev(socket);
1384 if (!i7core_dev) {
Hidetoshi Seto848b2f72010-08-20 04:24:44 -03001385 i7core_dev = alloc_i7core_dev(socket, table);
Hidetoshi Seto28966372010-08-20 04:28:51 -03001386 if (!i7core_dev) {
1387 pci_dev_put(pdev);
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001388 return -ENOMEM;
Hidetoshi Seto28966372010-08-20 04:28:51 -03001389 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001390 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001391
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001392 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001393 i7core_printk(KERN_ERR,
1394 "Duplicated device for "
1395 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001396 bus, dev_descr->dev, dev_descr->func,
1397 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001398 pci_dev_put(pdev);
1399 return -ENODEV;
1400 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001401
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001402 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001403
1404 /* Sanity check */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001405 if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1406 PCI_FUNC(pdev->devfn) != dev_descr->func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001407 i7core_printk(KERN_ERR,
1408 "Device PCI ID %04x:%04x "
1409 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001410 PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001411 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001412 bus, dev_descr->dev, dev_descr->func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001413 return -ENODEV;
1414 }
1415
1416 /* Be sure that the device is enabled */
1417 if (unlikely(pci_enable_device(pdev) < 0)) {
1418 i7core_printk(KERN_ERR,
1419 "Couldn't enable "
1420 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001421 bus, dev_descr->dev, dev_descr->func,
1422 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001423 return -ENODEV;
1424 }
1425
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001426 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001427 socket, bus, dev_descr->dev,
1428 dev_descr->func,
1429 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001430
1431 *prev = pdev;
1432
1433 return 0;
1434}
1435
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001436static int i7core_get_all_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001437{
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001438 int i, j, rc, last_bus;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001439 struct pci_dev *pdev = NULL;
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001440 const struct pci_id_table *table;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001441
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001442 last_bus = i7core_pci_lastbus();
1443
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001444 for (j = 0; j < ARRAY_SIZE(pci_dev_table); j++) {
1445 table = &pci_dev_table[j];
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001446 for (i = 0; i < table->n_devs; i++) {
1447 pdev = NULL;
1448 do {
Hidetoshi Setob197cba2010-08-20 04:24:31 -03001449 rc = i7core_get_onedevice(&pdev, table, i,
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001450 last_bus);
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001451 if (rc < 0) {
1452 if (i == 0) {
1453 i = table->n_devs;
1454 break;
1455 }
1456 i7core_put_all_devices();
1457 return -ENODEV;
1458 }
1459 } while (pdev);
1460 }
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001461 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001462
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001463 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001464}
1465
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001466static int mci_bind_devs(struct mem_ctl_info *mci,
1467 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001468{
1469 struct i7core_pvt *pvt = mci->pvt_info;
1470 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001471 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001472
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001473 pvt->is_registered = 0;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001474 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001475 pdev = i7core_dev->pdev[i];
1476 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001477 continue;
1478
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001479 func = PCI_FUNC(pdev->devfn);
1480 slot = PCI_SLOT(pdev->devfn);
1481 if (slot == 3) {
1482 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001483 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001484 pvt->pci_mcr[func] = pdev;
1485 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1486 if (unlikely(func > MAX_CHAN_FUNC))
1487 goto error;
1488 pvt->pci_ch[slot - 4][func] = pdev;
1489 } else if (!slot && !func)
1490 pvt->pci_noncore = pdev;
1491 else
1492 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001493
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001494 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1495 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1496 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001497
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001498 if (PCI_SLOT(pdev->devfn) == 3 &&
1499 PCI_FUNC(pdev->devfn) == 2)
1500 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001501 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001502
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001503 return 0;
1504
1505error:
1506 i7core_printk(KERN_ERR, "Device %d, function %d "
1507 "is out of the expected range\n",
1508 slot, func);
1509 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001510}
1511
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001512/****************************************************************************
1513 Error check routines
1514 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001515static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001516 const int chan,
1517 const int dimm,
1518 const int add)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001519{
1520 char *msg;
1521 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001522 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001523
1524 for (i = 0; i < add; i++) {
1525 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001526 "(Socket=%d channel=%d dimm=%d)",
1527 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001528
1529 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1530 kfree (msg);
1531 }
1532}
1533
1534static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001535 const int chan,
1536 const int new0,
1537 const int new1,
1538 const int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001539{
1540 struct i7core_pvt *pvt = mci->pvt_info;
1541 int add0 = 0, add1 = 0, add2 = 0;
1542 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001543 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001544 /* Updates CE counters */
1545
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001546 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1547 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1548 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001549
1550 if (add2 < 0)
1551 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001552 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001553
1554 if (add1 < 0)
1555 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001556 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001557
1558 if (add0 < 0)
1559 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001560 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001561 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001562 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001563
1564 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001565 pvt->rdimm_last_ce_count[chan][2] = new2;
1566 pvt->rdimm_last_ce_count[chan][1] = new1;
1567 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001568
1569 /*updated the edac core */
1570 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001571 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001572 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001573 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001574 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001575 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001576
1577}
1578
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001579static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001580{
1581 struct i7core_pvt *pvt = mci->pvt_info;
1582 u32 rcv[3][2];
1583 int i, new0, new1, new2;
1584
1585 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001586 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001587 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001588 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001589 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001590 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001591 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001592 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001593 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001594 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001595 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001596 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001597 &rcv[2][1]);
1598 for (i = 0 ; i < 3; i++) {
1599 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1600 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1601 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001602 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001603 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1604 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1605 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1606 } else {
1607 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1608 DIMM_BOT_COR_ERR(rcv[i][0]);
1609 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1610 DIMM_BOT_COR_ERR(rcv[i][1]);
1611 new2 = 0;
1612 }
1613
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001614 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001615 }
1616}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001617
1618/* This function is based on the device 3 function 4 registers as described on:
1619 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1620 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1621 * also available at:
1622 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1623 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001624static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001625{
1626 struct i7core_pvt *pvt = mci->pvt_info;
1627 u32 rcv1, rcv0;
1628 int new0, new1, new2;
1629
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001630 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001631 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001632 return;
1633 }
1634
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001635 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001636 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1637 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001638
1639 /* Store the new values */
1640 new2 = DIMM2_COR_ERR(rcv1);
1641 new1 = DIMM1_COR_ERR(rcv0);
1642 new0 = DIMM0_COR_ERR(rcv0);
1643
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001644 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001645 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001646 /* Updates CE counters */
1647 int add0, add1, add2;
1648
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001649 add2 = new2 - pvt->udimm_last_ce_count[2];
1650 add1 = new1 - pvt->udimm_last_ce_count[1];
1651 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001652
1653 if (add2 < 0)
1654 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001655 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001656
1657 if (add1 < 0)
1658 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001659 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001660
1661 if (add0 < 0)
1662 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001663 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001664
1665 if (add0 | add1 | add2)
1666 i7core_printk(KERN_ERR, "New Corrected error(s): "
1667 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1668 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001669 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001670 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001671
1672 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001673 pvt->udimm_last_ce_count[2] = new2;
1674 pvt->udimm_last_ce_count[1] = new1;
1675 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001676}
1677
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001678/*
1679 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1680 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001681 * Nehalem are defined as family 0x06, model 0x1a
1682 *
1683 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001684 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001685 * m->status MSR_IA32_MC8_STATUS
1686 * m->addr MSR_IA32_MC8_ADDR
1687 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001688 * In the case of Nehalem, the error information is masked at .status and .misc
1689 * fields
1690 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001691static void i7core_mce_output_error(struct mem_ctl_info *mci,
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001692 const struct mce *m)
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001693{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001694 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001695 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001696 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001697 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001698 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1699 u32 dimm = (m->misc >> 16) & 0x3;
1700 u32 channel = (m->misc >> 18) & 0x3;
1701 u32 syndrome = m->misc >> 32;
1702 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001703 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001704
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001705 if (m->mcgstatus & 1)
1706 type = "FATAL";
1707 else
1708 type = "NON_FATAL";
1709
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001710 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001711 case 0:
1712 optype = "generic undef request";
1713 break;
1714 case 1:
1715 optype = "read error";
1716 break;
1717 case 2:
1718 optype = "write error";
1719 break;
1720 case 3:
1721 optype = "addr/cmd error";
1722 break;
1723 case 4:
1724 optype = "scrubbing error";
1725 break;
1726 default:
1727 optype = "reserved";
1728 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001729 }
1730
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001731 switch (errnum) {
1732 case 16:
1733 err = "read ECC error";
1734 break;
1735 case 17:
1736 err = "RAS ECC error";
1737 break;
1738 case 18:
1739 err = "write parity error";
1740 break;
1741 case 19:
1742 err = "redundacy loss";
1743 break;
1744 case 20:
1745 err = "reserved";
1746 break;
1747 case 21:
1748 err = "memory range error";
1749 break;
1750 case 22:
1751 err = "RTID out of range";
1752 break;
1753 case 23:
1754 err = "address parity error";
1755 break;
1756 case 24:
1757 err = "byte enable parity error";
1758 break;
1759 default:
1760 err = "unknown";
1761 }
1762
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001763 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001764 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001765 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001766 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001767 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001768 syndrome, core_err_cnt, (long long)m->status,
1769 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001770
1771 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001772
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001773 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001774
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001775 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001776 if (m->mcgstatus & 1)
1777 edac_mc_handle_fbd_ue(mci, csrow, 0,
1778 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001779 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001780 edac_mc_handle_fbd_ce(mci, csrow,
1781 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001782
1783 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001784}
1785
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001786/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001787 * i7core_check_error Retrieve and process errors reported by the
1788 * hardware. Called by the Core module.
1789 */
1790static void i7core_check_error(struct mem_ctl_info *mci)
1791{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001792 struct i7core_pvt *pvt = mci->pvt_info;
1793 int i;
1794 unsigned count = 0;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001795 struct mce *m;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001796
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001797 /*
1798 * MCE first step: Copy all mce errors into a temporary buffer
1799 * We use a double buffering here, to reduce the risk of
1800 * loosing an error.
1801 */
1802 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001803 count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
1804 % MCE_LOG_LEN;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001805 if (!count)
Vernon Mauery8a311e12010-04-16 19:40:19 -03001806 goto check_ce_error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001807
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001808 m = pvt->mce_outentry;
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001809 if (pvt->mce_in + count > MCE_LOG_LEN) {
1810 unsigned l = MCE_LOG_LEN - pvt->mce_in;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001811
1812 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
1813 smp_wmb();
1814 pvt->mce_in = 0;
1815 count -= l;
1816 m += l;
1817 }
1818 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
1819 smp_wmb();
1820 pvt->mce_in += count;
1821
1822 smp_rmb();
1823 if (pvt->mce_overrun) {
1824 i7core_printk(KERN_ERR, "Lost %d memory errors\n",
1825 pvt->mce_overrun);
1826 smp_wmb();
1827 pvt->mce_overrun = 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001828 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001829
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001830 /*
1831 * MCE second step: parse errors and display
1832 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001833 for (i = 0; i < count; i++)
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001834 i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001835
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001836 /*
1837 * Now, let's increment CE error counts
1838 */
Vernon Mauery8a311e12010-04-16 19:40:19 -03001839check_ce_error:
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001840 if (!pvt->is_registered)
1841 i7core_udimm_check_mc_ecc_err(mci);
1842 else
1843 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001844}
1845
1846/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001847 * i7core_mce_check_error Replicates mcelog routine to get errors
1848 * This routine simply queues mcelog errors, and
1849 * return. The error itself should be handled later
1850 * by i7core_check_error.
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001851 * WARNING: As this routine should be called at NMI time, extra care should
1852 * be taken to avoid deadlocks, and to be as fast as possible.
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001853 */
1854static int i7core_mce_check_error(void *priv, struct mce *mce)
1855{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001856 struct mem_ctl_info *mci = priv;
1857 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001858
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001859 /*
1860 * Just let mcelog handle it if the error is
1861 * outside the memory controller
1862 */
1863 if (((mce->status & 0xffff) >> 7) != 1)
1864 return 0;
1865
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001866 /* Bank 8 registers are the only ones that we know how to handle */
1867 if (mce->bank != 8)
1868 return 0;
1869
Randy Dunlap3b918c12009-11-08 01:36:40 -02001870#ifdef CONFIG_SMP
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001871 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001872 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001873 return 0;
Randy Dunlap3b918c12009-11-08 01:36:40 -02001874#endif
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001875
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001876 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001877 if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001878 smp_wmb();
1879 pvt->mce_overrun++;
1880 return 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001881 }
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001882
1883 /* Copy memory error at the ringbuffer */
1884 memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001885 smp_wmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001886 pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001887
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001888 /* Handle fatal errors immediately */
1889 if (mce->mcgstatus & 1)
1890 i7core_check_error(mci);
1891
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001892 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001893 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001894}
1895
Hidetoshi Setoa3aa0a42010-08-20 04:25:18 -03001896static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
1897{
1898 pvt->i7core_pci = edac_pci_create_generic_ctl(
1899 &pvt->i7core_dev->pdev[0]->dev,
1900 EDAC_MOD_STR);
1901 if (unlikely(!pvt->i7core_pci))
1902 pr_warn("Unable to setup PCI error report via EDAC\n");
1903}
1904
1905static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
1906{
1907 if (likely(pvt->i7core_pci))
1908 edac_pci_release_generic_ctl(pvt->i7core_pci);
1909 else
1910 i7core_printk(KERN_ERR,
1911 "Couldn't find mem_ctl_info for socket %d\n",
1912 pvt->i7core_dev->socket);
1913 pvt->i7core_pci = NULL;
1914}
1915
Hidetoshi Seto1c6edbb2010-08-20 04:32:33 -03001916static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
1917{
1918 struct mem_ctl_info *mci = i7core_dev->mci;
1919 struct i7core_pvt *pvt;
1920
1921 if (unlikely(!mci || !mci->pvt_info)) {
1922 debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
1923 __func__, &i7core_dev->pdev[0]->dev);
1924
1925 i7core_printk(KERN_ERR, "Couldn't find mci handler\n");
1926 return;
1927 }
1928
1929 pvt = mci->pvt_info;
1930
1931 debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
1932 __func__, mci, &i7core_dev->pdev[0]->dev);
1933
1934 /* Disable MCE NMI handler */
1935 edac_mce_unregister(&pvt->edac_mce);
1936
1937 /* Disable EDAC polling */
1938 i7core_pci_ctl_release(pvt);
1939
1940 /* Remove MC sysfs nodes */
1941 edac_mc_del_mc(mci->dev);
1942
1943 debugf1("%s: free mci struct\n", mci->ctl_name);
1944 kfree(mci->ctl_name);
1945 edac_mc_free(mci);
1946 i7core_dev->mci = NULL;
1947}
1948
Hidetoshi Setoaace4282010-08-20 04:32:45 -03001949static int i7core_register_mci(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001950{
1951 struct mem_ctl_info *mci;
1952 struct i7core_pvt *pvt;
Hidetoshi Setoaace4282010-08-20 04:32:45 -03001953 int rc, channels, csrows;
1954
1955 /* Check the number of active and not disabled channels */
1956 rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows);
1957 if (unlikely(rc < 0))
1958 return rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001959
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001960 /* allocate a new MC control structure */
Hidetoshi Setoaace4282010-08-20 04:32:45 -03001961 mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001962 if (unlikely(!mci))
1963 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001964
Mauro Carvalho Chehab3cfd0142010-08-10 23:23:46 -03001965 debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
1966 __func__, mci, &i7core_dev->pdev[0]->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001967
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001968 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001969 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001970
Mauro Carvalho Chehab6d37d242010-08-20 12:48:26 -03001971 /* Associates i7core_dev and mci for future usage */
1972 pvt->i7core_dev = i7core_dev;
1973 i7core_dev->mci = mci;
1974
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001975 /*
1976 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1977 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1978 * memory channels
1979 */
1980 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001981 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1982 mci->edac_cap = EDAC_FLAG_NONE;
1983 mci->mod_name = "i7core_edac.c";
1984 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001985 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1986 i7core_dev->socket);
1987 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001988 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001989
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001990 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001991 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001992 if (unlikely(rc < 0))
Hidetoshi Seto628c5dd2010-08-20 04:28:40 -03001993 goto fail0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001994
Hidetoshi Seto59398132010-08-20 04:28:25 -03001995 if (pvt->is_registered)
1996 mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs;
1997 else
1998 mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs;
1999
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03002000 /* Get dimm basic config */
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -03002001 get_dimm_config(mci);
Hidetoshi Seto59398132010-08-20 04:28:25 -03002002 /* record ptr to the generic device */
2003 mci->dev = &i7core_dev->pdev[0]->dev;
2004 /* Set the function pointer to an actual operation function */
2005 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03002006
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002007 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03002008 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002009 debugf0("MC: " __FILE__
2010 ": %s(): failed edac_mc_add_mc()\n", __func__);
2011 /* FIXME: perhaps some code should go here that disables error
2012 * reporting if we just enabled it
2013 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03002014
2015 rc = -EINVAL;
Hidetoshi Seto628c5dd2010-08-20 04:28:40 -03002016 goto fail0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002017 }
2018
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03002019 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03002020 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03002021 pvt->inject.dimm = -1;
2022 pvt->inject.rank = -1;
2023 pvt->inject.bank = -1;
2024 pvt->inject.page = -1;
2025 pvt->inject.col = -1;
2026
Hidetoshi Setoa3aa0a42010-08-20 04:25:18 -03002027 /* allocating generic PCI control info */
2028 i7core_pci_ctl_create(pvt);
2029
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03002030 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03002031 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03002032 pvt->edac_mce.check_error = i7core_mce_check_error;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03002033 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03002034 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03002035 debugf0("MC: " __FILE__
2036 ": %s(): failed edac_mce_register()\n", __func__);
Hidetoshi Seto628c5dd2010-08-20 04:28:40 -03002037 goto fail1;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002038 }
2039
Hidetoshi Seto628c5dd2010-08-20 04:28:40 -03002040 return 0;
2041
2042fail1:
2043 i7core_pci_ctl_release(pvt);
2044 edac_mc_del_mc(mci->dev);
2045fail0:
2046 kfree(mci->ctl_name);
2047 edac_mc_free(mci);
Hidetoshi Seto1c6edbb2010-08-20 04:32:33 -03002048 i7core_dev->mci = NULL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002049 return rc;
2050}
2051
2052/*
2053 * i7core_probe Probe for ONE instance of device to see if it is
2054 * present.
2055 * return:
2056 * 0 for FOUND a device
2057 * < 0 for error code
2058 */
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002059
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002060static int __devinit i7core_probe(struct pci_dev *pdev,
2061 const struct pci_device_id *id)
2062{
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002063 int rc;
2064 struct i7core_dev *i7core_dev;
2065
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002066 /* get the pci devices we want to reserve for our use */
2067 mutex_lock(&i7core_edac_lock);
2068
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002069 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03002070 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002071 */
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002072 if (unlikely(probed >= 1)) {
2073 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002074 return -EINVAL;
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002075 }
2076 probed++;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03002077
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03002078 rc = i7core_get_all_devices();
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002079 if (unlikely(rc < 0))
2080 goto fail0;
2081
2082 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
Hidetoshi Setoaace4282010-08-20 04:32:45 -03002083 rc = i7core_register_mci(i7core_dev);
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03002084 if (unlikely(rc < 0))
2085 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03002086 }
2087
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03002088 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03002089
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03002090 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002091 return 0;
2092
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03002093fail1:
Hidetoshi Seto1c6edbb2010-08-20 04:32:33 -03002094 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
2095 if (i7core_dev->mci)
2096 i7core_unregister_mci(i7core_dev);
2097 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03002098 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03002099fail0:
2100 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03002101 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002102}
2103
2104/*
2105 * i7core_remove destructor for one instance of device
2106 *
2107 */
2108static void __devexit i7core_remove(struct pci_dev *pdev)
2109{
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03002110 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002111
2112 debugf0(__FILE__ ": %s()\n", __func__);
2113
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002114 /*
2115 * we have a trouble here: pdev value for removal will be wrong, since
2116 * it will point to the X58 register used to detect that the machine
2117 * is a Nehalem or upper design. However, due to the way several PCI
2118 * devices are grouped together to provide MC functionality, we need
2119 * to use a different method for releasing the devices
2120 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03002121
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03002122 mutex_lock(&i7core_edac_lock);
Hidetoshi Seto71fe0172010-08-20 04:29:47 -03002123
2124 if (unlikely(!probed)) {
2125 mutex_unlock(&i7core_edac_lock);
2126 return;
2127 }
2128
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03002129 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
Hidetoshi Seto1c6edbb2010-08-20 04:32:33 -03002130 if (i7core_dev->mci)
2131 i7core_unregister_mci(i7core_dev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002132 }
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03002133
2134 /* Release PCI resources */
2135 i7core_put_all_devices();
2136
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002137 probed--;
2138
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002139 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002140}
2141
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002142MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
2143
2144/*
2145 * i7core_driver pci_driver structure for this module
2146 *
2147 */
2148static struct pci_driver i7core_driver = {
2149 .name = "i7core_edac",
2150 .probe = i7core_probe,
2151 .remove = __devexit_p(i7core_remove),
2152 .id_table = i7core_pci_tbl,
2153};
2154
2155/*
2156 * i7core_init Module entry function
2157 * Try to initialize this module for its devices
2158 */
2159static int __init i7core_init(void)
2160{
2161 int pci_rc;
2162
2163 debugf2("MC: " __FILE__ ": %s()\n", __func__);
2164
2165 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
2166 opstate_init();
2167
Mauro Carvalho Chehab54a08ab2010-08-19 15:51:00 -03002168 if (use_pci_fixup)
2169 i7core_xeon_pci_fixup(pci_dev_table);
Keith Manntheybc2d7242009-09-03 00:05:05 -03002170
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002171 pci_rc = pci_register_driver(&i7core_driver);
2172
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03002173 if (pci_rc >= 0)
2174 return 0;
2175
2176 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
2177 pci_rc);
2178
2179 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002180}
2181
2182/*
2183 * i7core_exit() Module exit function
2184 * Unregister the driver
2185 */
2186static void __exit i7core_exit(void)
2187{
2188 debugf2("MC: " __FILE__ ": %s()\n", __func__);
2189 pci_unregister_driver(&i7core_driver);
2190}
2191
2192module_init(i7core_init);
2193module_exit(i7core_exit);
2194
2195MODULE_LICENSE("GPL");
2196MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
2197MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
2198MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
2199 I7CORE_REVISION);
2200
2201module_param(edac_op_state, int, 0444);
2202MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");