Blame view

drivers/edac/i7core_edac.c 64.1 KB
52707f918   Mauro Carvalho Chehab   i7core_edac: Bett...
1
2
  /* Intel i7 core/Nehalem Memory Controller kernel module
   *
e7bf068aa   David Sterba   i7core_edac: fix ...
3
   * This driver supports the memory controllers found on the Intel
52707f918   Mauro Carvalho Chehab   i7core_edac: Bett...
4
5
6
   * processor families i7core, i7core 7xx/8xx, i5core, Xeon 35xx,
   * Xeon 55xx and Xeon 56xx also known as Nehalem, Nehalem-EP, Lynnfield
   * and Westmere-EP.
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
7
8
9
10
   *
   * This file may be distributed under the terms of the
   * GNU General Public License version 2 only.
   *
52707f918   Mauro Carvalho Chehab   i7core_edac: Bett...
11
   * Copyright (c) 2009-2010 by:
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   *	 Mauro Carvalho Chehab <mchehab@redhat.com>
   *
   * Red Hat Inc. http://www.redhat.com
   *
   * Forked and adapted from the i5400_edac driver
   *
   * Based on the following public Intel datasheets:
   * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
   * Datasheet, Volume 2:
   *	http://download.intel.com/design/processor/datashts/320835.pdf
   * Intel Xeon Processor 5500 Series Datasheet Volume 2
   *	http://www.intel.com/Assets/PDF/datasheet/321322.pdf
   * also available at:
   * 	http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
   */
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
27
28
29
30
31
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/pci.h>
  #include <linux/pci_ids.h>
  #include <linux/slab.h>
3b918c12d   Randy Dunlap   edac: fix i7core ...
32
  #include <linux/delay.h>
535e9c78e   Nils Carlson   i7core_edac: scru...
33
  #include <linux/dmi.h>
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
34
35
  #include <linux/edac.h>
  #include <linux/mmzone.h>
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
36
  #include <linux/smp.h>
4140c5426   Borislav Petkov   i7core_edac: Drop...
37
  #include <asm/mce.h>
14d2c0834   Mauro Carvalho Chehab   i7core: Use regis...
38
  #include <asm/processor.h>
4fad8098b   Sedat Dilek   i7core_edac: Fix ...
39
  #include <asm/div64.h>
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
40
41
  
  #include "edac_core.h"
18c29002f   Mauro Carvalho Chehab   i7core_edac: move...
42
43
44
45
  /* Static vars */
  static LIST_HEAD(i7core_edac_list);
  static DEFINE_MUTEX(i7core_edac_lock);
  static int probed;
54a08ab15   Mauro Carvalho Chehab   i7core_edac: Don'...
46
47
48
  static int use_pci_fixup;
  module_param(use_pci_fixup, int, 0444);
  MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
49
  /*
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
50
51
52
53
54
55
56
57
58
   * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
   * registers start at bus 255, and are not reported by BIOS.
   * We currently find devices with only 2 sockets. In order to support more QPI
   * Quick Path Interconnect, just increment this number.
   */
  #define MAX_SOCKET_BUSES	2
  
  
  /*
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
59
60
   * Alter this version for the module when modifications are made
   */
152ba3942   Michal Marek   edac: Drop __DATE...
61
  #define I7CORE_REVISION    " Ver: 1.0.0"
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
62
  #define EDAC_MOD_STR      "i7core_edac"
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
63
64
65
66
67
68
69
70
71
72
73
74
  /*
   * Debug macros
   */
  #define i7core_printk(level, fmt, arg...)			\
  	edac_printk(level, "i7core", fmt, ##arg)
  
  #define i7core_mc_printk(mci, level, fmt, arg...)		\
  	edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
  
  /*
   * i7core Memory Controller Registers
   */
e9bd2e737   Mauro Carvalho Chehab   i7core_edac: Adds...
75
76
77
  	/* OFFSETS for Device 0 Function 0 */
  
  #define MC_CFG_CONTROL	0x90
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
78
79
    #define MC_CFG_UNLOCK		0x02
    #define MC_CFG_LOCK		0x00
e9bd2e737   Mauro Carvalho Chehab   i7core_edac: Adds...
80

a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
81
82
83
84
85
  	/* OFFSETS for Device 3 Function 0 */
  
  #define MC_CONTROL	0x48
  #define MC_STATUS	0x4c
  #define MC_MAX_DOD	0x64
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
86
87
88
89
90
91
92
93
94
95
96
  /*
   * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
   * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
   */
  
  #define MC_TEST_ERR_RCV1	0x60
    #define DIMM2_COR_ERR(r)			((r) & 0x7fff)
  
  #define MC_TEST_ERR_RCV0	0x64
    #define DIMM1_COR_ERR(r)			(((r) >> 16) & 0x7fff)
    #define DIMM0_COR_ERR(r)			((r) & 0x7fff)
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
97
  /* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
98
99
100
101
102
103
104
  #define MC_SSRCONTROL		0x48
    #define SSR_MODE_DISABLE	0x00
    #define SSR_MODE_ENABLE	0x01
    #define SSR_MODE_MASK		0x03
  
  #define MC_SCRUB_CONTROL	0x4c
    #define STARTSCRUB		(1 << 24)
535e9c78e   Nils Carlson   i7core_edac: scru...
105
    #define SCRUBINTERVAL_MASK    0xffffff
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
106

b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
107
108
109
110
111
112
113
114
115
  #define MC_COR_ECC_CNT_0	0x80
  #define MC_COR_ECC_CNT_1	0x84
  #define MC_COR_ECC_CNT_2	0x88
  #define MC_COR_ECC_CNT_3	0x8c
  #define MC_COR_ECC_CNT_4	0x90
  #define MC_COR_ECC_CNT_5	0x94
  
  #define DIMM_TOP_COR_ERR(r)			(((r) >> 16) & 0x7fff)
  #define DIMM_BOT_COR_ERR(r)			((r) & 0x7fff)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
116
  	/* OFFSETS for Devices 4,5 and 6 Function 0 */
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
117
118
119
120
121
  #define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
    #define THREE_DIMMS_PRESENT		(1 << 24)
    #define SINGLE_QUAD_RANK_PRESENT	(1 << 23)
    #define QUAD_RANK_PRESENT		(1 << 22)
    #define REGISTERED_DIMM		(1 << 15)
f122a8922   Mauro Carvalho Chehab   i7core_edac: Show...
122
123
124
  #define MC_CHANNEL_MAPPER	0x60
    #define RDLCH(r, ch)		((((r) >> (3 + (ch * 6))) & 0x07) - 1)
    #define WRLCH(r, ch)		((((r) >> (ch * 6)) & 0x07) - 1)
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
125
126
  #define MC_CHANNEL_RANK_PRESENT 0x7c
    #define RANK_PRESENT_MASK		0xffff
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
127
  #define MC_CHANNEL_ADDR_MATCH	0xf0
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
128
129
130
131
132
133
134
135
136
137
  #define MC_CHANNEL_ERROR_MASK	0xf8
  #define MC_CHANNEL_ERROR_INJECT	0xfc
    #define INJECT_ADDR_PARITY	0x10
    #define INJECT_ECC		0x08
    #define MASK_CACHELINE	0x06
    #define MASK_FULL_CACHELINE	0x06
    #define MASK_MSB32_CACHELINE	0x04
    #define MASK_LSB32_CACHELINE	0x02
    #define NO_MASK_CACHELINE	0x00
    #define REPEAT_EN		0x01
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
138

0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
139
  	/* OFFSETS for Devices 4,5 and 6 Function 1 */
b990538a7   Mauro Carvalho Chehab   i7core_edac: Codi...
140

0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
141
142
143
144
145
146
147
  #define MC_DOD_CH_DIMM0		0x48
  #define MC_DOD_CH_DIMM1		0x4c
  #define MC_DOD_CH_DIMM2		0x50
    #define RANKOFFSET_MASK	((1 << 12) | (1 << 11) | (1 << 10))
    #define RANKOFFSET(x)		((x & RANKOFFSET_MASK) >> 10)
    #define DIMM_PRESENT_MASK	(1 << 9)
    #define DIMM_PRESENT(x)	(((x) & DIMM_PRESENT_MASK) >> 9)
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
148
149
150
151
    #define MC_DOD_NUMBANK_MASK		((1 << 8) | (1 << 7))
    #define MC_DOD_NUMBANK(x)		(((x) & MC_DOD_NUMBANK_MASK) >> 7)
    #define MC_DOD_NUMRANK_MASK		((1 << 6) | (1 << 5))
    #define MC_DOD_NUMRANK(x)		(((x) & MC_DOD_NUMRANK_MASK) >> 5)
41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
152
    #define MC_DOD_NUMROW_MASK		((1 << 4) | (1 << 3) | (1 << 2))
5566cb7c9   Mauro Carvalho Chehab   i7core_edac: Memo...
153
    #define MC_DOD_NUMROW(x)		(((x) & MC_DOD_NUMROW_MASK) >> 2)
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
154
155
    #define MC_DOD_NUMCOL_MASK		3
    #define MC_DOD_NUMCOL(x)		((x) & MC_DOD_NUMCOL_MASK)
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
156

f122a8922   Mauro Carvalho Chehab   i7core_edac: Show...
157
  #define MC_RANK_PRESENT		0x7c
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  #define MC_SAG_CH_0	0x80
  #define MC_SAG_CH_1	0x84
  #define MC_SAG_CH_2	0x88
  #define MC_SAG_CH_3	0x8c
  #define MC_SAG_CH_4	0x90
  #define MC_SAG_CH_5	0x94
  #define MC_SAG_CH_6	0x98
  #define MC_SAG_CH_7	0x9c
  
  #define MC_RIR_LIMIT_CH_0	0x40
  #define MC_RIR_LIMIT_CH_1	0x44
  #define MC_RIR_LIMIT_CH_2	0x48
  #define MC_RIR_LIMIT_CH_3	0x4C
  #define MC_RIR_LIMIT_CH_4	0x50
  #define MC_RIR_LIMIT_CH_5	0x54
  #define MC_RIR_LIMIT_CH_6	0x58
  #define MC_RIR_LIMIT_CH_7	0x5C
  #define MC_RIR_LIMIT_MASK	((1 << 10) - 1)
  
  #define MC_RIR_WAY_CH		0x80
    #define MC_RIR_WAY_OFFSET_MASK	(((1 << 14) - 1) & ~0x7)
    #define MC_RIR_WAY_RANK_MASK		0x7
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
180
181
182
183
184
  /*
   * i7core structs
   */
  
  #define NUM_CHANS 3
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
185
186
187
  #define MAX_DIMMS 3		/* Max DIMMS per channel */
  #define MAX_MCR_FUNC  4
  #define MAX_CHAN_FUNC 3
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
188
189
190
191
192
  
  struct i7core_info {
  	u32	mc_control;
  	u32	mc_status;
  	u32	max_dod;
f122a8922   Mauro Carvalho Chehab   i7core_edac: Show...
193
  	u32	ch_map;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
194
  };
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
195
196
197
198
199
200
201
202
203
204
205
  
  struct i7core_inject {
  	int	enable;
  
  	u32	section;
  	u32	type;
  	u32	eccmask;
  
  	/* Error address mask */
  	int channel, dimm, rank, bank, page, col;
  };
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
206
  struct i7core_channel {
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
207
208
  	u32		ranks;
  	u32		dimms;
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
209
  };
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
210
  struct pci_id_descr {
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
211
212
213
  	int			dev;
  	int			func;
  	int 			dev_id;
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
214
  	int			optional;
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
215
  };
bd9e19ca4   Vernon Mauery   Add support for W...
216
  struct pci_id_table {
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
217
218
  	const struct pci_id_descr	*descr;
  	int				n_devs;
bd9e19ca4   Vernon Mauery   Add support for W...
219
  };
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
220
221
222
223
  struct i7core_dev {
  	struct list_head	list;
  	u8			socket;
  	struct pci_dev		**pdev;
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
224
  	int			n_devs;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
225
226
  	struct mem_ctl_info	*mci;
  };
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
227
  struct i7core_pvt {
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
228
229
230
231
232
  	struct pci_dev	*pci_noncore;
  	struct pci_dev	*pci_mcr[MAX_MCR_FUNC + 1];
  	struct pci_dev	*pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
  
  	struct i7core_dev *i7core_dev;
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
233

a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
234
  	struct i7core_info	info;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
235
  	struct i7core_inject	inject;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
236
  	struct i7core_channel	channel[NUM_CHANS];
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
237

f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
238
239
  	int		ce_count_available;
  	int 		csrow_map[NUM_CHANS][MAX_DIMMS];
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
240
241
  
  			/* ECC corrected errors counts per udimm */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
242
243
  	unsigned long	udimm_ce_count[MAX_DIMMS];
  	int		udimm_last_ce_count[MAX_DIMMS];
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
244
  			/* ECC corrected errors counts per rdimm */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
245
246
  	unsigned long	rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
  	int		rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
247

27100db0e   Mauro Carvalho Chehab   i7core_edac: Don'...
248
  	bool		is_registered, enable_scrub;
14d2c0834   Mauro Carvalho Chehab   i7core: Use regis...
249

ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
250
  	/* Fifo double buffers */
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
251
  	struct mce		mce_entry[MCE_LOG_LEN];
ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
252
253
254
255
256
257
258
  	struct mce		mce_outentry[MCE_LOG_LEN];
  
  	/* Fifo in/out counters */
  	unsigned		mce_in, mce_out;
  
  	/* Count indicator to show errors not got */
  	unsigned		mce_overrun;
939747bd6   Mauro Carvalho Chehab   i7core_edac: Be s...
259

535e9c78e   Nils Carlson   i7core_edac: scru...
260
261
  	/* DCLK Frequency used for computing scrub rate */
  	int			dclk_freq;
939747bd6   Mauro Carvalho Chehab   i7core_edac: Be s...
262
263
  	/* Struct to control EDAC polling */
  	struct edac_pci_ctl_info *i7core_pci;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
264
  };
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
265
266
267
268
  #define PCI_DESCR(device, function, device_id)	\
  	.dev = (device),			\
  	.func = (function),			\
  	.dev_id = (device_id)
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
269
  static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
270
271
272
  		/* Memory controller */
  	{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR)     },
  	{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD)  },
