blob: 9868796f48717506693b899393bb5f7a60168d1d [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 Chehab79daef22010-08-21 01:03:52 -0300284
285 /* Exists only for RDIMM */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300286 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300287 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
288
289 /* Channel 0 */
290 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
291 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
292 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
293 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
294
295 /* Channel 1 */
296 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
297 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
298 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
299 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
300
301 /* Channel 2 */
302 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
303 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
304 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
305 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300306};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300307
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300308static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300309 { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) },
310 { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) },
311 { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) },
312
313 { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) },
314 { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) },
315 { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) },
316 { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) },
317
Mauro Carvalho Chehab508fa172009-10-14 13:44:37 -0300318 { PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) },
319 { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
320 { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
321 { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300322};
323
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300324static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300325 /* Memory controller */
326 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) },
327 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) },
328 /* Exists only for RDIMM */
329 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1 },
330 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) },
331
332 /* Channel 0 */
333 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) },
334 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) },
335 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) },
336 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2) },
337
338 /* Channel 1 */
339 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) },
340 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) },
341 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) },
342 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2) },
343
344 /* Channel 2 */
345 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) },
346 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
347 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
348 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) },
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300349};
350
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300351#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
352static const struct pci_id_table pci_dev_table[] = {
Vernon Mauerybd9e19c2010-05-18 19:02:50 -0300353 PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
354 PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
355 PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
356};
357
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300358/*
359 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300360 */
361static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300362 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -0300363 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300364 {0,} /* 0 terminated list. */
365};
366
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300367/****************************************************************************
368 Anciliary status routines
369 ****************************************************************************/
370
371 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300372#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
373#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300374
375 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300376#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300377#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300378
379 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300380static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300381{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300382 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300383}
384
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300385static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300386{
387 static int ranks[4] = { 1, 2, 4, -EINVAL };
388
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300389 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300390}
391
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300392static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300393{
394 static int banks[4] = { 4, 8, 16, -EINVAL };
395
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300396 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300397}
398
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300399static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300400{
401 static int rows[8] = {
402 1 << 12, 1 << 13, 1 << 14, 1 << 15,
403 1 << 16, -EINVAL, -EINVAL, -EINVAL,
404 };
405
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300406 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300407}
408
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300409static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300410{
411 static int cols[8] = {
412 1 << 10, 1 << 11, 1 << 12, -EINVAL,
413 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300414 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300415}
416
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300417static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300418{
419 struct i7core_dev *i7core_dev;
420
421 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
422 if (i7core_dev->socket == socket)
423 return i7core_dev;
424 }
425
426 return NULL;
427}
428
Hidetoshi Seto848b2f72010-08-20 04:24:44 -0300429static struct i7core_dev *alloc_i7core_dev(u8 socket,
430 const struct pci_id_table *table)
431{
432 struct i7core_dev *i7core_dev;
433
434 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
435 if (!i7core_dev)
436 return NULL;
437
438 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs,
439 GFP_KERNEL);
440 if (!i7core_dev->pdev) {
441 kfree(i7core_dev);
442 return NULL;
443 }
444
445 i7core_dev->socket = socket;
446 i7core_dev->n_devs = table->n_devs;
447 list_add_tail(&i7core_dev->list, &i7core_edac_list);
448
449 return i7core_dev;
450}
451
Hidetoshi Seto2aa9be42010-08-20 04:25:00 -0300452static void free_i7core_dev(struct i7core_dev *i7core_dev)
453{
454 list_del(&i7core_dev->list);
455 kfree(i7core_dev->pdev);
456 kfree(i7core_dev);
457}
458
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300459/****************************************************************************
460 Memory check routines
461 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300462static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
463 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300464{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300465 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300466 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300467
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300468 if (!i7core_dev)
469 return NULL;
470
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300471 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300472 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300473 continue;
474
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300475 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
476 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
477 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300478 }
479 }
480
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300481 return NULL;
482}
483
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300484/**
485 * i7core_get_active_channels() - gets the number of channels and csrows
486 * @socket: Quick Path Interconnect socket
487 * @channels: Number of channels that will be returned
488 * @csrows: Number of csrows found
489 *
490 * Since EDAC core needs to know in advance the number of available channels
491 * and csrows, in order to allocate memory for csrows/channels, it is needed
492 * to run two similar steps. At the first step, implemented on this function,
493 * it checks the number of csrows/channels present at one socket.
494 * this is used in order to properly allocate the size of mci components.
495 *
496 * It should be noticed that none of the current available datasheets explain
497 * or even mention how csrows are seen by the memory controller. So, we need
498 * to add a fake description for csrows.
499 * So, this driver is attributing one DIMM memory for one csrow.
500 */
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300501static int i7core_get_active_channels(const u8 socket, unsigned *channels,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300502 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300503{
504 struct pci_dev *pdev = NULL;
505 int i, j;
506 u32 status, control;
507
508 *channels = 0;
509 *csrows = 0;
510
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300511 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300512 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300513 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
514 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300515 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300516 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300517
518 /* Device 3 function 0 reads */
519 pci_read_config_dword(pdev, MC_STATUS, &status);
520 pci_read_config_dword(pdev, MC_CONTROL, &control);
521
522 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300523 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300524 /* Check if the channel is active */
525 if (!(control & (1 << (8 + i))))
526 continue;
527
528 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300529 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300530 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300531
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300532 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300533 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300534 i7core_printk(KERN_ERR, "Couldn't find socket %d "
535 "fn %d.%d!!!\n",
536 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300537 return -ENODEV;
538 }
539 /* Devices 4-6 function 1 */
540 pci_read_config_dword(pdev,
541 MC_DOD_CH_DIMM0, &dimm_dod[0]);
542 pci_read_config_dword(pdev,
543 MC_DOD_CH_DIMM1, &dimm_dod[1]);
544 pci_read_config_dword(pdev,
545 MC_DOD_CH_DIMM2, &dimm_dod[2]);
546
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300547 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300548
549 for (j = 0; j < 3; j++) {
550 if (!DIMM_PRESENT(dimm_dod[j]))
551 continue;
552 (*csrows)++;
553 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300554 }
555
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300556 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300557 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300558
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300559 return 0;
560}
561
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300562static int get_dimm_config(const struct mem_ctl_info *mci)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300563{
564 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300565 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300566 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300567 int i, j;
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300568 int csrow = 0;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300569 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300570 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300571 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300572
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300573 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300574 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300575 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300576 return -ENODEV;
577
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300578 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300579 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
580 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
581 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
582 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300583
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300584 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300585 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300586 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300587
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300588 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300589 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300590 if (ECCx8(pvt))
591 mode = EDAC_S8ECD8ED;
592 else
593 mode = EDAC_S4ECD4ED;
594 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300595 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300596 mode = EDAC_NONE;
597 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300598
599 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300600 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
601 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300602 numdimms(pvt->info.max_dod),
603 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300604 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300605 numrow(pvt->info.max_dod >> 6),
606 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300607
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300608 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300609 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300610
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300611 if (!pvt->pci_ch[i][0])
612 continue;
613
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300614 if (!CH_ACTIVE(pvt, i)) {
615 debugf0("Channel %i is not active\n", i);
616 continue;
617 }
618 if (CH_DISABLED(pvt, i)) {
619 debugf0("Channel %i is disabled\n", i);
620 continue;
621 }
622
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300623 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300624 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300625 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
626
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300627 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300628 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300629
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300630 if (data & REGISTERED_DIMM)
631 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300632 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300633 mtype = MEM_DDR3;
634#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300635 if (data & THREE_DIMMS_PRESENT)
636 pvt->channel[i].dimms = 3;
637 else if (data & SINGLE_QUAD_RANK_PRESENT)
638 pvt->channel[i].dimms = 1;
639 else
640 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300641#endif
642
643 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300644 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300645 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300646 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300647 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300648 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300649 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300650
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300651 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300652 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300653 i,
654 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
655 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300656 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300657 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300658
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300659 for (j = 0; j < 3; j++) {
660 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300661 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300662
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300663 if (!DIMM_PRESENT(dimm_dod[j]))
664 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300665
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300666 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
667 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
668 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
669 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300670
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300671 /* DDR3 has 8 I/O banks */
672 size = (rows * cols * banks * ranks) >> (20 - 3);
673
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300674 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300675
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300676 debugf0("\tdimm %d %d Mb offset: %x, "
677 "bank: %d, rank: %d, row: %#x, col: %#x\n",
678 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300679 RANKOFFSET(dimm_dod[j]),
680 banks, ranks, rows, cols);
681
Mauro Carvalho Chehabe9144602010-08-10 20:26:35 -0300682 npages = MiB_TO_PAGES(size);
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300683
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300684 csr = &mci->csrows[csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300685 csr->first_page = last_page + 1;
686 last_page += npages;
687 csr->last_page = last_page;
688 csr->nr_pages = npages;
689
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300690 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300691 csr->grain = 8;
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300692 csr->csrow_idx = csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300693 csr->nr_channels = 1;
694
695 csr->channels[0].chan_idx = i;
696 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300697
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300698 pvt->csrow_map[i][j] = csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300699
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300700 switch (banks) {
701 case 4:
702 csr->dtype = DEV_X4;
703 break;
704 case 8:
705 csr->dtype = DEV_X8;
706 break;
707 case 16:
708 csr->dtype = DEV_X16;
709 break;
710 default:
711 csr->dtype = DEV_UNKNOWN;
712 }
713
714 csr->edac_mode = mode;
715 csr->mtype = mtype;
716
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -0300717 csrow++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300718 }
719
720 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
721 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
722 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
723 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
724 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
725 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
726 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
727 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300728 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300729 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300730 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300731 (value[j] >> 27) & 0x1,
732 (value[j] >> 24) & 0x7,
733 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300734 }
735
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300736 return 0;
737}
738
739/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300740 Error insertion routines
741 ****************************************************************************/
742
743/* The i7core has independent error injection features per channel.
744 However, to have a simpler code, we don't allow enabling error injection
745 on more than one channel.
746 Also, since a change at an inject parameter will be applied only at enable,
747 we're disabling error injection on all write calls to the sysfs nodes that
748 controls the error code injection.
749 */
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300750static int disable_inject(const struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300751{
752 struct i7core_pvt *pvt = mci->pvt_info;
753
754 pvt->inject.enable = 0;
755
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300756 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300757 return -ENODEV;
758
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300759 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300760 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300761
762 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300763}
764
765/*
766 * i7core inject inject.section
767 *
768 * accept and store error injection inject.section value
769 * bit 0 - refers to the lower 32-byte half cacheline
770 * bit 1 - refers to the upper 32-byte half cacheline
771 */
772static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
773 const char *data, size_t count)
774{
775 struct i7core_pvt *pvt = mci->pvt_info;
776 unsigned long value;
777 int rc;
778
779 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300780 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300781
782 rc = strict_strtoul(data, 10, &value);
783 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300784 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300785
786 pvt->inject.section = (u32) value;
787 return count;
788}
789
790static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
791 char *data)
792{
793 struct i7core_pvt *pvt = mci->pvt_info;
794 return sprintf(data, "0x%08x\n", pvt->inject.section);
795}
796
797/*
798 * i7core inject.type
799 *
800 * accept and store error injection inject.section value
801 * bit 0 - repeat enable - Enable error repetition
802 * bit 1 - inject ECC error
803 * bit 2 - inject parity error
804 */
805static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
806 const char *data, size_t count)
807{
808 struct i7core_pvt *pvt = mci->pvt_info;
809 unsigned long value;
810 int rc;
811
812 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300813 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300814
815 rc = strict_strtoul(data, 10, &value);
816 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300817 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300818
819 pvt->inject.type = (u32) value;
820 return count;
821}
822
823static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
824 char *data)
825{
826 struct i7core_pvt *pvt = mci->pvt_info;
827 return sprintf(data, "0x%08x\n", pvt->inject.type);
828}
829
830/*
831 * i7core_inject_inject.eccmask_store
832 *
833 * The type of error (UE/CE) will depend on the inject.eccmask value:
834 * Any bits set to a 1 will flip the corresponding ECC bit
835 * Correctable errors can be injected by flipping 1 bit or the bits within
836 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
837 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
838 * uncorrectable error to be injected.
839 */
840static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
841 const char *data, size_t count)
842{
843 struct i7core_pvt *pvt = mci->pvt_info;
844 unsigned long value;
845 int rc;
846
847 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300848 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300849
850 rc = strict_strtoul(data, 10, &value);
851 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300852 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300853
854 pvt->inject.eccmask = (u32) value;
855 return count;
856}
857
858static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
859 char *data)
860{
861 struct i7core_pvt *pvt = mci->pvt_info;
862 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
863}
864
865/*
866 * i7core_addrmatch
867 *
868 * The type of error (UE/CE) will depend on the inject.eccmask value:
869 * Any bits set to a 1 will flip the corresponding ECC bit
870 * Correctable errors can be injected by flipping 1 bit or the bits within
871 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
872 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
873 * uncorrectable error to be injected.
874 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300875
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300876#define DECLARE_ADDR_MATCH(param, limit) \
877static ssize_t i7core_inject_store_##param( \
878 struct mem_ctl_info *mci, \
879 const char *data, size_t count) \
880{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300881 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300882 long value; \
883 int rc; \
884 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300885 debugf1("%s()\n", __func__); \
886 pvt = mci->pvt_info; \
887 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300888 if (pvt->inject.enable) \
889 disable_inject(mci); \
890 \
Mauro Carvalho Chehab4f87fad2009-10-04 11:54:56 -0300891 if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300892 value = -1; \
893 else { \
894 rc = strict_strtoul(data, 10, &value); \
895 if ((rc < 0) || (value >= limit)) \
896 return -EIO; \
897 } \
898 \
899 pvt->inject.param = value; \
900 \
901 return count; \
902} \
903 \
904static ssize_t i7core_inject_show_##param( \
905 struct mem_ctl_info *mci, \
906 char *data) \
907{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300908 struct i7core_pvt *pvt; \
909 \
910 pvt = mci->pvt_info; \
911 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300912 if (pvt->inject.param < 0) \
913 return sprintf(data, "any\n"); \
914 else \
915 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300916}
917
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300918#define ATTR_ADDR_MATCH(param) \
919 { \
920 .attr = { \
921 .name = #param, \
922 .mode = (S_IRUGO | S_IWUSR) \
923 }, \
924 .show = i7core_inject_show_##param, \
925 .store = i7core_inject_store_##param, \
926 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300927
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300928DECLARE_ADDR_MATCH(channel, 3);
929DECLARE_ADDR_MATCH(dimm, 3);
930DECLARE_ADDR_MATCH(rank, 4);
931DECLARE_ADDR_MATCH(bank, 32);
932DECLARE_ADDR_MATCH(page, 0x10000);
933DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300934
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -0300935static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300936{
937 u32 read;
938 int count;
939
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300940 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
941 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
942 where, val);
943
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300944 for (count = 0; count < 10; count++) {
945 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300946 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300947 pci_write_config_dword(dev, where, val);
948 pci_read_config_dword(dev, where, &read);
949
950 if (read == val)
951 return 0;
952 }
953
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300954 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
955 "write=%08x. Read=%08x\n",
956 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
957 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300958
959 return -EINVAL;
960}
961
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300962/*
963 * This routine prepares the Memory Controller for error injection.
964 * The error will be injected when some process tries to write to the
965 * memory that matches the given criteria.
966 * The criteria can be set in terms of a mask where dimm, rank, bank, page
967 * and col can be specified.
968 * A -1 value for any of the mask items will make the MCU to ignore
969 * that matching criteria for error injection.
970 *
971 * It should be noticed that the error will only happen after a write operation
972 * on a memory that matches the condition. if REPEAT_EN is not enabled at
973 * inject mask, then it will produce just one error. Otherwise, it will repeat
974 * until the injectmask would be cleaned.
975 *
976 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
977 * is reliable enough to check if the MC is using the
978 * three channels. However, this is not clear at the datasheet.
979 */
980static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
981 const char *data, size_t count)
982{
983 struct i7core_pvt *pvt = mci->pvt_info;
984 u32 injectmask;
985 u64 mask = 0;
986 int rc;
987 long enable;
988
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300989 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300990 return 0;
991
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300992 rc = strict_strtoul(data, 10, &enable);
993 if ((rc < 0))
994 return 0;
995
996 if (enable) {
997 pvt->inject.enable = 1;
998 } else {
999 disable_inject(mci);
1000 return count;
1001 }
1002
1003 /* Sets pvt->inject.dimm mask */
1004 if (pvt->inject.dimm < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001005 mask |= 1LL << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001006 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001007 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -02001008 mask |= (pvt->inject.dimm & 0x3LL) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001009 else
Alan Cox486dd092009-11-08 01:34:27 -02001010 mask |= (pvt->inject.dimm & 0x1LL) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001011 }
1012
1013 /* Sets pvt->inject.rank mask */
1014 if (pvt->inject.rank < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001015 mask |= 1LL << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001016 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001017 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -02001018 mask |= (pvt->inject.rank & 0x1LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001019 else
Alan Cox486dd092009-11-08 01:34:27 -02001020 mask |= (pvt->inject.rank & 0x3LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001021 }
1022
1023 /* Sets pvt->inject.bank mask */
1024 if (pvt->inject.bank < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001025 mask |= 1LL << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001026 else
Alan Cox486dd092009-11-08 01:34:27 -02001027 mask |= (pvt->inject.bank & 0x15LL) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001028
1029 /* Sets pvt->inject.page mask */
1030 if (pvt->inject.page < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001031 mask |= 1LL << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001032 else
Alan Cox486dd092009-11-08 01:34:27 -02001033 mask |= (pvt->inject.page & 0xffff) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001034
1035 /* Sets pvt->inject.column mask */
1036 if (pvt->inject.col < 0)
Alan Cox486dd092009-11-08 01:34:27 -02001037 mask |= 1LL << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001038 else
Alan Cox486dd092009-11-08 01:34:27 -02001039 mask |= (pvt->inject.col & 0x3fff);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001040
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001041 /*
1042 * bit 0: REPEAT_EN
1043 * bits 1-2: MASK_HALF_CACHELINE
1044 * bit 3: INJECT_ECC
1045 * bit 4: INJECT_ADDR_PARITY
1046 */
1047
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001048 injectmask = (pvt->inject.type & 1) |
1049 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001050 (pvt->inject.type & 0x6) << (3 - 1);
1051
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001052 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001053 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001054 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001055
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001056 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001057 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001058 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001059 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1060
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001061 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001062 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1063
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001064 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001065 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001066
1067 /*
1068 * This is something undocumented, based on my tests
1069 * Without writing 8 to this register, errors aren't injected. Not sure
1070 * why.
1071 */
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, 8);
1074
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001075 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1076 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001077 mask, pvt->inject.eccmask, injectmask);
1078
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001079
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001080 return count;
1081}
1082
1083static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1084 char *data)
1085{
1086 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001087 u32 injectmask;
1088
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -03001089 if (!pvt->pci_ch[pvt->inject.channel][0])
1090 return 0;
1091
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001092 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001093 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001094
1095 debugf0("Inject error read: 0x%018x\n", injectmask);
1096
1097 if (injectmask & 0x0c)
1098 pvt->inject.enable = 1;
1099
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001100 return sprintf(data, "%d\n", pvt->inject.enable);
1101}
1102
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001103#define DECLARE_COUNTER(param) \
1104static ssize_t i7core_show_counter_##param( \
1105 struct mem_ctl_info *mci, \
1106 char *data) \
1107{ \
1108 struct i7core_pvt *pvt = mci->pvt_info; \
1109 \
1110 debugf1("%s() \n", __func__); \
1111 if (!pvt->ce_count_available || (pvt->is_registered)) \
1112 return sprintf(data, "data unavailable\n"); \
1113 return sprintf(data, "%lu\n", \
1114 pvt->udimm_ce_count[param]); \
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001115}
1116
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001117#define ATTR_COUNTER(param) \
1118 { \
1119 .attr = { \
1120 .name = __stringify(udimm##param), \
1121 .mode = (S_IRUGO | S_IWUSR) \
1122 }, \
1123 .show = i7core_show_counter_##param \
1124 }
1125
1126DECLARE_COUNTER(0);
1127DECLARE_COUNTER(1);
1128DECLARE_COUNTER(2);
1129
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001130/*
1131 * Sysfs struct
1132 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001133
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001134static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001135 ATTR_ADDR_MATCH(channel),
1136 ATTR_ADDR_MATCH(dimm),
1137 ATTR_ADDR_MATCH(rank),
1138 ATTR_ADDR_MATCH(bank),
1139 ATTR_ADDR_MATCH(page),
1140 ATTR_ADDR_MATCH(col),
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001141 { } /* End of list */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001142};
1143
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001144static const struct mcidev_sysfs_group i7core_inject_addrmatch = {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001145 .name = "inject_addrmatch",
1146 .mcidev_attr = i7core_addrmatch_attrs,
1147};
1148
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001149static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001150 ATTR_COUNTER(0),
1151 ATTR_COUNTER(1),
1152 ATTR_COUNTER(2),
Marcin Slusarz64aab722010-09-30 15:15:30 -07001153 { .attr = { .name = NULL } }
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001154};
1155
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001156static const struct mcidev_sysfs_group i7core_udimm_counters = {
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001157 .name = "all_channel_counts",
1158 .mcidev_attr = i7core_udimm_counters_attrs,
1159};
1160
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001161static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001162 {
1163 .attr = {
1164 .name = "inject_section",
1165 .mode = (S_IRUGO | S_IWUSR)
1166 },
1167 .show = i7core_inject_section_show,
1168 .store = i7core_inject_section_store,
1169 }, {
1170 .attr = {
1171 .name = "inject_type",
1172 .mode = (S_IRUGO | S_IWUSR)
1173 },
1174 .show = i7core_inject_type_show,
1175 .store = i7core_inject_type_store,
1176 }, {
1177 .attr = {
1178 .name = "inject_eccmask",
1179 .mode = (S_IRUGO | S_IWUSR)
1180 },
1181 .show = i7core_inject_eccmask_show,
1182 .store = i7core_inject_eccmask_store,
1183 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001184 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001185 }, {
1186 .attr = {
1187 .name = "inject_enable",
1188 .mode = (S_IRUGO | S_IWUSR)
1189 },
1190 .show = i7core_inject_enable_show,
1191 .store = i7core_inject_enable_store,
1192 },
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001193 { } /* End of list */
1194};
1195
1196static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = {
1197 {
1198 .attr = {
1199 .name = "inject_section",
1200 .mode = (S_IRUGO | S_IWUSR)
1201 },
1202 .show = i7core_inject_section_show,
1203 .store = i7core_inject_section_store,
1204 }, {
1205 .attr = {
1206 .name = "inject_type",
1207 .mode = (S_IRUGO | S_IWUSR)
1208 },
1209 .show = i7core_inject_type_show,
1210 .store = i7core_inject_type_store,
1211 }, {
1212 .attr = {
1213 .name = "inject_eccmask",
1214 .mode = (S_IRUGO | S_IWUSR)
1215 },
1216 .show = i7core_inject_eccmask_show,
1217 .store = i7core_inject_eccmask_store,
1218 }, {
1219 .grp = &i7core_inject_addrmatch,
1220 }, {
1221 .attr = {
1222 .name = "inject_enable",
1223 .mode = (S_IRUGO | S_IWUSR)
1224 },
1225 .show = i7core_inject_enable_show,
1226 .store = i7core_inject_enable_store,
1227 }, {
1228 .grp = &i7core_udimm_counters,
1229 },
1230 { } /* End of list */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001231};
1232
1233/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001234 Device initialization routines: put/get, init/exit
1235 ****************************************************************************/
1236
1237/*
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001238 * i7core_put_all_devices 'put' all the devices that we have
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001239 * reserved via 'get'
1240 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001241static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001242{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001243 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001244
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001245 debugf0(__FILE__ ": %s()\n", __func__);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001246 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001247 struct pci_dev *pdev = i7core_dev->pdev[i];
1248 if (!pdev)
1249 continue;
1250 debugf0("Removing dev %02x:%02x.%d\n",
1251 pdev->bus->number,
1252 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1253 pci_dev_put(pdev);
1254 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001255}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001256
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001257static void i7core_put_all_devices(void)
1258{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001259 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001260
Mauro Carvalho Chehab39300e72010-08-11 23:40:15 -03001261 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001262 i7core_put_devices(i7core_dev);
Hidetoshi Seto2aa9be42010-08-20 04:25:00 -03001263 free_i7core_dev(i7core_dev);
Mauro Carvalho Chehab39300e72010-08-11 23:40:15 -03001264 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001265}
1266
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001267static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001268{
1269 struct pci_dev *pdev = NULL;
1270 int i;
Mauro Carvalho Chehab54a08ab2010-08-19 15:51:00 -03001271
Keith Manntheybc2d7242009-09-03 00:05:05 -03001272 /*
1273 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1274 * aren't announced by acpi. So, we need to use a legacy scan probing
1275 * to detect them
1276 */
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001277 while (table && table->descr) {
1278 pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL);
1279 if (unlikely(!pdev)) {
1280 for (i = 0; i < MAX_SOCKET_BUSES; i++)
1281 pcibios_scan_specific_bus(255-i);
1282 }
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001283 pci_dev_put(pdev);
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001284 table++;
Keith Manntheybc2d7242009-09-03 00:05:05 -03001285 }
1286}
1287
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001288static unsigned i7core_pci_lastbus(void)
1289{
1290 int last_bus = 0, bus;
1291 struct pci_bus *b = NULL;
1292
1293 while ((b = pci_find_next_bus(b)) != NULL) {
1294 bus = b->number;
1295 debugf0("Found bus %d\n", bus);
1296 if (bus > last_bus)
1297 last_bus = bus;
1298 }
1299
1300 debugf0("Last bus %d\n", last_bus);
1301
1302 return last_bus;
1303}
1304
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001305/*
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001306 * i7core_get_all_devices Find and perform 'get' operation on the MCH's
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001307 * device/functions we want to reference for this driver
1308 *
1309 * Need to 'get' device 16 func 1 and func 2
1310 */
Hidetoshi Setob197cba2010-08-20 04:24:31 -03001311static int i7core_get_onedevice(struct pci_dev **prev,
1312 const struct pci_id_table *table,
1313 const unsigned devno,
1314 const unsigned last_bus)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001315{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001316 struct i7core_dev *i7core_dev;
Hidetoshi Setob197cba2010-08-20 04:24:31 -03001317 const struct pci_id_descr *dev_descr = &table->descr[devno];
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001318
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001319 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001320 u8 bus = 0;
1321 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001322
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001323 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001324 dev_descr->dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001325
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001326 if (!pdev) {
1327 if (*prev) {
1328 *prev = pdev;
1329 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001330 }
1331
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001332 if (dev_descr->optional)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001333 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001334
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001335 if (devno == 0)
1336 return -ENODEV;
1337
Daniel J Bluemanab089372010-07-23 23:16:52 +01001338 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001339 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001340 dev_descr->dev, dev_descr->func,
1341 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001342
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001343 /* End of list, leave */
1344 return -ENODEV;
1345 }
1346 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001347
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001348 socket = last_bus - bus;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001349
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001350 i7core_dev = get_i7core_dev(socket);
1351 if (!i7core_dev) {
Hidetoshi Seto848b2f72010-08-20 04:24:44 -03001352 i7core_dev = alloc_i7core_dev(socket, table);
Hidetoshi Seto28966372010-08-20 04:28:51 -03001353 if (!i7core_dev) {
1354 pci_dev_put(pdev);
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001355 return -ENOMEM;
Hidetoshi Seto28966372010-08-20 04:28:51 -03001356 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001357 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001358
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001359 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001360 i7core_printk(KERN_ERR,
1361 "Duplicated device for "
1362 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001363 bus, dev_descr->dev, dev_descr->func,
1364 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001365 pci_dev_put(pdev);
1366 return -ENODEV;
1367 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001368
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001369 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001370
1371 /* Sanity check */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001372 if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1373 PCI_FUNC(pdev->devfn) != dev_descr->func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001374 i7core_printk(KERN_ERR,
1375 "Device PCI ID %04x:%04x "
1376 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001377 PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001378 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001379 bus, dev_descr->dev, dev_descr->func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001380 return -ENODEV;
1381 }
1382
1383 /* Be sure that the device is enabled */
1384 if (unlikely(pci_enable_device(pdev) < 0)) {
1385 i7core_printk(KERN_ERR,
1386 "Couldn't enable "
1387 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001388 bus, dev_descr->dev, dev_descr->func,
1389 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001390 return -ENODEV;
1391 }
1392
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001393 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001394 socket, bus, dev_descr->dev,
1395 dev_descr->func,
1396 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001397
1398 *prev = pdev;
1399
1400 return 0;
1401}
1402
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001403static int i7core_get_all_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001404{
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001405 int i, j, rc, last_bus;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001406 struct pci_dev *pdev = NULL;
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001407 const struct pci_id_table *table;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001408
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001409 last_bus = i7core_pci_lastbus();
1410
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03001411 for (j = 0; j < ARRAY_SIZE(pci_dev_table); j++) {
1412 table = &pci_dev_table[j];
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001413 for (i = 0; i < table->n_devs; i++) {
1414 pdev = NULL;
1415 do {
Hidetoshi Setob197cba2010-08-20 04:24:31 -03001416 rc = i7core_get_onedevice(&pdev, table, i,
Mauro Carvalho Chehabbda14282010-06-30 01:41:35 -03001417 last_bus);
Vernon Mauerybd9e19c2010-05-18 19:02:50 -03001418 if (rc < 0) {
1419 if (i == 0) {
1420 i = table->n_devs;
1421 break;
1422 }
1423 i7core_put_all_devices();
1424 return -ENODEV;
1425 }
1426 } while (pdev);
1427 }
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001428 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001429
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001430 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001431}
1432
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001433static int mci_bind_devs(struct mem_ctl_info *mci,
1434 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001435{
1436 struct i7core_pvt *pvt = mci->pvt_info;
1437 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001438 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001439
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001440 pvt->is_registered = 0;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001441 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001442 pdev = i7core_dev->pdev[i];
1443 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001444 continue;
1445
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001446 func = PCI_FUNC(pdev->devfn);
1447 slot = PCI_SLOT(pdev->devfn);
1448 if (slot == 3) {
1449 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001450 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001451 pvt->pci_mcr[func] = pdev;
1452 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1453 if (unlikely(func > MAX_CHAN_FUNC))
1454 goto error;
1455 pvt->pci_ch[slot - 4][func] = pdev;
1456 } else if (!slot && !func)
1457 pvt->pci_noncore = pdev;
1458 else
1459 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001460
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001461 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1462 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1463 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001464
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001465 if (PCI_SLOT(pdev->devfn) == 3 &&
1466 PCI_FUNC(pdev->devfn) == 2)
1467 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001468 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001469
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001470 return 0;
1471
1472error:
1473 i7core_printk(KERN_ERR, "Device %d, function %d "
1474 "is out of the expected range\n",
1475 slot, func);
1476 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001477}
1478
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001479/****************************************************************************
1480 Error check routines
1481 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001482static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001483 const int chan,
1484 const int dimm,
1485 const int add)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001486{
1487 char *msg;
1488 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001489 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001490
1491 for (i = 0; i < add; i++) {
1492 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001493 "(Socket=%d channel=%d dimm=%d)",
1494 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001495
1496 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1497 kfree (msg);
1498 }
1499}
1500
1501static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001502 const int chan,
1503 const int new0,
1504 const int new1,
1505 const int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001506{
1507 struct i7core_pvt *pvt = mci->pvt_info;
1508 int add0 = 0, add1 = 0, add2 = 0;
1509 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001510 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001511 /* Updates CE counters */
1512
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001513 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1514 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1515 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001516
1517 if (add2 < 0)
1518 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001519 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001520
1521 if (add1 < 0)
1522 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001523 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001524
1525 if (add0 < 0)
1526 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001527 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001528 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001529 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001530
1531 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001532 pvt->rdimm_last_ce_count[chan][2] = new2;
1533 pvt->rdimm_last_ce_count[chan][1] = new1;
1534 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001535
1536 /*updated the edac core */
1537 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001538 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001539 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001540 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001541 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001542 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001543
1544}
1545
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001546static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001547{
1548 struct i7core_pvt *pvt = mci->pvt_info;
1549 u32 rcv[3][2];
1550 int i, new0, new1, new2;
1551
1552 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001553 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001554 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001555 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001556 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001557 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001558 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001559 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001560 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001561 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001562 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001563 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001564 &rcv[2][1]);
1565 for (i = 0 ; i < 3; i++) {
1566 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1567 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1568 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001569 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001570 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1571 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1572 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1573 } else {
1574 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1575 DIMM_BOT_COR_ERR(rcv[i][0]);
1576 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1577 DIMM_BOT_COR_ERR(rcv[i][1]);
1578 new2 = 0;
1579 }
1580
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001581 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001582 }
1583}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001584
1585/* This function is based on the device 3 function 4 registers as described on:
1586 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1587 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1588 * also available at:
1589 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1590 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001591static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001592{
1593 struct i7core_pvt *pvt = mci->pvt_info;
1594 u32 rcv1, rcv0;
1595 int new0, new1, new2;
1596
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001597 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001598 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001599 return;
1600 }
1601
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001602 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001603 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1604 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001605
1606 /* Store the new values */
1607 new2 = DIMM2_COR_ERR(rcv1);
1608 new1 = DIMM1_COR_ERR(rcv0);
1609 new0 = DIMM0_COR_ERR(rcv0);
1610
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001611 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001612 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001613 /* Updates CE counters */
1614 int add0, add1, add2;
1615
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001616 add2 = new2 - pvt->udimm_last_ce_count[2];
1617 add1 = new1 - pvt->udimm_last_ce_count[1];
1618 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001619
1620 if (add2 < 0)
1621 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001622 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001623
1624 if (add1 < 0)
1625 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001626 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001627
1628 if (add0 < 0)
1629 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001630 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001631
1632 if (add0 | add1 | add2)
1633 i7core_printk(KERN_ERR, "New Corrected error(s): "
1634 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1635 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001636 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001637 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001638
1639 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001640 pvt->udimm_last_ce_count[2] = new2;
1641 pvt->udimm_last_ce_count[1] = new1;
1642 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001643}
1644
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001645/*
1646 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1647 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001648 * Nehalem are defined as family 0x06, model 0x1a
1649 *
1650 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001651 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001652 * m->status MSR_IA32_MC8_STATUS
1653 * m->addr MSR_IA32_MC8_ADDR
1654 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001655 * In the case of Nehalem, the error information is masked at .status and .misc
1656 * fields
1657 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001658static void i7core_mce_output_error(struct mem_ctl_info *mci,
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001659 const struct mce *m)
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001660{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001661 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001662 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001663 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001664 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001665 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1666 u32 dimm = (m->misc >> 16) & 0x3;
1667 u32 channel = (m->misc >> 18) & 0x3;
1668 u32 syndrome = m->misc >> 32;
1669 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001670 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001671
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001672 if (m->mcgstatus & 1)
1673 type = "FATAL";
1674 else
1675 type = "NON_FATAL";
1676
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001677 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001678 case 0:
1679 optype = "generic undef request";
1680 break;
1681 case 1:
1682 optype = "read error";
1683 break;
1684 case 2:
1685 optype = "write error";
1686 break;
1687 case 3:
1688 optype = "addr/cmd error";
1689 break;
1690 case 4:
1691 optype = "scrubbing error";
1692 break;
1693 default:
1694 optype = "reserved";
1695 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001696 }
1697
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001698 switch (errnum) {
1699 case 16:
1700 err = "read ECC error";
1701 break;
1702 case 17:
1703 err = "RAS ECC error";
1704 break;
1705 case 18:
1706 err = "write parity error";
1707 break;
1708 case 19:
1709 err = "redundacy loss";
1710 break;
1711 case 20:
1712 err = "reserved";
1713 break;
1714 case 21:
1715 err = "memory range error";
1716 break;
1717 case 22:
1718 err = "RTID out of range";
1719 break;
1720 case 23:
1721 err = "address parity error";
1722 break;
1723 case 24:
1724 err = "byte enable parity error";
1725 break;
1726 default:
1727 err = "unknown";
1728 }
1729
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001730 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001731 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001732 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001733 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001734 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001735 syndrome, core_err_cnt, (long long)m->status,
1736 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001737
1738 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001739
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001740 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001741
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001742 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001743 if (m->mcgstatus & 1)
1744 edac_mc_handle_fbd_ue(mci, csrow, 0,
1745 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001746 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001747 edac_mc_handle_fbd_ce(mci, csrow,
1748 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001749
1750 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001751}
1752
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001753/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001754 * i7core_check_error Retrieve and process errors reported by the
1755 * hardware. Called by the Core module.
1756 */
1757static void i7core_check_error(struct mem_ctl_info *mci)
1758{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001759 struct i7core_pvt *pvt = mci->pvt_info;
1760 int i;
1761 unsigned count = 0;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001762 struct mce *m;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001763
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001764 /*
1765 * MCE first step: Copy all mce errors into a temporary buffer
1766 * We use a double buffering here, to reduce the risk of
1767 * loosing an error.
1768 */
1769 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001770 count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
1771 % MCE_LOG_LEN;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001772 if (!count)
Vernon Mauery8a311e12010-04-16 19:40:19 -03001773 goto check_ce_error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001774
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001775 m = pvt->mce_outentry;
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001776 if (pvt->mce_in + count > MCE_LOG_LEN) {
1777 unsigned l = MCE_LOG_LEN - pvt->mce_in;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001778
1779 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
1780 smp_wmb();
1781 pvt->mce_in = 0;
1782 count -= l;
1783 m += l;
1784 }
1785 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
1786 smp_wmb();
1787 pvt->mce_in += count;
1788
1789 smp_rmb();
1790 if (pvt->mce_overrun) {
1791 i7core_printk(KERN_ERR, "Lost %d memory errors\n",
1792 pvt->mce_overrun);
1793 smp_wmb();
1794 pvt->mce_overrun = 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001795 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001796
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001797 /*
1798 * MCE second step: parse errors and display
1799 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001800 for (i = 0; i < count; i++)
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001801 i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001802
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001803 /*
1804 * Now, let's increment CE error counts
1805 */
Vernon Mauery8a311e12010-04-16 19:40:19 -03001806check_ce_error:
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001807 if (!pvt->is_registered)
1808 i7core_udimm_check_mc_ecc_err(mci);
1809 else
1810 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001811}
1812
1813/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001814 * i7core_mce_check_error Replicates mcelog routine to get errors
1815 * This routine simply queues mcelog errors, and
1816 * return. The error itself should be handled later
1817 * by i7core_check_error.
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001818 * WARNING: As this routine should be called at NMI time, extra care should
1819 * be taken to avoid deadlocks, and to be as fast as possible.
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001820 */
1821static int i7core_mce_check_error(void *priv, struct mce *mce)
1822{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001823 struct mem_ctl_info *mci = priv;
1824 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001825
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001826 /*
1827 * Just let mcelog handle it if the error is
1828 * outside the memory controller
1829 */
1830 if (((mce->status & 0xffff) >> 7) != 1)
1831 return 0;
1832
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001833 /* Bank 8 registers are the only ones that we know how to handle */
1834 if (mce->bank != 8)
1835 return 0;
1836
Randy Dunlap3b918c12009-11-08 01:36:40 -02001837#ifdef CONFIG_SMP
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001838 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001839 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001840 return 0;
Randy Dunlap3b918c12009-11-08 01:36:40 -02001841#endif
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001842
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001843 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001844 if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001845 smp_wmb();
1846 pvt->mce_overrun++;
1847 return 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001848 }
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001849
1850 /* Copy memory error at the ringbuffer */
1851 memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001852 smp_wmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001853 pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001854
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001855 /* Handle fatal errors immediately */
1856 if (mce->mcgstatus & 1)
1857 i7core_check_error(mci);
1858
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001859 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001860 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001861}
1862
Hidetoshi Setoa3aa0a42010-08-20 04:25:18 -03001863static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
1864{
1865 pvt->i7core_pci = edac_pci_create_generic_ctl(
1866 &pvt->i7core_dev->pdev[0]->dev,
1867 EDAC_MOD_STR);
1868 if (unlikely(!pvt->i7core_pci))
1869 pr_warn("Unable to setup PCI error report via EDAC\n");
1870}
1871
1872static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
1873{
1874 if (likely(pvt->i7core_pci))
1875 edac_pci_release_generic_ctl(pvt->i7core_pci);
1876 else
1877 i7core_printk(KERN_ERR,
1878 "Couldn't find mem_ctl_info for socket %d\n",
1879 pvt->i7core_dev->socket);
1880 pvt->i7core_pci = NULL;
1881}
1882
Hidetoshi Seto1c6edbb2010-08-20 04:32:33 -03001883static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
1884{
1885 struct mem_ctl_info *mci = i7core_dev->mci;
1886 struct i7core_pvt *pvt;
1887
1888 if (unlikely(!mci || !mci->pvt_info)) {
1889 debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
1890 __func__, &i7core_dev->pdev[0]->dev);
1891
1892 i7core_printk(KERN_ERR, "Couldn't find mci handler\n");
1893 return;
1894 }
1895
1896 pvt = mci->pvt_info;
1897
1898 debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
1899 __func__, mci, &i7core_dev->pdev[0]->dev);
1900
1901 /* Disable MCE NMI handler */
1902 edac_mce_unregister(&pvt->edac_mce);
1903
1904 /* Disable EDAC polling */
1905 i7core_pci_ctl_release(pvt);
1906
1907 /* Remove MC sysfs nodes */
1908 edac_mc_del_mc(mci->dev);
1909
1910 debugf1("%s: free mci struct\n", mci->ctl_name);
1911 kfree(mci->ctl_name);
1912 edac_mc_free(mci);
1913 i7core_dev->mci = NULL;
1914}
1915
Hidetoshi Setoaace4282010-08-20 04:32:45 -03001916static int i7core_register_mci(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001917{
1918 struct mem_ctl_info *mci;
1919 struct i7core_pvt *pvt;
Hidetoshi Setoaace4282010-08-20 04:32:45 -03001920 int rc, channels, csrows;
1921
1922 /* Check the number of active and not disabled channels */
1923 rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows);
1924 if (unlikely(rc < 0))
1925 return rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001926
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001927 /* allocate a new MC control structure */
Hidetoshi Setoaace4282010-08-20 04:32:45 -03001928 mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001929 if (unlikely(!mci))
1930 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001931
Mauro Carvalho Chehab3cfd0142010-08-10 23:23:46 -03001932 debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
1933 __func__, mci, &i7core_dev->pdev[0]->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001934
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001935 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001936 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001937
Mauro Carvalho Chehab6d37d242010-08-20 12:48:26 -03001938 /* Associates i7core_dev and mci for future usage */
1939 pvt->i7core_dev = i7core_dev;
1940 i7core_dev->mci = mci;
1941
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001942 /*
1943 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1944 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1945 * memory channels
1946 */
1947 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001948 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1949 mci->edac_cap = EDAC_FLAG_NONE;
1950 mci->mod_name = "i7core_edac.c";
1951 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001952 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1953 i7core_dev->socket);
1954 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001955 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab1288c182010-08-10 18:57:01 -03001956
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001957 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001958 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001959 if (unlikely(rc < 0))
Hidetoshi Seto628c5dd2010-08-20 04:28:40 -03001960 goto fail0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001961
Hidetoshi Seto59398132010-08-20 04:28:25 -03001962 if (pvt->is_registered)
1963 mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs;
1964 else
1965 mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs;
1966
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001967 /* Get dimm basic config */
Hidetoshi Seto2e5185f2010-08-20 04:32:56 -03001968 get_dimm_config(mci);
Hidetoshi Seto59398132010-08-20 04:28:25 -03001969 /* record ptr to the generic device */
1970 mci->dev = &i7core_dev->pdev[0]->dev;
1971 /* Set the function pointer to an actual operation function */
1972 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001973
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001974 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001975 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001976 debugf0("MC: " __FILE__
1977 ": %s(): failed edac_mc_add_mc()\n", __func__);
1978 /* FIXME: perhaps some code should go here that disables error
1979 * reporting if we just enabled it
1980 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001981
1982 rc = -EINVAL;
Hidetoshi Seto628c5dd2010-08-20 04:28:40 -03001983 goto fail0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001984 }
1985
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001986 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001987 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001988 pvt->inject.dimm = -1;
1989 pvt->inject.rank = -1;
1990 pvt->inject.bank = -1;
1991 pvt->inject.page = -1;
1992 pvt->inject.col = -1;
1993
Hidetoshi Setoa3aa0a42010-08-20 04:25:18 -03001994 /* allocating generic PCI control info */
1995 i7core_pci_ctl_create(pvt);
1996
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001997 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001998 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001999 pvt->edac_mce.check_error = i7core_mce_check_error;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03002000 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03002001 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03002002 debugf0("MC: " __FILE__
2003 ": %s(): failed edac_mce_register()\n", __func__);
Hidetoshi Seto628c5dd2010-08-20 04:28:40 -03002004 goto fail1;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002005 }
2006
Hidetoshi Seto628c5dd2010-08-20 04:28:40 -03002007 return 0;
2008
2009fail1:
2010 i7core_pci_ctl_release(pvt);
2011 edac_mc_del_mc(mci->dev);
2012fail0:
2013 kfree(mci->ctl_name);
2014 edac_mc_free(mci);
Hidetoshi Seto1c6edbb2010-08-20 04:32:33 -03002015 i7core_dev->mci = NULL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002016 return rc;
2017}
2018
2019/*
2020 * i7core_probe Probe for ONE instance of device to see if it is
2021 * present.
2022 * return:
2023 * 0 for FOUND a device
2024 * < 0 for error code
2025 */
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002026
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002027static int __devinit i7core_probe(struct pci_dev *pdev,
2028 const struct pci_device_id *id)
2029{
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002030 int rc;
2031 struct i7core_dev *i7core_dev;
2032
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002033 /* get the pci devices we want to reserve for our use */
2034 mutex_lock(&i7core_edac_lock);
2035
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002036 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03002037 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002038 */
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002039 if (unlikely(probed >= 1)) {
2040 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002041 return -EINVAL;
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002042 }
2043 probed++;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03002044
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03002045 rc = i7core_get_all_devices();
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03002046 if (unlikely(rc < 0))
2047 goto fail0;
2048
2049 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
Hidetoshi Setoaace4282010-08-20 04:32:45 -03002050 rc = i7core_register_mci(i7core_dev);
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03002051 if (unlikely(rc < 0))
2052 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03002053 }
2054
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03002055 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03002056
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03002057 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002058 return 0;
2059
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03002060fail1:
Mauro Carvalho Chehab88ef5ea2010-08-20 15:39:38 -03002061 list_for_each_entry(i7core_dev, &i7core_edac_list, list)
2062 i7core_unregister_mci(i7core_dev);
2063
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03002064 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03002065fail0:
2066 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03002067 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002068}
2069
2070/*
2071 * i7core_remove destructor for one instance of device
2072 *
2073 */
2074static void __devexit i7core_remove(struct pci_dev *pdev)
2075{
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03002076 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002077
2078 debugf0(__FILE__ ": %s()\n", __func__);
2079
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002080 /*
2081 * we have a trouble here: pdev value for removal will be wrong, since
2082 * it will point to the X58 register used to detect that the machine
2083 * is a Nehalem or upper design. However, due to the way several PCI
2084 * devices are grouped together to provide MC functionality, we need
2085 * to use a different method for releasing the devices
2086 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03002087
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03002088 mutex_lock(&i7core_edac_lock);
Hidetoshi Seto71fe0172010-08-20 04:29:47 -03002089
2090 if (unlikely(!probed)) {
2091 mutex_unlock(&i7core_edac_lock);
2092 return;
2093 }
2094
Mauro Carvalho Chehab88ef5ea2010-08-20 15:39:38 -03002095 list_for_each_entry(i7core_dev, &i7core_edac_list, list)
2096 i7core_unregister_mci(i7core_dev);
Hidetoshi Seto64c10f62010-08-20 04:28:14 -03002097
2098 /* Release PCI resources */
2099 i7core_put_all_devices();
2100
Mauro Carvalho Chehab2d95d812010-06-30 01:42:21 -03002101 probed--;
2102
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03002103 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002104}
2105
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002106MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
2107
2108/*
2109 * i7core_driver pci_driver structure for this module
2110 *
2111 */
2112static struct pci_driver i7core_driver = {
2113 .name = "i7core_edac",
2114 .probe = i7core_probe,
2115 .remove = __devexit_p(i7core_remove),
2116 .id_table = i7core_pci_tbl,
2117};
2118
2119/*
2120 * i7core_init Module entry function
2121 * Try to initialize this module for its devices
2122 */
2123static int __init i7core_init(void)
2124{
2125 int pci_rc;
2126
2127 debugf2("MC: " __FILE__ ": %s()\n", __func__);
2128
2129 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
2130 opstate_init();
2131
Mauro Carvalho Chehab54a08ab2010-08-19 15:51:00 -03002132 if (use_pci_fixup)
2133 i7core_xeon_pci_fixup(pci_dev_table);
Keith Manntheybc2d7242009-09-03 00:05:05 -03002134
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002135 pci_rc = pci_register_driver(&i7core_driver);
2136
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03002137 if (pci_rc >= 0)
2138 return 0;
2139
2140 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
2141 pci_rc);
2142
2143 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03002144}
2145
2146/*
2147 * i7core_exit() Module exit function
2148 * Unregister the driver
2149 */
2150static void __exit i7core_exit(void)
2151{
2152 debugf2("MC: " __FILE__ ": %s()\n", __func__);
2153 pci_unregister_driver(&i7core_driver);
2154}
2155
2156module_init(i7core_init);
2157module_exit(i7core_exit);
2158
2159MODULE_LICENSE("GPL");
2160MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
2161MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
2162MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
2163 I7CORE_REVISION);
2164
2165module_param(edac_op_state, int, 0444);
2166MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");