blob: e525d571cb25e9680cfddb1591c79837e61946e9 [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
317 { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
318};
319
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300320/*
321 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300322 */
323static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300324 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300325 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300326 {0,} /* 0 terminated list. */
327};
328
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300329static struct edac_pci_ctl_info *i7core_pci;
330
331/****************************************************************************
332 Anciliary status routines
333 ****************************************************************************/
334
335 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300336#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
337#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300338
339 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300340#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300341#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300342
343 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300344static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300345{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300346 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300347}
348
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300349static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300350{
351 static int ranks[4] = { 1, 2, 4, -EINVAL };
352
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300353 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300354}
355
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300356static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300357{
358 static int banks[4] = { 4, 8, 16, -EINVAL };
359
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300360 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300361}
362
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300363static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300364{
365 static int rows[8] = {
366 1 << 12, 1 << 13, 1 << 14, 1 << 15,
367 1 << 16, -EINVAL, -EINVAL, -EINVAL,
368 };
369
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300370 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300371}
372
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300373static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300374{
375 static int cols[8] = {
376 1 << 10, 1 << 11, 1 << 12, -EINVAL,
377 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300378 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300379}
380
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300381static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300382{
383 struct i7core_dev *i7core_dev;
384
385 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
386 if (i7core_dev->socket == socket)
387 return i7core_dev;
388 }
389
390 return NULL;
391}
392
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300393/****************************************************************************
394 Memory check routines
395 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300396static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
397 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300398{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300399 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300400 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300401
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300402 if (!i7core_dev)
403 return NULL;
404
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -0300405 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300406 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300407 continue;
408
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300409 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
410 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
411 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300412 }
413 }
414
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300415 return NULL;
416}
417
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300418/**
419 * i7core_get_active_channels() - gets the number of channels and csrows
420 * @socket: Quick Path Interconnect socket
421 * @channels: Number of channels that will be returned
422 * @csrows: Number of csrows found
423 *
424 * Since EDAC core needs to know in advance the number of available channels
425 * and csrows, in order to allocate memory for csrows/channels, it is needed
426 * to run two similar steps. At the first step, implemented on this function,
427 * it checks the number of csrows/channels present at one socket.
428 * this is used in order to properly allocate the size of mci components.
429 *
430 * It should be noticed that none of the current available datasheets explain
431 * or even mention how csrows are seen by the memory controller. So, we need
432 * to add a fake description for csrows.
433 * So, this driver is attributing one DIMM memory for one csrow.
434 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300435static int i7core_get_active_channels(u8 socket, unsigned *channels,
436 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300437{
438 struct pci_dev *pdev = NULL;
439 int i, j;
440 u32 status, control;
441
442 *channels = 0;
443 *csrows = 0;
444
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300445 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300446 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300447 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
448 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300449 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300450 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300451
452 /* Device 3 function 0 reads */
453 pci_read_config_dword(pdev, MC_STATUS, &status);
454 pci_read_config_dword(pdev, MC_CONTROL, &control);
455
456 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300457 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300458 /* Check if the channel is active */
459 if (!(control & (1 << (8 + i))))
460 continue;
461
462 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300463 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300464 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300465
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300466 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300467 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300468 i7core_printk(KERN_ERR, "Couldn't find socket %d "
469 "fn %d.%d!!!\n",
470 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300471 return -ENODEV;
472 }
473 /* Devices 4-6 function 1 */
474 pci_read_config_dword(pdev,
475 MC_DOD_CH_DIMM0, &dimm_dod[0]);
476 pci_read_config_dword(pdev,
477 MC_DOD_CH_DIMM1, &dimm_dod[1]);
478 pci_read_config_dword(pdev,
479 MC_DOD_CH_DIMM2, &dimm_dod[2]);
480
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300481 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300482
483 for (j = 0; j < 3; j++) {
484 if (!DIMM_PRESENT(dimm_dod[j]))
485 continue;
486 (*csrows)++;
487 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300488 }
489
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300490 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300491 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300492
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300493 return 0;
494}
495
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300496static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300497{
498 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300499 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300500 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300501 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300502 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300503 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300504 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300505
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300506 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300507 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300508 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300509 return -ENODEV;
510
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300511 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300512 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
513 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
514 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
515 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300516
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300517 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300518 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300519 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300520
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300521 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300522 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300523 if (ECCx8(pvt))
524 mode = EDAC_S8ECD8ED;
525 else
526 mode = EDAC_S4ECD4ED;
527 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300528 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300529 mode = EDAC_NONE;
530 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300531
532 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300533 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
534 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300535 numdimms(pvt->info.max_dod),
536 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300537 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300538 numrow(pvt->info.max_dod >> 6),
539 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300540
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300541 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300542 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300543
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -0300544 if (!pvt->pci_ch[i][0])
545 continue;
546
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300547 if (!CH_ACTIVE(pvt, i)) {
548 debugf0("Channel %i is not active\n", i);
549 continue;
550 }
551 if (CH_DISABLED(pvt, i)) {
552 debugf0("Channel %i is disabled\n", i);
553 continue;
554 }
555
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300556 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300557 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300558 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
559
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300560 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300561 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300562
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300563 if (data & REGISTERED_DIMM)
564 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300565 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300566 mtype = MEM_DDR3;
567#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300568 if (data & THREE_DIMMS_PRESENT)
569 pvt->channel[i].dimms = 3;
570 else if (data & SINGLE_QUAD_RANK_PRESENT)
571 pvt->channel[i].dimms = 1;
572 else
573 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300574#endif
575
576 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300577 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300578 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300579 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300580 MC_DOD_CH_DIMM1, &dimm_dod[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_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300583
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300584 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300585 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300586 i,
587 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
588 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300589 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300590 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300591
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300592 for (j = 0; j < 3; j++) {
593 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300594 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300595
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300596 if (!DIMM_PRESENT(dimm_dod[j]))
597 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300598
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300599 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
600 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
601 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
602 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300603
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300604 /* DDR3 has 8 I/O banks */
605 size = (rows * cols * banks * ranks) >> (20 - 3);
606
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300607 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300608
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300609 debugf0("\tdimm %d %d Mb offset: %x, "
610 "bank: %d, rank: %d, row: %#x, col: %#x\n",
611 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300612 RANKOFFSET(dimm_dod[j]),
613 banks, ranks, rows, cols);
614
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300615#if PAGE_SHIFT > 20
616 npages = size >> (PAGE_SHIFT - 20);
617#else
618 npages = size << (20 - PAGE_SHIFT);
619#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300620
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300621 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300622 csr->first_page = last_page + 1;
623 last_page += npages;
624 csr->last_page = last_page;
625 csr->nr_pages = npages;
626
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300627 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300628 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300629 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300630 csr->nr_channels = 1;
631
632 csr->channels[0].chan_idx = i;
633 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300634
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300635 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300636
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300637 switch (banks) {
638 case 4:
639 csr->dtype = DEV_X4;
640 break;
641 case 8:
642 csr->dtype = DEV_X8;
643 break;
644 case 16:
645 csr->dtype = DEV_X16;
646 break;
647 default:
648 csr->dtype = DEV_UNKNOWN;
649 }
650
651 csr->edac_mode = mode;
652 csr->mtype = mtype;
653
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300654 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300655 }
656
657 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
658 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
659 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
660 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
661 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
662 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
663 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
664 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300665 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300666 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300667 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300668 (value[j] >> 27) & 0x1,
669 (value[j] >> 24) & 0x7,
670 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300671 }
672
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300673 return 0;
674}
675
676/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300677 Error insertion routines
678 ****************************************************************************/
679
680/* The i7core has independent error injection features per channel.
681 However, to have a simpler code, we don't allow enabling error injection
682 on more than one channel.
683 Also, since a change at an inject parameter will be applied only at enable,
684 we're disabling error injection on all write calls to the sysfs nodes that
685 controls the error code injection.
686 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300687static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300688{
689 struct i7core_pvt *pvt = mci->pvt_info;
690
691 pvt->inject.enable = 0;
692
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300693 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300694 return -ENODEV;
695
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300696 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300697 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300698
699 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300700}
701
702/*
703 * i7core inject inject.section
704 *
705 * accept and store error injection inject.section value
706 * bit 0 - refers to the lower 32-byte half cacheline
707 * bit 1 - refers to the upper 32-byte half cacheline
708 */
709static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
710 const char *data, size_t count)
711{
712 struct i7core_pvt *pvt = mci->pvt_info;
713 unsigned long value;
714 int rc;
715
716 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300717 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300718
719 rc = strict_strtoul(data, 10, &value);
720 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300721 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300722
723 pvt->inject.section = (u32) value;
724 return count;
725}
726
727static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
728 char *data)
729{
730 struct i7core_pvt *pvt = mci->pvt_info;
731 return sprintf(data, "0x%08x\n", pvt->inject.section);
732}
733
734/*
735 * i7core inject.type
736 *
737 * accept and store error injection inject.section value
738 * bit 0 - repeat enable - Enable error repetition
739 * bit 1 - inject ECC error
740 * bit 2 - inject parity error
741 */
742static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
743 const char *data, size_t count)
744{
745 struct i7core_pvt *pvt = mci->pvt_info;
746 unsigned long value;
747 int rc;
748
749 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300750 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300751
752 rc = strict_strtoul(data, 10, &value);
753 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300754 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300755
756 pvt->inject.type = (u32) value;
757 return count;
758}
759
760static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
761 char *data)
762{
763 struct i7core_pvt *pvt = mci->pvt_info;
764 return sprintf(data, "0x%08x\n", pvt->inject.type);
765}
766
767/*
768 * i7core_inject_inject.eccmask_store
769 *
770 * The type of error (UE/CE) will depend on the inject.eccmask value:
771 * Any bits set to a 1 will flip the corresponding ECC bit
772 * Correctable errors can be injected by flipping 1 bit or the bits within
773 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
774 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
775 * uncorrectable error to be injected.
776 */
777static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
778 const char *data, size_t count)
779{
780 struct i7core_pvt *pvt = mci->pvt_info;
781 unsigned long value;
782 int rc;
783
784 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300785 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300786
787 rc = strict_strtoul(data, 10, &value);
788 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300789 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300790
791 pvt->inject.eccmask = (u32) value;
792 return count;
793}
794
795static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
796 char *data)
797{
798 struct i7core_pvt *pvt = mci->pvt_info;
799 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
800}
801
802/*
803 * i7core_addrmatch
804 *
805 * The type of error (UE/CE) will depend on the inject.eccmask value:
806 * Any bits set to a 1 will flip the corresponding ECC bit
807 * Correctable errors can be injected by flipping 1 bit or the bits within
808 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
809 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
810 * uncorrectable error to be injected.
811 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300812
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300813#define DECLARE_ADDR_MATCH(param, limit) \
814static ssize_t i7core_inject_store_##param( \
815 struct mem_ctl_info *mci, \
816 const char *data, size_t count) \
817{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300818 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300819 long value; \
820 int rc; \
821 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300822 debugf1("%s()\n", __func__); \
823 pvt = mci->pvt_info; \
824 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300825 if (pvt->inject.enable) \
826 disable_inject(mci); \
827 \
Mauro Carvalho Chehab4f87fad2009-10-04 11:54:56 -0300828 if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300829 value = -1; \
830 else { \
831 rc = strict_strtoul(data, 10, &value); \
832 if ((rc < 0) || (value >= limit)) \
833 return -EIO; \
834 } \
835 \
836 pvt->inject.param = value; \
837 \
838 return count; \
839} \
840 \
841static ssize_t i7core_inject_show_##param( \
842 struct mem_ctl_info *mci, \
843 char *data) \
844{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300845 struct i7core_pvt *pvt; \
846 \
847 pvt = mci->pvt_info; \
848 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300849 if (pvt->inject.param < 0) \
850 return sprintf(data, "any\n"); \
851 else \
852 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300853}
854
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300855#define ATTR_ADDR_MATCH(param) \
856 { \
857 .attr = { \
858 .name = #param, \
859 .mode = (S_IRUGO | S_IWUSR) \
860 }, \
861 .show = i7core_inject_show_##param, \
862 .store = i7core_inject_store_##param, \
863 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300864
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300865DECLARE_ADDR_MATCH(channel, 3);
866DECLARE_ADDR_MATCH(dimm, 3);
867DECLARE_ADDR_MATCH(rank, 4);
868DECLARE_ADDR_MATCH(bank, 32);
869DECLARE_ADDR_MATCH(page, 0x10000);
870DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300871
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300872static int write_and_test(struct pci_dev *dev, int where, u32 val)
873{
874 u32 read;
875 int count;
876
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300877 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
878 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
879 where, val);
880
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300881 for (count = 0; count < 10; count++) {
882 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300883 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300884 pci_write_config_dword(dev, where, val);
885 pci_read_config_dword(dev, where, &read);
886
887 if (read == val)
888 return 0;
889 }
890
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300891 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
892 "write=%08x. Read=%08x\n",
893 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
894 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300895
896 return -EINVAL;
897}
898
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300899/*
900 * This routine prepares the Memory Controller for error injection.
901 * The error will be injected when some process tries to write to the
902 * memory that matches the given criteria.
903 * The criteria can be set in terms of a mask where dimm, rank, bank, page
904 * and col can be specified.
905 * A -1 value for any of the mask items will make the MCU to ignore
906 * that matching criteria for error injection.
907 *
908 * It should be noticed that the error will only happen after a write operation
909 * on a memory that matches the condition. if REPEAT_EN is not enabled at
910 * inject mask, then it will produce just one error. Otherwise, it will repeat
911 * until the injectmask would be cleaned.
912 *
913 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
914 * is reliable enough to check if the MC is using the
915 * three channels. However, this is not clear at the datasheet.
916 */
917static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
918 const char *data, size_t count)
919{
920 struct i7core_pvt *pvt = mci->pvt_info;
921 u32 injectmask;
922 u64 mask = 0;
923 int rc;
924 long enable;
925
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300926 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300927 return 0;
928
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300929 rc = strict_strtoul(data, 10, &enable);
930 if ((rc < 0))
931 return 0;
932
933 if (enable) {
934 pvt->inject.enable = 1;
935 } else {
936 disable_inject(mci);
937 return count;
938 }
939
940 /* Sets pvt->inject.dimm mask */
941 if (pvt->inject.dimm < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200942 mask |= 1LL << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300943 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300944 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -0200945 mask |= (pvt->inject.dimm & 0x3LL) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300946 else
Alan Cox486dd092009-11-08 01:34:27 -0200947 mask |= (pvt->inject.dimm & 0x1LL) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300948 }
949
950 /* Sets pvt->inject.rank mask */
951 if (pvt->inject.rank < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200952 mask |= 1LL << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300953 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300954 if (pvt->channel[pvt->inject.channel].dimms > 2)
Alan Cox486dd092009-11-08 01:34:27 -0200955 mask |= (pvt->inject.rank & 0x1LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300956 else
Alan Cox486dd092009-11-08 01:34:27 -0200957 mask |= (pvt->inject.rank & 0x3LL) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300958 }
959
960 /* Sets pvt->inject.bank mask */
961 if (pvt->inject.bank < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200962 mask |= 1LL << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300963 else
Alan Cox486dd092009-11-08 01:34:27 -0200964 mask |= (pvt->inject.bank & 0x15LL) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300965
966 /* Sets pvt->inject.page mask */
967 if (pvt->inject.page < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200968 mask |= 1LL << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300969 else
Alan Cox486dd092009-11-08 01:34:27 -0200970 mask |= (pvt->inject.page & 0xffff) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300971
972 /* Sets pvt->inject.column mask */
973 if (pvt->inject.col < 0)
Alan Cox486dd092009-11-08 01:34:27 -0200974 mask |= 1LL << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300975 else
Alan Cox486dd092009-11-08 01:34:27 -0200976 mask |= (pvt->inject.col & 0x3fff);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300977
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300978 /*
979 * bit 0: REPEAT_EN
980 * bits 1-2: MASK_HALF_CACHELINE
981 * bit 3: INJECT_ECC
982 * bit 4: INJECT_ADDR_PARITY
983 */
984
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300985 injectmask = (pvt->inject.type & 1) |
986 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300987 (pvt->inject.type & 0x6) << (3 - 1);
988
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300989 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300990 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300991 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300992
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300993 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300994 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300995 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300996 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
997
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300998 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300999 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1000
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001001 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001002 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001003
1004 /*
1005 * This is something undocumented, based on my tests
1006 * Without writing 8 to this register, errors aren't injected. Not sure
1007 * why.
1008 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001009 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001010 MC_CFG_CONTROL, 8);
1011
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001012 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1013 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001014 mask, pvt->inject.eccmask, injectmask);
1015
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001016
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001017 return count;
1018}
1019
1020static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1021 char *data)
1022{
1023 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001024 u32 injectmask;
1025
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -03001026 if (!pvt->pci_ch[pvt->inject.channel][0])
1027 return 0;
1028
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001029 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001030 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001031
1032 debugf0("Inject error read: 0x%018x\n", injectmask);
1033
1034 if (injectmask & 0x0c)
1035 pvt->inject.enable = 1;
1036
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001037 return sprintf(data, "%d\n", pvt->inject.enable);
1038}
1039
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001040#define DECLARE_COUNTER(param) \
1041static ssize_t i7core_show_counter_##param( \
1042 struct mem_ctl_info *mci, \
1043 char *data) \
1044{ \
1045 struct i7core_pvt *pvt = mci->pvt_info; \
1046 \
1047 debugf1("%s() \n", __func__); \
1048 if (!pvt->ce_count_available || (pvt->is_registered)) \
1049 return sprintf(data, "data unavailable\n"); \
1050 return sprintf(data, "%lu\n", \
1051 pvt->udimm_ce_count[param]); \
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001052}
1053
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001054#define ATTR_COUNTER(param) \
1055 { \
1056 .attr = { \
1057 .name = __stringify(udimm##param), \
1058 .mode = (S_IRUGO | S_IWUSR) \
1059 }, \
1060 .show = i7core_show_counter_##param \
1061 }
1062
1063DECLARE_COUNTER(0);
1064DECLARE_COUNTER(1);
1065DECLARE_COUNTER(2);
1066
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001067/*
1068 * Sysfs struct
1069 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001070
1071
1072static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
1073 ATTR_ADDR_MATCH(channel),
1074 ATTR_ADDR_MATCH(dimm),
1075 ATTR_ADDR_MATCH(rank),
1076 ATTR_ADDR_MATCH(bank),
1077 ATTR_ADDR_MATCH(page),
1078 ATTR_ADDR_MATCH(col),
1079 { .attr = { .name = NULL } }
1080};
1081
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001082static struct mcidev_sysfs_group i7core_inject_addrmatch = {
1083 .name = "inject_addrmatch",
1084 .mcidev_attr = i7core_addrmatch_attrs,
1085};
1086
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001087static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
1088 ATTR_COUNTER(0),
1089 ATTR_COUNTER(1),
1090 ATTR_COUNTER(2),
1091};
1092
1093static struct mcidev_sysfs_group i7core_udimm_counters = {
1094 .name = "all_channel_counts",
1095 .mcidev_attr = i7core_udimm_counters_attrs,
1096};
1097
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001098static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001099 {
1100 .attr = {
1101 .name = "inject_section",
1102 .mode = (S_IRUGO | S_IWUSR)
1103 },
1104 .show = i7core_inject_section_show,
1105 .store = i7core_inject_section_store,
1106 }, {
1107 .attr = {
1108 .name = "inject_type",
1109 .mode = (S_IRUGO | S_IWUSR)
1110 },
1111 .show = i7core_inject_type_show,
1112 .store = i7core_inject_type_store,
1113 }, {
1114 .attr = {
1115 .name = "inject_eccmask",
1116 .mode = (S_IRUGO | S_IWUSR)
1117 },
1118 .show = i7core_inject_eccmask_show,
1119 .store = i7core_inject_eccmask_store,
1120 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001121 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001122 }, {
1123 .attr = {
1124 .name = "inject_enable",
1125 .mode = (S_IRUGO | S_IWUSR)
1126 },
1127 .show = i7core_inject_enable_show,
1128 .store = i7core_inject_enable_store,
1129 },
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001130 { .attr = { .name = NULL } }, /* Reserved for udimm counters */
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001131 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001132};
1133
1134/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001135 Device initialization routines: put/get, init/exit
1136 ****************************************************************************/
1137
1138/*
1139 * i7core_put_devices 'put' all the devices that we have
1140 * reserved via 'get'
1141 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001142static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001143{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001144 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001145
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001146 debugf0(__FILE__ ": %s()\n", __func__);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001147 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001148 struct pci_dev *pdev = i7core_dev->pdev[i];
1149 if (!pdev)
1150 continue;
1151 debugf0("Removing dev %02x:%02x.%d\n",
1152 pdev->bus->number,
1153 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1154 pci_dev_put(pdev);
1155 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001156 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001157 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001158 kfree(i7core_dev);
1159}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001160
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001161static void i7core_put_all_devices(void)
1162{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001163 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001164
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001165 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001166 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001167}
1168
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001169static void i7core_xeon_pci_fixup(int dev_id)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001170{
1171 struct pci_dev *pdev = NULL;
1172 int i;
1173 /*
1174 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1175 * aren't announced by acpi. So, we need to use a legacy scan probing
1176 * to detect them
1177 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001178 pdev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001179 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001180 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001181 pcibios_scan_specific_bus(255-i);
1182 }
1183}
1184
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001185/*
1186 * i7core_get_devices Find and perform 'get' operation on the MCH's
1187 * device/functions we want to reference for this driver
1188 *
1189 * Need to 'get' device 16 func 1 and func 2
1190 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001191int i7core_get_onedevice(struct pci_dev **prev, int devno,
1192 struct pci_id_descr *dev_descr, unsigned n_devs)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001193{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001194 struct i7core_dev *i7core_dev;
1195
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001196 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001197 u8 bus = 0;
1198 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001199
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001200 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001201 dev_descr->dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001202
1203 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001204 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1205 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1206 * to probe for the alternate address in case of failure
1207 */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001208 if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001209 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabfd382652009-10-14 06:07:07 -03001210 PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001211
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001212 if (!pdev) {
1213 if (*prev) {
1214 *prev = pdev;
1215 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001216 }
1217
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001218 if (dev_descr->optional)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001219 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001220
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001221 i7core_printk(KERN_ERR,
1222 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001223 dev_descr->dev, dev_descr->func,
1224 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001225
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001226 /* End of list, leave */
1227 return -ENODEV;
1228 }
1229 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001230
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001231 if (bus == 0x3f)
1232 socket = 0;
1233 else
1234 socket = 255 - bus;
1235
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001236 i7core_dev = get_i7core_dev(socket);
1237 if (!i7core_dev) {
1238 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1239 if (!i7core_dev)
1240 return -ENOMEM;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001241 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001242 GFP_KERNEL);
1243 if (!i7core_dev->pdev)
1244 return -ENOMEM;
1245 i7core_dev->socket = socket;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001246 i7core_dev->n_devs = n_devs;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001247 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001248 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001249
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001250 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001251 i7core_printk(KERN_ERR,
1252 "Duplicated device for "
1253 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001254 bus, dev_descr->dev, dev_descr->func,
1255 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001256 pci_dev_put(pdev);
1257 return -ENODEV;
1258 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001259
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001260 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001261
1262 /* Sanity check */
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001263 if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1264 PCI_FUNC(pdev->devfn) != dev_descr->func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001265 i7core_printk(KERN_ERR,
1266 "Device PCI ID %04x:%04x "
1267 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001268 PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001269 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001270 bus, dev_descr->dev, dev_descr->func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001271 return -ENODEV;
1272 }
1273
1274 /* Be sure that the device is enabled */
1275 if (unlikely(pci_enable_device(pdev) < 0)) {
1276 i7core_printk(KERN_ERR,
1277 "Couldn't enable "
1278 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001279 bus, dev_descr->dev, dev_descr->func,
1280 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001281 return -ENODEV;
1282 }
1283
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001284 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001285 socket, bus, dev_descr->dev,
1286 dev_descr->func,
1287 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001288
1289 *prev = pdev;
1290
1291 return 0;
1292}
1293
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001294static int i7core_get_devices(struct pci_id_descr dev_descr[], unsigned n_devs)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001295{
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001296 int i, rc;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001297 struct pci_dev *pdev = NULL;
1298
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001299 for (i = 0; i < n_devs; i++) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001300 pdev = NULL;
1301 do {
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001302 rc = i7core_get_onedevice(&pdev, i, &dev_descr[i],
1303 n_devs);
1304 if (rc < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001305 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001306 return -ENODEV;
1307 }
1308 } while (pdev);
1309 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001310
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001311 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001312}
1313
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001314static int mci_bind_devs(struct mem_ctl_info *mci,
1315 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001316{
1317 struct i7core_pvt *pvt = mci->pvt_info;
1318 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001319 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001320
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001321 /* Associates i7core_dev and mci for future usage */
1322 pvt->i7core_dev = i7core_dev;
1323 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001324
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001325 pvt->is_registered = 0;
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001326 for (i = 0; i < i7core_dev->n_devs; i++) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001327 pdev = i7core_dev->pdev[i];
1328 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001329 continue;
1330
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001331 func = PCI_FUNC(pdev->devfn);
1332 slot = PCI_SLOT(pdev->devfn);
1333 if (slot == 3) {
1334 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001335 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001336 pvt->pci_mcr[func] = pdev;
1337 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1338 if (unlikely(func > MAX_CHAN_FUNC))
1339 goto error;
1340 pvt->pci_ch[slot - 4][func] = pdev;
1341 } else if (!slot && !func)
1342 pvt->pci_noncore = pdev;
1343 else
1344 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001345
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001346 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1347 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1348 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001349
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001350 if (PCI_SLOT(pdev->devfn) == 3 &&
1351 PCI_FUNC(pdev->devfn) == 2)
1352 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001353 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001354
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001355 /*
1356 * Add extra nodes to count errors on udimm
1357 * For registered memory, this is not needed, since the counters
1358 * are already displayed at the standard locations
1359 */
1360 if (!pvt->is_registered)
1361 i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp =
1362 &i7core_udimm_counters;
1363
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001364 return 0;
1365
1366error:
1367 i7core_printk(KERN_ERR, "Device %d, function %d "
1368 "is out of the expected range\n",
1369 slot, func);
1370 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001371}
1372
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001373/****************************************************************************
1374 Error check routines
1375 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001376static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001377 int chan, int dimm, int add)
1378{
1379 char *msg;
1380 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001381 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001382
1383 for (i = 0; i < add; i++) {
1384 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001385 "(Socket=%d channel=%d dimm=%d)",
1386 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001387
1388 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1389 kfree (msg);
1390 }
1391}
1392
1393static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001394 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001395{
1396 struct i7core_pvt *pvt = mci->pvt_info;
1397 int add0 = 0, add1 = 0, add2 = 0;
1398 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001399 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001400 /* Updates CE counters */
1401
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001402 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1403 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1404 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001405
1406 if (add2 < 0)
1407 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001408 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001409
1410 if (add1 < 0)
1411 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001412 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001413
1414 if (add0 < 0)
1415 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001416 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001417 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001418 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001419
1420 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001421 pvt->rdimm_last_ce_count[chan][2] = new2;
1422 pvt->rdimm_last_ce_count[chan][1] = new1;
1423 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001424
1425 /*updated the edac core */
1426 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001427 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001428 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001429 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001430 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001431 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001432
1433}
1434
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001435static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001436{
1437 struct i7core_pvt *pvt = mci->pvt_info;
1438 u32 rcv[3][2];
1439 int i, new0, new1, new2;
1440
1441 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001442 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001443 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001444 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001445 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001446 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001447 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001448 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001449 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001450 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001451 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001452 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001453 &rcv[2][1]);
1454 for (i = 0 ; i < 3; i++) {
1455 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1456 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1457 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001458 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001459 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1460 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1461 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1462 } else {
1463 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1464 DIMM_BOT_COR_ERR(rcv[i][0]);
1465 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1466 DIMM_BOT_COR_ERR(rcv[i][1]);
1467 new2 = 0;
1468 }
1469
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001470 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001471 }
1472}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001473
1474/* This function is based on the device 3 function 4 registers as described on:
1475 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1476 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1477 * also available at:
1478 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1479 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001480static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001481{
1482 struct i7core_pvt *pvt = mci->pvt_info;
1483 u32 rcv1, rcv0;
1484 int new0, new1, new2;
1485
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001486 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001487 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001488 return;
1489 }
1490
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001491 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001492 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1493 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001494
1495 /* Store the new values */
1496 new2 = DIMM2_COR_ERR(rcv1);
1497 new1 = DIMM1_COR_ERR(rcv0);
1498 new0 = DIMM0_COR_ERR(rcv0);
1499
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001500 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001501 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001502 /* Updates CE counters */
1503 int add0, add1, add2;
1504
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001505 add2 = new2 - pvt->udimm_last_ce_count[2];
1506 add1 = new1 - pvt->udimm_last_ce_count[1];
1507 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001508
1509 if (add2 < 0)
1510 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001511 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001512
1513 if (add1 < 0)
1514 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001515 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001516
1517 if (add0 < 0)
1518 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001519 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001520
1521 if (add0 | add1 | add2)
1522 i7core_printk(KERN_ERR, "New Corrected error(s): "
1523 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1524 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001525 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001526 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001527
1528 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001529 pvt->udimm_last_ce_count[2] = new2;
1530 pvt->udimm_last_ce_count[1] = new1;
1531 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001532}
1533
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001534/*
1535 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1536 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001537 * Nehalem are defined as family 0x06, model 0x1a
1538 *
1539 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001540 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001541 * m->status MSR_IA32_MC8_STATUS
1542 * m->addr MSR_IA32_MC8_ADDR
1543 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001544 * In the case of Nehalem, the error information is masked at .status and .misc
1545 * fields
1546 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001547static void i7core_mce_output_error(struct mem_ctl_info *mci,
1548 struct mce *m)
1549{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001550 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001551 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001552 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001553 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001554 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1555 u32 dimm = (m->misc >> 16) & 0x3;
1556 u32 channel = (m->misc >> 18) & 0x3;
1557 u32 syndrome = m->misc >> 32;
1558 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001559 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001560
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001561 if (m->mcgstatus & 1)
1562 type = "FATAL";
1563 else
1564 type = "NON_FATAL";
1565
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001566 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001567 case 0:
1568 optype = "generic undef request";
1569 break;
1570 case 1:
1571 optype = "read error";
1572 break;
1573 case 2:
1574 optype = "write error";
1575 break;
1576 case 3:
1577 optype = "addr/cmd error";
1578 break;
1579 case 4:
1580 optype = "scrubbing error";
1581 break;
1582 default:
1583 optype = "reserved";
1584 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001585 }
1586
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001587 switch (errnum) {
1588 case 16:
1589 err = "read ECC error";
1590 break;
1591 case 17:
1592 err = "RAS ECC error";
1593 break;
1594 case 18:
1595 err = "write parity error";
1596 break;
1597 case 19:
1598 err = "redundacy loss";
1599 break;
1600 case 20:
1601 err = "reserved";
1602 break;
1603 case 21:
1604 err = "memory range error";
1605 break;
1606 case 22:
1607 err = "RTID out of range";
1608 break;
1609 case 23:
1610 err = "address parity error";
1611 break;
1612 case 24:
1613 err = "byte enable parity error";
1614 break;
1615 default:
1616 err = "unknown";
1617 }
1618
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001619 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001620 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001621 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001622 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001623 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001624 syndrome, core_err_cnt, (long long)m->status,
1625 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001626
1627 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001628
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001629 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001630
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001631 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001632 if (m->mcgstatus & 1)
1633 edac_mc_handle_fbd_ue(mci, csrow, 0,
1634 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001635 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001636 edac_mc_handle_fbd_ce(mci, csrow,
1637 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001638
1639 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001640}
1641
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001642/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001643 * i7core_check_error Retrieve and process errors reported by the
1644 * hardware. Called by the Core module.
1645 */
1646static void i7core_check_error(struct mem_ctl_info *mci)
1647{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001648 struct i7core_pvt *pvt = mci->pvt_info;
1649 int i;
1650 unsigned count = 0;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001651 struct mce *m;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001652
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001653 /*
1654 * MCE first step: Copy all mce errors into a temporary buffer
1655 * We use a double buffering here, to reduce the risk of
1656 * loosing an error.
1657 */
1658 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001659 count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
1660 % MCE_LOG_LEN;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001661 if (!count)
1662 return;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001663
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001664 m = pvt->mce_outentry;
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001665 if (pvt->mce_in + count > MCE_LOG_LEN) {
1666 unsigned l = MCE_LOG_LEN - pvt->mce_in;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001667
1668 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
1669 smp_wmb();
1670 pvt->mce_in = 0;
1671 count -= l;
1672 m += l;
1673 }
1674 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
1675 smp_wmb();
1676 pvt->mce_in += count;
1677
1678 smp_rmb();
1679 if (pvt->mce_overrun) {
1680 i7core_printk(KERN_ERR, "Lost %d memory errors\n",
1681 pvt->mce_overrun);
1682 smp_wmb();
1683 pvt->mce_overrun = 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001684 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001685
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001686 /*
1687 * MCE second step: parse errors and display
1688 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001689 for (i = 0; i < count; i++)
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001690 i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001691
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001692 /*
1693 * Now, let's increment CE error counts
1694 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001695 if (!pvt->is_registered)
1696 i7core_udimm_check_mc_ecc_err(mci);
1697 else
1698 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001699}
1700
1701/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001702 * i7core_mce_check_error Replicates mcelog routine to get errors
1703 * This routine simply queues mcelog errors, and
1704 * return. The error itself should be handled later
1705 * by i7core_check_error.
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001706 * WARNING: As this routine should be called at NMI time, extra care should
1707 * be taken to avoid deadlocks, and to be as fast as possible.
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001708 */
1709static int i7core_mce_check_error(void *priv, struct mce *mce)
1710{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001711 struct mem_ctl_info *mci = priv;
1712 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001713
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001714 /*
1715 * Just let mcelog handle it if the error is
1716 * outside the memory controller
1717 */
1718 if (((mce->status & 0xffff) >> 7) != 1)
1719 return 0;
1720
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001721 /* Bank 8 registers are the only ones that we know how to handle */
1722 if (mce->bank != 8)
1723 return 0;
1724
Randy Dunlap3b918c12009-11-08 01:36:40 -02001725#ifdef CONFIG_SMP
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001726 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001727 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001728 return 0;
Randy Dunlap3b918c12009-11-08 01:36:40 -02001729#endif
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001730
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001731 smp_rmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001732 if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001733 smp_wmb();
1734 pvt->mce_overrun++;
1735 return 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001736 }
Mauro Carvalho Chehab6e103be2009-10-05 09:40:09 -03001737
1738 /* Copy memory error at the ringbuffer */
1739 memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001740 smp_wmb();
Mauro Carvalho Chehab321ece42009-10-08 13:11:08 -03001741 pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001742
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001743 /* Handle fatal errors immediately */
1744 if (mce->mcgstatus & 1)
1745 i7core_check_error(mci);
1746
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001747 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001748 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001749}
1750
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001751static int i7core_register_mci(struct i7core_dev *i7core_dev,
1752 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001753{
1754 struct mem_ctl_info *mci;
1755 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001756 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001757 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001758
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001759 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001760 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1761 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001762 if (unlikely(!mci))
1763 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001764
1765 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1766
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001767 /* record ptr to the generic device */
1768 mci->dev = &i7core_dev->pdev[0]->dev;
1769
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001770 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001771 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001772
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001773 /*
1774 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1775 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1776 * memory channels
1777 */
1778 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001779 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1780 mci->edac_cap = EDAC_FLAG_NONE;
1781 mci->mod_name = "i7core_edac.c";
1782 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001783 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1784 i7core_dev->socket);
1785 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001786 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001787 mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001788 /* Set the function pointer to an actual operation function */
1789 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001790
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001791 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001792 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001793 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001794 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001795
1796 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001797 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001798
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001799 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001800 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001801 debugf0("MC: " __FILE__
1802 ": %s(): failed edac_mc_add_mc()\n", __func__);
1803 /* FIXME: perhaps some code should go here that disables error
1804 * reporting if we just enabled it
1805 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001806
1807 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001808 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001809 }
1810
1811 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001812 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1813 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001814 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001815 printk(KERN_WARNING
1816 "%s(): Unable to create PCI control\n",
1817 __func__);
1818 printk(KERN_WARNING
1819 "%s(): PCI error report via EDAC not setup\n",
1820 __func__);
1821 }
1822
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001823 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001824 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001825 pvt->inject.dimm = -1;
1826 pvt->inject.rank = -1;
1827 pvt->inject.bank = -1;
1828 pvt->inject.page = -1;
1829 pvt->inject.col = -1;
1830
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001831 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001832 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001833 pvt->edac_mce.check_error = i7core_mce_check_error;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001834
1835 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001836 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001837 debugf0("MC: " __FILE__
1838 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001839 }
1840
1841fail:
1842 edac_mc_free(mci);
1843 return rc;
1844}
1845
1846/*
1847 * i7core_probe Probe for ONE instance of device to see if it is
1848 * present.
1849 * return:
1850 * 0 for FOUND a device
1851 * < 0 for error code
1852 */
1853static int __devinit i7core_probe(struct pci_dev *pdev,
1854 const struct pci_device_id *id)
1855{
1856 int dev_idx = id->driver_data;
1857 int rc;
1858 struct i7core_dev *i7core_dev;
1859
1860 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001861 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001862 */
1863 if (unlikely(dev_idx >= 1))
1864 return -EINVAL;
1865
1866 /* get the pci devices we want to reserve for our use */
1867 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001868
Mauro Carvalho Chehab52a2e4fc2009-10-14 11:21:58 -03001869 if (pdev->device == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) {
1870 printk(KERN_INFO "i7core_edac: detected a "
1871 "Lynnfield processor\n");
1872 rc = i7core_get_devices(pci_dev_descr_lynnfield,
1873 ARRAY_SIZE(pci_dev_descr_lynnfield));
1874 } else {
1875 printk(KERN_INFO "i7core_edac: detected a "
1876 "Nehalem/Nehalem-EP processor\n");
1877 rc = i7core_get_devices(pci_dev_descr_i7core,
1878 ARRAY_SIZE(pci_dev_descr_i7core));
1879 }
1880
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001881 if (unlikely(rc < 0))
1882 goto fail0;
1883
1884 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1885 int channels;
1886 int csrows;
1887
1888 /* Check the number of active and not disabled channels */
1889 rc = i7core_get_active_channels(i7core_dev->socket,
1890 &channels, &csrows);
1891 if (unlikely(rc < 0))
1892 goto fail1;
1893
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001894 rc = i7core_register_mci(i7core_dev, channels, csrows);
1895 if (unlikely(rc < 0))
1896 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001897 }
1898
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001899 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001900
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001901 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001902 return 0;
1903
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001904fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001905 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001906fail0:
1907 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001908 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001909}
1910
1911/*
1912 * i7core_remove destructor for one instance of device
1913 *
1914 */
1915static void __devexit i7core_remove(struct pci_dev *pdev)
1916{
1917 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001918 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001919
1920 debugf0(__FILE__ ": %s()\n", __func__);
1921
1922 if (i7core_pci)
1923 edac_pci_release_generic_ctl(i7core_pci);
1924
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001925 /*
1926 * we have a trouble here: pdev value for removal will be wrong, since
1927 * it will point to the X58 register used to detect that the machine
1928 * is a Nehalem or upper design. However, due to the way several PCI
1929 * devices are grouped together to provide MC functionality, we need
1930 * to use a different method for releasing the devices
1931 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001932
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001933 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001934 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1935 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1936 if (mci) {
1937 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001938
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001939 i7core_dev = pvt->i7core_dev;
1940 edac_mce_unregister(&pvt->edac_mce);
1941 kfree(mci->ctl_name);
1942 edac_mc_free(mci);
1943 i7core_put_devices(i7core_dev);
1944 } else {
1945 i7core_printk(KERN_ERR,
1946 "Couldn't find mci for socket %d\n",
1947 i7core_dev->socket);
1948 }
1949 }
1950 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001951}
1952
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001953MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1954
1955/*
1956 * i7core_driver pci_driver structure for this module
1957 *
1958 */
1959static struct pci_driver i7core_driver = {
1960 .name = "i7core_edac",
1961 .probe = i7core_probe,
1962 .remove = __devexit_p(i7core_remove),
1963 .id_table = i7core_pci_tbl,
1964};
1965
1966/*
1967 * i7core_init Module entry function
1968 * Try to initialize this module for its devices
1969 */
1970static int __init i7core_init(void)
1971{
1972 int pci_rc;
1973
1974 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1975
1976 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1977 opstate_init();
1978
Mauro Carvalho Chehabde06eee2009-10-14 08:02:40 -03001979 i7core_xeon_pci_fixup(pci_dev_descr_i7core[0].dev_id);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001980
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001981 pci_rc = pci_register_driver(&i7core_driver);
1982
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001983 if (pci_rc >= 0)
1984 return 0;
1985
1986 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1987 pci_rc);
1988
1989 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001990}
1991
1992/*
1993 * i7core_exit() Module exit function
1994 * Unregister the driver
1995 */
1996static void __exit i7core_exit(void)
1997{
1998 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1999 pci_unregister_driver(&i7core_driver);
2000}
2001
2002module_init(i7core_init);
2003module_exit(i7core_exit);
2004
2005MODULE_LICENSE("GPL");
2006MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
2007MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
2008MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
2009 I7CORE_REVISION);
2010
2011module_param(edac_op_state, int, 0444);
2012MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");