224e871f3   Mauro Carvalho Chehab   i7core_edac: Fix ...
273
  			/* Exists only for RDIMM */
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
274
  	{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1  },
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
  	{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
  
  		/* Channel 0 */
  	{ PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
  	{ PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
  	{ PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
  	{ PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC)   },
  
  		/* Channel 1 */
  	{ PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
  	{ PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
  	{ PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
  	{ PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC)   },
  
  		/* Channel 2 */
  	{ PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
  	{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
  	{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
  	{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC)   },
224e871f3   Mauro Carvalho Chehab   i7core_edac: Fix ...
294
295
296
297
298
299
300
301
302
  
  		/* Generic Non-core registers */
  	/*
  	 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
  	 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
  	 * the probing code needs to test for the other address in case of
  	 * failure of this one
  	 */
  	{ PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE)  },
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
303
  };
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
304

1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
305
  static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
52a2e4fc3   Mauro Carvalho Chehab   i7core_edac: Add ...
306
307
308
309
310
311
312
313
  	{ PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR)         },
  	{ PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD)      },
  	{ PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST)     },
  
  	{ PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) },
  	{ PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) },
  	{ PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) },
  	{ PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC)   },
508fa179f   Mauro Carvalho Chehab   i7core_edac: Fix ...
314
315
316
317
  	{ PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) },
  	{ PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
  	{ PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
  	{ PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC)   },
224e871f3   Mauro Carvalho Chehab   i7core_edac: Fix ...
318
319
320
321
322
323
  
  	/*
  	 * This is the PCI device has an alternate address on some
  	 * processors like Core i7 860
  	 */
  	{ PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE)     },
52a2e4fc3   Mauro Carvalho Chehab   i7core_edac: Add ...
324
  };
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
325
  static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
bd9e19ca4   Vernon Mauery   Add support for W...
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  		/* Memory controller */
  	{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2)     },
  	{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2)  },
  			/* Exists only for RDIMM */
  	{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1  },
  	{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) },
  
  		/* Channel 0 */
  	{ PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) },
  	{ PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) },
  	{ PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) },
  	{ PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2)   },
  
  		/* Channel 1 */
  	{ PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) },
  	{ PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) },
  	{ PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) },
  	{ PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2)   },
  
  		/* Channel 2 */
  	{ PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) },
  	{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
  	{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
  	{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2)   },
224e871f3   Mauro Carvalho Chehab   i7core_edac: Fix ...
350
351
352
  
  		/* Generic Non-core registers */
  	{ PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2)  },
bd9e19ca4   Vernon Mauery   Add support for W...
353
  };
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
354
355
  #define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
  static const struct pci_id_table pci_dev_table[] = {
bd9e19ca4   Vernon Mauery   Add support for W...
356
357
358
  	PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
  	PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
  	PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
3c52cc57c   Mauro Carvalho Chehab   i7core_edac: prop...
359
  	{0,}			/* 0 terminated list. */
bd9e19ca4   Vernon Mauery   Add support for W...
360
  };
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
361
362
  /*
   *	pci_device_id	table for which devices we are looking for
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
363
364
   */
  static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
d1fd4fb69   Mauro Carvalho Chehab   i7core_edac: Add ...
365
  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
f05da2f78   Mauro Carvalho Chehab   i7core: add suppo...
366
  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)},
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
367
368
  	{0,}			/* 0 terminated list. */
  };
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
369
370
371
372
373
  /****************************************************************************
  			Anciliary status routines
   ****************************************************************************/
  
  	/* MC_CONTROL bits */
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
374
375
  #define CH_ACTIVE(pvt, ch)	((pvt)->info.mc_control & (1 << (8 + ch)))
  #define ECCx8(pvt)		((pvt)->info.mc_control & (1 << 1))
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
376
377
  
  	/* MC_STATUS bits */
61053fded   Keith Mannthey   i7core_edac: Fix ...
378
  #define ECC_ENABLED(pvt)	((pvt)->info.mc_status & (1 << 4))
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
379
  #define CH_DISABLED(pvt, ch)	((pvt)->info.mc_status & (1 << ch))
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
380
381
  
  	/* MC_MAX_DOD read functions */
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
382
  static inline int numdimms(u32 dimms)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
383
  {
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
384
  	return (dimms & 0x3) + 1;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
385
  }
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
386
  static inline int numrank(u32 rank)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
387
388
  {
  	static int ranks[4] = { 1, 2, 4, -EINVAL };
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
389
  	return ranks[rank & 0x3];
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
390
  }
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
391
  static inline int numbank(u32 bank)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
392
393
  {
  	static int banks[4] = { 4, 8, 16, -EINVAL };
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
394
  	return banks[bank & 0x3];
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
395
  }
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
396
  static inline int numrow(u32 row)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
397
398
399
400
401
  {
  	static int rows[8] = {
  		1 << 12, 1 << 13, 1 << 14, 1 << 15,
  		1 << 16, -EINVAL, -EINVAL, -EINVAL,
  	};
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
402
  	return rows[row & 0x7];
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
403
  }
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
404
  static inline int numcol(u32 col)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
405
406
407
408
  {
  	static int cols[8] = {
  		1 << 10, 1 << 11, 1 << 12, -EINVAL,
  	};
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
409
  	return cols[col & 0x3];
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
410
  }
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
411
  static struct i7core_dev *get_i7core_dev(u8 socket)
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
412
413
414
415
416
417
418
419
420
421
  {
  	struct i7core_dev *i7core_dev;
  
  	list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
  		if (i7core_dev->socket == socket)
  			return i7core_dev;
  	}
  
  	return NULL;
  }
848b2f7ed   Hidetoshi Seto   i7core_edac: Intr...
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  static struct i7core_dev *alloc_i7core_dev(u8 socket,
  					   const struct pci_id_table *table)
  {
  	struct i7core_dev *i7core_dev;
  
  	i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
  	if (!i7core_dev)
  		return NULL;
  
  	i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs,
  				   GFP_KERNEL);
  	if (!i7core_dev->pdev) {
  		kfree(i7core_dev);
  		return NULL;
  	}
  
  	i7core_dev->socket = socket;
  	i7core_dev->n_devs = table->n_devs;
  	list_add_tail(&i7core_dev->list, &i7core_edac_list);
  
  	return i7core_dev;
  }
2aa9be448   Hidetoshi Seto   i7core_edac: Intr...
444
445
446
447
448
449
  static void free_i7core_dev(struct i7core_dev *i7core_dev)
  {
  	list_del(&i7core_dev->list);
  	kfree(i7core_dev->pdev);
  	kfree(i7core_dev);
  }
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
450
451
452
  /****************************************************************************
  			Memory check routines
   ****************************************************************************/
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
453
454
  static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
  					  unsigned func)
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
455
  {
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
456
  	struct i7core_dev *i7core_dev = get_i7core_dev(socket);
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
457
  	int i;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
458

66607706c   Mauro Carvalho Chehab   Dynamically alloc...
459
460
  	if (!i7core_dev)
  		return NULL;
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
461
  	for (i = 0; i < i7core_dev->n_devs; i++) {
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
462
  		if (!i7core_dev->pdev[i])
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
463
  			continue;
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
464
465
466
  		if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
  		    PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
  			return i7core_dev->pdev[i];
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
467
468
  		}
  	}
eb94fc402   Mauro Carvalho Chehab   i7core_edac: fill...
469
470
  	return NULL;
  }
ec6df24c1   Mauro Carvalho Chehab   i7core: better do...
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
  /**
   * i7core_get_active_channels() - gets the number of channels and csrows
   * @socket:	Quick Path Interconnect socket
   * @channels:	Number of channels that will be returned
   * @csrows:	Number of csrows found
   *
   * Since EDAC core needs to know in advance the number of available channels
   * and csrows, in order to allocate memory for csrows/channels, it is needed
   * to run two similar steps. At the first step, implemented on this function,
   * it checks the number of csrows/channels present at one socket.
   * this is used in order to properly allocate the size of mci components.
   *
   * It should be noticed that none of the current available datasheets explain
   * or even mention how csrows are seen by the memory controller. So, we need
   * to add a fake description for csrows.
   * So, this driver is attributing one DIMM memory for one csrow.
   */
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
488
  static int i7core_get_active_channels(const u8 socket, unsigned *channels,
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
489
  				      unsigned *csrows)
eb94fc402   Mauro Carvalho Chehab   i7core_edac: fill...
490
491
492
493
494
495
496
  {
  	struct pci_dev *pdev = NULL;
  	int i, j;
  	u32 status, control;
  
  	*channels = 0;
  	*csrows = 0;
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
497
  	pdev = get_pdev_slot_func(socket, 3, 0);
b7c761512   Mauro Carvalho Chehab   i7core_edac: Impr...
498
  	if (!pdev) {
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
499
500
501
  		i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!
  ",
  			      socket);
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
502
  		return -ENODEV;
b7c761512   Mauro Carvalho Chehab   i7core_edac: Impr...
503
  	}
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
504
505
506
507
508
509
  
  	/* Device 3 function 0 reads */
  	pci_read_config_dword(pdev, MC_STATUS, &status);
  	pci_read_config_dword(pdev, MC_CONTROL, &control);
  
  	for (i = 0; i < NUM_CHANS; i++) {
eb94fc402   Mauro Carvalho Chehab   i7core_edac: fill...
510
  		u32 dimm_dod[3];
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
511
512
513
514
515
  		/* Check if the channel is active */
  		if (!(control & (1 << (8 + i))))
  			continue;
  
  		/* Check if the channel is disabled */
41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
516
  		if (status & (1 << i))
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
517
  			continue;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
518

67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
519
  		pdev = get_pdev_slot_func(socket, i + 4, 1);
eb94fc402   Mauro Carvalho Chehab   i7core_edac: fill...
520
  		if (!pdev) {
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
521
522
523
524
  			i7core_printk(KERN_ERR, "Couldn't find socket %d "
  						"fn %d.%d!!!
  ",
  						socket, i + 4, 1);
eb94fc402   Mauro Carvalho Chehab   i7core_edac: fill...
525
526
527
528
529
530
531
532
533
  			return -ENODEV;
  		}
  		/* Devices 4-6 function 1 */
  		pci_read_config_dword(pdev,
  				MC_DOD_CH_DIMM0, &dimm_dod[0]);
  		pci_read_config_dword(pdev,
  				MC_DOD_CH_DIMM1, &dimm_dod[1]);
  		pci_read_config_dword(pdev,
  				MC_DOD_CH_DIMM2, &dimm_dod[2]);
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
534
  		(*channels)++;
eb94fc402   Mauro Carvalho Chehab   i7core_edac: fill...
535
536
537
538
539
540
  
  		for (j = 0; j < 3; j++) {
  			if (!DIMM_PRESENT(dimm_dod[j]))
  				continue;
  			(*csrows)++;
  		}
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
541
  	}
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
542
543
  	debugf0("Number of active channels on socket %d: %d
  ",
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
544
  		socket, *channels);
1c6fed808   Mauro Carvalho Chehab   i7core_edac: Prop...
545

ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
546
547
  	return 0;
  }
2e5185f7f   Hidetoshi Seto   i7core_edac: Remo...
548
  static int get_dimm_config(const struct mem_ctl_info *mci)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
549
550
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
1c6fed808   Mauro Carvalho Chehab   i7core_edac: Prop...
551
  	struct csrow_info *csr;
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
552
  	struct pci_dev *pdev;
ba6c5c62e   Mauro Carvalho Chehab   i7core_edac: maps...
553
  	int i, j;
2e5185f7f   Hidetoshi Seto   i7core_edac: Remo...
554
  	int csrow = 0;
5566cb7c9   Mauro Carvalho Chehab   i7core_edac: Memo...
555
  	unsigned long last_page = 0;
1c6fed808   Mauro Carvalho Chehab   i7core_edac: Prop...
556
  	enum edac_type mode;
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
557
  	enum mem_type mtype;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
558

854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
559
  	/* Get data from the MC register, function 0 */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
560
  	pdev = pvt->pci_mcr[0];
7dd6953c5   Mauro Carvalho Chehab   i7core_edac: Add ...
561
  	if (!pdev)
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
562
  		return -ENODEV;
f122a8922   Mauro Carvalho Chehab   i7core_edac: Show...
563
  	/* Device 3 function 0 reads */
7dd6953c5   Mauro Carvalho Chehab   i7core_edac: Add ...
564
565
566
567
  	pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
  	pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
  	pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
  	pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
f122a8922   Mauro Carvalho Chehab   i7core_edac: Show...
568

17cb7b0cf   Mauro Carvalho Chehab   i7core_edac: Some...
569
570
  	debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x
  ",
4af91889e   Mauro Carvalho Chehab   i7core_edac: Avoi...
571
  		pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
f122a8922   Mauro Carvalho Chehab   i7core_edac: Show...
572
  		pvt->info.max_dod, pvt->info.ch_map);
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
573

