blob: d3f5c016c5ebc7cc270c0cf70ff6909dfb147f0a [file] [log] [blame]
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001/* Intel 7 core Memory Controller kernel module (Nehalem)
2 *
3 * This file may be distributed under the terms of the
4 * GNU General Public License version 2 only.
5 *
6 * Copyright (c) 2009 by:
7 * Mauro Carvalho Chehab <mchehab@redhat.com>
8 *
9 * Red Hat Inc. http://www.redhat.com
10 *
11 * Forked and adapted from the i5400_edac driver
12 *
13 * Based on the following public Intel datasheets:
14 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
15 * Datasheet, Volume 2:
16 * http://download.intel.com/design/processor/datashts/320835.pdf
17 * Intel Xeon Processor 5500 Series Datasheet Volume 2
18 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
19 * also available at:
20 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
21 */
22
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030023#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/pci.h>
26#include <linux/pci_ids.h>
27#include <linux/slab.h>
Randy Dunlap3b918c12009-11-08 01:36:40 -020028#include <linux/delay.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030029#include <linux/edac.h>
30#include <linux/mmzone.h>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030031#include <linux/edac_mce.h>
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030032#include <linux/smp.h>
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -030033#include <asm/processor.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030034
35#include "edac_core.h"
36
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030037/*
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030038 * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
39 * registers start at bus 255, and are not reported by BIOS.
40 * We currently find devices with only 2 sockets. In order to support more QPI
41 * Quick Path Interconnect, just increment this number.
42 */
43#define MAX_SOCKET_BUSES 2
44
45
46/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030047 * Alter this version for the module when modifications are made
48 */
49#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
50#define EDAC_MOD_STR "i7core_edac"
51
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030052/*
53 * Debug macros
54 */
55#define i7core_printk(level, fmt, arg...) \
56 edac_printk(level, "i7core", fmt, ##arg)
57
58#define i7core_mc_printk(mci, level, fmt, arg...) \
59 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
60
61/*
62 * i7core Memory Controller Registers
63 */
64
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030065 /* OFFSETS for Device 0 Function 0 */
66
67#define MC_CFG_CONTROL 0x90
68
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030069 /* OFFSETS for Device 3 Function 0 */
70
71#define MC_CONTROL 0x48
72#define MC_STATUS 0x4c
73#define MC_MAX_DOD 0x64
74
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030075/*
76 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
77 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
78 */
79
80#define MC_TEST_ERR_RCV1 0x60
81 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
82
83#define MC_TEST_ERR_RCV0 0x64
84 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
85 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
86
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -030087/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
88#define MC_COR_ECC_CNT_0 0x80
89#define MC_COR_ECC_CNT_1 0x84
90#define MC_COR_ECC_CNT_2 0x88
91#define MC_COR_ECC_CNT_3 0x8c
92#define MC_COR_ECC_CNT_4 0x90
93#define MC_COR_ECC_CNT_5 0x94
94
95#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
96#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
97
98
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030099 /* OFFSETS for Devices 4,5 and 6 Function 0 */
100
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300101#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
102 #define THREE_DIMMS_PRESENT (1 << 24)
103 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
104 #define QUAD_RANK_PRESENT (1 << 22)
105 #define REGISTERED_DIMM (1 << 15)
106
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300107#define MC_CHANNEL_MAPPER 0x60
108 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
109 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
110
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300111#define MC_CHANNEL_RANK_PRESENT 0x7c
112 #define RANK_PRESENT_MASK 0xffff
113
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300114#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300115#define MC_CHANNEL_ERROR_MASK 0xf8
116#define MC_CHANNEL_ERROR_INJECT 0xfc
117 #define INJECT_ADDR_PARITY 0x10
118 #define INJECT_ECC 0x08
119 #define MASK_CACHELINE 0x06
120 #define MASK_FULL_CACHELINE 0x06
121 #define MASK_MSB32_CACHELINE 0x04
122 #define MASK_LSB32_CACHELINE 0x02
123 #define NO_MASK_CACHELINE 0x00
124 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300125
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300126 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300127
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300128#define MC_DOD_CH_DIMM0 0x48
129#define MC_DOD_CH_DIMM1 0x4c
130#define MC_DOD_CH_DIMM2 0x50
131 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
132 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
133 #define DIMM_PRESENT_MASK (1 << 9)
134 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300135 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
136 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
137 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
138 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300139 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300140 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300141 #define MC_DOD_NUMCOL_MASK 3
142 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300143
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300144#define MC_RANK_PRESENT 0x7c
145
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300146#define MC_SAG_CH_0 0x80
147#define MC_SAG_CH_1 0x84
148#define MC_SAG_CH_2 0x88
149#define MC_SAG_CH_3 0x8c
150#define MC_SAG_CH_4 0x90
151#define MC_SAG_CH_5 0x94
152#define MC_SAG_CH_6 0x98
153#define MC_SAG_CH_7 0x9c
154
155#define MC_RIR_LIMIT_CH_0 0x40
156#define MC_RIR_LIMIT_CH_1 0x44
157#define MC_RIR_LIMIT_CH_2 0x48
158#define MC_RIR_LIMIT_CH_3 0x4C
159#define MC_RIR_LIMIT_CH_4 0x50
160#define MC_RIR_LIMIT_CH_5 0x54
161#define MC_RIR_LIMIT_CH_6 0x58
162#define MC_RIR_LIMIT_CH_7 0x5C
163#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
164
165#define MC_RIR_WAY_CH 0x80
166 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
167 #define MC_RIR_WAY_RANK_MASK 0x7
168
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300169/*
170 * i7core structs
171 */
172
173#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300174#define MAX_DIMMS 3 /* Max DIMMS per channel */
175#define MAX_MCR_FUNC 4
176#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300177
178struct i7core_info {
179 u32 mc_control;
180 u32 mc_status;
181 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300182 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300183};
184
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300185
186struct i7core_inject {
187 int enable;
188
189 u32 section;
190 u32 type;
191 u32 eccmask;
192
193 /* Error address mask */
194 int channel, dimm, rank, bank, page, col;
195};
196
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300197struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300198 u32 ranks;
199 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300200};
201
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300202struct pci_id_descr {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300203 int dev;
204 int func;
205 int dev_id;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300206 int optional;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300207};
208
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300209struct i7core_dev {
210 struct list_head list;
211 u8 socket;
212 struct pci_dev **pdev;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300213 int n_devs;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300214 struct mem_ctl_info *mci;
215};
216
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300217struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300218 struct pci_dev *pci_noncore;
219 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
220 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
221
222 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300223
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300224 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300225 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300226 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300227
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300228 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300229
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300230 int ce_count_available;
231 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300232
233 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300234 unsigned long udimm_ce_count[MAX_DIMMS];
235 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300236 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300237 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
238 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300239
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300240 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300241
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300242 /* mcelog glue */
243 struct edac_mce edac_mce;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300244
245 /* Fifo double buffers */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300246 struct mce mce_entry[MCE_LOG_LEN];
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300247 struct mce mce_outentry[MCE_LOG_LEN];
248
249 /* Fifo in/out counters */
250 unsigned mce_in, mce_out;
251
252 /* Count indicator to show errors not got */
253 unsigned mce_overrun;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300254};
255
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300256/* Static vars */
257static LIST_HEAD(i7core_edac_list);
258static DEFINE_MUTEX(i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300259
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300260#define PCI_DESCR(device, function, device_id) \
261 .dev = (device), \
262 .func = (function), \
263 .dev_id = (device_id)
264
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300265struct pci_id_descr pci_dev_descr_i7core[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300266 /* Memory controller */
267 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
268 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300269 /* Exists only for RDIMM */
270 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300271 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
272
273 /* Channel 0 */
274 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
275 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
276 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
277 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
278
279 /* Channel 1 */
280 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
281 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
282 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
283 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
284
285 /* Channel 2 */
286 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
287 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
288 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
289 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300290
291 /* Generic Non-core registers */
292 /*
293 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
294 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
295 * the probing code needs to test for the other address in case of
296 * failure of this one
297 */
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -0300298 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300299
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300300};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300301
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300302struct pci_id_descr pci_dev_descr_lynnfield[] = {
303 { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) },
304 { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) },
305 { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) },
306
307 { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) },
308 { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) },
309 { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) },
310 { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) },
311
312 { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) },
313 { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
314 { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
315 { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
316
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -0300317 /*
318 * This is the PCI device has an alternate address on some
319 * processors like Core i7 860
320 */
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300321 { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
322};
323
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300324/*
325 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300326 */
327static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300328 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -0300329 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300330 {0,} /* 0 terminated list. */
331};
332
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300333static struct edac_pci_ctl_info *i7core_pci;
334
335/****************************************************************************
336 Anciliary status routines
337 ****************************************************************************/
338
339 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300340#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
341#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300342
343 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300344#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300345#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300346
347 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300348static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300349{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300350 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300351}
352
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300353static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300354{
355 static int ranks[4] = { 1, 2, 4, -EINVAL };
356
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300357 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300358}
359
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300360static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300361{
362 static int banks[4] = { 4, 8, 16, -EINVAL };
363
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300364 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300365}
366
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300367static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300368{
369 static int rows[8] = {
370 1 << 12, 1 << 13, 1 << 14, 1 << 15,
371 1 << 16, -EINVAL, -EINVAL, -EINVAL,
372 };
373
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300374 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300375}
376
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300377static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300378{
379 static int cols[8] = {
380 1 << 10, 1 << 11, 1 << 12, -EINVAL,
381 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300382 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300383}
384
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300385static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300386{
387 struct i7core_dev *i7core_dev;
388
389 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
390 if (i7core_dev->socket == socket)
391 return i7core_dev;
392 }
393
394 return NULL;
395}
396
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300397/****************************************************************************
398 Memory check routines
399 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300400static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
401 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300402{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300403 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300404 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300405
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300406 if (!i7core_dev)
407 return NULL;
408
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300409 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300410 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300411 continue;
412
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300413 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
414 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
415 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300416 }
417 }
418
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300419 return NULL;
420}
421
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300422/**
423 * i7core_get_active_channels() - gets the number of channels and csrows
424 * @socket: Quick Path Interconnect socket
425 * @channels: Number of channels that will be returned
426 * @csrows: Number of csrows found
427 *
428 * Since EDAC core needs to know in advance the number of available channels
429 * and csrows, in order to allocate memory for csrows/channels, it is needed
430 * to run two similar steps. At the first step, implemented on this function,
431 * it checks the number of csrows/channels present at one socket.
432 * this is used in order to properly allocate the size of mci components.
433 *
434 * It should be noticed that none of the current available datasheets explain
435 * or even mention how csrows are seen by the memory controller. So, we need
436 * to add a fake description for csrows.
437 * So, this driver is attributing one DIMM memory for one csrow.
438 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300439static int i7core_get_active_channels(u8 socket, unsigned *channels,
440 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300441{
442 struct pci_dev *pdev = NULL;
443 int i, j;
444 u32 status, control;
445
446 *channels = 0;
447 *csrows = 0;
448
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300449 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300450 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300451 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
452 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300453 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300454 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300455
456 /* Device 3 function 0 reads */
457 pci_read_config_dword(pdev, MC_STATUS, &status);
458 pci_read_config_dword(pdev, MC_CONTROL, &control);
459
460 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300461 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300462 /* Check if the channel is active */
463 if (!(control & (1 << (8 + i))))
464 continue;
465
466 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300467 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300468 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300469
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300470 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300471 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300472 i7core_printk(KERN_ERR, "Couldn't find socket %d "
473 "fn %d.%d!!!\n",
474 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300475 return -ENODEV;
476 }
477 /* Devices 4-6 function 1 */
478 pci_read_config_dword(pdev,
479 MC_DOD_CH_DIMM0, &dimm_dod[0]);
480 pci_read_config_dword(pdev,
481 MC_DOD_CH_DIMM1, &dimm_dod[1]);
482 pci_read_config_dword(pdev,
483 MC_DOD_CH_DIMM2, &dimm_dod[2]);
484
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300485 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300486
487 for (j = 0; j < 3; j++) {
488 if (!DIMM_PRESENT(dimm_dod[j]))
489 continue;
490 (*csrows)++;
491 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300492 }
493
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300494 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300495 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300496
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300497 return 0;
498}
499
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300500static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300501{
502 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300503 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300504 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300505 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300506 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300507 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300508 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300509
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300510 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300511 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300512 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300513 return -ENODEV;
514
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300515 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300516 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
517 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
518 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
519 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300520
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300521 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300522 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300523 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300524
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300525 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300526 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300527 if (ECCx8(pvt))
528 mode = EDAC_S8ECD8ED;
529 else
530 mode = EDAC_S4ECD4ED;
531 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300532 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300533 mode = EDAC_NONE;
534 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300535
536 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300537 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
538 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300539 numdimms(pvt->info.max_dod),
540 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300541 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300542 numrow(pvt->info.max_dod >> 6),
543 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300544
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300545 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300546 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300547
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300548 if (!pvt->pci_ch[i][0])
549 continue;
550
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300551 if (!CH_ACTIVE(pvt, i)) {
552 debugf0("Channel %i is not active\n", i);
553 continue;
554 }
555 if (CH_DISABLED(pvt, i)) {
556 debugf0("Channel %i is disabled\n", i);
557 continue;
558 }
559
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300560 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300561 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300562 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
563
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300564 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300565 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300566
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300567 if (data & REGISTERED_DIMM)
568 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300569 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300570 mtype = MEM_DDR3;
571#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300572 if (data & THREE_DIMMS_PRESENT)
573 pvt->channel[i].dimms = 3;
574 else if (data & SINGLE_QUAD_RANK_PRESENT)
575 pvt->channel[i].dimms = 1;
576 else
577 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300578#endif
579
580 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300581 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300582 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300583 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300584 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300585 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300586 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300587
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300588 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300589 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300590 i,
591 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
592 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300593 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300594 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300595
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300596 for (j = 0; j < 3; j++) {
597 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300598 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300599
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300600 if (!DIMM_PRESENT(dimm_dod[j]))
601 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300602
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300603 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
604 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
605 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
606 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300607
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300608 /* DDR3 has 8 I/O banks */
609 size = (rows * cols * banks * ranks) >> (20 - 3);
610
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300611 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300612
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300613 debugf0("\tdimm %d %d Mb offset: %x, "
614 "bank: %d, rank: %d, row: %#x, col: %#x\n",
615 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300616 RANKOFFSET(dimm_dod[j]),
617 banks, ranks, rows, cols);
618
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300619#if PAGE_SHIFT > 20
620 npages = size >> (PAGE_SHIFT - 20);
621#else
622 npages = size << (20 - PAGE_SHIFT);
623#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300624
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300625 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300626 csr->first_page = last_page + 1;
627 last_page += npages;
628 csr->last_page = last_page;
629 csr->nr_pages = npages;
630
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300631 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300632 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300633 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300634 csr->nr_channels = 1;
635
636 csr->channels[0].chan_idx = i;
637 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300638
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300639 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300640
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300641 switch (banks) {
642 case 4:
643 csr->dtype = DEV_X4;
644 break;
645 case 8:
646 csr->dtype = DEV_X8;
647 break;
648 case 16:
649 csr->dtype = DEV_X16;
650 break;
651 default:
652 csr->dtype = DEV_UNKNOWN;
653 }
654
655 csr->edac_mode = mode;
656 csr->mtype = mtype;
657
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300658 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300659 }
660
661 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
662 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
663 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
664 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
665 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
666 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
667 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
668 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300669 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300670 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300671 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300672 (value[j] >> 27) & 0x1,
673 (value[j] >> 24) & 0x7,
674 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300675 }
676
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300677 return 0;
678}
679
680/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300681 Error insertion routines
682 ****************************************************************************/
683
684/* The i7core has independent error injection features per channel.
685 However, to have a simpler code, we don't allow enabling error injection
686 on more than one channel.
687 Also, since a change at an inject parameter will be applied only at enable,
688 we're disabling error injection on all write calls to the sysfs nodes that
689 controls the error code injection.
690 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300691static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300692{
693 struct i7core_pvt *pvt = mci->pvt_info;
694
695 pvt->inject.enable = 0;
696
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300697 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300698 return -ENODEV;
699
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300700 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300701 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300702
703 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300704}
705
706/*
707 * i7core inject inject.section
708 *
709 * accept and store error injection inject.section value
710 * bit 0 - refers to the lower 32-byte half cacheline
711 * bit 1 - refers to the upper 32-byte half cacheline
712 */
713static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
714 const char *data, size_t count)
715{
716 struct i7core_pvt *pvt = mci->pvt_info;
717 unsigned long value;
718 int rc;
719
720 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300721 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300722
723 rc = strict_strtoul(data, 10, &value);
724 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300725 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300726
727 pvt->inject.section = (u32) value;
728 return count;
729}
730
731static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
732 char *data)
733{
734 struct i7core_pvt *pvt = mci->pvt_info;
735 return sprintf(data, "0x%08x\n", pvt->inject.section);
736}
737
738/*
739 * i7core inject.type
740 *
741 * accept and store error injection inject.section value
742 * bit 0 - repeat enable - Enable error repetition
743 * bit 1 - inject ECC error
744 * bit 2 - inject parity error
745 */
746static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
747 const char *data, size_t count)
748{
749 struct i7core_pvt *pvt = mci->pvt_info;
750 unsigned long value;
751 int rc;
752
753 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300754 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300755
756 rc = strict_strtoul(data, 10, &value);
757 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300758 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300759
760 pvt->inject.type = (u32) value;
761 return count;
762}
763
764static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
765 char *data)
766{
767 struct i7core_pvt *pvt = mci->pvt_info;
768 return sprintf(data, "0x%08x\n", pvt->inject.type);
769}
770
771/*
772 * i7core_inject_inject.eccmask_store
773 *
774 * The type of error (UE/CE) will depend on the inject.eccmask value:
775 * Any bits set to a 1 will flip the corresponding ECC bit
776 * Correctable errors can be injected by flipping 1 bit or the bits within
777 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
778 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
779 * uncorrectable error to be injected.
780 */
781static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
782 const char *data, size_t count)
783{
784 struct i7core_pvt *pvt = mci->pvt_info;
785 unsigned long value;
786 int rc;
787
788 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300789 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300790
791 rc = strict_strtoul(data, 10, &value);
792 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300793 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300794
795 pvt->inject.eccmask = (u32) value;
796 return count;
797}
798
799static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
800 char *data)
801{
802 struct i7core_pvt *pvt = mci->pvt_info;
803 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
804}
805
806/*
807 * i7core_addrmatch
808 *
809 * The type of error (UE/CE) will depend on the inject.eccmask value:
810 * Any bits set to a 1 will flip the corresponding ECC bit
811 * Correctable errors can be injected by flipping 1 bit or the bits within
812 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
813 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
814 * uncorrectable error to be injected.
815 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300816
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300817#define DECLARE_ADDR_MATCH(param, limit) \
818static ssize_t i7core_inject_store_##param( \
819 struct mem_ctl_info *mci, \
820 const char *data, size_t count) \
821{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300822 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300823 long value; \
824 int rc; \
825 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300826 debugf1("%s()\n", __func__); \
827 pvt = mci->pvt_info; \
828 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300829 if (pvt->inject.enable) \
830 disable_inject(mci); \
831 \
Mauro Carvalho Chehab4f87fad2009-10-04 11:54:56 -0300832 if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300833 value = -1; \
834 else { \
835 rc = strict_strtoul(data, 10, &value); \
836 if ((rc < 0) || (value >= limit)) \
837 return -EIO; \
838 } \
839 \
840 pvt->inject.param = value; \
841 \
842 return count; \
843} \
844 \
845static ssize_t i7core_inject_show_##param( \
846 struct mem_ctl_info *mci, \
847 char *data) \
848{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300849 struct i7core_pvt *pvt; \
850 \
851 pvt = mci->pvt_info; \
852 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300853 if (pvt->inject.param < 0) \
854 return sprintf(data, "any\n"); \
855 else \
856 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300857}
858
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300859#define ATTR_ADDR_MATCH(param) \
860 { \
861 .attr = { \
862 .name = #param, \
863 .mode = (S_IRUGO | S_IWUSR) \
864 }, \
865 .show = i7core_inject_show_##param, \
866 .store = i7core_inject_store_##param, \
867 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300868
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300869DECLARE_ADDR_MATCH(channel, 3);
870DECLARE_ADDR_MATCH(dimm, 3);
871DECLARE_ADDR_MATCH(rank, 4);
872DECLARE_ADDR_MATCH(bank, 32);
873DECLARE_ADDR_MATCH(page, 0x10000);
874DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300875
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300876static int write_and_test(struct pci_dev *dev, int where, u32 val)
877{
878 u32 read;
879 int count;
880
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300881 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
882 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
883 where, val);
884
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300885 for (count = 0; count < 10; count++) {
886 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300887 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300888 pci_write_config_dword(dev, where, val);
889 pci_read_config_dword(dev, where, &read);
890
891 if (read == val)
892 return 0;
893 }
894
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300895 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
896 "write=%08x. Read=%08x\n",
897 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
898 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300899
900 return -EINVAL;
901}
902
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300903/*
904 * This routine prepares the Memory Controller for error injection.
905 * The error will be injected when some process tries to write to the
906 * memory that matches the given criteria.
907 * The criteria can be set in terms of a mask where dimm, rank, bank, page
908 * and col can be specified.
909 * A -1 value for any of the mask items will make the MCU to ignore
910 * that matching criteria for error injection.
911 *
912 * It should be noticed that the error will only happen after a write operation
913 * on a memory that matches the condition. if REPEAT_EN is not enabled at
914 * inject mask, then it will produce just one error. Otherwise, it will repeat
915 * until the injectmask would be cleaned.
916 *
917 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
918 * is reliable enough to check if the MC is using the
919 * three channels. However, this is not clear at the datasheet.
920 */
921static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
922 const char *data, size_t count)
923{
924 struct i7core_pvt *pvt = mci->pvt_info;
925 u32 injectmask;
926 u64 mask = 0;
927 int rc;
928 long enable;
929
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300930 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300931 return 0;
932
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300933 rc = strict_strtoul(data, 10, &enable);
934 if ((rc < 0))
935 return 0;
936
937 if (enable) {
938 pvt->inject.enable = 1;
939 } else {
940 disable_inject(mci);
941 return count;
942 }
943
944 /* Sets pvt->inject.dimm mask */
945 if (pvt->inject.dimm < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200946 mask |= 1LL << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300947 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300948 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -0200949 mask |= (pvt->inject.dimm & 0x3LL) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300950 else
Alan Cox486dd092009-11-08 01:34:27 -0200951 mask |= (pvt->inject.dimm & 0x1LL) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300952 }
953
954 /* Sets pvt->inject.rank mask */
955 if (pvt->inject.rank < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200956 mask |= 1LL << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300957 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300958 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -0200959 mask |= (pvt->inject.rank & 0x1LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300960 else
Alan Cox486dd092009-11-08 01:34:27 -0200961 mask |= (pvt->inject.rank & 0x3LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300962 }
963
964 /* Sets pvt->inject.bank mask */
965 if (pvt->inject.bank < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200966 mask |= 1LL << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300967 else
Alan Cox486dd092009-11-08 01:34:27 -0200968 mask |= (pvt->inject.bank & 0x15LL) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300969
970 /* Sets pvt->inject.page mask */
971 if (pvt->inject.page < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200972 mask |= 1LL << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300973 else
Alan Cox486dd092009-11-08 01:34:27 -0200974 mask |= (pvt->inject.page & 0xffff) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300975
976 /* Sets pvt->inject.column mask */
977 if (pvt->inject.col < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200978 mask |= 1LL << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300979 else
Alan Cox486dd092009-11-08 01:34:27 -0200980 mask |= (pvt->inject.col & 0x3fff);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300981
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300982 /*
983 * bit 0: REPEAT_EN
984 * bits 1-2: MASK_HALF_CACHELINE
985 * bit 3: INJECT_ECC
986 * bit 4: INJECT_ADDR_PARITY
987 */
988
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300989 injectmask = (pvt->inject.type & 1) |
990 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300991 (pvt->inject.type & 0x6) << (3 - 1);
992
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300993 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300994 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300995 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300996
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300997 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300998 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300999 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001000 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1001
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001002 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001003 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1004
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001005 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001006 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001007
1008 /*
1009 * This is something undocumented, based on my tests
1010 * Without writing 8 to this register, errors aren't injected. Not sure
1011 * why.
1012 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001013 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001014 MC_CFG_CONTROL, 8);
1015
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001016 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1017 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001018 mask, pvt->inject.eccmask, injectmask);
1019
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001020
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001021 return count;
1022}
1023
1024static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1025 char *data)
1026{
1027 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001028 u32 injectmask;
1029
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -03001030 if (!pvt->pci_ch[pvt->inject.channel][0])
1031 return 0;
1032
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001033 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001034 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001035
1036 debugf0("Inject error read: 0x%018x\n", injectmask);
1037
1038 if (injectmask & 0x0c)
1039 pvt->inject.enable = 1;
1040
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001041 return sprintf(data, "%d\n", pvt->inject.enable);
1042}
1043
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001044#define DECLARE_COUNTER(param) \
1045static ssize_t i7core_show_counter_##param( \
1046 struct mem_ctl_info *mci, \
1047 char *data) \
1048{ \
1049 struct i7core_pvt *pvt = mci->pvt_info; \
1050 \
1051 debugf1("%s() \n", __func__); \
1052 if (!pvt->ce_count_available || (pvt->is_registered)) \
1053 return sprintf(data, "data unavailable\n"); \
1054 return sprintf(data, "%lu\n", \
1055 pvt->udimm_ce_count[param]); \
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001056}
1057
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001058#define ATTR_COUNTER(param) \
1059 { \
1060 .attr = { \
1061 .name = __stringify(udimm##param), \
1062 .mode = (S_IRUGO | S_IWUSR) \
1063 }, \
1064 .show = i7core_show_counter_##param \
1065 }
1066
1067DECLARE_COUNTER(0);
1068DECLARE_COUNTER(1);
1069DECLARE_COUNTER(2);
1070
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001071/*
1072 * Sysfs struct
1073 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001074
1075
1076static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
1077 ATTR_ADDR_MATCH(channel),
1078 ATTR_ADDR_MATCH(dimm),
1079 ATTR_ADDR_MATCH(rank),
1080 ATTR_ADDR_MATCH(bank),
1081 ATTR_ADDR_MATCH(page),
1082 ATTR_ADDR_MATCH(col),
1083 { .attr = { .name = NULL } }
1084};
1085
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001086static struct mcidev_sysfs_group i7core_inject_addrmatch = {
1087 .name = "inject_addrmatch",
1088 .mcidev_attr = i7core_addrmatch_attrs,
1089};
1090
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001091static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
1092 ATTR_COUNTER(0),
1093 ATTR_COUNTER(1),
1094 ATTR_COUNTER(2),
1095};
1096
1097static struct mcidev_sysfs_group i7core_udimm_counters = {
1098 .name = "all_channel_counts",
1099 .mcidev_attr = i7core_udimm_counters_attrs,
1100};
1101
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001102static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001103 {
1104 .attr = {
1105 .name = "inject_section",
1106 .mode = (S_IRUGO | S_IWUSR)
1107 },
1108 .show = i7core_inject_section_show,
1109 .store = i7core_inject_section_store,
1110 }, {
1111 .attr = {
1112 .name = "inject_type",
1113 .mode = (S_IRUGO | S_IWUSR)
1114 },
1115 .show = i7core_inject_type_show,
1116 .store = i7core_inject_type_store,
1117 }, {
1118 .attr = {
1119 .name = "inject_eccmask",
1120 .mode = (S_IRUGO | S_IWUSR)
1121 },
1122 .show = i7core_inject_eccmask_show,
1123 .store = i7core_inject_eccmask_store,
1124 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001125 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001126 }, {
1127 .attr = {
1128 .name = "inject_enable",
1129 .mode = (S_IRUGO | S_IWUSR)
1130 },
1131 .show = i7core_inject_enable_show,
1132 .store = i7core_inject_enable_store,
1133 },
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001134 { .attr = { .name = NULL } }, /* Reserved for udimm counters */
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001135 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001136};
1137
1138/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001139 Device initialization routines: put/get, init/exit
1140 ****************************************************************************/
1141
1142/*
1143 * i7core_put_devices 'put' all the devices that we have
1144 * reserved via 'get'
1145 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001146static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001147{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001148 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001149
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001150 debugf0(__FILE__ ": %s()\n", __func__);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001151 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001152 struct pci_dev *pdev = i7core_dev->pdev[i];
1153 if (!pdev)
1154 continue;
1155 debugf0("Removing dev %02x:%02x.%d\n",
1156 pdev->bus->number,
1157 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1158 pci_dev_put(pdev);
1159 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001160 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001161 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001162 kfree(i7core_dev);
1163}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001164
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001165static void i7core_put_all_devices(void)
1166{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001167 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001168
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001169 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001170 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001171}
1172
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001173static void i7core_xeon_pci_fixup(int dev_id)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001174{
1175 struct pci_dev *pdev = NULL;
1176 int i;
1177 /*
1178 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1179 * aren't announced by acpi. So, we need to use a legacy scan probing
1180 * to detect them
1181 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001182 pdev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001183 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001184 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001185 pcibios_scan_specific_bus(255-i);
1186 }
1187}
1188
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001189/*
1190 * i7core_get_devices Find and perform 'get' operation on the MCH's
1191 * device/functions we want to reference for this driver
1192 *
1193 * Need to 'get' device 16 func 1 and func 2
1194 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001195int i7core_get_onedevice(struct pci_dev **prev, int devno,
1196 struct pci_id_descr *dev_descr, unsigned n_devs)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001197{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001198 struct i7core_dev *i7core_dev;
1199
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001200 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001201 u8 bus = 0;
1202 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001203
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001204 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001205 dev_descr->dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001206
1207 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001208 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1209 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1210 * to probe for the alternate address in case of failure
1211 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001212 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001213 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -03001214 PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001215
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -03001216 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
1217 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1218 PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
1219 *prev);
1220
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001221 if (!pdev) {
1222 if (*prev) {
1223 *prev = pdev;
1224 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001225 }
1226
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001227 if (dev_descr->optional)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001228 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001229
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001230 i7core_printk(KERN_ERR,
1231 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001232 dev_descr->dev, dev_descr->func,
1233 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001234
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001235 /* End of list, leave */
1236 return -ENODEV;
1237 }
1238 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001239
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001240 if (bus == 0x3f)
1241 socket = 0;
1242 else
1243 socket = 255 - bus;
1244
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001245 i7core_dev = get_i7core_dev(socket);
1246 if (!i7core_dev) {
1247 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1248 if (!i7core_dev)
1249 return -ENOMEM;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001250 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001251 GFP_KERNEL);
1252 if (!i7core_dev->pdev)
1253 return -ENOMEM;
1254 i7core_dev->socket = socket;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001255 i7core_dev->n_devs = n_devs;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001256 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001257 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001258
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001259 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001260 i7core_printk(KERN_ERR,
1261 "Duplicated device for "
1262 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001263 bus, dev_descr->dev, dev_descr->func,
1264 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001265 pci_dev_put(pdev);
1266 return -ENODEV;
1267 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001268
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001269 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001270
1271 /* Sanity check */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001272 if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1273 PCI_FUNC(pdev->devfn) != dev_descr->func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001274 i7core_printk(KERN_ERR,
1275 "Device PCI ID %04x:%04x "
1276 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001277 PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001278 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001279 bus, dev_descr->dev, dev_descr->func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001280 return -ENODEV;
1281 }
1282
1283 /* Be sure that the device is enabled */
1284 if (unlikely(pci_enable_device(pdev) < 0)) {
1285 i7core_printk(KERN_ERR,
1286 "Couldn't enable "
1287 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001288 bus, dev_descr->dev, dev_descr->func,
1289 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001290 return -ENODEV;
1291 }
1292
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001293 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001294 socket, bus, dev_descr->dev,
1295 dev_descr->func,
1296 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001297
1298 *prev = pdev;
1299
1300 return 0;
1301}
1302
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001303static int i7core_get_devices(struct pci_id_descr dev_descr[], unsigned n_devs)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001304{
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001305 int i, rc;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001306 struct pci_dev *pdev = NULL;
1307
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001308 for (i = 0; i < n_devs; i++) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001309 pdev = NULL;
1310 do {
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001311 rc = i7core_get_onedevice(&pdev, i, &dev_descr[i],
1312 n_devs);
1313 if (rc < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001314 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001315 return -ENODEV;
1316 }
1317 } while (pdev);
1318 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001319
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001320 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001321}
1322
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001323static int mci_bind_devs(struct mem_ctl_info *mci,
1324 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001325{
1326 struct i7core_pvt *pvt = mci->pvt_info;
1327 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001328 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001329
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001330 /* Associates i7core_dev and mci for future usage */
1331 pvt->i7core_dev = i7core_dev;
1332 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001333
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001334 pvt->is_registered = 0;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001335 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001336 pdev = i7core_dev->pdev[i];
1337 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001338 continue;
1339
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001340 func = PCI_FUNC(pdev->devfn);
1341 slot = PCI_SLOT(pdev->devfn);
1342 if (slot == 3) {
1343 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001344 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001345 pvt->pci_mcr[func] = pdev;
1346 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1347 if (unlikely(func > MAX_CHAN_FUNC))
1348 goto error;
1349 pvt->pci_ch[slot - 4][func] = pdev;
1350 } else if (!slot && !func)
1351 pvt->pci_noncore = pdev;
1352 else
1353 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001354
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001355 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1356 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1357 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001358
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001359 if (PCI_SLOT(pdev->devfn) == 3 &&
1360 PCI_FUNC(pdev->devfn) == 2)
1361 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001362 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001363
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001364 /*
1365 * Add extra nodes to count errors on udimm
1366 * For registered memory, this is not needed, since the counters
1367 * are already displayed at the standard locations
1368 */
1369 if (!pvt->is_registered)
1370 i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp =
1371 &i7core_udimm_counters;
1372
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001373 return 0;
1374
1375error:
1376 i7core_printk(KERN_ERR, "Device %d, function %d "
1377 "is out of the expected range\n",
1378 slot, func);
1379 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001380}
1381
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001382/****************************************************************************
1383 Error check routines
1384 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001385static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001386 int chan, int dimm, int add)
1387{
1388 char *msg;
1389 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001390 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001391
1392 for (i = 0; i < add; i++) {
1393 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001394 "(Socket=%d channel=%d dimm=%d)",
1395 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001396
1397 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1398 kfree (msg);
1399 }
1400}
1401
1402static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001403 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001404{
1405 struct i7core_pvt *pvt = mci->pvt_info;
1406 int add0 = 0, add1 = 0, add2 = 0;
1407 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001408 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001409 /* Updates CE counters */
1410
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001411 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1412 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1413 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001414
1415 if (add2 < 0)
1416 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001417 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001418
1419 if (add1 < 0)
1420 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001421 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001422
1423 if (add0 < 0)
1424 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001425 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001426 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001427 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001428
1429 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001430 pvt->rdimm_last_ce_count[chan][2] = new2;
1431 pvt->rdimm_last_ce_count[chan][1] = new1;
1432 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001433
1434 /*updated the edac core */
1435 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001436 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001437 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001438 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001439 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001440 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001441
1442}
1443
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001444static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001445{
1446 struct i7core_pvt *pvt = mci->pvt_info;
1447 u32 rcv[3][2];
1448 int i, new0, new1, new2;
1449
1450 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001451 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001452 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001453 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001454 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001455 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001456 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001457 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001458 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001459 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001460 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001461 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001462 &rcv[2][1]);
1463 for (i = 0 ; i < 3; i++) {
1464 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1465 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1466 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001467 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001468 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1469 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1470 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1471 } else {
1472 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1473 DIMM_BOT_COR_ERR(rcv[i][0]);
1474 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1475 DIMM_BOT_COR_ERR(rcv[i][1]);
1476 new2 = 0;
1477 }
1478
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001479 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001480 }
1481}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001482
1483/* This function is based on the device 3 function 4 registers as described on:
1484 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1485 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1486 * also available at:
1487 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1488 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001489static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001490{
1491 struct i7core_pvt *pvt = mci->pvt_info;
1492 u32 rcv1, rcv0;
1493 int new0, new1, new2;
1494
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001495 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001496 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001497 return;
1498 }
1499
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001500 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001501 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1502 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001503
1504 /* Store the new values */
1505 new2 = DIMM2_COR_ERR(rcv1);
1506 new1 = DIMM1_COR_ERR(rcv0);
1507 new0 = DIMM0_COR_ERR(rcv0);
1508
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001509 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001510 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001511 /* Updates CE counters */
1512 int add0, add1, add2;
1513
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001514 add2 = new2 - pvt->udimm_last_ce_count[2];
1515 add1 = new1 - pvt->udimm_last_ce_count[1];
1516 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001517
1518 if (add2 < 0)
1519 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001520 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001521
1522 if (add1 < 0)
1523 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001524 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001525
1526 if (add0 < 0)
1527 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001528 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001529
1530 if (add0 | add1 | add2)
1531 i7core_printk(KERN_ERR, "New Corrected error(s): "
1532 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1533 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001534 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001535 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001536
1537 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001538 pvt->udimm_last_ce_count[2] = new2;
1539 pvt->udimm_last_ce_count[1] = new1;
1540 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001541}
1542
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001543/*
1544 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1545 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001546 * Nehalem are defined as family 0x06, model 0x1a
1547 *
1548 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001549 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001550 * m->status MSR_IA32_MC8_STATUS
1551 * m->addr MSR_IA32_MC8_ADDR
1552 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001553 * In the case of Nehalem, the error information is masked at .status and .misc
1554 * fields
1555 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001556static void i7core_mce_output_error(struct mem_ctl_info *mci,
1557 struct mce *m)
1558{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001559 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001560 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001561 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001562 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001563 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1564 u32 dimm = (m->misc >> 16) & 0x3;
1565 u32 channel = (m->misc >> 18) & 0x3;
1566 u32 syndrome = m->misc >> 32;
1567 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001568 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001569
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001570 if (m->mcgstatus & 1)
1571 type = "FATAL";
1572 else
1573 type = "NON_FATAL";
1574
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001575 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001576 case 0:
1577 optype = "generic undef request";
1578 break;
1579 case 1:
1580 optype = "read error";
1581 break;
1582 case 2:
1583 optype = "write error";
1584 break;
1585 case 3:
1586 optype = "addr/cmd error";
1587 break;
1588 case 4:
1589 optype = "scrubbing error";
1590 break;
1591 default:
1592 optype = "reserved";
1593 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001594 }
1595
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001596 switch (errnum) {
1597 case 16:
1598 err = "read ECC error";
1599 break;
1600 case 17:
1601 err = "RAS ECC error";
1602 break;
1603 case 18:
1604 err = "write parity error";
1605 break;
1606 case 19:
1607 err = "redundacy loss";
1608 break;
1609 case 20:
1610 err = "reserved";
1611 break;
1612 case 21:
1613 err = "memory range error";
1614 break;
1615 case 22:
1616 err = "RTID out of range";
1617 break;
1618 case 23:
1619 err = "address parity error";
1620 break;
1621 case 24:
1622 err = "byte enable parity error";
1623 break;
1624 default:
1625 err = "unknown";
1626 }
1627
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001628 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001629 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001630 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001631 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001632 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001633 syndrome, core_err_cnt, (long long)m->status,
1634 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001635
1636 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001637
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001638 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001639
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001640 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001641 if (m->mcgstatus & 1)
1642 edac_mc_handle_fbd_ue(mci, csrow, 0,
1643 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001644 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001645 edac_mc_handle_fbd_ce(mci, csrow,
1646 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001647
1648 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001649}
1650
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001651/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001652 * i7core_check_error Retrieve and process errors reported by the
1653 * hardware. Called by the Core module.
1654 */
1655static void i7core_check_error(struct mem_ctl_info *mci)
1656{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001657 struct i7core_pvt *pvt = mci->pvt_info;
1658 int i;
1659 unsigned count = 0;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001660 struct mce *m;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001661
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001662 /*
1663 * MCE first step: Copy all mce errors into a temporary buffer
1664 * We use a double buffering here, to reduce the risk of
1665 * loosing an error.
1666 */
1667 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001668 count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
1669 % MCE_LOG_LEN;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001670 if (!count)
1671 return;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001672
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001673 m = pvt->mce_outentry;
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001674 if (pvt->mce_in + count > MCE_LOG_LEN) {
1675 unsigned l = MCE_LOG_LEN - pvt->mce_in;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001676
1677 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
1678 smp_wmb();
1679 pvt->mce_in = 0;
1680 count -= l;
1681 m += l;
1682 }
1683 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
1684 smp_wmb();
1685 pvt->mce_in += count;
1686
1687 smp_rmb();
1688 if (pvt->mce_overrun) {
1689 i7core_printk(KERN_ERR, "Lost %d memory errors\n",
1690 pvt->mce_overrun);
1691 smp_wmb();
1692 pvt->mce_overrun = 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001693 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001694
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001695 /*
1696 * MCE second step: parse errors and display
1697 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001698 for (i = 0; i < count; i++)
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001699 i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001700
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001701 /*
1702 * Now, let's increment CE error counts
1703 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001704 if (!pvt->is_registered)
1705 i7core_udimm_check_mc_ecc_err(mci);
1706 else
1707 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001708}
1709
1710/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001711 * i7core_mce_check_error Replicates mcelog routine to get errors
1712 * This routine simply queues mcelog errors, and
1713 * return. The error itself should be handled later
1714 * by i7core_check_error.
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001715 * WARNING: As this routine should be called at NMI time, extra care should
1716 * be taken to avoid deadlocks, and to be as fast as possible.
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001717 */
1718static int i7core_mce_check_error(void *priv, struct mce *mce)
1719{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001720 struct mem_ctl_info *mci = priv;
1721 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001722
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001723 /*
1724 * Just let mcelog handle it if the error is
1725 * outside the memory controller
1726 */
1727 if (((mce->status & 0xffff) >> 7) != 1)
1728 return 0;
1729
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001730 /* Bank 8 registers are the only ones that we know how to handle */
1731 if (mce->bank != 8)
1732 return 0;
1733
Randy Dunlap3b918c12009-11-08 01:36:40 -02001734#ifdef CONFIG_SMP
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001735 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001736 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001737 return 0;
Randy Dunlap3b918c12009-11-08 01:36:40 -02001738#endif
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001739
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001740 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001741 if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001742 smp_wmb();
1743 pvt->mce_overrun++;
1744 return 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001745 }
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001746
1747 /* Copy memory error at the ringbuffer */
1748 memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001749 smp_wmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001750 pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001751
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001752 /* Handle fatal errors immediately */
1753 if (mce->mcgstatus & 1)
1754 i7core_check_error(mci);
1755
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001756 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001757 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001758}
1759
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001760static int i7core_register_mci(struct i7core_dev *i7core_dev,
1761 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001762{
1763 struct mem_ctl_info *mci;
1764 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001765 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001766 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001767
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001768 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001769 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1770 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001771 if (unlikely(!mci))
1772 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001773
1774 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1775
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001776 /* record ptr to the generic device */
1777 mci->dev = &i7core_dev->pdev[0]->dev;
1778
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001779 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001780 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001781
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001782 /*
1783 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1784 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1785 * memory channels
1786 */
1787 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001788 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1789 mci->edac_cap = EDAC_FLAG_NONE;
1790 mci->mod_name = "i7core_edac.c";
1791 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001792 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1793 i7core_dev->socket);
1794 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001795 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001796 mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001797 /* Set the function pointer to an actual operation function */
1798 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001799
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001800 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001801 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001802 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001803 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001804
1805 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001806 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001807
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001808 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001809 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001810 debugf0("MC: " __FILE__
1811 ": %s(): failed edac_mc_add_mc()\n", __func__);
1812 /* FIXME: perhaps some code should go here that disables error
1813 * reporting if we just enabled it
1814 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001815
1816 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001817 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001818 }
1819
1820 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001821 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1822 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001823 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001824 printk(KERN_WARNING
1825 "%s(): Unable to create PCI control\n",
1826 __func__);
1827 printk(KERN_WARNING
1828 "%s(): PCI error report via EDAC not setup\n",
1829 __func__);
1830 }
1831
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001832 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001833 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001834 pvt->inject.dimm = -1;
1835 pvt->inject.rank = -1;
1836 pvt->inject.bank = -1;
1837 pvt->inject.page = -1;
1838 pvt->inject.col = -1;
1839
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001840 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001841 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001842 pvt->edac_mce.check_error = i7core_mce_check_error;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001843
1844 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001845 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001846 debugf0("MC: " __FILE__
1847 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001848 }
1849
1850fail:
1851 edac_mc_free(mci);
1852 return rc;
1853}
1854
1855/*
1856 * i7core_probe Probe for ONE instance of device to see if it is
1857 * present.
1858 * return:
1859 * 0 for FOUND a device
1860 * < 0 for error code
1861 */
1862static int __devinit i7core_probe(struct pci_dev *pdev,
1863 const struct pci_device_id *id)
1864{
1865 int dev_idx = id->driver_data;
1866 int rc;
1867 struct i7core_dev *i7core_dev;
1868
1869 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001870 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001871 */
1872 if (unlikely(dev_idx >= 1))
1873 return -EINVAL;
1874
1875 /* get the pci devices we want to reserve for our use */
1876 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001877
Mauro Carvalho Chehabf05da2f2009-10-14 13:31:06 -03001878 if (pdev->device == PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0) {
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -03001879 printk(KERN_INFO "i7core_edac: detected a "
1880 "Lynnfield processor\n");
1881 rc = i7core_get_devices(pci_dev_descr_lynnfield,
1882 ARRAY_SIZE(pci_dev_descr_lynnfield));
1883 } else {
1884 printk(KERN_INFO "i7core_edac: detected a "
1885 "Nehalem/Nehalem-EP processor\n");
1886 rc = i7core_get_devices(pci_dev_descr_i7core,
1887 ARRAY_SIZE(pci_dev_descr_i7core));
1888 }
1889
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001890 if (unlikely(rc < 0))
1891 goto fail0;
1892
1893 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1894 int channels;
1895 int csrows;
1896
1897 /* Check the number of active and not disabled channels */
1898 rc = i7core_get_active_channels(i7core_dev->socket,
1899 &channels, &csrows);
1900 if (unlikely(rc < 0))
1901 goto fail1;
1902
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001903 rc = i7core_register_mci(i7core_dev, channels, csrows);
1904 if (unlikely(rc < 0))
1905 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001906 }
1907
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001908 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001909
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001910 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001911 return 0;
1912
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001913fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001914 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001915fail0:
1916 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001917 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001918}
1919
1920/*
1921 * i7core_remove destructor for one instance of device
1922 *
1923 */
1924static void __devexit i7core_remove(struct pci_dev *pdev)
1925{
1926 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001927 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001928
1929 debugf0(__FILE__ ": %s()\n", __func__);
1930
1931 if (i7core_pci)
1932 edac_pci_release_generic_ctl(i7core_pci);
1933
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001934 /*
1935 * we have a trouble here: pdev value for removal will be wrong, since
1936 * it will point to the X58 register used to detect that the machine
1937 * is a Nehalem or upper design. However, due to the way several PCI
1938 * devices are grouped together to provide MC functionality, we need
1939 * to use a different method for releasing the devices
1940 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001941
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001942 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001943 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1944 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1945 if (mci) {
1946 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001947
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001948 i7core_dev = pvt->i7core_dev;
1949 edac_mce_unregister(&pvt->edac_mce);
1950 kfree(mci->ctl_name);
1951 edac_mc_free(mci);
1952 i7core_put_devices(i7core_dev);
1953 } else {
1954 i7core_printk(KERN_ERR,
1955 "Couldn't find mci for socket %d\n",
1956 i7core_dev->socket);
1957 }
1958 }
1959 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001960}
1961
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001962MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1963
1964/*
1965 * i7core_driver pci_driver structure for this module
1966 *
1967 */
1968static struct pci_driver i7core_driver = {
1969 .name = "i7core_edac",
1970 .probe = i7core_probe,
1971 .remove = __devexit_p(i7core_remove),
1972 .id_table = i7core_pci_tbl,
1973};
1974
1975/*
1976 * i7core_init Module entry function
1977 * Try to initialize this module for its devices
1978 */
1979static int __init i7core_init(void)
1980{
1981 int pci_rc;
1982
1983 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1984
1985 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1986 opstate_init();
1987
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001988 i7core_xeon_pci_fixup(pci_dev_descr_i7core[0].dev_id);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001989
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001990 pci_rc = pci_register_driver(&i7core_driver);
1991
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001992 if (pci_rc >= 0)
1993 return 0;
1994
1995 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1996 pci_rc);
1997
1998 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001999}
2000
2001/*
2002 * i7core_exit() Module exit function
2003 * Unregister the driver
2004 */
2005static void __exit i7core_exit(void)
2006{
2007 debugf2("MC: " __FILE__ ": %s()\n", __func__);
2008 pci_unregister_driver(&i7core_driver);
2009}
2010
2011module_init(i7core_init);
2012module_exit(i7core_exit);
2013
2014MODULE_LICENSE("GPL");
2015MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
2016MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
2017MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
2018 I7CORE_REVISION);
2019
2020module_param(edac_op_state, int, 0444);
2021MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");