blob: b6fce2e38e3d852a306d09f1c181b780065814b2 [file] [log] [blame]
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001/* Intel 7 core Memory Controller kernel module (Nehalem)
2 *
3 * This file may be distributed under the terms of the
4 * GNU General Public License version 2 only.
5 *
6 * Copyright (c) 2009 by:
7 * Mauro Carvalho Chehab <mchehab@redhat.com>
8 *
9 * Red Hat Inc. http://www.redhat.com
10 *
11 * Forked and adapted from the i5400_edac driver
12 *
13 * Based on the following public Intel datasheets:
14 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
15 * Datasheet, Volume 2:
16 * http://download.intel.com/design/processor/datashts/320835.pdf
17 * Intel Xeon Processor 5500 Series Datasheet Volume 2
18 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
19 * also available at:
20 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
21 */
22
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030023#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/pci.h>
26#include <linux/pci_ids.h>
27#include <linux/slab.h>
28#include <linux/edac.h>
29#include <linux/mmzone.h>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030030#include <linux/edac_mce.h>
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030031#include <linux/smp.h>
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -030032#include <asm/processor.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030033
34#include "edac_core.h"
35
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030036/*
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030037 * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
38 * registers start at bus 255, and are not reported by BIOS.
39 * We currently find devices with only 2 sockets. In order to support more QPI
40 * Quick Path Interconnect, just increment this number.
41 */
42#define MAX_SOCKET_BUSES 2
43
44
45/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030046 * Alter this version for the module when modifications are made
47 */
48#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
49#define EDAC_MOD_STR "i7core_edac"
50
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030051/*
52 * Debug macros
53 */
54#define i7core_printk(level, fmt, arg...) \
55 edac_printk(level, "i7core", fmt, ##arg)
56
57#define i7core_mc_printk(mci, level, fmt, arg...) \
58 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
59
60/*
61 * i7core Memory Controller Registers
62 */
63
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030064 /* OFFSETS for Device 0 Function 0 */
65
66#define MC_CFG_CONTROL 0x90
67
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030068 /* OFFSETS for Device 3 Function 0 */
69
70#define MC_CONTROL 0x48
71#define MC_STATUS 0x4c
72#define MC_MAX_DOD 0x64
73
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030074/*
75 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
76 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
77 */
78
79#define MC_TEST_ERR_RCV1 0x60
80 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
81
82#define MC_TEST_ERR_RCV0 0x64
83 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
84 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
85
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -030086/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
87#define MC_COR_ECC_CNT_0 0x80
88#define MC_COR_ECC_CNT_1 0x84
89#define MC_COR_ECC_CNT_2 0x88
90#define MC_COR_ECC_CNT_3 0x8c
91#define MC_COR_ECC_CNT_4 0x90
92#define MC_COR_ECC_CNT_5 0x94
93
94#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
95#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
96
97
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030098 /* OFFSETS for Devices 4,5 and 6 Function 0 */
99
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300100#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
101 #define THREE_DIMMS_PRESENT (1 << 24)
102 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
103 #define QUAD_RANK_PRESENT (1 << 22)
104 #define REGISTERED_DIMM (1 << 15)
105
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300106#define MC_CHANNEL_MAPPER 0x60
107 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
108 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
109
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300110#define MC_CHANNEL_RANK_PRESENT 0x7c
111 #define RANK_PRESENT_MASK 0xffff
112
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300113#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300114#define MC_CHANNEL_ERROR_MASK 0xf8
115#define MC_CHANNEL_ERROR_INJECT 0xfc
116 #define INJECT_ADDR_PARITY 0x10
117 #define INJECT_ECC 0x08
118 #define MASK_CACHELINE 0x06
119 #define MASK_FULL_CACHELINE 0x06
120 #define MASK_MSB32_CACHELINE 0x04
121 #define MASK_LSB32_CACHELINE 0x02
122 #define NO_MASK_CACHELINE 0x00
123 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300124
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300125 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300126
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300127#define MC_DOD_CH_DIMM0 0x48
128#define MC_DOD_CH_DIMM1 0x4c
129#define MC_DOD_CH_DIMM2 0x50
130 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
131 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
132 #define DIMM_PRESENT_MASK (1 << 9)
133 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300134 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
135 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
136 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
137 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300138 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300139 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300140 #define MC_DOD_NUMCOL_MASK 3
141 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300142
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300143#define MC_RANK_PRESENT 0x7c
144
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300145#define MC_SAG_CH_0 0x80
146#define MC_SAG_CH_1 0x84
147#define MC_SAG_CH_2 0x88
148#define MC_SAG_CH_3 0x8c
149#define MC_SAG_CH_4 0x90
150#define MC_SAG_CH_5 0x94
151#define MC_SAG_CH_6 0x98
152#define MC_SAG_CH_7 0x9c
153
154#define MC_RIR_LIMIT_CH_0 0x40
155#define MC_RIR_LIMIT_CH_1 0x44
156#define MC_RIR_LIMIT_CH_2 0x48
157#define MC_RIR_LIMIT_CH_3 0x4C
158#define MC_RIR_LIMIT_CH_4 0x50
159#define MC_RIR_LIMIT_CH_5 0x54
160#define MC_RIR_LIMIT_CH_6 0x58
161#define MC_RIR_LIMIT_CH_7 0x5C
162#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
163
164#define MC_RIR_WAY_CH 0x80
165 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
166 #define MC_RIR_WAY_RANK_MASK 0x7
167
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300168/*
169 * i7core structs
170 */
171
172#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300173#define MAX_DIMMS 3 /* Max DIMMS per channel */
174#define MAX_MCR_FUNC 4
175#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300176
177struct i7core_info {
178 u32 mc_control;
179 u32 mc_status;
180 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300181 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300182};
183
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300184
185struct i7core_inject {
186 int enable;
187
188 u32 section;
189 u32 type;
190 u32 eccmask;
191
192 /* Error address mask */
193 int channel, dimm, rank, bank, page, col;
194};
195
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300196struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300197 u32 ranks;
198 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300199};
200
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300201struct pci_id_descr {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300202 int dev;
203 int func;
204 int dev_id;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300205 int optional;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300206};
207
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300208struct i7core_dev {
209 struct list_head list;
210 u8 socket;
211 struct pci_dev **pdev;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300212 int n_devs;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300213 struct mem_ctl_info *mci;
214};
215
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300216struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300217 struct pci_dev *pci_noncore;
218 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
219 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
220
221 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300222
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300223 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300224 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300225 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300226
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300227 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300228
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300229 int ce_count_available;
230 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300231
232 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300233 unsigned long udimm_ce_count[MAX_DIMMS];
234 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300235 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300236 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
237 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300238
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300239 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300240
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300241 /* mcelog glue */
242 struct edac_mce edac_mce;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300243
244 /* Fifo double buffers */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300245 struct mce mce_entry[MCE_LOG_LEN];
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300246 struct mce mce_outentry[MCE_LOG_LEN];
247
248 /* Fifo in/out counters */
249 unsigned mce_in, mce_out;
250
251 /* Count indicator to show errors not got */
252 unsigned mce_overrun;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300253};
254
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300255/* Static vars */
256static LIST_HEAD(i7core_edac_list);
257static DEFINE_MUTEX(i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300258
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300259#define PCI_DESCR(device, function, device_id) \
260 .dev = (device), \
261 .func = (function), \
262 .dev_id = (device_id)
263
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300264struct pci_id_descr pci_dev_descr_i7core[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300265 /* Memory controller */
266 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
267 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300268 /* Exists only for RDIMM */
269 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300270 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
271
272 /* Channel 0 */
273 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
274 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
275 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
276 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
277
278 /* Channel 1 */
279 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
280 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
281 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
282 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
283
284 /* Channel 2 */
285 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
286 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
287 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
288 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300289
290 /* Generic Non-core registers */
291 /*
292 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
293 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
294 * the probing code needs to test for the other address in case of
295 * failure of this one
296 */
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -0300297 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300298
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300299};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300300
301/*
302 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300303 */
304static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300305 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300306 {0,} /* 0 terminated list. */
307};
308
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300309static struct edac_pci_ctl_info *i7core_pci;
310
311/****************************************************************************
312 Anciliary status routines
313 ****************************************************************************/
314
315 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300316#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
317#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300318
319 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300320#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300321#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300322
323 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300324static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300325{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300326 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300327}
328
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300329static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300330{
331 static int ranks[4] = { 1, 2, 4, -EINVAL };
332
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300333 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300334}
335
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300336static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300337{
338 static int banks[4] = { 4, 8, 16, -EINVAL };
339
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300340 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300341}
342
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300343static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300344{
345 static int rows[8] = {
346 1 << 12, 1 << 13, 1 << 14, 1 << 15,
347 1 << 16, -EINVAL, -EINVAL, -EINVAL,
348 };
349
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300350 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300351}
352
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300353static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300354{
355 static int cols[8] = {
356 1 << 10, 1 << 11, 1 << 12, -EINVAL,
357 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300358 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300359}
360
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300361static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300362{
363 struct i7core_dev *i7core_dev;
364
365 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
366 if (i7core_dev->socket == socket)
367 return i7core_dev;
368 }
369
370 return NULL;
371}
372
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300373/****************************************************************************
374 Memory check routines
375 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300376static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
377 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300378{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300379 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300380 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300381
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300382 if (!i7core_dev)
383 return NULL;
384
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300385 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300386 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300387 continue;
388
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300389 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
390 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
391 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300392 }
393 }
394
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300395 return NULL;
396}
397
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300398/**
399 * i7core_get_active_channels() - gets the number of channels and csrows
400 * @socket: Quick Path Interconnect socket
401 * @channels: Number of channels that will be returned
402 * @csrows: Number of csrows found
403 *
404 * Since EDAC core needs to know in advance the number of available channels
405 * and csrows, in order to allocate memory for csrows/channels, it is needed
406 * to run two similar steps. At the first step, implemented on this function,
407 * it checks the number of csrows/channels present at one socket.
408 * this is used in order to properly allocate the size of mci components.
409 *
410 * It should be noticed that none of the current available datasheets explain
411 * or even mention how csrows are seen by the memory controller. So, we need
412 * to add a fake description for csrows.
413 * So, this driver is attributing one DIMM memory for one csrow.
414 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300415static int i7core_get_active_channels(u8 socket, unsigned *channels,
416 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300417{
418 struct pci_dev *pdev = NULL;
419 int i, j;
420 u32 status, control;
421
422 *channels = 0;
423 *csrows = 0;
424
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300425 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300426 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300427 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
428 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300429 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300430 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300431
432 /* Device 3 function 0 reads */
433 pci_read_config_dword(pdev, MC_STATUS, &status);
434 pci_read_config_dword(pdev, MC_CONTROL, &control);
435
436 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300437 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300438 /* Check if the channel is active */
439 if (!(control & (1 << (8 + i))))
440 continue;
441
442 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300443 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300444 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300445
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300446 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300447 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300448 i7core_printk(KERN_ERR, "Couldn't find socket %d "
449 "fn %d.%d!!!\n",
450 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300451 return -ENODEV;
452 }
453 /* Devices 4-6 function 1 */
454 pci_read_config_dword(pdev,
455 MC_DOD_CH_DIMM0, &dimm_dod[0]);
456 pci_read_config_dword(pdev,
457 MC_DOD_CH_DIMM1, &dimm_dod[1]);
458 pci_read_config_dword(pdev,
459 MC_DOD_CH_DIMM2, &dimm_dod[2]);
460
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300461 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300462
463 for (j = 0; j < 3; j++) {
464 if (!DIMM_PRESENT(dimm_dod[j]))
465 continue;
466 (*csrows)++;
467 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300468 }
469
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300470 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300471 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300472
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300473 return 0;
474}
475
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300476static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300477{
478 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300479 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300480 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300481 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300482 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300483 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300484 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300485
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300486 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300487 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300488 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300489 return -ENODEV;
490
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300491 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300492 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
493 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
494 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
495 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300496
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300497 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300498 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300499 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300500
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300501 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300502 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300503 if (ECCx8(pvt))
504 mode = EDAC_S8ECD8ED;
505 else
506 mode = EDAC_S4ECD4ED;
507 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300508 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300509 mode = EDAC_NONE;
510 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300511
512 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300513 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
514 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300515 numdimms(pvt->info.max_dod),
516 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300517 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300518 numrow(pvt->info.max_dod >> 6),
519 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300520
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300521 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300522 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300523
524 if (!CH_ACTIVE(pvt, i)) {
525 debugf0("Channel %i is not active\n", i);
526 continue;
527 }
528 if (CH_DISABLED(pvt, i)) {
529 debugf0("Channel %i is disabled\n", i);
530 continue;
531 }
532
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300533 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300534 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300535 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
536
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300537 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300538 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300539
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300540 if (data & REGISTERED_DIMM)
541 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300542 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300543 mtype = MEM_DDR3;
544#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300545 if (data & THREE_DIMMS_PRESENT)
546 pvt->channel[i].dimms = 3;
547 else if (data & SINGLE_QUAD_RANK_PRESENT)
548 pvt->channel[i].dimms = 1;
549 else
550 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300551#endif
552
553 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300554 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300555 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300556 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300557 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300558 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300559 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300560
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300561 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300562 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300563 i,
564 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
565 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300566 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300567 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300568
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300569 for (j = 0; j < 3; j++) {
570 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300571 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300572
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300573 if (!DIMM_PRESENT(dimm_dod[j]))
574 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300575
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300576 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
577 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
578 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
579 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300580
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300581 /* DDR3 has 8 I/O banks */
582 size = (rows * cols * banks * ranks) >> (20 - 3);
583
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300584 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300585
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300586 debugf0("\tdimm %d %d Mb offset: %x, "
587 "bank: %d, rank: %d, row: %#x, col: %#x\n",
588 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300589 RANKOFFSET(dimm_dod[j]),
590 banks, ranks, rows, cols);
591
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300592#if PAGE_SHIFT > 20
593 npages = size >> (PAGE_SHIFT - 20);
594#else
595 npages = size << (20 - PAGE_SHIFT);
596#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300597
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300598 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300599 csr->first_page = last_page + 1;
600 last_page += npages;
601 csr->last_page = last_page;
602 csr->nr_pages = npages;
603
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300604 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300605 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300606 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300607 csr->nr_channels = 1;
608
609 csr->channels[0].chan_idx = i;
610 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300611
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300612 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300613
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300614 switch (banks) {
615 case 4:
616 csr->dtype = DEV_X4;
617 break;
618 case 8:
619 csr->dtype = DEV_X8;
620 break;
621 case 16:
622 csr->dtype = DEV_X16;
623 break;
624 default:
625 csr->dtype = DEV_UNKNOWN;
626 }
627
628 csr->edac_mode = mode;
629 csr->mtype = mtype;
630
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300631 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300632 }
633
634 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
635 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
636 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
637 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
638 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
639 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
640 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
641 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300642 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300643 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300644 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300645 (value[j] >> 27) & 0x1,
646 (value[j] >> 24) & 0x7,
647 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300648 }
649
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300650 return 0;
651}
652
653/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300654 Error insertion routines
655 ****************************************************************************/
656
657/* The i7core has independent error injection features per channel.
658 However, to have a simpler code, we don't allow enabling error injection
659 on more than one channel.
660 Also, since a change at an inject parameter will be applied only at enable,
661 we're disabling error injection on all write calls to the sysfs nodes that
662 controls the error code injection.
663 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300664static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300665{
666 struct i7core_pvt *pvt = mci->pvt_info;
667
668 pvt->inject.enable = 0;
669
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300670 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300671 return -ENODEV;
672
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300673 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300674 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300675
676 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300677}
678
679/*
680 * i7core inject inject.section
681 *
682 * accept and store error injection inject.section value
683 * bit 0 - refers to the lower 32-byte half cacheline
684 * bit 1 - refers to the upper 32-byte half cacheline
685 */
686static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
687 const char *data, size_t count)
688{
689 struct i7core_pvt *pvt = mci->pvt_info;
690 unsigned long value;
691 int rc;
692
693 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300694 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300695
696 rc = strict_strtoul(data, 10, &value);
697 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300698 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300699
700 pvt->inject.section = (u32) value;
701 return count;
702}
703
704static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
705 char *data)
706{
707 struct i7core_pvt *pvt = mci->pvt_info;
708 return sprintf(data, "0x%08x\n", pvt->inject.section);
709}
710
711/*
712 * i7core inject.type
713 *
714 * accept and store error injection inject.section value
715 * bit 0 - repeat enable - Enable error repetition
716 * bit 1 - inject ECC error
717 * bit 2 - inject parity error
718 */
719static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
720 const char *data, size_t count)
721{
722 struct i7core_pvt *pvt = mci->pvt_info;
723 unsigned long value;
724 int rc;
725
726 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300727 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300728
729 rc = strict_strtoul(data, 10, &value);
730 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300731 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300732
733 pvt->inject.type = (u32) value;
734 return count;
735}
736
737static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
738 char *data)
739{
740 struct i7core_pvt *pvt = mci->pvt_info;
741 return sprintf(data, "0x%08x\n", pvt->inject.type);
742}
743
744/*
745 * i7core_inject_inject.eccmask_store
746 *
747 * The type of error (UE/CE) will depend on the inject.eccmask value:
748 * Any bits set to a 1 will flip the corresponding ECC bit
749 * Correctable errors can be injected by flipping 1 bit or the bits within
750 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
751 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
752 * uncorrectable error to be injected.
753 */
754static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
755 const char *data, size_t count)
756{
757 struct i7core_pvt *pvt = mci->pvt_info;
758 unsigned long value;
759 int rc;
760
761 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300762 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300763
764 rc = strict_strtoul(data, 10, &value);
765 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300766 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300767
768 pvt->inject.eccmask = (u32) value;
769 return count;
770}
771
772static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
773 char *data)
774{
775 struct i7core_pvt *pvt = mci->pvt_info;
776 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
777}
778
779/*
780 * i7core_addrmatch
781 *
782 * The type of error (UE/CE) will depend on the inject.eccmask value:
783 * Any bits set to a 1 will flip the corresponding ECC bit
784 * Correctable errors can be injected by flipping 1 bit or the bits within
785 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
786 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
787 * uncorrectable error to be injected.
788 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300789
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300790#define DECLARE_ADDR_MATCH(param, limit) \
791static ssize_t i7core_inject_store_##param( \
792 struct mem_ctl_info *mci, \
793 const char *data, size_t count) \
794{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300795 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300796 long value; \
797 int rc; \
798 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300799 debugf1("%s()\n", __func__); \
800 pvt = mci->pvt_info; \
801 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300802 if (pvt->inject.enable) \
803 disable_inject(mci); \
804 \
Mauro Carvalho Chehab4f87fad2009-10-04 11:54:56 -0300805 if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300806 value = -1; \
807 else { \
808 rc = strict_strtoul(data, 10, &value); \
809 if ((rc < 0) || (value >= limit)) \
810 return -EIO; \
811 } \
812 \
813 pvt->inject.param = value; \
814 \
815 return count; \
816} \
817 \
818static ssize_t i7core_inject_show_##param( \
819 struct mem_ctl_info *mci, \
820 char *data) \
821{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300822 struct i7core_pvt *pvt; \
823 \
824 pvt = mci->pvt_info; \
825 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300826 if (pvt->inject.param < 0) \
827 return sprintf(data, "any\n"); \
828 else \
829 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300830}
831
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300832#define ATTR_ADDR_MATCH(param) \
833 { \
834 .attr = { \
835 .name = #param, \
836 .mode = (S_IRUGO | S_IWUSR) \
837 }, \
838 .show = i7core_inject_show_##param, \
839 .store = i7core_inject_store_##param, \
840 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300841
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300842DECLARE_ADDR_MATCH(channel, 3);
843DECLARE_ADDR_MATCH(dimm, 3);
844DECLARE_ADDR_MATCH(rank, 4);
845DECLARE_ADDR_MATCH(bank, 32);
846DECLARE_ADDR_MATCH(page, 0x10000);
847DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300848
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300849static int write_and_test(struct pci_dev *dev, int where, u32 val)
850{
851 u32 read;
852 int count;
853
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300854 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
855 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
856 where, val);
857
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300858 for (count = 0; count < 10; count++) {
859 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300860 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300861 pci_write_config_dword(dev, where, val);
862 pci_read_config_dword(dev, where, &read);
863
864 if (read == val)
865 return 0;
866 }
867
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300868 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
869 "write=%08x. Read=%08x\n",
870 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
871 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300872
873 return -EINVAL;
874}
875
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300876/*
877 * This routine prepares the Memory Controller for error injection.
878 * The error will be injected when some process tries to write to the
879 * memory that matches the given criteria.
880 * The criteria can be set in terms of a mask where dimm, rank, bank, page
881 * and col can be specified.
882 * A -1 value for any of the mask items will make the MCU to ignore
883 * that matching criteria for error injection.
884 *
885 * It should be noticed that the error will only happen after a write operation
886 * on a memory that matches the condition. if REPEAT_EN is not enabled at
887 * inject mask, then it will produce just one error. Otherwise, it will repeat
888 * until the injectmask would be cleaned.
889 *
890 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
891 * is reliable enough to check if the MC is using the
892 * three channels. However, this is not clear at the datasheet.
893 */
894static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
895 const char *data, size_t count)
896{
897 struct i7core_pvt *pvt = mci->pvt_info;
898 u32 injectmask;
899 u64 mask = 0;
900 int rc;
901 long enable;
902
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300903 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300904 return 0;
905
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300906 rc = strict_strtoul(data, 10, &enable);
907 if ((rc < 0))
908 return 0;
909
910 if (enable) {
911 pvt->inject.enable = 1;
912 } else {
913 disable_inject(mci);
914 return count;
915 }
916
917 /* Sets pvt->inject.dimm mask */
918 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300919 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300920 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300921 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300922 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300923 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300924 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300925 }
926
927 /* Sets pvt->inject.rank mask */
928 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300929 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300930 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300931 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300932 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300933 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300934 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300935 }
936
937 /* Sets pvt->inject.bank mask */
938 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300939 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300940 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300941 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300942
943 /* Sets pvt->inject.page mask */
944 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300945 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300946 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300947 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300948
949 /* Sets pvt->inject.column mask */
950 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300951 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300952 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300953 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300954
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300955 /*
956 * bit 0: REPEAT_EN
957 * bits 1-2: MASK_HALF_CACHELINE
958 * bit 3: INJECT_ECC
959 * bit 4: INJECT_ADDR_PARITY
960 */
961
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300962 injectmask = (pvt->inject.type & 1) |
963 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300964 (pvt->inject.type & 0x6) << (3 - 1);
965
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300966 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300967 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300968 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300969
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300970 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300971 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300972 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300973 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
974
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300975 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300976 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
977
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300978 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300979 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300980
981 /*
982 * This is something undocumented, based on my tests
983 * Without writing 8 to this register, errors aren't injected. Not sure
984 * why.
985 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300986 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300987 MC_CFG_CONTROL, 8);
988
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300989 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
990 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300991 mask, pvt->inject.eccmask, injectmask);
992
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300993
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300994 return count;
995}
996
997static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
998 char *data)
999{
1000 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001001 u32 injectmask;
1002
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001003 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001004 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001005
1006 debugf0("Inject error read: 0x%018x\n", injectmask);
1007
1008 if (injectmask & 0x0c)
1009 pvt->inject.enable = 1;
1010
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001011 return sprintf(data, "%d\n", pvt->inject.enable);
1012}
1013
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001014#define DECLARE_COUNTER(param) \
1015static ssize_t i7core_show_counter_##param( \
1016 struct mem_ctl_info *mci, \
1017 char *data) \
1018{ \
1019 struct i7core_pvt *pvt = mci->pvt_info; \
1020 \
1021 debugf1("%s() \n", __func__); \
1022 if (!pvt->ce_count_available || (pvt->is_registered)) \
1023 return sprintf(data, "data unavailable\n"); \
1024 return sprintf(data, "%lu\n", \
1025 pvt->udimm_ce_count[param]); \
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001026}
1027
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001028#define ATTR_COUNTER(param) \
1029 { \
1030 .attr = { \
1031 .name = __stringify(udimm##param), \
1032 .mode = (S_IRUGO | S_IWUSR) \
1033 }, \
1034 .show = i7core_show_counter_##param \
1035 }
1036
1037DECLARE_COUNTER(0);
1038DECLARE_COUNTER(1);
1039DECLARE_COUNTER(2);
1040
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001041/*
1042 * Sysfs struct
1043 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001044
1045
1046static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
1047 ATTR_ADDR_MATCH(channel),
1048 ATTR_ADDR_MATCH(dimm),
1049 ATTR_ADDR_MATCH(rank),
1050 ATTR_ADDR_MATCH(bank),
1051 ATTR_ADDR_MATCH(page),
1052 ATTR_ADDR_MATCH(col),
1053 { .attr = { .name = NULL } }
1054};
1055
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001056static struct mcidev_sysfs_group i7core_inject_addrmatch = {
1057 .name = "inject_addrmatch",
1058 .mcidev_attr = i7core_addrmatch_attrs,
1059};
1060
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001061static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
1062 ATTR_COUNTER(0),
1063 ATTR_COUNTER(1),
1064 ATTR_COUNTER(2),
1065};
1066
1067static struct mcidev_sysfs_group i7core_udimm_counters = {
1068 .name = "all_channel_counts",
1069 .mcidev_attr = i7core_udimm_counters_attrs,
1070};
1071
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001072static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001073 {
1074 .attr = {
1075 .name = "inject_section",
1076 .mode = (S_IRUGO | S_IWUSR)
1077 },
1078 .show = i7core_inject_section_show,
1079 .store = i7core_inject_section_store,
1080 }, {
1081 .attr = {
1082 .name = "inject_type",
1083 .mode = (S_IRUGO | S_IWUSR)
1084 },
1085 .show = i7core_inject_type_show,
1086 .store = i7core_inject_type_store,
1087 }, {
1088 .attr = {
1089 .name = "inject_eccmask",
1090 .mode = (S_IRUGO | S_IWUSR)
1091 },
1092 .show = i7core_inject_eccmask_show,
1093 .store = i7core_inject_eccmask_store,
1094 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001095 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001096 }, {
1097 .attr = {
1098 .name = "inject_enable",
1099 .mode = (S_IRUGO | S_IWUSR)
1100 },
1101 .show = i7core_inject_enable_show,
1102 .store = i7core_inject_enable_store,
1103 },
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001104 { .attr = { .name = NULL } }, /* Reserved for udimm counters */
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001105 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001106};
1107
1108/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001109 Device initialization routines: put/get, init/exit
1110 ****************************************************************************/
1111
1112/*
1113 * i7core_put_devices 'put' all the devices that we have
1114 * reserved via 'get'
1115 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001116static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001117{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001118 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001119
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001120 debugf0(__FILE__ ": %s()\n", __func__);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001121 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001122 struct pci_dev *pdev = i7core_dev->pdev[i];
1123 if (!pdev)
1124 continue;
1125 debugf0("Removing dev %02x:%02x.%d\n",
1126 pdev->bus->number,
1127 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1128 pci_dev_put(pdev);
1129 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001130 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001131 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001132 kfree(i7core_dev);
1133}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001134
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001135static void i7core_put_all_devices(void)
1136{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001137 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001138
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001139 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001140 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001141}
1142
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001143static void i7core_xeon_pci_fixup(int dev_id)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001144{
1145 struct pci_dev *pdev = NULL;
1146 int i;
1147 /*
1148 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1149 * aren't announced by acpi. So, we need to use a legacy scan probing
1150 * to detect them
1151 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001152 pdev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001153 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001154 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001155 pcibios_scan_specific_bus(255-i);
1156 }
1157}
1158
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001159/*
1160 * i7core_get_devices Find and perform 'get' operation on the MCH's
1161 * device/functions we want to reference for this driver
1162 *
1163 * Need to 'get' device 16 func 1 and func 2
1164 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001165int i7core_get_onedevice(struct pci_dev **prev, int devno,
1166 struct pci_id_descr *dev_descr, unsigned n_devs)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001167{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001168 struct i7core_dev *i7core_dev;
1169
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001170 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001171 u8 bus = 0;
1172 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001173
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001174 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001175 dev_descr->dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001176
1177 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001178 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1179 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1180 * to probe for the alternate address in case of failure
1181 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001182 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001183 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -03001184 PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001185
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001186 if (!pdev) {
1187 if (*prev) {
1188 *prev = pdev;
1189 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001190 }
1191
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001192 if (dev_descr->optional)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001193 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001194
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001195 i7core_printk(KERN_ERR,
1196 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001197 dev_descr->dev, dev_descr->func,
1198 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001199
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001200 /* End of list, leave */
1201 return -ENODEV;
1202 }
1203 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001204
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001205 if (bus == 0x3f)
1206 socket = 0;
1207 else
1208 socket = 255 - bus;
1209
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001210 i7core_dev = get_i7core_dev(socket);
1211 if (!i7core_dev) {
1212 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1213 if (!i7core_dev)
1214 return -ENOMEM;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001215 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001216 GFP_KERNEL);
1217 if (!i7core_dev->pdev)
1218 return -ENOMEM;
1219 i7core_dev->socket = socket;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001220 i7core_dev->n_devs = n_devs;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001221 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001222 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001223
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001224 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001225 i7core_printk(KERN_ERR,
1226 "Duplicated device for "
1227 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001228 bus, dev_descr->dev, dev_descr->func,
1229 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001230 pci_dev_put(pdev);
1231 return -ENODEV;
1232 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001233
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001234 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001235
1236 /* Sanity check */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001237 if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1238 PCI_FUNC(pdev->devfn) != dev_descr->func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001239 i7core_printk(KERN_ERR,
1240 "Device PCI ID %04x:%04x "
1241 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001242 PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001243 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001244 bus, dev_descr->dev, dev_descr->func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001245 return -ENODEV;
1246 }
1247
1248 /* Be sure that the device is enabled */
1249 if (unlikely(pci_enable_device(pdev) < 0)) {
1250 i7core_printk(KERN_ERR,
1251 "Couldn't enable "
1252 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001253 bus, dev_descr->dev, dev_descr->func,
1254 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001255 return -ENODEV;
1256 }
1257
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001258 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001259 socket, bus, dev_descr->dev,
1260 dev_descr->func,
1261 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001262
1263 *prev = pdev;
1264
1265 return 0;
1266}
1267
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001268static int i7core_get_devices(struct pci_id_descr dev_descr[], unsigned n_devs)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001269{
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001270 int i, rc;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001271 struct pci_dev *pdev = NULL;
1272
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001273 for (i = 0; i < n_devs; i++) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001274 pdev = NULL;
1275 do {
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001276 rc = i7core_get_onedevice(&pdev, i, &dev_descr[i],
1277 n_devs);
1278 if (rc < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001279 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001280 return -ENODEV;
1281 }
1282 } while (pdev);
1283 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001284
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001285 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001286}
1287
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001288static int mci_bind_devs(struct mem_ctl_info *mci,
1289 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001290{
1291 struct i7core_pvt *pvt = mci->pvt_info;
1292 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001293 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001294
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001295 /* Associates i7core_dev and mci for future usage */
1296 pvt->i7core_dev = i7core_dev;
1297 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001298
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001299 pvt->is_registered = 0;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001300 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001301 pdev = i7core_dev->pdev[i];
1302 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001303 continue;
1304
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001305 func = PCI_FUNC(pdev->devfn);
1306 slot = PCI_SLOT(pdev->devfn);
1307 if (slot == 3) {
1308 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001309 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001310 pvt->pci_mcr[func] = pdev;
1311 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1312 if (unlikely(func > MAX_CHAN_FUNC))
1313 goto error;
1314 pvt->pci_ch[slot - 4][func] = pdev;
1315 } else if (!slot && !func)
1316 pvt->pci_noncore = pdev;
1317 else
1318 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001319
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001320 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1321 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1322 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001323
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001324 if (PCI_SLOT(pdev->devfn) == 3 &&
1325 PCI_FUNC(pdev->devfn) == 2)
1326 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001327 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001328
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001329 /*
1330 * Add extra nodes to count errors on udimm
1331 * For registered memory, this is not needed, since the counters
1332 * are already displayed at the standard locations
1333 */
1334 if (!pvt->is_registered)
1335 i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp =
1336 &i7core_udimm_counters;
1337
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001338 return 0;
1339
1340error:
1341 i7core_printk(KERN_ERR, "Device %d, function %d "
1342 "is out of the expected range\n",
1343 slot, func);
1344 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001345}
1346
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001347/****************************************************************************
1348 Error check routines
1349 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001350static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001351 int chan, int dimm, int add)
1352{
1353 char *msg;
1354 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001355 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001356
1357 for (i = 0; i < add; i++) {
1358 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001359 "(Socket=%d channel=%d dimm=%d)",
1360 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001361
1362 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1363 kfree (msg);
1364 }
1365}
1366
1367static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001368 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001369{
1370 struct i7core_pvt *pvt = mci->pvt_info;
1371 int add0 = 0, add1 = 0, add2 = 0;
1372 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001373 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001374 /* Updates CE counters */
1375
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001376 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1377 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1378 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001379
1380 if (add2 < 0)
1381 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001382 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001383
1384 if (add1 < 0)
1385 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001386 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001387
1388 if (add0 < 0)
1389 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001390 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001391 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001392 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001393
1394 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001395 pvt->rdimm_last_ce_count[chan][2] = new2;
1396 pvt->rdimm_last_ce_count[chan][1] = new1;
1397 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001398
1399 /*updated the edac core */
1400 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001401 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001402 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001403 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001404 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001405 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001406
1407}
1408
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001409static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001410{
1411 struct i7core_pvt *pvt = mci->pvt_info;
1412 u32 rcv[3][2];
1413 int i, new0, new1, new2;
1414
1415 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001416 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001417 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001418 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001419 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001420 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001421 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001422 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001423 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001424 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001425 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001426 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001427 &rcv[2][1]);
1428 for (i = 0 ; i < 3; i++) {
1429 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1430 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1431 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001432 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001433 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1434 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1435 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1436 } else {
1437 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1438 DIMM_BOT_COR_ERR(rcv[i][0]);
1439 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1440 DIMM_BOT_COR_ERR(rcv[i][1]);
1441 new2 = 0;
1442 }
1443
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001444 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001445 }
1446}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001447
1448/* This function is based on the device 3 function 4 registers as described on:
1449 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1450 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1451 * also available at:
1452 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1453 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001454static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001455{
1456 struct i7core_pvt *pvt = mci->pvt_info;
1457 u32 rcv1, rcv0;
1458 int new0, new1, new2;
1459
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001460 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001461 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001462 return;
1463 }
1464
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001465 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001466 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1467 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001468
1469 /* Store the new values */
1470 new2 = DIMM2_COR_ERR(rcv1);
1471 new1 = DIMM1_COR_ERR(rcv0);
1472 new0 = DIMM0_COR_ERR(rcv0);
1473
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001474 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001475 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001476 /* Updates CE counters */
1477 int add0, add1, add2;
1478
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001479 add2 = new2 - pvt->udimm_last_ce_count[2];
1480 add1 = new1 - pvt->udimm_last_ce_count[1];
1481 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001482
1483 if (add2 < 0)
1484 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001485 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001486
1487 if (add1 < 0)
1488 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001489 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001490
1491 if (add0 < 0)
1492 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001493 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001494
1495 if (add0 | add1 | add2)
1496 i7core_printk(KERN_ERR, "New Corrected error(s): "
1497 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1498 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001499 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001500 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001501
1502 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001503 pvt->udimm_last_ce_count[2] = new2;
1504 pvt->udimm_last_ce_count[1] = new1;
1505 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001506}
1507
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001508/*
1509 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1510 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001511 * Nehalem are defined as family 0x06, model 0x1a
1512 *
1513 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001514 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001515 * m->status MSR_IA32_MC8_STATUS
1516 * m->addr MSR_IA32_MC8_ADDR
1517 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001518 * In the case of Nehalem, the error information is masked at .status and .misc
1519 * fields
1520 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001521static void i7core_mce_output_error(struct mem_ctl_info *mci,
1522 struct mce *m)
1523{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001524 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001525 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001526 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001527 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001528 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1529 u32 dimm = (m->misc >> 16) & 0x3;
1530 u32 channel = (m->misc >> 18) & 0x3;
1531 u32 syndrome = m->misc >> 32;
1532 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001533 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001534
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001535 if (m->mcgstatus & 1)
1536 type = "FATAL";
1537 else
1538 type = "NON_FATAL";
1539
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001540 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001541 case 0:
1542 optype = "generic undef request";
1543 break;
1544 case 1:
1545 optype = "read error";
1546 break;
1547 case 2:
1548 optype = "write error";
1549 break;
1550 case 3:
1551 optype = "addr/cmd error";
1552 break;
1553 case 4:
1554 optype = "scrubbing error";
1555 break;
1556 default:
1557 optype = "reserved";
1558 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001559 }
1560
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001561 switch (errnum) {
1562 case 16:
1563 err = "read ECC error";
1564 break;
1565 case 17:
1566 err = "RAS ECC error";
1567 break;
1568 case 18:
1569 err = "write parity error";
1570 break;
1571 case 19:
1572 err = "redundacy loss";
1573 break;
1574 case 20:
1575 err = "reserved";
1576 break;
1577 case 21:
1578 err = "memory range error";
1579 break;
1580 case 22:
1581 err = "RTID out of range";
1582 break;
1583 case 23:
1584 err = "address parity error";
1585 break;
1586 case 24:
1587 err = "byte enable parity error";
1588 break;
1589 default:
1590 err = "unknown";
1591 }
1592
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001593 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001594 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001595 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001596 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001597 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001598 syndrome, core_err_cnt, (long long)m->status,
1599 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001600
1601 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001602
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001603 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001604
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001605 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001606 if (m->mcgstatus & 1)
1607 edac_mc_handle_fbd_ue(mci, csrow, 0,
1608 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001609 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001610 edac_mc_handle_fbd_ce(mci, csrow,
1611 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001612
1613 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001614}
1615
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001616/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001617 * i7core_check_error Retrieve and process errors reported by the
1618 * hardware. Called by the Core module.
1619 */
1620static void i7core_check_error(struct mem_ctl_info *mci)
1621{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001622 struct i7core_pvt *pvt = mci->pvt_info;
1623 int i;
1624 unsigned count = 0;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001625 struct mce *m;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001626
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001627 /*
1628 * MCE first step: Copy all mce errors into a temporary buffer
1629 * We use a double buffering here, to reduce the risk of
1630 * loosing an error.
1631 */
1632 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001633 count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
1634 % MCE_LOG_LEN;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001635 if (!count)
1636 return;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001637
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001638 m = pvt->mce_outentry;
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001639 if (pvt->mce_in + count > MCE_LOG_LEN) {
1640 unsigned l = MCE_LOG_LEN - pvt->mce_in;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001641
1642 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
1643 smp_wmb();
1644 pvt->mce_in = 0;
1645 count -= l;
1646 m += l;
1647 }
1648 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
1649 smp_wmb();
1650 pvt->mce_in += count;
1651
1652 smp_rmb();
1653 if (pvt->mce_overrun) {
1654 i7core_printk(KERN_ERR, "Lost %d memory errors\n",
1655 pvt->mce_overrun);
1656 smp_wmb();
1657 pvt->mce_overrun = 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001658 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001659
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001660 /*
1661 * MCE second step: parse errors and display
1662 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001663 for (i = 0; i < count; i++)
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001664 i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001665
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001666 /*
1667 * Now, let's increment CE error counts
1668 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001669 if (!pvt->is_registered)
1670 i7core_udimm_check_mc_ecc_err(mci);
1671 else
1672 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001673}
1674
1675/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001676 * i7core_mce_check_error Replicates mcelog routine to get errors
1677 * This routine simply queues mcelog errors, and
1678 * return. The error itself should be handled later
1679 * by i7core_check_error.
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001680 * WARNING: As this routine should be called at NMI time, extra care should
1681 * be taken to avoid deadlocks, and to be as fast as possible.
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001682 */
1683static int i7core_mce_check_error(void *priv, struct mce *mce)
1684{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001685 struct mem_ctl_info *mci = priv;
1686 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001687
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001688 /*
1689 * Just let mcelog handle it if the error is
1690 * outside the memory controller
1691 */
1692 if (((mce->status & 0xffff) >> 7) != 1)
1693 return 0;
1694
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001695 /* Bank 8 registers are the only ones that we know how to handle */
1696 if (mce->bank != 8)
1697 return 0;
1698
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001699 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001700 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001701 return 0;
1702
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001703 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001704 if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001705 smp_wmb();
1706 pvt->mce_overrun++;
1707 return 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001708 }
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001709
1710 /* Copy memory error at the ringbuffer */
1711 memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001712 smp_wmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001713 pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001714
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001715 /* Handle fatal errors immediately */
1716 if (mce->mcgstatus & 1)
1717 i7core_check_error(mci);
1718
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001719 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001720 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001721}
1722
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001723static int i7core_register_mci(struct i7core_dev *i7core_dev,
1724 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001725{
1726 struct mem_ctl_info *mci;
1727 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001728 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001729 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001730
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001731 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001732 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1733 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001734 if (unlikely(!mci))
1735 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001736
1737 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1738
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001739 /* record ptr to the generic device */
1740 mci->dev = &i7core_dev->pdev[0]->dev;
1741
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001742 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001743 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001744
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001745 /*
1746 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1747 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1748 * memory channels
1749 */
1750 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001751 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1752 mci->edac_cap = EDAC_FLAG_NONE;
1753 mci->mod_name = "i7core_edac.c";
1754 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001755 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1756 i7core_dev->socket);
1757 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001758 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001759 mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001760 /* Set the function pointer to an actual operation function */
1761 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001762
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001763 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001764 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001765 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001766 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001767
1768 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001769 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001770
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001771 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001772 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001773 debugf0("MC: " __FILE__
1774 ": %s(): failed edac_mc_add_mc()\n", __func__);
1775 /* FIXME: perhaps some code should go here that disables error
1776 * reporting if we just enabled it
1777 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001778
1779 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001780 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001781 }
1782
1783 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001784 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1785 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001786 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001787 printk(KERN_WARNING
1788 "%s(): Unable to create PCI control\n",
1789 __func__);
1790 printk(KERN_WARNING
1791 "%s(): PCI error report via EDAC not setup\n",
1792 __func__);
1793 }
1794
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001795 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001796 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001797 pvt->inject.dimm = -1;
1798 pvt->inject.rank = -1;
1799 pvt->inject.bank = -1;
1800 pvt->inject.page = -1;
1801 pvt->inject.col = -1;
1802
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001803 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001804 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001805 pvt->edac_mce.check_error = i7core_mce_check_error;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001806
1807 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001808 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001809 debugf0("MC: " __FILE__
1810 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001811 }
1812
1813fail:
1814 edac_mc_free(mci);
1815 return rc;
1816}
1817
1818/*
1819 * i7core_probe Probe for ONE instance of device to see if it is
1820 * present.
1821 * return:
1822 * 0 for FOUND a device
1823 * < 0 for error code
1824 */
1825static int __devinit i7core_probe(struct pci_dev *pdev,
1826 const struct pci_device_id *id)
1827{
1828 int dev_idx = id->driver_data;
1829 int rc;
1830 struct i7core_dev *i7core_dev;
1831
1832 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001833 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001834 */
1835 if (unlikely(dev_idx >= 1))
1836 return -EINVAL;
1837
1838 /* get the pci devices we want to reserve for our use */
1839 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001840
1841 rc = i7core_get_devices(pci_dev_descr_i7core,
1842 ARRAY_SIZE(pci_dev_descr_i7core));
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001843 if (unlikely(rc < 0))
1844 goto fail0;
1845
1846 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1847 int channels;
1848 int csrows;
1849
1850 /* Check the number of active and not disabled channels */
1851 rc = i7core_get_active_channels(i7core_dev->socket,
1852 &channels, &csrows);
1853 if (unlikely(rc < 0))
1854 goto fail1;
1855
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001856 rc = i7core_register_mci(i7core_dev, channels, csrows);
1857 if (unlikely(rc < 0))
1858 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001859 }
1860
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001861 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001862
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001863 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001864 return 0;
1865
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001866fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001867 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001868fail0:
1869 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001870 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001871}
1872
1873/*
1874 * i7core_remove destructor for one instance of device
1875 *
1876 */
1877static void __devexit i7core_remove(struct pci_dev *pdev)
1878{
1879 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001880 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001881
1882 debugf0(__FILE__ ": %s()\n", __func__);
1883
1884 if (i7core_pci)
1885 edac_pci_release_generic_ctl(i7core_pci);
1886
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001887 /*
1888 * we have a trouble here: pdev value for removal will be wrong, since
1889 * it will point to the X58 register used to detect that the machine
1890 * is a Nehalem or upper design. However, due to the way several PCI
1891 * devices are grouped together to provide MC functionality, we need
1892 * to use a different method for releasing the devices
1893 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001894
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001895 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001896 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1897 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1898 if (mci) {
1899 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001900
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001901 i7core_dev = pvt->i7core_dev;
1902 edac_mce_unregister(&pvt->edac_mce);
1903 kfree(mci->ctl_name);
1904 edac_mc_free(mci);
1905 i7core_put_devices(i7core_dev);
1906 } else {
1907 i7core_printk(KERN_ERR,
1908 "Couldn't find mci for socket %d\n",
1909 i7core_dev->socket);
1910 }
1911 }
1912 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001913}
1914
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001915MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1916
1917/*
1918 * i7core_driver pci_driver structure for this module
1919 *
1920 */
1921static struct pci_driver i7core_driver = {
1922 .name = "i7core_edac",
1923 .probe = i7core_probe,
1924 .remove = __devexit_p(i7core_remove),
1925 .id_table = i7core_pci_tbl,
1926};
1927
1928/*
1929 * i7core_init Module entry function
1930 * Try to initialize this module for its devices
1931 */
1932static int __init i7core_init(void)
1933{
1934 int pci_rc;
1935
1936 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1937
1938 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1939 opstate_init();
1940
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001941 i7core_xeon_pci_fixup(pci_dev_descr_i7core[0].dev_id);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001942
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001943 pci_rc = pci_register_driver(&i7core_driver);
1944
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001945 if (pci_rc >= 0)
1946 return 0;
1947
1948 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1949 pci_rc);
1950
1951 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001952}
1953
1954/*
1955 * i7core_exit() Module exit function
1956 * Unregister the driver
1957 */
1958static void __exit i7core_exit(void)
1959{
1960 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1961 pci_unregister_driver(&i7core_driver);
1962}
1963
1964module_init(i7core_init);
1965module_exit(i7core_exit);
1966
1967MODULE_LICENSE("GPL");
1968MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1969MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1970MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1971 I7CORE_REVISION);
1972
1973module_param(edac_op_state, int, 0444);
1974MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");