1c6fed808   Mauro Carvalho Chehab   i7core_edac: Prop...
574
  	if (ECC_ENABLED(pvt)) {
41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
575
576
  		debugf0("ECC enabled with x%d SDCC
  ", ECCx8(pvt) ? 8 : 4);
1c6fed808   Mauro Carvalho Chehab   i7core_edac: Prop...
577
578
579
580
581
  		if (ECCx8(pvt))
  			mode = EDAC_S8ECD8ED;
  		else
  			mode = EDAC_S4ECD4ED;
  	} else {
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
582
583
  		debugf0("ECC disabled
  ");
1c6fed808   Mauro Carvalho Chehab   i7core_edac: Prop...
584
585
  		mode = EDAC_NONE;
  	}
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
586
587
  
  	/* FIXME: need to handle the error codes */
17cb7b0cf   Mauro Carvalho Chehab   i7core_edac: Some...
588
589
590
  	debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
  		"x%x x 0x%x
  ",
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
591
592
  		numdimms(pvt->info.max_dod),
  		numrank(pvt->info.max_dod >> 2),
276b824c3   Mauro Carvalho Chehab   i7core_edac: some...
593
  		numbank(pvt->info.max_dod >> 4),
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
594
595
  		numrow(pvt->info.max_dod >> 6),
  		numcol(pvt->info.max_dod >> 9));
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
596

0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
597
  	for (i = 0; i < NUM_CHANS; i++) {
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
598
  		u32 data, dimm_dod[3], value[8];
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
599

52a2e4fc3   Mauro Carvalho Chehab   i7core_edac: Add ...
600
601
  		if (!pvt->pci_ch[i][0])
  			continue;
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
602
603
604
605
606
607
608
609
610
611
  		if (!CH_ACTIVE(pvt, i)) {
  			debugf0("Channel %i is not active
  ", i);
  			continue;
  		}
  		if (CH_DISABLED(pvt, i)) {
  			debugf0("Channel %i is disabled
  ", i);
  			continue;
  		}
f122a8922   Mauro Carvalho Chehab   i7core_edac: Show...
612
  		/* Devices 4-6 function 0 */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
613
  		pci_read_config_dword(pvt->pci_ch[i][0],
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
614
  				MC_CHANNEL_DIMM_INIT_PARAMS, &data);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
615
  		pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
616
  						4 : 2;
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
617

854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
618
619
  		if (data & REGISTERED_DIMM)
  			mtype = MEM_RDDR3;
14d2c0834   Mauro Carvalho Chehab   i7core: Use regis...
620
  		else
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
621
622
  			mtype = MEM_DDR3;
  #if 0
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
623
624
625
626
627
628
  		if (data & THREE_DIMMS_PRESENT)
  			pvt->channel[i].dimms = 3;
  		else if (data & SINGLE_QUAD_RANK_PRESENT)
  			pvt->channel[i].dimms = 1;
  		else
  			pvt->channel[i].dimms = 2;
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
629
630
631
  #endif
  
  		/* Devices 4-6 function 1 */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
632
  		pci_read_config_dword(pvt->pci_ch[i][1],
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
633
  				MC_DOD_CH_DIMM0, &dimm_dod[0]);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
634
  		pci_read_config_dword(pvt->pci_ch[i][1],
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
635
  				MC_DOD_CH_DIMM1, &dimm_dod[1]);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
636
  		pci_read_config_dword(pvt->pci_ch[i][1],
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
637
  				MC_DOD_CH_DIMM2, &dimm_dod[2]);
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
638

1c6fed808   Mauro Carvalho Chehab   i7core_edac: Prop...
639
  		debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
640
641
  			"%d ranks, %cDIMMs
  ",
1c6fed808   Mauro Carvalho Chehab   i7core_edac: Prop...
642
643
644
  			i,
  			RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
  			data,
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
645
  			pvt->channel[i].ranks,
41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
646
  			(data & REGISTERED_DIMM) ? 'R' : 'U');
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
647
648
649
  
  		for (j = 0; j < 3; j++) {
  			u32 banks, ranks, rows, cols;
5566cb7c9   Mauro Carvalho Chehab   i7core_edac: Memo...
650
  			u32 size, npages;
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
651
652
653
654
655
656
657
658
  
  			if (!DIMM_PRESENT(dimm_dod[j]))
  				continue;
  
  			banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
  			ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
  			rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
  			cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
5566cb7c9   Mauro Carvalho Chehab   i7core_edac: Memo...
659
660
  			/* DDR3 has 8 I/O banks */
  			size = (rows * cols * banks * ranks) >> (20 - 3);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
661
  			pvt->channel[i].dimms++;
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
662

17cb7b0cf   Mauro Carvalho Chehab   i7core_edac: Some...
663
664
665
666
  			debugf0("\tdimm %d %d Mb offset: %x, "
  				"bank: %d, rank: %d, row: %#x, col: %#x
  ",
  				j, size,
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
667
668
  				RANKOFFSET(dimm_dod[j]),
  				banks, ranks, rows, cols);
e9144601d   Mauro Carvalho Chehab   i7core_edac: move...
669
  			npages = MiB_TO_PAGES(size);
5566cb7c9   Mauro Carvalho Chehab   i7core_edac: Memo...
670

2e5185f7f   Hidetoshi Seto   i7core_edac: Remo...
671
  			csr = &mci->csrows[csrow];
5566cb7c9   Mauro Carvalho Chehab   i7core_edac: Memo...
672
673
674
675
  			csr->first_page = last_page + 1;
  			last_page += npages;
  			csr->last_page = last_page;
  			csr->nr_pages = npages;
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
676
  			csr->page_mask = 0;
eb94fc402   Mauro Carvalho Chehab   i7core_edac: fill...
677
  			csr->grain = 8;
2e5185f7f   Hidetoshi Seto   i7core_edac: Remo...
678
  			csr->csrow_idx = csrow;
eb94fc402   Mauro Carvalho Chehab   i7core_edac: fill...
679
680
681
682
  			csr->nr_channels = 1;
  
  			csr->channels[0].chan_idx = i;
  			csr->channels[0].ce_count = 0;
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
683

2e5185f7f   Hidetoshi Seto   i7core_edac: Remo...
684
  			pvt->csrow_map[i][j] = csrow;
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
685

854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
  			switch (banks) {
  			case 4:
  				csr->dtype = DEV_X4;
  				break;
  			case 8:
  				csr->dtype = DEV_X8;
  				break;
  			case 16:
  				csr->dtype = DEV_X16;
  				break;
  			default:
  				csr->dtype = DEV_UNKNOWN;
  			}
  
  			csr->edac_mode = mode;
  			csr->mtype = mtype;
767ba4a52   Mauro Carvalho Chehab   i7core_edac: Init...
702
703
704
705
  			snprintf(csr->channels[0].label,
  					sizeof(csr->channels[0].label),
  					"CPU#%uChannel#%u_DIMM#%u",
  					pvt->i7core_dev->socket, i, j);
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
706

2e5185f7f   Hidetoshi Seto   i7core_edac: Remo...
707
  			csrow++;
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
708
  		}
1c6fed808   Mauro Carvalho Chehab   i7core_edac: Prop...
709

854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
710
711
712
713
714
715
716
717
  		pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
  		pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
  		pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
  		pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
  		pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
  		pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
  		pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
  		pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
17cb7b0cf   Mauro Carvalho Chehab   i7core_edac: Some...
718
719
  		debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET
  ", i);
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
720
  		for (j = 0; j < 8; j++)
17cb7b0cf   Mauro Carvalho Chehab   i7core_edac: Some...
721
722
  			debugf1("\t\t%#x\t%#x\t%#x
  ",
854d33499   Mauro Carvalho Chehab   i7core_edac: Get ...
723
724
  				(value[j] >> 27) & 0x1,
  				(value[j] >> 24) & 0x7,
80b8ce89e   David Sterba   i7core_edac: fix ...
725
  				(value[j] & ((1 << 24) - 1)));
0b2b7b7ec   Mauro Carvalho Chehab   i7core_edac: Add ...
726
  	}
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
727
728
729
730
  	return 0;
  }
  
  /****************************************************************************
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
731
732
733
734
735
736
737
738
739
740
  			Error insertion routines
   ****************************************************************************/
  
  /* The i7core has independent error injection features per channel.
     However, to have a simpler code, we don't allow enabling error injection
     on more than one channel.
     Also, since a change at an inject parameter will be applied only at enable,
     we're disabling error injection on all write calls to the sysfs nodes that
     controls the error code injection.
   */
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
741
  static int disable_inject(const struct mem_ctl_info *mci)
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
742
743
744
745
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  
  	pvt->inject.enable = 0;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
746
  	if (!pvt->pci_ch[pvt->inject.channel][0])
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
747
  		return -ENODEV;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
748
  	pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
4157d9f55   Mauro Carvalho Chehab   i7core_edac: fix ...
749
  				MC_CHANNEL_ERROR_INJECT, 0);
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
750
751
  
  	return 0;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
  }
  
  /*
   * i7core inject inject.section
   *
   *	accept and store error injection inject.section value
   *	bit 0 - refers to the lower 32-byte half cacheline
   *	bit 1 - refers to the upper 32-byte half cacheline
   */
  static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
  					   const char *data, size_t count)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	unsigned long value;
  	int rc;
  
  	if (pvt->inject.enable)
41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
769
  		disable_inject(mci);
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
770
771
772
  
  	rc = strict_strtoul(data, 10, &value);
  	if ((rc < 0) || (value > 3))
2068def56   Mauro Carvalho Chehab   i7core_edac: fix ...
773
  		return -EIO;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
  
  	pvt->inject.section = (u32) value;
  	return count;
  }
  
  static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
  					      char *data)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	return sprintf(data, "0x%08x
  ", pvt->inject.section);
  }
  
  /*
   * i7core inject.type
   *
   *	accept and store error injection inject.section value
   *	bit 0 - repeat enable - Enable error repetition
   *	bit 1 - inject ECC error
   *	bit 2 - inject parity error
   */
  static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
  					const char *data, size_t count)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	unsigned long value;
  	int rc;
  
  	if (pvt->inject.enable)
41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
803
  		disable_inject(mci);
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
804
805
806
  
  	rc = strict_strtoul(data, 10, &value);
  	if ((rc < 0) || (value > 7))
2068def56   Mauro Carvalho Chehab   i7core_edac: fix ...
807
  		return -EIO;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
  
  	pvt->inject.type = (u32) value;
  	return count;
  }
  
  static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
  					      char *data)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	return sprintf(data, "0x%08x
  ", pvt->inject.type);
  }
  
  /*
   * i7core_inject_inject.eccmask_store
   *
   * The type of error (UE/CE) will depend on the inject.eccmask value:
   *   Any bits set to a 1 will flip the corresponding ECC bit
   *   Correctable errors can be injected by flipping 1 bit or the bits within
   *   a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
   *   23:16 and 31:24). Flipping bits in two symbol pairs will cause an
   *   uncorrectable error to be injected.
   */
  static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
  					const char *data, size_t count)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	unsigned long value;
  	int rc;
  
  	if (pvt->inject.enable)
41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
839
  		disable_inject(mci);
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
840
841
842
  
  	rc = strict_strtoul(data, 10, &value);
  	if (rc < 0)
2068def56   Mauro Carvalho Chehab   i7core_edac: fix ...
843
  		return -EIO;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
  
  	pvt->inject.eccmask = (u32) value;
  	return count;
  }
  
  static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
  					      char *data)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	return sprintf(data, "0x%08x
  ", pvt->inject.eccmask);
  }
  
  /*
   * i7core_addrmatch
   *
   * The type of error (UE/CE) will depend on the inject.eccmask value:
   *   Any bits set to a 1 will flip the corresponding ECC bit
   *   Correctable errors can be injected by flipping 1 bit or the bits within
   *   a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
   *   23:16 and 31:24). Flipping bits in two symbol pairs will cause an
   *   uncorrectable error to be injected.
   */
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
867

a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
868
869
870
871
872
  #define DECLARE_ADDR_MATCH(param, limit)			\
  static ssize_t i7core_inject_store_##param(			\
  		struct mem_ctl_info *mci,			\
  		const char *data, size_t count)			\
  {								\
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
873
  	struct i7core_pvt *pvt;					\
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
874
875
876
  	long value;						\
  	int rc;							\
  								\
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
877
878
879
880
  	debugf1("%s()
  ", __func__);				\
  	pvt = mci->pvt_info;					\
  								\
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
881
882
883
  	if (pvt->inject.enable)					\
  		disable_inject(mci);				\
  								\
4f87fad1d   Mauro Carvalho Chehab   i7core_edac: Bett...
884
885
  	if (!strcasecmp(data, "any") || !strcasecmp(data, "any
  "))\
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
  		value = -1;					\
  	else {							\
  		rc = strict_strtoul(data, 10, &value);		\
  		if ((rc < 0) || (value >= limit))		\
  			return -EIO;				\
  	}							\
  								\
  	pvt->inject.param = value;				\
  								\
  	return count;						\
  }								\
  								\
  static ssize_t i7core_inject_show_##param(			\
  		struct mem_ctl_info *mci,			\
  		char *data)					\
  {								\
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
902
903
904
905
906
  	struct i7core_pvt *pvt;					\
  								\
  	pvt = mci->pvt_info;					\
  	debugf1("%s() pvt=%p
  ", __func__, pvt);		\
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
907
908
909
910
911
912
  	if (pvt->inject.param < 0)				\
  		return sprintf(data, "any
  ");			\
  	else							\
  		return sprintf(data, "%d
  ", pvt->inject.param);\
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
913
  }
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
914
915
916
917
918
919
920
921
922
  #define ATTR_ADDR_MATCH(param)					\
  	{							\
  		.attr = {					\
  			.name = #param,				\
  			.mode = (S_IRUGO | S_IWUSR)		\
  		},						\
  		.show  = i7core_inject_show_##param,		\
  		.store = i7core_inject_store_##param,		\
  	}
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
923

a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
924
925
926
927
928
929
  DECLARE_ADDR_MATCH(channel, 3);
  DECLARE_ADDR_MATCH(dimm, 3);
  DECLARE_ADDR_MATCH(rank, 4);
  DECLARE_ADDR_MATCH(bank, 32);
  DECLARE_ADDR_MATCH(page, 0x10000);
  DECLARE_ADDR_MATCH(col, 0x4000);
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
930

1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
931
  static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
276b824c3   Mauro Carvalho Chehab   i7core_edac: some...
932
933
934
  {
  	u32 read;
  	int count;
4157d9f55   Mauro Carvalho Chehab   i7core_edac: fix ...
935
936
937
938
  	debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x
  ",
  		dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
  		where, val);
276b824c3   Mauro Carvalho Chehab   i7core_edac: some...
939
940
  	for (count = 0; count < 10; count++) {
  		if (count)
b990538a7   Mauro Carvalho Chehab   i7core_edac: Codi...
941
  			msleep(100);
276b824c3   Mauro Carvalho Chehab   i7core_edac: some...
942
943
944
945
946
947
  		pci_write_config_dword(dev, where, val);
  		pci_read_config_dword(dev, where, &read);
  
  		if (read == val)
  			return 0;
  	}
4157d9f55   Mauro Carvalho Chehab   i7core_edac: fix ...
948
949
950
951
952
  	i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
  		"write=%08x. Read=%08x
  ",
  		dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
  		where, val, read);
276b824c3   Mauro Carvalho Chehab   i7core_edac: some...
953
954
955
  
  	return -EINVAL;
  }
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
  /*
   * This routine prepares the Memory Controller for error injection.
   * The error will be injected when some process tries to write to the
   * memory that matches the given criteria.
   * The criteria can be set in terms of a mask where dimm, rank, bank, page
   * and col can be specified.
   * A -1 value for any of the mask items will make the MCU to ignore
   * that matching criteria for error injection.
   *
   * It should be noticed that the error will only happen after a write operation
   * on a memory that matches the condition. if REPEAT_EN is not enabled at
   * inject mask, then it will produce just one error. Otherwise, it will repeat
   * until the injectmask would be cleaned.
   *
   * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
   *    is reliable enough to check if the MC is using the
   *    three channels. However, this is not clear at the datasheet.
   */
  static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
  				       const char *data, size_t count)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	u32 injectmask;
  	u64 mask = 0;
  	int  rc;
  	long enable;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
982
  	if (!pvt->pci_ch[pvt->inject.channel][0])
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
983
  		return 0;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
984
985
986
987
988
989
990
991
992
993
994
995
996
  	rc = strict_strtoul(data, 10, &enable);
  	if ((rc < 0))
  		return 0;
  
  	if (enable) {
  		pvt->inject.enable = 1;
  	} else {
  		disable_inject(mci);
  		return count;
  	}
  
  	/* Sets pvt->inject.dimm mask */
  	if (pvt->inject.dimm < 0)
486dd09f1   Alan Cox   edac: i7core_edac...
997
  		mask |= 1LL << 41;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
998
  	else {
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
999
  		if (pvt->channel[pvt->inject.channel].dimms > 2)
486dd09f1   Alan Cox   edac: i7core_edac...
1000
  			mask |= (pvt->inject.dimm & 0x3LL) << 35;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1001
  		else
486dd09f1   Alan Cox   edac: i7core_edac...
1002
  			mask |= (pvt->inject.dimm & 0x1LL) << 36;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1003
1004
1005
1006
  	}
  
  	/* Sets pvt->inject.rank mask */
  	if (pvt->inject.rank < 0)
486dd09f1   Alan Cox   edac: i7core_edac...
1007
  		mask |= 1LL << 40;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1008
  	else {
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1009
  		if (pvt->channel[pvt->inject.channel].dimms > 2)
486dd09f1   Alan Cox   edac: i7core_edac...
1010
  			mask |= (pvt->inject.rank & 0x1LL) << 34;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1011
  		else
486dd09f1   Alan Cox   edac: i7core_edac...
1012
  			mask |= (pvt->inject.rank & 0x3LL) << 34;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1013
1014
1015
1016
  	}
  
  	/* Sets pvt->inject.bank mask */
  	if (pvt->inject.bank < 0)
486dd09f1   Alan Cox   edac: i7core_edac...
1017
  		mask |= 1LL << 39;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1018
  	else
486dd09f1   Alan Cox   edac: i7core_edac...
1019
  		mask |= (pvt->inject.bank & 0x15LL) << 30;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1020
1021
1022
  
  	/* Sets pvt->inject.page mask */
  	if (pvt->inject.page < 0)
486dd09f1   Alan Cox   edac: i7core_edac...
1023
  		mask |= 1LL << 38;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1024
  	else
486dd09f1   Alan Cox   edac: i7core_edac...
1025
  		mask |= (pvt->inject.page & 0xffff) << 14;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1026
1027
1028
  
  	/* Sets pvt->inject.column mask */
  	if (pvt->inject.col < 0)
486dd09f1   Alan Cox   edac: i7core_edac...
1029
  		mask |= 1LL << 37;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1030
  	else
486dd09f1   Alan Cox   edac: i7core_edac...
1031
  		mask |= (pvt->inject.col & 0x3fff);
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1032

276b824c3   Mauro Carvalho Chehab   i7core_edac: some...
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
  	/*
  	 * bit    0: REPEAT_EN
  	 * bits 1-2: MASK_HALF_CACHELINE
  	 * bit    3: INJECT_ECC
  	 * bit    4: INJECT_ADDR_PARITY
  	 */
  
  	injectmask = (pvt->inject.type & 1) |
  		     (pvt->inject.section & 0x3) << 1 |
  		     (pvt->inject.type & 0x6) << (3 - 1);
  
  	/* Unlock writes to registers - this register is write only */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1045
  	pci_write_config_dword(pvt->pci_noncore,
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
1046
  			       MC_CFG_CONTROL, 0x2);
e9bd2e737   Mauro Carvalho Chehab   i7core_edac: Adds...
1047

f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1048
  	write_and_test(pvt->pci_ch[pvt->inject.channel][0],
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1049
  			       MC_CHANNEL_ADDR_MATCH, mask);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1050
  	write_and_test(pvt->pci_ch[pvt->inject.channel][0],
7b029d03c   Mauro Carvalho Chehab   i7core_edac: A fe...
1051
  			       MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
7b029d03c   Mauro Carvalho Chehab   i7core_edac: A fe...
1052

f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1053
  	write_and_test(pvt->pci_ch[pvt->inject.channel][0],
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1054
  			       MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1055
  	write_and_test(pvt->pci_ch[pvt->inject.channel][0],
4157d9f55   Mauro Carvalho Chehab   i7core_edac: fix ...
1056
  			       MC_CHANNEL_ERROR_INJECT, injectmask);
276b824c3   Mauro Carvalho Chehab   i7core_edac: some...
1057

194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1058
  	/*
276b824c3   Mauro Carvalho Chehab   i7core_edac: some...
1059
1060
1061
  	 * This is something undocumented, based on my tests
  	 * Without writing 8 to this register, errors aren't injected. Not sure
  	 * why.
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1062
  	 */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1063
  	pci_write_config_dword(pvt->pci_noncore,
276b824c3   Mauro Carvalho Chehab   i7core_edac: some...
1064
  			       MC_CFG_CONTROL, 8);
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1065

41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
1066
1067
1068
  	debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
  		" inject 0x%08x
  ",
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1069
  		mask, pvt->inject.eccmask, injectmask);
7b029d03c   Mauro Carvalho Chehab   i7core_edac: A fe...
1070

194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1071
1072
1073
1074
1075
1076
1077
  	return count;
  }
  
  static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
  					char *data)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
7b029d03c   Mauro Carvalho Chehab   i7core_edac: A fe...
1078
  	u32 injectmask;
52a2e4fc3   Mauro Carvalho Chehab   i7core_edac: Add ...
1079
1080
  	if (!pvt->pci_ch[pvt->inject.channel][0])
  		return 0;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1081
  	pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
4157d9f55   Mauro Carvalho Chehab   i7core_edac: fix ...
1082
  			       MC_CHANNEL_ERROR_INJECT, &injectmask);
7b029d03c   Mauro Carvalho Chehab   i7core_edac: A fe...
1083
1084
1085
1086
1087
1088
  
  	debugf0("Inject error read: 0x%018x
  ", injectmask);
  
  	if (injectmask & 0x0c)
  		pvt->inject.enable = 1;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1089
1090
1091
  	return sprintf(data, "%d
  ", pvt->inject.enable);
  }
f338d7369   Mauro Carvalho Chehab   i7core_edac: Conv...
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
  #define DECLARE_COUNTER(param)					\
  static ssize_t i7core_show_counter_##param(			\
  		struct mem_ctl_info *mci,			\
  		char *data)					\
  {								\
  	struct i7core_pvt *pvt = mci->pvt_info;			\
  								\
  	debugf1("%s() 
  ", __func__);				\
  	if (!pvt->ce_count_available || (pvt->is_registered))	\
  		return sprintf(data, "data unavailable
  ");	\
  	return sprintf(data, "%lu
  ",				\
  			pvt->udimm_ce_count[param]);		\
  }
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1108

f338d7369   Mauro Carvalho Chehab   i7core_edac: Conv...
1109
1110
1111
1112
1113
1114
1115
  #define ATTR_COUNTER(param)					\
  	{							\
  		.attr = {					\
  			.name = __stringify(udimm##param),	\
  			.mode = (S_IRUGO | S_IWUSR)		\
  		},						\
  		.show  = i7core_show_counter_##param		\
d88b85072   Mauro Carvalho Chehab   i7core_edac: Fix ...
1116
  	}
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1117

f338d7369   Mauro Carvalho Chehab   i7core_edac: Conv...
1118
1119
1120
  DECLARE_COUNTER(0);
  DECLARE_COUNTER(1);
  DECLARE_COUNTER(2);
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1121

194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1122
1123
1124
  /*
   * Sysfs struct
   */
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
1125

1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1126
  static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
1127
1128
1129
1130
1131
1132
  	ATTR_ADDR_MATCH(channel),
  	ATTR_ADDR_MATCH(dimm),
  	ATTR_ADDR_MATCH(rank),
  	ATTR_ADDR_MATCH(bank),
  	ATTR_ADDR_MATCH(page),
  	ATTR_ADDR_MATCH(col),
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1133
  	{ } /* End of list */
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
1134
  };
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1135
  static const struct mcidev_sysfs_group i7core_inject_addrmatch = {
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
1136
1137
1138
  	.name  = "inject_addrmatch",
  	.mcidev_attr = i7core_addrmatch_attrs,
  };
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1139
  static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
f338d7369   Mauro Carvalho Chehab   i7core_edac: Conv...
1140
1141
1142
  	ATTR_COUNTER(0),
  	ATTR_COUNTER(1),
  	ATTR_COUNTER(2),
64aab720b   Marcin Slusarz   i7core_edac: fix ...
1143
  	{ .attr = { .name = NULL } }
f338d7369   Mauro Carvalho Chehab   i7core_edac: Conv...
1144
  };
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1145
  static const struct mcidev_sysfs_group i7core_udimm_counters = {
f338d7369   Mauro Carvalho Chehab   i7core_edac: Conv...
1146
1147
1148
  	.name  = "all_channel_counts",
  	.mcidev_attr = i7core_udimm_counters_attrs,
  };
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1149
  static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = {
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
  	{
  		.attr = {
  			.name = "inject_section",
  			.mode = (S_IRUGO | S_IWUSR)
  		},
  		.show  = i7core_inject_section_show,
  		.store = i7core_inject_section_store,
  	}, {
  		.attr = {
  			.name = "inject_type",
  			.mode = (S_IRUGO | S_IWUSR)
  		},
  		.show  = i7core_inject_type_show,
  		.store = i7core_inject_type_store,
  	}, {
  		.attr = {
  			.name = "inject_eccmask",
  			.mode = (S_IRUGO | S_IWUSR)
  		},
  		.show  = i7core_inject_eccmask_show,
  		.store = i7core_inject_eccmask_store,
  	}, {
a5538e531   Mauro Carvalho Chehab   i7core_edac: Add ...
1172
  		.grp = &i7core_inject_addrmatch,
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1173
1174
1175
1176
1177
1178
1179
1180
  	}, {
  		.attr = {
  			.name = "inject_enable",
  			.mode = (S_IRUGO | S_IWUSR)
  		},
  		.show  = i7core_inject_enable_show,
  		.store = i7core_inject_enable_store,
  	},
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
  	{ }	/* End of list */
  };
  
  static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = {
  	{
  		.attr = {
  			.name = "inject_section",
  			.mode = (S_IRUGO | S_IWUSR)
  		},
  		.show  = i7core_inject_section_show,
  		.store = i7core_inject_section_store,
  	}, {
  		.attr = {
  			.name = "inject_type",
  			.mode = (S_IRUGO | S_IWUSR)
  		},
  		.show  = i7core_inject_type_show,
  		.store = i7core_inject_type_store,
  	}, {
  		.attr = {
  			.name = "inject_eccmask",
  			.mode = (S_IRUGO | S_IWUSR)
  		},
  		.show  = i7core_inject_eccmask_show,
  		.store = i7core_inject_eccmask_store,
  	}, {
  		.grp = &i7core_inject_addrmatch,
  	}, {
  		.attr = {
  			.name = "inject_enable",
  			.mode = (S_IRUGO | S_IWUSR)
  		},
  		.show  = i7core_inject_enable_show,
  		.store = i7core_inject_enable_store,
  	}, {
  		.grp = &i7core_udimm_counters,
  	},
  	{ }	/* End of list */
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
1219
1220
1221
  };
  
  /****************************************************************************
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1222
1223
1224
1225
  	Device initialization routines: put/get, init/exit
   ****************************************************************************/
  
  /*
64c10f6e0   Hidetoshi Seto   i7core_edac: Alwa...
1226
   *	i7core_put_all_devices	'put' all the devices that we have
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1227
1228
   *				reserved via 'get'
   */
13d6e9b65   Mauro Carvalho Chehab   i7core_edac: at r...
1229
  static void i7core_put_devices(struct i7core_dev *i7core_dev)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1230
  {
13d6e9b65   Mauro Carvalho Chehab   i7core_edac: at r...
1231
  	int i;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1232

22e6bcbdc   Mauro Carvalho Chehab   i7core_edac: chan...
1233
1234
  	debugf0(__FILE__ ": %s()
  ", __func__);
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1235
  	for (i = 0; i < i7core_dev->n_devs; i++) {
22e6bcbdc   Mauro Carvalho Chehab   i7core_edac: chan...
1236
1237
1238
1239
1240
1241
1242
1243
1244
  		struct pci_dev *pdev = i7core_dev->pdev[i];
  		if (!pdev)
  			continue;
  		debugf0("Removing dev %02x:%02x.%d
  ",
  			pdev->bus->number,
  			PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
  		pci_dev_put(pdev);
  	}
13d6e9b65   Mauro Carvalho Chehab   i7core_edac: at r...
1245
  }
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
1246

13d6e9b65   Mauro Carvalho Chehab   i7core_edac: at r...
1247
1248
  static void i7core_put_all_devices(void)
  {
425386803   Mauro Carvalho Chehab   i7core_edac: We n...
1249
  	struct i7core_dev *i7core_dev, *tmp;
13d6e9b65   Mauro Carvalho Chehab   i7core_edac: at r...
1250

39300e714   Mauro Carvalho Chehab   i7core_edac: expl...
1251
  	list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
13d6e9b65   Mauro Carvalho Chehab   i7core_edac: at r...
1252
  		i7core_put_devices(i7core_dev);
2aa9be448   Hidetoshi Seto   i7core_edac: Intr...
1253
  		free_i7core_dev(i7core_dev);
39300e714   Mauro Carvalho Chehab   i7core_edac: expl...
1254
  	}
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1255
  }
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1256
  static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table)
bc2d7245f   Keith Mannthey   i7core_edac: Prob...
1257
1258
1259
  {
  	struct pci_dev *pdev = NULL;
  	int i;
54a08ab15   Mauro Carvalho Chehab   i7core_edac: Don'...
1260

bc2d7245f   Keith Mannthey   i7core_edac: Prob...
1261
  	/*
e7bf068aa   David Sterba   i7core_edac: fix ...
1262
  	 * On Xeon 55xx, the Intel Quick Path Arch Generic Non-core pci buses
bc2d7245f   Keith Mannthey   i7core_edac: Prob...
1263
1264
1265
  	 * aren't announced by acpi. So, we need to use a legacy scan probing
  	 * to detect them
  	 */
bd9e19ca4   Vernon Mauery   Add support for W...
1266
1267
1268
1269
1270
1271
  	while (table && table->descr) {
  		pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL);
  		if (unlikely(!pdev)) {
  			for (i = 0; i < MAX_SOCKET_BUSES; i++)
  				pcibios_scan_specific_bus(255-i);
  		}
bda142890   Mauro Carvalho Chehab   i7core_edac: Prop...
1272
  		pci_dev_put(pdev);
bd9e19ca4   Vernon Mauery   Add support for W...
1273
  		table++;
bc2d7245f   Keith Mannthey   i7core_edac: Prob...
1274
1275
  	}
  }
bda142890   Mauro Carvalho Chehab   i7core_edac: Prop...
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
  static unsigned i7core_pci_lastbus(void)
  {
  	int last_bus = 0, bus;
  	struct pci_bus *b = NULL;
  
  	while ((b = pci_find_next_bus(b)) != NULL) {
  		bus = b->number;
  		debugf0("Found bus %d
  ", bus);
  		if (bus > last_bus)
  			last_bus = bus;
  	}
  
  	debugf0("Last bus %d
  ", last_bus);
  
  	return last_bus;
  }
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1294
  /*
64c10f6e0   Hidetoshi Seto   i7core_edac: Alwa...
1295
   *	i7core_get_all_devices	Find and perform 'get' operation on the MCH's
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1296
1297
1298
1299
   *			device/functions we want to reference for this driver
   *
   *			Need to 'get' device 16 func 1 and func 2
   */
b197cba07   Hidetoshi Seto   i7core_edac: Redu...
1300
1301
1302
1303
  static int i7core_get_onedevice(struct pci_dev **prev,
  				const struct pci_id_table *table,
  				const unsigned devno,
  				const unsigned last_bus)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1304
  {
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
1305
  	struct i7core_dev *i7core_dev;
b197cba07   Hidetoshi Seto   i7core_edac: Redu...
1306
  	const struct pci_id_descr *dev_descr = &table->descr[devno];
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
1307

8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
1308
  	struct pci_dev *pdev = NULL;
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
1309
1310
  	u8 bus = 0;
  	u8 socket = 0;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1311

c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1312
  	pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1313
  			      dev_descr->dev_id, *prev);
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1314

224e871f3   Mauro Carvalho Chehab   i7core_edac: Fix ...
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
  	/*
  	 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
  	 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
  	 * to probe for the alternate address in case of failure
  	 */
  	if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
  		pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
  				      PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
  
  	if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
  		pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
  				      PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
  				      *prev);
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1328
1329
1330
1331
  	if (!pdev) {
  		if (*prev) {
  			*prev = pdev;
  			return 0;
d1fd4fb69   Mauro Carvalho Chehab   i7core_edac: Add ...
1332
  		}
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1333
  		if (dev_descr->optional)
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1334
  			return 0;
310cbb728   Mauro Carvalho Chehab   i7core: fix probi...
1335

bd9e19ca4   Vernon Mauery   Add support for W...
1336
1337
  		if (devno == 0)
  			return -ENODEV;
ab0893740   Daniel J Blueman   quiesce EDAC init...
1338
  		i7core_printk(KERN_INFO,
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1339
1340
  			"Device not found: dev %02x.%d PCI ID %04x:%04x
  ",
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1341
1342
  			dev_descr->dev, dev_descr->func,
  			PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
1343

c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1344
1345
1346
1347
  		/* End of list, leave */
  		return -ENODEV;
  	}
  	bus = pdev->bus->number;
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
1348

bda142890   Mauro Carvalho Chehab   i7core_edac: Prop...
1349
  	socket = last_bus - bus;
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1350

66607706c   Mauro Carvalho Chehab   Dynamically alloc...
1351
1352
  	i7core_dev = get_i7core_dev(socket);
  	if (!i7core_dev) {
848b2f7ed   Hidetoshi Seto   i7core_edac: Intr...
1353
  		i7core_dev = alloc_i7core_dev(socket, table);
2896637b8   Hidetoshi Seto   i7core_edac: Call...
1354
1355
  		if (!i7core_dev) {
  			pci_dev_put(pdev);
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
1356
  			return -ENOMEM;
2896637b8   Hidetoshi Seto   i7core_edac: Call...
1357
  		}
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1358
  	}
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
1359

66607706c   Mauro Carvalho Chehab   Dynamically alloc...
1360
  	if (i7core_dev->pdev[devno]) {
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1361
1362
1363
1364
  		i7core_printk(KERN_ERR,
  			"Duplicated device for "
  			"dev %02x:%02x.%d PCI ID %04x:%04x
  ",
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1365
1366
  			bus, dev_descr->dev, dev_descr->func,
  			PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1367
1368
1369
  		pci_dev_put(pdev);
  		return -ENODEV;
  	}
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
1370

66607706c   Mauro Carvalho Chehab   Dynamically alloc...
1371
  	i7core_dev->pdev[devno] = pdev;
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1372
1373
  
  	/* Sanity check */
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1374
1375
  	if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
  			PCI_FUNC(pdev->devfn) != dev_descr->func)) {
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1376
1377
1378
1379
  		i7core_printk(KERN_ERR,
  			"Device PCI ID %04x:%04x "
  			"has dev %02x:%02x.%d instead of dev %02x:%02x.%d
  ",
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1380
  			PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1381
  			bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1382
  			bus, dev_descr->dev, dev_descr->func);
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1383
1384
  		return -ENODEV;
  	}
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1385

c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1386
1387
1388
1389
1390
1391
  	/* Be sure that the device is enabled */
  	if (unlikely(pci_enable_device(pdev) < 0)) {
  		i7core_printk(KERN_ERR,
  			"Couldn't enable "
  			"dev %02x:%02x.%d PCI ID %04x:%04x
  ",
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1392
1393
  			bus, dev_descr->dev, dev_descr->func,
  			PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1394
1395
  		return -ENODEV;
  	}
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1396

d4c277957   Mauro Carvalho Chehab   i7core_edac: a fe...
1397
1398
  	debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x
  ",
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1399
1400
1401
  		socket, bus, dev_descr->dev,
  		dev_descr->func,
  		PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
1402

a3e154163   Mauro Carvalho Chehab   i7core_edac: Avoi...
1403
1404
1405
1406
1407
1408
  	/*
  	 * As stated on drivers/pci/search.c, the reference count for
  	 * @from is always decremented if it is not %NULL. So, as we need
  	 * to get all devices up to null, we need to do a get for the device
  	 */
  	pci_dev_get(pdev);
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1409
  	*prev = pdev;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1410

c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1411
1412
  	return 0;
  }
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1413

64c10f6e0   Hidetoshi Seto   i7core_edac: Alwa...
1414
  static int i7core_get_all_devices(void)
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1415
  {
3c52cc57c   Mauro Carvalho Chehab   i7core_edac: prop...
1416
  	int i, rc, last_bus;
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1417
  	struct pci_dev *pdev = NULL;
3c52cc57c   Mauro Carvalho Chehab   i7core_edac: prop...
1418
  	const struct pci_id_table *table = pci_dev_table;
bd9e19ca4   Vernon Mauery   Add support for W...
1419

bda142890   Mauro Carvalho Chehab   i7core_edac: Prop...
1420
  	last_bus = i7core_pci_lastbus();
3c52cc57c   Mauro Carvalho Chehab   i7core_edac: prop...
1421
  	while (table && table->descr) {
bd9e19ca4   Vernon Mauery   Add support for W...
1422
1423
1424
  		for (i = 0; i < table->n_devs; i++) {
  			pdev = NULL;
  			do {
b197cba07   Hidetoshi Seto   i7core_edac: Redu...
1425
  				rc = i7core_get_onedevice(&pdev, table, i,
bda142890   Mauro Carvalho Chehab   i7core_edac: Prop...
1426
  							  last_bus);
bd9e19ca4   Vernon Mauery   Add support for W...
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
  				if (rc < 0) {
  					if (i == 0) {
  						i = table->n_devs;
  						break;
  					}
  					i7core_put_all_devices();
  					return -ENODEV;
  				}
  			} while (pdev);
  		}
3c52cc57c   Mauro Carvalho Chehab   i7core_edac: prop...
1437
  		table++;
c77720b95   Mauro Carvalho Chehab   i7core: fix get_d...
1438
  	}
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
1439

ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1440
  	return 0;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1441
  }
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1442
1443
  static int mci_bind_devs(struct mem_ctl_info *mci,
  			 struct i7core_dev *i7core_dev)
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1444
1445
1446
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	struct pci_dev *pdev;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1447
  	int i, func, slot;
27100db0e   Mauro Carvalho Chehab   i7core_edac: Don'...
1448
  	char *family;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1449

27100db0e   Mauro Carvalho Chehab   i7core_edac: Don'...
1450
1451
  	pvt->is_registered = false;
  	pvt->enable_scrub  = false;
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
1452
  	for (i = 0; i < i7core_dev->n_devs; i++) {
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1453
1454
  		pdev = i7core_dev->pdev[i];
  		if (!pdev)
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
1455
  			continue;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1456
1457
1458
1459
1460
1461
1462
1463
  		func = PCI_FUNC(pdev->devfn);
  		slot = PCI_SLOT(pdev->devfn);
  		if (slot == 3) {
  			if (unlikely(func > MAX_MCR_FUNC))
  				goto error;
  			pvt->pci_mcr[func] = pdev;
  		} else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
  			if (unlikely(func > MAX_CHAN_FUNC))
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1464
  				goto error;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1465
  			pvt->pci_ch[slot - 4][func] = pdev;
27100db0e   Mauro Carvalho Chehab   i7core_edac: Don'...
1466
  		} else if (!slot && !func) {
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1467
  			pvt->pci_noncore = pdev;
27100db0e   Mauro Carvalho Chehab   i7core_edac: Don'...
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
  
  			/* Detect the processor family */
  			switch (pdev->device) {
  			case PCI_DEVICE_ID_INTEL_I7_NONCORE:
  				family = "Xeon 35xx/ i7core";
  				pvt->enable_scrub = false;
  				break;
  			case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT:
  				family = "i7-800/i5-700";
  				pvt->enable_scrub = false;
  				break;
  			case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE:
  				family = "Xeon 34xx";
  				pvt->enable_scrub = false;
  				break;
  			case PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT:
  				family = "Xeon 55xx";
  				pvt->enable_scrub = true;
  				break;
  			case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2:
  				family = "Xeon 56xx / i7-900";
  				pvt->enable_scrub = true;
  				break;
  			default:
  				family = "unknown";
  				pvt->enable_scrub = false;
  			}
  			debugf0("Detected a processor type %s
  ", family);
  		} else
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1498
  			goto error;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1499

f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1500
1501
1502
1503
  		debugf0("Associated fn %d.%d, dev = %p, socket %d
  ",
  			PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
  			pdev, i7core_dev->socket);
14d2c0834   Mauro Carvalho Chehab   i7core: Use regis...
1504

f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1505
1506
  		if (PCI_SLOT(pdev->devfn) == 3 &&
  			PCI_FUNC(pdev->devfn) == 2)
27100db0e   Mauro Carvalho Chehab   i7core_edac: Don'...
1507
  			pvt->is_registered = true;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1508
  	}
e9bd2e737   Mauro Carvalho Chehab   i7core_edac: Adds...
1509

a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1510
  	return 0;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
1511
1512
1513
1514
1515
1516
1517
  
  error:
  	i7core_printk(KERN_ERR, "Device %d, function %d "
  		      "is out of the expected range
  ",
  		      slot, func);
  	return -EINVAL;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1518
  }
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1519
1520
1521
  /****************************************************************************
  			Error check routines
   ****************************************************************************/
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1522
  static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1523
1524
1525
  				      const int chan,
  				      const int dimm,
  				      const int add)
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1526
1527
1528
  {
  	char *msg;
  	struct i7core_pvt *pvt = mci->pvt_info;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1529
  	int row = pvt->csrow_map[chan][dimm], i;
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1530
1531
1532
  
  	for (i = 0; i < add; i++) {
  		msg = kasprintf(GFP_KERNEL, "Corrected error "
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1533
1534
  				"(Socket=%d channel=%d dimm=%d)",
  				pvt->i7core_dev->socket, chan, dimm);
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1535
1536
1537
1538
1539
1540
1541
  
  		edac_mc_handle_fbd_ce(mci, row, 0, msg);
  		kfree (msg);
  	}
  }
  
  static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1542
1543
1544
1545
  					 const int chan,
  					 const int new0,
  					 const int new1,
  					 const int new2)
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1546
1547
1548
1549
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	int add0 = 0, add1 = 0, add2 = 0;
  	/* Updates CE counters if it is not the first time here */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1550
  	if (pvt->ce_count_available) {
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1551
  		/* Updates CE counters */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1552
1553
1554
  		add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
  		add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
  		add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1555
1556
1557
  
  		if (add2 < 0)
  			add2 += 0x7fff;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1558
  		pvt->rdimm_ce_count[chan][2] += add2;
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1559
1560
1561
  
  		if (add1 < 0)
  			add1 += 0x7fff;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1562
  		pvt->rdimm_ce_count[chan][1] += add1;
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1563
1564
1565
  
  		if (add0 < 0)
  			add0 += 0x7fff;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1566
  		pvt->rdimm_ce_count[chan][0] += add0;
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1567
  	} else
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1568
  		pvt->ce_count_available = 1;
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1569
1570
  
  	/* Store the new values */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1571
1572
1573
  	pvt->rdimm_last_ce_count[chan][2] = new2;
  	pvt->rdimm_last_ce_count[chan][1] = new1;
  	pvt->rdimm_last_ce_count[chan][0] = new0;
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1574
1575
1576
  
  	/*updated the edac core */
  	if (add0 != 0)
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1577
  		i7core_rdimm_update_csrow(mci, chan, 0, add0);
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1578
  	if (add1 != 0)
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1579
  		i7core_rdimm_update_csrow(mci, chan, 1, add1);
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1580
  	if (add2 != 0)
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1581
  		i7core_rdimm_update_csrow(mci, chan, 2, add2);
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1582
1583
  
  }
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1584
  static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1585
1586
1587
1588
1589
1590
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	u32 rcv[3][2];
  	int i, new0, new1, new2;
  
  	/*Read DEV 3: FUN 2:  MC_COR_ECC_CNT regs directly*/
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1591
  	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1592
  								&rcv[0][0]);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1593
  	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1594
  								&rcv[0][1]);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1595
  	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1596
  								&rcv[1][0]);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1597
  	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1598
  								&rcv[1][1]);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1599
  	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1600
  								&rcv[2][0]);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1601
  	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1602
1603
1604
1605
1606
1607
  								&rcv[2][1]);
  	for (i = 0 ; i < 3; i++) {
  		debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x
  ",
  			(i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
  		/*if the channel has 3 dimms*/
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1608
  		if (pvt->channel[i].dimms > 2) {
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
  			new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
  			new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
  			new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
  		} else {
  			new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
  					DIMM_BOT_COR_ERR(rcv[i][0]);
  			new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
  					DIMM_BOT_COR_ERR(rcv[i][1]);
  			new2 = 0;
  		}
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1619
  		i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1620
1621
  	}
  }
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1622
1623
1624
1625
1626
1627
1628
  
  /* This function is based on the device 3 function 4 registers as described on:
   * Intel Xeon Processor 5500 Series Datasheet Volume 2
   *	http://www.intel.com/Assets/PDF/datasheet/321322.pdf
   * also available at:
   * 	http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
   */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1629
  static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1630
1631
1632
1633
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	u32 rcv1, rcv0;
  	int new0, new1, new2;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1634
  	if (!pvt->pci_mcr[4]) {
b990538a7   Mauro Carvalho Chehab   i7core_edac: Codi...
1635
1636
  		debugf0("%s MCR registers not found
  ", __func__);
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1637
1638
  		return;
  	}
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1639
  	/* Corrected test errors */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1640
1641
  	pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
  	pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1642
1643
1644
1645
1646
  
  	/* Store the new values */
  	new2 = DIMM2_COR_ERR(rcv1);
  	new1 = DIMM1_COR_ERR(rcv0);
  	new0 = DIMM0_COR_ERR(rcv0);
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1647
  	/* Updates CE counters if it is not the first time here */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1648
  	if (pvt->ce_count_available) {
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1649
1650
  		/* Updates CE counters */
  		int add0, add1, add2;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1651
1652
1653
  		add2 = new2 - pvt->udimm_last_ce_count[2];
  		add1 = new1 - pvt->udimm_last_ce_count[1];
  		add0 = new0 - pvt->udimm_last_ce_count[0];
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1654
1655
1656
  
  		if (add2 < 0)
  			add2 += 0x7fff;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1657
  		pvt->udimm_ce_count[2] += add2;
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1658
1659
1660
  
  		if (add1 < 0)
  			add1 += 0x7fff;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1661
  		pvt->udimm_ce_count[1] += add1;
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1662
1663
1664
  
  		if (add0 < 0)
  			add0 += 0x7fff;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1665
  		pvt->udimm_ce_count[0] += add0;
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1666
1667
1668
1669
1670
1671
  
  		if (add0 | add1 | add2)
  			i7core_printk(KERN_ERR, "New Corrected error(s): "
  				      "dimm0: +%d, dimm1: +%d, dimm2 +%d
  ",
  				      add0, add1, add2);
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1672
  	} else
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1673
  		pvt->ce_count_available = 1;
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1674
1675
  
  	/* Store the new values */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1676
1677
1678
  	pvt->udimm_last_ce_count[2] = new2;
  	pvt->udimm_last_ce_count[1] = new1;
  	pvt->udimm_last_ce_count[0] = new0;
442305b15   Mauro Carvalho Chehab   i7core_edac: Add ...
1679
  }
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1680
1681
1682
  /*
   * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
   * Architectures Software Developer’s Manual Volume 3B.
f237fcf2b   Mauro Carvalho Chehab   i7core_edac: some...
1683
1684
1685
   * Nehalem are defined as family 0x06, model 0x1a
   *
   * The MCA registers used here are the following ones:
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1686
   *     struct mce field	MCA Register
f237fcf2b   Mauro Carvalho Chehab   i7core_edac: some...
1687
1688
1689
   *     m->status	MSR_IA32_MC8_STATUS
   *     m->addr		MSR_IA32_MC8_ADDR
   *     m->misc		MSR_IA32_MC8_MISC
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1690
1691
1692
   * In the case of Nehalem, the error information is masked at .status and .misc
   * fields
   */
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1693
  static void i7core_mce_output_error(struct mem_ctl_info *mci,
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
1694
  				    const struct mce *m)
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1695
  {
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1696
  	struct i7core_pvt *pvt = mci->pvt_info;
a639539fa   Mauro Carvalho Chehab   i7core: enrich er...
1697
  	char *type, *optype, *err, *msg;
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1698
  	unsigned long error = m->status & 0x1ff0000l;
a639539fa   Mauro Carvalho Chehab   i7core: enrich er...
1699
  	u32 optypenum = (m->status >> 4) & 0x07;
8cf2d2399   Mathias Krause   i7core_edac: fixe...
1700
  	u32 core_err_cnt = (m->status >> 38) & 0x7fff;
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1701
1702
1703
1704
  	u32 dimm = (m->misc >> 16) & 0x3;
  	u32 channel = (m->misc >> 18) & 0x3;
  	u32 syndrome = m->misc >> 32;
  	u32 errnum = find_first_bit(&error, 32);
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1705
  	int csrow;
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1706

c5d345286   Mauro Carvalho Chehab   i7core: check if ...
1707
1708
1709
1710
  	if (m->mcgstatus & 1)
  		type = "FATAL";
  	else
  		type = "NON_FATAL";
a639539fa   Mauro Carvalho Chehab   i7core: enrich er...
1711
  	switch (optypenum) {
b990538a7   Mauro Carvalho Chehab   i7core_edac: Codi...
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
  	case 0:
  		optype = "generic undef request";
  		break;
  	case 1:
  		optype = "read error";
  		break;
  	case 2:
  		optype = "write error";
  		break;
  	case 3:
  		optype = "addr/cmd error";
  		break;
  	case 4:
  		optype = "scrubbing error";
  		break;
  	default:
  		optype = "reserved";
  		break;
a639539fa   Mauro Carvalho Chehab   i7core: enrich er...
1730
  	}
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
  	switch (errnum) {
  	case 16:
  		err = "read ECC error";
  		break;
  	case 17:
  		err = "RAS ECC error";
  		break;
  	case 18:
  		err = "write parity error";
  		break;
  	case 19:
  		err = "redundacy loss";
  		break;
  	case 20:
  		err = "reserved";
  		break;
  	case 21:
  		err = "memory range error";
  		break;
  	case 22:
  		err = "RTID out of range";
  		break;
  	case 23:
  		err = "address parity error";
  		break;
  	case 24:
  		err = "byte enable parity error";
  		break;
  	default:
  		err = "unknown";
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1761
  	}
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1762

f237fcf2b   Mauro Carvalho Chehab   i7core_edac: some...
1763
  	/* FIXME: should convert addr into bank and rank information */
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1764
  	msg = kasprintf(GFP_ATOMIC,
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1765
  		"%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
a639539fa   Mauro Carvalho Chehab   i7core: enrich er...
1766
1767
  		"syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))
  ",
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1768
  		type, (long long) m->addr, m->cpu, dimm, channel,
a639539fa   Mauro Carvalho Chehab   i7core: enrich er...
1769
1770
  		syndrome, core_err_cnt, (long long)m->status,
  		(long long)m->misc, optype, err);
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1771
1772
  
  	debugf0("%s", msg);
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1773

f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1774
  	csrow = pvt->csrow_map[channel][dimm];
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1775

d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1776
  	/* Call the helper to output message */
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1777
1778
1779
  	if (m->mcgstatus & 1)
  		edac_mc_handle_fbd_ue(mci, csrow, 0,
  				0 /* FIXME: should be channel here */, msg);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1780
  	else if (!pvt->is_registered)
b4e8f0b6e   Mauro Carvalho Chehab   i7core_edac: Use ...
1781
1782
  		edac_mc_handle_fbd_ce(mci, csrow,
  				0 /* FIXME: should be channel here */, msg);
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1783
1784
  
  	kfree(msg);
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1785
  }
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
1786
  /*
87d1d272b   Mauro Carvalho Chehab   i7core_edac: need...
1787
1788
1789
1790
1791
   *	i7core_check_error	Retrieve and process errors reported by the
   *				hardware. Called by the Core module.
   */
  static void i7core_check_error(struct mem_ctl_info *mci)
  {
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1792
1793
1794
  	struct i7core_pvt *pvt = mci->pvt_info;
  	int i;
  	unsigned count = 0;
ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1795
  	struct mce *m;
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1796

ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1797
1798
1799
  	/*
  	 * MCE first step: Copy all mce errors into a temporary buffer
  	 * We use a double buffering here, to reduce the risk of
25985edce   Lucas De Marchi   Fix common misspe...
1800
  	 * losing an error.
ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1801
1802
  	 */
  	smp_rmb();
321ece4dd   Mauro Carvalho Chehab   i7core_edac: Fix ...
1803
1804
  	count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
  		% MCE_LOG_LEN;
ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1805
  	if (!count)
8a311e179   Vernon Mauery   Always call i7cor...
1806
  		goto check_ce_error;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1807

ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1808
  	m = pvt->mce_outentry;
321ece4dd   Mauro Carvalho Chehab   i7core_edac: Fix ...
1809
1810
  	if (pvt->mce_in + count > MCE_LOG_LEN) {
  		unsigned l = MCE_LOG_LEN - pvt->mce_in;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1811

ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
  		memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
  		smp_wmb();
  		pvt->mce_in = 0;
  		count -= l;
  		m += l;
  	}
  	memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
  	smp_wmb();
  	pvt->mce_in += count;
  
  	smp_rmb();
  	if (pvt->mce_overrun) {
  		i7core_printk(KERN_ERR, "Lost %d memory errors
  ",
  			      pvt->mce_overrun);
  		smp_wmb();
  		pvt->mce_overrun = 0;
  	}
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1830

ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1831
1832
1833
  	/*
  	 * MCE second step: parse errors and display
  	 */
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1834
  	for (i = 0; i < count; i++)
ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1835
  		i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1836

ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1837
1838
1839
  	/*
  	 * Now, let's increment CE error counts
  	 */
8a311e179   Vernon Mauery   Always call i7cor...
1840
  check_ce_error:
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1841
1842
1843
1844
  	if (!pvt->is_registered)
  		i7core_udimm_check_mc_ecc_err(mci);
  	else
  		i7core_rdimm_check_mc_ecc_err(mci);
87d1d272b   Mauro Carvalho Chehab   i7core_edac: need...
1845
1846
1847
  }
  
  /*
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1848
1849
1850
1851
   * i7core_mce_check_error	Replicates mcelog routine to get errors
   *				This routine simply queues mcelog errors, and
   *				return. The error itself should be handled later
   *				by i7core_check_error.
6e103be1c   Mauro Carvalho Chehab   i7core_edac: Firs...
1852
1853
   * WARNING: As this routine should be called at NMI time, extra care should
   * be taken to avoid deadlocks, and to be as fast as possible.
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1854
   */
4140c5426   Borislav Petkov   i7core_edac: Drop...
1855
1856
  static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val,
  				  void *data)
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1857
  {
4140c5426   Borislav Petkov   i7core_edac: Drop...
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
  	struct mce *mce = (struct mce *)data;
  	struct i7core_dev *i7_dev;
  	struct mem_ctl_info *mci;
  	struct i7core_pvt *pvt;
  
  	i7_dev = get_i7core_dev(mce->socketid);
  	if (!i7_dev)
  		return NOTIFY_BAD;
  
  	mci = i7_dev->mci;
  	pvt = mci->pvt_info;
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1869

8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1870
1871
1872
1873
1874
  	/*
  	 * Just let mcelog handle it if the error is
  	 * outside the memory controller
  	 */
  	if (((mce->status & 0xffff) >> 7) != 1)
4140c5426   Borislav Petkov   i7core_edac: Drop...
1875
  		return NOTIFY_DONE;
8a2f118e3   Mauro Carvalho Chehab   i7core_edac: deco...
1876

f237fcf2b   Mauro Carvalho Chehab   i7core_edac: some...
1877
1878
  	/* Bank 8 registers are the only ones that we know how to handle */
  	if (mce->bank != 8)
4140c5426   Borislav Petkov   i7core_edac: Drop...
1879
  		return NOTIFY_DONE;
f237fcf2b   Mauro Carvalho Chehab   i7core_edac: some...
1880

3b918c12d   Randy Dunlap   edac: fix i7core ...
1881
  #ifdef CONFIG_SMP
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1882
  	/* Only handle if it is the right mc controller */
5034086b7   Thomas Renninger   EDAC i7core: Use ...
1883
  	if (mce->socketid != pvt->i7core_dev->socket)
4140c5426   Borislav Petkov   i7core_edac: Drop...
1884
  		return NOTIFY_DONE;
3b918c12d   Randy Dunlap   edac: fix i7core ...
1885
  #endif
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
1886

ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1887
  	smp_rmb();
321ece4dd   Mauro Carvalho Chehab   i7core_edac: Fix ...
1888
  	if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1889
1890
  		smp_wmb();
  		pvt->mce_overrun++;
4140c5426   Borislav Petkov   i7core_edac: Drop...
1891
  		return NOTIFY_DONE;
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1892
  	}
6e103be1c   Mauro Carvalho Chehab   i7core_edac: Firs...
1893
1894
1895
  
  	/* Copy memory error at the ringbuffer */
  	memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
ca9c90ba0   Mauro Carvalho Chehab   i7core_edac: Use ...
1896
  	smp_wmb();
321ece4dd   Mauro Carvalho Chehab   i7core_edac: Fix ...
1897
  	pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1898

c5d345286   Mauro Carvalho Chehab   i7core: check if ...
1899
1900
1901
  	/* Handle fatal errors immediately */
  	if (mce->mcgstatus & 1)
  		i7core_check_error(mci);
e7bf068aa   David Sterba   i7core_edac: fix ...
1902
  	/* Advise mcelog that the errors were handled */
4140c5426   Borislav Petkov   i7core_edac: Drop...
1903
  	return NOTIFY_STOP;
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
1904
  }
4140c5426   Borislav Petkov   i7core_edac: Drop...
1905
1906
1907
  static struct notifier_block i7_mce_dec = {
  	.notifier_call	= i7core_mce_check_error,
  };
535e9c78e   Nils Carlson   i7core_edac: scru...
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
  struct memdev_dmi_entry {
  	u8 type;
  	u8 length;
  	u16 handle;
  	u16 phys_mem_array_handle;
  	u16 mem_err_info_handle;
  	u16 total_width;
  	u16 data_width;
  	u16 size;
  	u8 form;
  	u8 device_set;
  	u8 device_locator;
  	u8 bank_locator;
  	u8 memory_type;
  	u16 type_detail;
  	u16 speed;
  	u8 manufacturer;
  	u8 serial_number;
  	u8 asset_tag;
  	u8 part_number;
  	u8 attributes;
  	u32 extended_size;
  	u16 conf_mem_clk_speed;
  } __attribute__((__packed__));
  
  
  /*
   * Decode the DRAM Clock Frequency, be paranoid, make sure that all
   * memory devices show the same speed, and if they don't then consider
   * all speeds to be invalid.
   */
  static void decode_dclk(const struct dmi_header *dh, void *_dclk_freq)
  {
  	int *dclk_freq = _dclk_freq;
  	u16 dmi_mem_clk_speed;
  
  	if (*dclk_freq == -1)
  		return;
  
  	if (dh->type == DMI_ENTRY_MEM_DEVICE) {
  		struct memdev_dmi_entry *memdev_dmi_entry =
  			(struct memdev_dmi_entry *)dh;
  		unsigned long conf_mem_clk_speed_offset =
  			(unsigned long)&memdev_dmi_entry->conf_mem_clk_speed -
  			(unsigned long)&memdev_dmi_entry->type;
  		unsigned long speed_offset =
  			(unsigned long)&memdev_dmi_entry->speed -
  			(unsigned long)&memdev_dmi_entry->type;
  
  		/* Check that a DIMM is present */
  		if (memdev_dmi_entry->size == 0)
  			return;
  
  		/*
  		 * Pick the configured speed if it's available, otherwise
  		 * pick the DIMM speed, or we don't have a speed.
  		 */
  		if (memdev_dmi_entry->length > conf_mem_clk_speed_offset) {
  			dmi_mem_clk_speed =
  				memdev_dmi_entry->conf_mem_clk_speed;
  		} else if (memdev_dmi_entry->length > speed_offset) {
  			dmi_mem_clk_speed = memdev_dmi_entry->speed;
  		} else {
  			*dclk_freq = -1;
  			return;
  		}
  
  		if (*dclk_freq == 0) {
  			/* First pass, speed was 0 */
  			if (dmi_mem_clk_speed > 0) {
  				/* Set speed if a valid speed is read */
  				*dclk_freq = dmi_mem_clk_speed;
  			} else {
  				/* Otherwise we don't have a valid speed */
  				*dclk_freq = -1;
  			}
  		} else if (*dclk_freq > 0 &&
  			   *dclk_freq != dmi_mem_clk_speed) {
  			/*
  			 * If we have a speed, check that all DIMMS are the same
  			 * speed, otherwise set the speed as invalid.
  			 */
  			*dclk_freq = -1;
  		}
  	}
  }
  
  /*
   * The default DCLK frequency is used as a fallback if we
   * fail to find anything reliable in the DMI. The value
   * is taken straight from the datasheet.
   */
  #define DEFAULT_DCLK_FREQ 800
  
  static int get_dclk_freq(void)
  {
  	int dclk_freq = 0;
  
  	dmi_walk(decode_dclk, (void *)&dclk_freq);
  
  	if (dclk_freq < 1)
  		return DEFAULT_DCLK_FREQ;
  
  	return dclk_freq;
  }
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2013
2014
2015
2016
2017
2018
2019
2020
2021
  /*
   * set_sdram_scrub_rate		This routine sets byte/sec bandwidth scrub rate
   *				to hardware according to SCRUBINTERVAL formula
   *				found in datasheet.
   */
  static int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 new_bw)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	struct pci_dev *pdev;
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
  	u32 dw_scrub;
  	u32 dw_ssr;
  
  	/* Get data from the MC register, function 2 */
  	pdev = pvt->pci_mcr[2];
  	if (!pdev)
  		return -ENODEV;
  
  	pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &dw_scrub);
  
  	if (new_bw == 0) {
  		/* Prepare to disable petrol scrub */
  		dw_scrub &= ~STARTSCRUB;
  		/* Stop the patrol scrub engine */
535e9c78e   Nils Carlson   i7core_edac: scru...
2036
2037
  		write_and_test(pdev, MC_SCRUB_CONTROL,
  			       dw_scrub & ~SCRUBINTERVAL_MASK);
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2038
2039
2040
2041
2042
2043
  
  		/* Get current status of scrub rate and set bit to disable */
  		pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
  		dw_ssr &= ~SSR_MODE_MASK;
  		dw_ssr |= SSR_MODE_DISABLE;
  	} else {
535e9c78e   Nils Carlson   i7core_edac: scru...
2044
2045
2046
  		const int cache_line_size = 64;
  		const u32 freq_dclk_mhz = pvt->dclk_freq;
  		unsigned long long scrub_interval;
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2047
2048
  		/*
  		 * Translate the desired scrub rate to a register value and
535e9c78e   Nils Carlson   i7core_edac: scru...
2049
  		 * program the corresponding register value.
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2050
  		 */
535e9c78e   Nils Carlson   i7core_edac: scru...
2051
  		scrub_interval = (unsigned long long)freq_dclk_mhz *
4fad8098b   Sedat Dilek   i7core_edac: Fix ...
2052
2053
  			cache_line_size * 1000000;
  		do_div(scrub_interval, new_bw);
535e9c78e   Nils Carlson   i7core_edac: scru...
2054
2055
2056
2057
2058
  
  		if (!scrub_interval || scrub_interval > SCRUBINTERVAL_MASK)
  			return -EINVAL;
  
  		dw_scrub = SCRUBINTERVAL_MASK & scrub_interval;
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
  
  		/* Start the patrol scrub engine */
  		pci_write_config_dword(pdev, MC_SCRUB_CONTROL,
  				       STARTSCRUB | dw_scrub);
  
  		/* Get current status of scrub rate and set bit to enable */
  		pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
  		dw_ssr &= ~SSR_MODE_MASK;
  		dw_ssr |= SSR_MODE_ENABLE;
  	}
  	/* Disable or enable scrubbing */
  	pci_write_config_dword(pdev, MC_SSRCONTROL, dw_ssr);
  
  	return new_bw;
  }
  
  /*
   * get_sdram_scrub_rate		This routine convert current scrub rate value
   *				into byte/sec bandwidth accourding to
   *				SCRUBINTERVAL formula found in datasheet.
   */
  static int get_sdram_scrub_rate(struct mem_ctl_info *mci)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	struct pci_dev *pdev;
  	const u32 cache_line_size = 64;
535e9c78e   Nils Carlson   i7core_edac: scru...
2085
2086
  	const u32 freq_dclk_mhz = pvt->dclk_freq;
  	unsigned long long scrub_rate;
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
  	u32 scrubval;
  
  	/* Get data from the MC register, function 2 */
  	pdev = pvt->pci_mcr[2];
  	if (!pdev)
  		return -ENODEV;
  
  	/* Get current scrub control data */
  	pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &scrubval);
  
  	/* Mask highest 8-bits to 0 */
535e9c78e   Nils Carlson   i7core_edac: scru...
2098
  	scrubval &=  SCRUBINTERVAL_MASK;
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2099
2100
2101
2102
  	if (!scrubval)
  		return 0;
  
  	/* Calculate scrub rate value into byte/sec bandwidth */
535e9c78e   Nils Carlson   i7core_edac: scru...
2103
  	scrub_rate =  (unsigned long long)freq_dclk_mhz *
4fad8098b   Sedat Dilek   i7core_edac: Fix ...
2104
2105
  		1000000 * cache_line_size;
  	do_div(scrub_rate, scrubval);
535e9c78e   Nils Carlson   i7core_edac: scru...
2106
  	return (int)scrub_rate;
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
  }
  
  static void enable_sdram_scrub_setting(struct mem_ctl_info *mci)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	u32 pci_lock;
  
  	/* Unlock writes to pci registers */
  	pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
  	pci_lock &= ~0x3;
  	pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
  			       pci_lock | MC_CFG_UNLOCK);
  
  	mci->set_sdram_scrub_rate = set_sdram_scrub_rate;
  	mci->get_sdram_scrub_rate = get_sdram_scrub_rate;
  }
  
  static void disable_sdram_scrub_setting(struct mem_ctl_info *mci)
  {
  	struct i7core_pvt *pvt = mci->pvt_info;
  	u32 pci_lock;
  
  	/* Lock writes to pci registers */
  	pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
  	pci_lock &= ~0x3;
  	pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
  			       pci_lock | MC_CFG_LOCK);
  }
a3aa0a4ab   Hidetoshi Seto   i7core_edac: Intr...
2135
2136
2137
2138
2139
2140
  static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
  {
  	pvt->i7core_pci = edac_pci_create_generic_ctl(
  						&pvt->i7core_dev->pdev[0]->dev,
  						EDAC_MOD_STR);
  	if (unlikely(!pvt->i7core_pci))
f9902f24f   Mauro Carvalho Chehab   i7core_edac: use ...
2141
2142
2143
  		i7core_printk(KERN_WARNING,
  			      "Unable to setup PCI error report via EDAC
  ");
a3aa0a4ab   Hidetoshi Seto   i7core_edac: Intr...
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
  }
  
  static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
  {
  	if (likely(pvt->i7core_pci))
  		edac_pci_release_generic_ctl(pvt->i7core_pci);
  	else
  		i7core_printk(KERN_ERR,
  				"Couldn't find mem_ctl_info for socket %d
  ",
  				pvt->i7core_dev->socket);
  	pvt->i7core_pci = NULL;
  }
1c6edbbe2   Hidetoshi Seto   i7core_edac: Intr...
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
  static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
  {
  	struct mem_ctl_info *mci = i7core_dev->mci;
  	struct i7core_pvt *pvt;
  
  	if (unlikely(!mci || !mci->pvt_info)) {
  		debugf0("MC: " __FILE__ ": %s(): dev = %p
  ",
  			__func__, &i7core_dev->pdev[0]->dev);
  
  		i7core_printk(KERN_ERR, "Couldn't find mci handler
  ");
  		return;
  	}
  
  	pvt = mci->pvt_info;
  
  	debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p
  ",
  		__func__, mci, &i7core_dev->pdev[0]->dev);
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2177
  	/* Disable scrubrate setting */
27100db0e   Mauro Carvalho Chehab   i7core_edac: Don'...
2178
2179
  	if (pvt->enable_scrub)
  		disable_sdram_scrub_setting(mci);
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2180

3653ada5d   Borislav Petkov   x86, mce: Add wra...
2181
  	mce_unregister_decode_chain(&i7_mce_dec);
1c6edbbe2   Hidetoshi Seto   i7core_edac: Intr...
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
  
  	/* Disable EDAC polling */
  	i7core_pci_ctl_release(pvt);
  
  	/* Remove MC sysfs nodes */
  	edac_mc_del_mc(mci->dev);
  
  	debugf1("%s: free mci struct
  ", mci->ctl_name);
  	kfree(mci->ctl_name);
  	edac_mc_free(mci);
  	i7core_dev->mci = NULL;
  }
aace42831   Hidetoshi Seto   i7core_edac: Redu...
2195
  static int i7core_register_mci(struct i7core_dev *i7core_dev)
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2196
2197
2198
  {
  	struct mem_ctl_info *mci;
  	struct i7core_pvt *pvt;
aace42831   Hidetoshi Seto   i7core_edac: Redu...
2199
2200
2201
2202
2203
2204
  	int rc, channels, csrows;
  
  	/* Check the number of active and not disabled channels */
  	rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows);
  	if (unlikely(rc < 0))
  		return rc;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2205

a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2206
  	/* allocate a new MC control structure */
aace42831   Hidetoshi Seto   i7core_edac: Redu...
2207
  	mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2208
2209
  	if (unlikely(!mci))
  		return -ENOMEM;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2210

3cfd01468   Mauro Carvalho Chehab   i7core_edac: Impr...
2211
2212
2213
  	debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p
  ",
  		__func__, mci, &i7core_dev->pdev[0]->dev);
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2214

a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2215
  	pvt = mci->pvt_info;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
2216
  	memset(pvt, 0, sizeof(*pvt));
67166af4a   Mauro Carvalho Chehab   i7core_edac: add ...
2217

6d37d240f   Mauro Carvalho Chehab   i7core_edac: Fix ...
2218
2219
2220
  	/* Associates i7core_dev and mci for future usage */
  	pvt->i7core_dev = i7core_dev;
  	i7core_dev->mci = mci;
41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
2221
2222
2223
2224
2225
2226
  	/*
  	 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
  	 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
  	 * memory channels
  	 */
  	mci->mtype_cap = MEM_FLAG_DDR3;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2227
2228
2229
2230
  	mci->edac_ctl_cap = EDAC_FLAG_NONE;
  	mci->edac_cap = EDAC_FLAG_NONE;
  	mci->mod_name = "i7core_edac.c";
  	mci->mod_ver = I7CORE_REVISION;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2231
2232
2233
  	mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
  				  i7core_dev->socket);
  	mci->dev_name = pci_name(i7core_dev->pdev[0]);
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2234
  	mci->ctl_page_to_phys = NULL;
1288c18f4   Mauro Carvalho Chehab   i7core_edac: Prop...
2235

ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
2236
  	/* Store pci devices at mci for faster access */
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2237
  	rc = mci_bind_devs(mci, i7core_dev);
41fcb7fee   Mauro Carvalho Chehab   i7core_edac: Codi...
2238
  	if (unlikely(rc < 0))
628c5ddfb   Hidetoshi Seto   i7core_edac: Fix ...
2239
  		goto fail0;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
2240

5939813b9   Hidetoshi Seto   i7core_edac: Fix ...
2241
2242
2243
2244
  	if (pvt->is_registered)
  		mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs;
  	else
  		mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
2245
  	/* Get dimm basic config */
2e5185f7f   Hidetoshi Seto   i7core_edac: Remo...
2246
  	get_dimm_config(mci);
5939813b9   Hidetoshi Seto   i7core_edac: Fix ...
2247
2248
2249
2250
  	/* record ptr to the generic device */
  	mci->dev = &i7core_dev->pdev[0]->dev;
  	/* Set the function pointer to an actual operation function */
  	mci->edac_check = i7core_check_error;
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
2251

e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2252
  	/* Enable scrubrate setting */
27100db0e   Mauro Carvalho Chehab   i7core_edac: Don'...
2253
2254
  	if (pvt->enable_scrub)
  		enable_sdram_scrub_setting(mci);
e8b6a1271   Samuel Gabrielsson   i7core_edac: Add ...
2255

a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2256
  	/* add this new MC control structure to EDAC's list of MCs */
b7c761512   Mauro Carvalho Chehab   i7core_edac: Impr...
2257
  	if (unlikely(edac_mc_add_mc(mci))) {
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2258
2259
2260
2261
2262
2263
  		debugf0("MC: " __FILE__
  			": %s(): failed edac_mc_add_mc()
  ", __func__);
  		/* FIXME: perhaps some code should go here that disables error
  		 * reporting if we just enabled it
  		 */
b7c761512   Mauro Carvalho Chehab   i7core_edac: Impr...
2264
2265
  
  		rc = -EINVAL;
628c5ddfb   Hidetoshi Seto   i7core_edac: Fix ...
2266
  		goto fail0;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2267
  	}
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
2268
  	/* Default error mask is any memory */
ef708b53b   Mauro Carvalho Chehab   i7core_edac: Add ...
2269
  	pvt->inject.channel = 0;
194a40fea   Mauro Carvalho Chehab   i7core_edac: Add ...
2270
2271
2272
2273
2274
  	pvt->inject.dimm = -1;
  	pvt->inject.rank = -1;
  	pvt->inject.bank = -1;
  	pvt->inject.page = -1;
  	pvt->inject.col = -1;
a3aa0a4ab   Hidetoshi Seto   i7core_edac: Intr...
2275
2276
  	/* allocating generic PCI control info */
  	i7core_pci_ctl_create(pvt);
535e9c78e   Nils Carlson   i7core_edac: scru...
2277
2278
  	/* DCLK for scrub rate setting */
  	pvt->dclk_freq = get_dclk_freq();
3653ada5d   Borislav Petkov   x86, mce: Add wra...
2279
  	mce_register_decode_chain(&i7_mce_dec);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2280

628c5ddfb   Hidetoshi Seto   i7core_edac: Fix ...
2281
  	return 0;
628c5ddfb   Hidetoshi Seto   i7core_edac: Fix ...
2282
2283
2284
  fail0:
  	kfree(mci->ctl_name);
  	edac_mc_free(mci);
1c6edbbe2   Hidetoshi Seto   i7core_edac: Intr...
2285
  	i7core_dev->mci = NULL;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
  	return rc;
  }
  
  /*
   *	i7core_probe	Probe for ONE instance of device to see if it is
   *			present.
   *	return:
   *		0 for FOUND a device
   *		< 0 for error code
   */
2d95d8158   Mauro Carvalho Chehab   i7core_edac: Avoi...
2296

f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2297
2298
2299
  static int __devinit i7core_probe(struct pci_dev *pdev,
  				  const struct pci_device_id *id)
  {
405575914   Mauro Carvalho Chehab   i7core_edac: retu...
2300
  	int rc, count = 0;
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2301
  	struct i7core_dev *i7core_dev;
2d95d8158   Mauro Carvalho Chehab   i7core_edac: Avoi...
2302
2303
  	/* get the pci devices we want to reserve for our use */
  	mutex_lock(&i7core_edac_lock);
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2304
  	/*
d4c277957   Mauro Carvalho Chehab   i7core_edac: a fe...
2305
  	 * All memory controllers are allocated at the first pass.
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2306
  	 */
2d95d8158   Mauro Carvalho Chehab   i7core_edac: Avoi...
2307
2308
  	if (unlikely(probed >= 1)) {
  		mutex_unlock(&i7core_edac_lock);
76a7bd811   Mauro Carvalho Chehab   i7core_edac: retu...
2309
  		return -ENODEV;
2d95d8158   Mauro Carvalho Chehab   i7core_edac: Avoi...
2310
2311
  	}
  	probed++;
de06eeef5   Mauro Carvalho Chehab   i7core_edac: Use ...
2312

64c10f6e0   Hidetoshi Seto   i7core_edac: Alwa...
2313
  	rc = i7core_get_all_devices();
f47429494   Mauro Carvalho Chehab   i7core_edac: crea...
2314
2315
2316
2317
  	if (unlikely(rc < 0))
  		goto fail0;
  
  	list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
405575914   Mauro Carvalho Chehab   i7core_edac: retu...
2318
  		count++;
aace42831   Hidetoshi Seto   i7core_edac: Redu...
2319
  		rc = i7core_register_mci(i7core_dev);
d4c277957   Mauro Carvalho Chehab   i7core_edac: a fe...
2320
2321
  		if (unlikely(rc < 0))
  			goto fail1;
d5381642a   Mauro Carvalho Chehab   i7core_edac: Add ...
2322
  	}
405575914   Mauro Carvalho Chehab   i7core_edac: retu...
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
  	/*
  	 * Nehalem-EX uses a different memory controller. However, as the
  	 * memory controller is not visible on some Nehalem/Nehalem-EP, we
  	 * need to indirectly probe via a X58 PCI device. The same devices
  	 * are found on (some) Nehalem-EX. So, on those machines, the
  	 * probe routine needs to return -ENODEV, as the actual Memory
  	 * Controller registers won't be detected.
  	 */
  	if (!count) {
  		rc = -ENODEV;
  		goto fail1;
  	}
  
  	i7core_printk(KERN_INFO,
  		      "Driver loaded, %d memory controller(s) found.
  ",
  		      count);
8f3319075   Mauro Carvalho Chehab   i7core_edac: Regi...
2340

66607706c   Mauro Carvalho Chehab   Dynamically alloc...
2341
  	mutex_unlock(&i7core_edac_lock);
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2342
  	return 0;
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
2343
  fail1:
88ef5ea97   Mauro Carvalho Chehab   i7core_edac: it i...
2344
2345
  	list_for_each_entry(i7core_dev, &i7core_edac_list, list)
  		i7core_unregister_mci(i7core_dev);
13d6e9b65   Mauro Carvalho Chehab   i7core_edac: at r...
2346
  	i7core_put_all_devices();
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
2347
2348
  fail0:
  	mutex_unlock(&i7core_edac_lock);
b7c761512   Mauro Carvalho Chehab   i7core_edac: Impr...
2349
  	return rc;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2350
2351
2352
2353
2354
2355
2356
2357
  }
  
  /*
   *	i7core_remove	destructor for one instance of device
   *
   */
  static void __devexit i7core_remove(struct pci_dev *pdev)
  {
64c10f6e0   Hidetoshi Seto   i7core_edac: Alwa...
2358
  	struct i7core_dev *i7core_dev;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2359
2360
2361
  
  	debugf0(__FILE__ ": %s()
  ", __func__);
22e6bcbdc   Mauro Carvalho Chehab   i7core_edac: chan...
2362
2363
2364
2365
2366
2367
2368
  	/*
  	 * we have a trouble here: pdev value for removal will be wrong, since
  	 * it will point to the X58 register used to detect that the machine
  	 * is a Nehalem or upper design. However, due to the way several PCI
  	 * devices are grouped together to provide MC functionality, we need
  	 * to use a different method for releasing the devices
  	 */
87d1d272b   Mauro Carvalho Chehab   i7core_edac: need...
2369

66607706c   Mauro Carvalho Chehab   Dynamically alloc...
2370
  	mutex_lock(&i7core_edac_lock);
71fe01706   Hidetoshi Seto   i7core_edac: Chec...
2371
2372
2373
2374
2375
  
  	if (unlikely(!probed)) {
  		mutex_unlock(&i7core_edac_lock);
  		return;
  	}
88ef5ea97   Mauro Carvalho Chehab   i7core_edac: it i...
2376
2377
  	list_for_each_entry(i7core_dev, &i7core_edac_list, list)
  		i7core_unregister_mci(i7core_dev);
64c10f6e0   Hidetoshi Seto   i7core_edac: Alwa...
2378
2379
2380
  
  	/* Release PCI resources */
  	i7core_put_all_devices();
2d95d8158   Mauro Carvalho Chehab   i7core_edac: Avoi...
2381
  	probed--;
66607706c   Mauro Carvalho Chehab   Dynamically alloc...
2382
  	mutex_unlock(&i7core_edac_lock);
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2383
  }
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
  MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
  
  /*
   *	i7core_driver	pci_driver structure for this module
   *
   */
  static struct pci_driver i7core_driver = {
  	.name     = "i7core_edac",
  	.probe    = i7core_probe,
  	.remove   = __devexit_p(i7core_remove),
  	.id_table = i7core_pci_tbl,
  };
  
  /*
   *	i7core_init		Module entry function
   *			Try to initialize this module for its devices
   */
  static int __init i7core_init(void)
  {
  	int pci_rc;
  
  	debugf2("MC: " __FILE__ ": %s()
  ", __func__);
  
  	/* Ensure that the OPSTATE is set correctly for POLL or NMI */
  	opstate_init();
54a08ab15   Mauro Carvalho Chehab   i7core_edac: Don'...
2410
2411
  	if (use_pci_fixup)
  		i7core_xeon_pci_fixup(pci_dev_table);
bc2d7245f   Keith Mannthey   i7core_edac: Prob...
2412

a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2413
  	pci_rc = pci_register_driver(&i7core_driver);
3ef288a98   Mauro Carvalho Chehab   i7core_edac: Prin...
2414
2415
2416
2417
2418
2419
2420
2421
  	if (pci_rc >= 0)
  		return 0;
  
  	i7core_printk(KERN_ERR, "Failed to register device with error %d.
  ",
  		      pci_rc);
  
  	return pci_rc;
a0c36a1f0   Mauro Carvalho Chehab   i7core_edac: Add ...
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
  }
  
  /*
   *	i7core_exit()	Module exit function
   *			Unregister the driver
   */
  static void __exit i7core_exit(void)
  {
  	debugf2("MC: " __FILE__ ": %s()
  ", __func__);
  	pci_unregister_driver(&i7core_driver);
  }
  
  module_init(i7core_init);
  module_exit(i7core_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
  MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
  MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
  		   I7CORE_REVISION);
  
  module_param(edac_op_state, int, 0444);
  MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");