Blame view

drivers/edac/i5100_edac.c 30.3 KB
8f421c595   Arthur Jones   edac: i5100 new i...
1
2
3
4
5
6
7
8
9
10
11
  /*
   * Intel 5100 Memory Controllers kernel module
   *
   * This file may be distributed under the terms of the
   * GNU General Public License.
   *
   * This module is based on the following document:
   *
   * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet
   *      http://download.intel.com/design/chipsets/datashts/318378.pdf
   *
bbead2104   Nils Carlson   edac: i5100 add 6...
12
13
   * The intel 5100 has two independent channels. EDAC core currently
   * can not reflect this configuration so instead the chip-select
25985edce   Lucas De Marchi   Fix common misspe...
14
   * rows for each respective channel are laid out one after another,
bbead2104   Nils Carlson   edac: i5100 add 6...
15
16
   * the first half belonging to channel 0, the second half belonging
   * to channel 1.
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
17
18
19
20
21
   *
   * This driver is for DDR2 DIMMs, and it uses chip select to select among the
   * several ranks. However, instead of showing memories as ranks, it outputs
   * them as DIMM's. An internal table creates the association between ranks
   * and DIMM's.
8f421c595   Arthur Jones   edac: i5100 new i...
22
23
24
25
26
   */
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/pci.h>
  #include <linux/pci_ids.h>
8f421c595   Arthur Jones   edac: i5100 new i...
27
28
29
  #include <linux/edac.h>
  #include <linux/delay.h>
  #include <linux/mmzone.h>
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
30
  #include <linux/debugfs.h>
8f421c595   Arthur Jones   edac: i5100 new i...
31

52019e406   Borislav Petkov   EDAC, i5100: Conv...
32
  #include "edac_module.h"
8f421c595   Arthur Jones   edac: i5100 new i...
33

b238e5772   Arthur Jones   edac: i5100: cleanup
34
  /* register addresses */
8f421c595   Arthur Jones   edac: i5100 new i...
35
36
  
  /* device 16, func 1 */
43920a598   Arthur Jones   edac: i5100 fix e...
37
  #define I5100_MC		0x40	/* Memory Control Register */
295439f2a   Nils Carlson   edac: i5100 add s...
38
39
  #define 	I5100_MC_SCRBEN_MASK	(1 << 7)
  #define 	I5100_MC_SCRBDONE_MASK	(1 << 4)
8f421c595   Arthur Jones   edac: i5100 new i...
40
41
  #define I5100_MS		0x44	/* Memory Status Register */
  #define I5100_SPDDATA		0x48	/* Serial Presence Detect Status Reg */
8f421c595   Arthur Jones   edac: i5100 new i...
42
  #define I5100_SPDCMD		0x4c	/* Serial Presence Detect Command Reg */
8f421c595   Arthur Jones   edac: i5100 new i...
43
  #define I5100_TOLM		0x6c	/* Top of Low Memory */
8f421c595   Arthur Jones   edac: i5100 new i...
44
45
46
47
  #define I5100_MIR0		0x80	/* Memory Interleave Range 0 */
  #define I5100_MIR1		0x84	/* Memory Interleave Range 1 */
  #define I5100_AMIR_0		0x8c	/* Adjusted Memory Interleave Range 0 */
  #define I5100_AMIR_1		0x90	/* Adjusted Memory Interleave Range 1 */
8f421c595   Arthur Jones   edac: i5100 new i...
48
  #define I5100_FERR_NF_MEM	0xa0	/* MC First Non Fatal Errors */
8f421c595   Arthur Jones   edac: i5100 new i...
49
50
51
  #define		I5100_FERR_NF_MEM_M16ERR_MASK	(1 << 16)
  #define		I5100_FERR_NF_MEM_M15ERR_MASK	(1 << 15)
  #define		I5100_FERR_NF_MEM_M14ERR_MASK	(1 << 14)
f7952ffcf   Arthur Jones   edac: i5100 fix m...
52
53
54
55
56
57
  #define		I5100_FERR_NF_MEM_M12ERR_MASK	(1 << 12)
  #define		I5100_FERR_NF_MEM_M11ERR_MASK	(1 << 11)
  #define		I5100_FERR_NF_MEM_M10ERR_MASK	(1 << 10)
  #define		I5100_FERR_NF_MEM_M6ERR_MASK	(1 << 6)
  #define		I5100_FERR_NF_MEM_M5ERR_MASK	(1 << 5)
  #define		I5100_FERR_NF_MEM_M4ERR_MASK	(1 << 4)
b6378cb3e   Niklas Söderlund   edac: i5100 fix e...
58
  #define		I5100_FERR_NF_MEM_M1ERR_MASK	(1 << 1)
8f421c595   Arthur Jones   edac: i5100 new i...
59
60
61
  #define		I5100_FERR_NF_MEM_ANY_MASK	\
  			(I5100_FERR_NF_MEM_M16ERR_MASK | \
  			I5100_FERR_NF_MEM_M15ERR_MASK | \
f7952ffcf   Arthur Jones   edac: i5100 fix m...
62
63
64
65
66
67
68
69
  			I5100_FERR_NF_MEM_M14ERR_MASK | \
  			I5100_FERR_NF_MEM_M12ERR_MASK | \
  			I5100_FERR_NF_MEM_M11ERR_MASK | \
  			I5100_FERR_NF_MEM_M10ERR_MASK | \
  			I5100_FERR_NF_MEM_M6ERR_MASK | \
  			I5100_FERR_NF_MEM_M5ERR_MASK | \
  			I5100_FERR_NF_MEM_M4ERR_MASK | \
  			I5100_FERR_NF_MEM_M1ERR_MASK)
8f421c595   Arthur Jones   edac: i5100 new i...
70
  #define	I5100_NERR_NF_MEM	0xa4	/* MC Next Non-Fatal Errors */
178d5a742   Arthur Jones   edac: i5100 fix u...
71
  #define I5100_EMASK_MEM		0xa8	/* MC Error Mask Register */
53ceafd6a   Niklas Söderlund   i5100_edac: add f...
72
73
74
75
76
77
78
79
  #define I5100_MEM0EINJMSK0	0x200	/* Injection Mask0 Register Channel 0 */
  #define I5100_MEM1EINJMSK0	0x208	/* Injection Mask0 Register Channel 1 */
  #define		I5100_MEMXEINJMSK0_EINJEN	(1 << 27)
  #define I5100_MEM0EINJMSK1	0x204	/* Injection Mask1 Register Channel 0 */
  #define I5100_MEM1EINJMSK1	0x206	/* Injection Mask1 Register Channel 1 */
  
  /* Device 19, Function 0 */
  #define I5100_DINJ0 0x9a
8f421c595   Arthur Jones   edac: i5100 new i...
80
81
82
83
  
  /* device 21 and 22, func 0 */
  #define I5100_MTR_0	0x154	/* Memory Technology Registers 0-3 */
  #define I5100_DMIR	0x15c	/* DIMM Interleave Range */
8f421c595   Arthur Jones   edac: i5100 new i...
84
  #define	I5100_VALIDLOG	0x18c	/* Valid Log Markers */
8f421c595   Arthur Jones   edac: i5100 new i...
85
  #define	I5100_NRECMEMA	0x190	/* Non-Recoverable Memory Error Log Reg A */
8f421c595   Arthur Jones   edac: i5100 new i...
86
  #define	I5100_NRECMEMB	0x194	/* Non-Recoverable Memory Error Log Reg B */
8f421c595   Arthur Jones   edac: i5100 new i...
87
  #define	I5100_REDMEMA	0x198	/* Recoverable Memory Data Error Log Reg A */
8f421c595   Arthur Jones   edac: i5100 new i...
88
  #define	I5100_REDMEMB	0x19c	/* Recoverable Memory Data Error Log Reg B */
8f421c595   Arthur Jones   edac: i5100 new i...
89
  #define	I5100_RECMEMA	0x1a0	/* Recoverable Memory Error Log Reg A */
8f421c595   Arthur Jones   edac: i5100 new i...
90
  #define	I5100_RECMEMB	0x1a4	/* Recoverable Memory Error Log Reg B */
b238e5772   Arthur Jones   edac: i5100: cleanup
91
92
93
  #define I5100_MTR_4	0x1b0	/* Memory Technology Registers 4,5 */
  
  /* bit field accessors */
295439f2a   Nils Carlson   edac: i5100 add s...
94
95
96
97
  static inline u32 i5100_mc_scrben(u32 mc)
  {
  	return mc >> 7 & 1;
  }
b238e5772   Arthur Jones   edac: i5100: cleanup
98
99
100
101
  static inline u32 i5100_mc_errdeten(u32 mc)
  {
  	return mc >> 5 & 1;
  }
295439f2a   Nils Carlson   edac: i5100 add s...
102
103
104
105
  static inline u32 i5100_mc_scrbdone(u32 mc)
  {
  	return mc >> 4 & 1;
  }
b238e5772   Arthur Jones   edac: i5100: cleanup
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  static inline u16 i5100_spddata_rdo(u16 a)
  {
  	return a >> 15 & 1;
  }
  
  static inline u16 i5100_spddata_sbe(u16 a)
  {
  	return a >> 13 & 1;
  }
  
  static inline u16 i5100_spddata_busy(u16 a)
  {
  	return a >> 12 & 1;
  }
  
  static inline u16 i5100_spddata_data(u16 a)
  {
  	return a & ((1 << 8) - 1);
  }
  
  static inline u32 i5100_spdcmd_create(u32 dti, u32 ckovrd, u32 sa, u32 ba,
  				      u32 data, u32 cmd)
  {
  	return	((dti & ((1 << 4) - 1))  << 28) |
  		((ckovrd & 1)            << 27) |
  		((sa & ((1 << 3) - 1))   << 24) |
  		((ba & ((1 << 8) - 1))   << 16) |
  		((data & ((1 << 8) - 1)) <<  8) |
  		(cmd & 1);
  }
  
  static inline u16 i5100_tolm_tolm(u16 a)
  {
  	return a >> 12 & ((1 << 4) - 1);
  }
  
  static inline u16 i5100_mir_limit(u16 a)
  {
  	return a >> 4 & ((1 << 12) - 1);
  }
  
  static inline u16 i5100_mir_way1(u16 a)
  {
  	return a >> 1 & 1;
  }
  
  static inline u16 i5100_mir_way0(u16 a)
  {
  	return a & 1;
  }
  
  static inline u32 i5100_ferr_nf_mem_chan_indx(u32 a)
  {
  	return a >> 28 & 1;
  }
  
  static inline u32 i5100_ferr_nf_mem_any(u32 a)
  {
  	return a & I5100_FERR_NF_MEM_ANY_MASK;
  }
  
  static inline u32 i5100_nerr_nf_mem_any(u32 a)
  {
  	return i5100_ferr_nf_mem_any(a);
  }
  
  static inline u32 i5100_dmir_limit(u32 a)
  {
  	return a >> 16 & ((1 << 11) - 1);
  }
  
  static inline u32 i5100_dmir_rank(u32 a, u32 i)
  {
  	return a >> (4 * i) & ((1 << 2) - 1);
  }
  
  static inline u16 i5100_mtr_present(u16 a)
  {
  	return a >> 10 & 1;
  }
  
  static inline u16 i5100_mtr_ethrottle(u16 a)
  {
  	return a >> 9 & 1;
  }
  
  static inline u16 i5100_mtr_width(u16 a)
  {
  	return a >> 8 & 1;
  }
  
  static inline u16 i5100_mtr_numbank(u16 a)
  {
  	return a >> 6 & 1;
  }
  
  static inline u16 i5100_mtr_numrow(u16 a)
  {
  	return a >> 2 & ((1 << 2) - 1);
  }
  
  static inline u16 i5100_mtr_numcol(u16 a)
  {
  	return a & ((1 << 2) - 1);
  }
  
  
  static inline u32 i5100_validlog_redmemvalid(u32 a)
  {
  	return a >> 2 & 1;
  }
  
  static inline u32 i5100_validlog_recmemvalid(u32 a)
  {
  	return a >> 1 & 1;
  }
  
  static inline u32 i5100_validlog_nrecmemvalid(u32 a)
  {
  	return a & 1;
  }
  
  static inline u32 i5100_nrecmema_merr(u32 a)
  {
  	return a >> 15 & ((1 << 5) - 1);
  }
  
  static inline u32 i5100_nrecmema_bank(u32 a)
  {
  	return a >> 12 & ((1 << 3) - 1);
  }
  
  static inline u32 i5100_nrecmema_rank(u32 a)
  {
  	return a >>  8 & ((1 << 3) - 1);
  }
  
  static inline u32 i5100_nrecmema_dm_buf_id(u32 a)
  {
  	return a & ((1 << 8) - 1);
  }
  
  static inline u32 i5100_nrecmemb_cas(u32 a)
  {
  	return a >> 16 & ((1 << 13) - 1);
  }
  
  static inline u32 i5100_nrecmemb_ras(u32 a)
  {
  	return a & ((1 << 16) - 1);
  }
  
  static inline u32 i5100_redmemb_ecc_locator(u32 a)
  {
  	return a & ((1 << 18) - 1);
  }
  
  static inline u32 i5100_recmema_merr(u32 a)
  {
  	return i5100_nrecmema_merr(a);
  }
  
  static inline u32 i5100_recmema_bank(u32 a)
  {
  	return i5100_nrecmema_bank(a);
  }
  
  static inline u32 i5100_recmema_rank(u32 a)
  {
  	return i5100_nrecmema_rank(a);
  }
b238e5772   Arthur Jones   edac: i5100: cleanup
277
278
279
280
281
282
283
284
285
  static inline u32 i5100_recmemb_cas(u32 a)
  {
  	return i5100_nrecmemb_cas(a);
  }
  
  static inline u32 i5100_recmemb_ras(u32 a)
  {
  	return i5100_nrecmemb_ras(a);
  }
8f421c595   Arthur Jones   edac: i5100 new i...
286
287
  
  /* some generic limits */
b18dfd05f   Nils Carlson   edac: i5100 clean...
288
289
  #define I5100_MAX_RANKS_PER_CHAN	6
  #define I5100_CHANNELS			    2
8f421c595   Arthur Jones   edac: i5100 new i...
290
291
  #define I5100_MAX_RANKS_PER_DIMM	4
  #define I5100_DIMM_ADDR_LINES		(6 - 3)	/* 64 bits / 8 bits per byte */
b18dfd05f   Nils Carlson   edac: i5100 clean...
292
  #define I5100_MAX_DIMM_SLOTS_PER_CHAN	4
8f421c595   Arthur Jones   edac: i5100 new i...
293
294
  #define I5100_MAX_RANK_INTERLEAVE	4
  #define I5100_MAX_DMIRS			5
295439f2a   Nils Carlson   edac: i5100 add s...
295
  #define I5100_SCRUB_REFRESH_RATE	(5 * 60 * HZ)
8f421c595   Arthur Jones   edac: i5100 new i...
296
297
298
  
  struct i5100_priv {
  	/* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
b18dfd05f   Nils Carlson   edac: i5100 clean...
299
  	int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN];
8f421c595   Arthur Jones   edac: i5100 new i...
300
301
302
303
  
  	/*
  	 * mainboard chip select map -- maps i5100 chip selects to
  	 * DIMM slot chip selects.  In the case of only 4 ranks per
b18dfd05f   Nils Carlson   edac: i5100 clean...
304
305
  	 * channel, the mapping is fairly obvious but not unique.
  	 * we map -1 -> NC and assume both channels use the same
8f421c595   Arthur Jones   edac: i5100 new i...
306
307
308
  	 * map...
  	 *
  	 */
b18dfd05f   Nils Carlson   edac: i5100 clean...
309
  	int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM];
8f421c595   Arthur Jones   edac: i5100 new i...
310
311
312
313
314
  
  	/* memory interleave range */
  	struct {
  		u64	 limit;
  		unsigned way[2];
b18dfd05f   Nils Carlson   edac: i5100 clean...
315
  	} mir[I5100_CHANNELS];
8f421c595   Arthur Jones   edac: i5100 new i...
316
317
  
  	/* adjusted memory interleave range register */
b18dfd05f   Nils Carlson   edac: i5100 clean...
318
  	unsigned amir[I5100_CHANNELS];
8f421c595   Arthur Jones   edac: i5100 new i...
319
320
321
322
323
  
  	/* dimm interleave range */
  	struct {
  		unsigned rank[I5100_MAX_RANK_INTERLEAVE];
  		u64	 limit;
b18dfd05f   Nils Carlson   edac: i5100 clean...
324
  	} dmir[I5100_CHANNELS][I5100_MAX_DMIRS];
8f421c595   Arthur Jones   edac: i5100 new i...
325
326
327
328
329
330
331
332
333
  
  	/* memory technology registers... */
  	struct {
  		unsigned present;	/* 0 or 1 */
  		unsigned ethrottle;	/* 0 or 1 */
  		unsigned width;		/* 4 or 8 bits  */
  		unsigned numbank;	/* 2 or 3 lines */
  		unsigned numrow;	/* 13 .. 16 lines */
  		unsigned numcol;	/* 11 .. 12 lines */
b18dfd05f   Nils Carlson   edac: i5100 clean...
334
  	} mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN];
8f421c595   Arthur Jones   edac: i5100 new i...
335
336
  
  	u64 tolm;		/* top of low memory in bytes */
b18dfd05f   Nils Carlson   edac: i5100 clean...
337
  	unsigned ranksperchan;	/* number of ranks per channel */
8f421c595   Arthur Jones   edac: i5100 new i...
338
339
  
  	struct pci_dev *mc;	/* device 16 func 1 */
52608ba20   Niklas Söderlund   i5100_edac: probe...
340
  	struct pci_dev *einj;	/* device 19 func 0 */
8f421c595   Arthur Jones   edac: i5100 new i...
341
342
  	struct pci_dev *ch0mm;	/* device 21 func 0 */
  	struct pci_dev *ch1mm;	/* device 22 func 0 */
295439f2a   Nils Carlson   edac: i5100 add s...
343
344
345
  
  	struct delayed_work i5100_scrubbing;
  	int scrub_enable;
53ceafd6a   Niklas Söderlund   i5100_edac: add f...
346
347
348
349
350
351
352
353
  
  	/* Error injection */
  	u8 inject_channel;
  	u8 inject_hlinesel;
  	u8 inject_deviceptr1;
  	u8 inject_deviceptr2;
  	u16 inject_eccmask1;
  	u16 inject_eccmask2;
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
354
355
  
  	struct dentry *debugfs;
8f421c595   Arthur Jones   edac: i5100 new i...
356
  };
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
357
  static struct dentry *i5100_debugfs;
b18dfd05f   Nils Carlson   edac: i5100 clean...
358
  /* map a rank/chan to a slot number on the mainboard */
8f421c595   Arthur Jones   edac: i5100 new i...
359
  static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
b18dfd05f   Nils Carlson   edac: i5100 clean...
360
  			      int chan, int rank)
8f421c595   Arthur Jones   edac: i5100 new i...
361
362
363
  {
  	const struct i5100_priv *priv = mci->pvt_info;
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
364
  	for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
365
  		int j;
b18dfd05f   Nils Carlson   edac: i5100 clean...
366
  		const int numrank = priv->dimm_numrank[chan][i];
8f421c595   Arthur Jones   edac: i5100 new i...
367
368
369
  
  		for (j = 0; j < numrank; j++)
  			if (priv->dimm_csmap[i][j] == rank)
b18dfd05f   Nils Carlson   edac: i5100 clean...
370
  				return i * 2 + chan;
8f421c595   Arthur Jones   edac: i5100 new i...
371
372
373
374
  	}
  
  	return -1;
  }
8f421c595   Arthur Jones   edac: i5100 new i...
375
376
  static const char *i5100_err_msg(unsigned err)
  {
b238e5772   Arthur Jones   edac: i5100: cleanup
377
  	static const char *merrs[] = {
8f421c595   Arthur Jones   edac: i5100 new i...
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
  		"unknown", /* 0 */
  		"uncorrectable data ECC on replay", /* 1 */
  		"unknown", /* 2 */
  		"unknown", /* 3 */
  		"aliased uncorrectable demand data ECC", /* 4 */
  		"aliased uncorrectable spare-copy data ECC", /* 5 */
  		"aliased uncorrectable patrol data ECC", /* 6 */
  		"unknown", /* 7 */
  		"unknown", /* 8 */
  		"unknown", /* 9 */
  		"non-aliased uncorrectable demand data ECC", /* 10 */
  		"non-aliased uncorrectable spare-copy data ECC", /* 11 */
  		"non-aliased uncorrectable patrol data ECC", /* 12 */
  		"unknown", /* 13 */
  		"correctable demand data ECC", /* 14 */
  		"correctable spare-copy data ECC", /* 15 */
  		"correctable patrol data ECC", /* 16 */
  		"unknown", /* 17 */
  		"SPD protocol error", /* 18 */
  		"unknown", /* 19 */
  		"spare copy initiated", /* 20 */
  		"spare copy completed", /* 21 */
  	};
  	unsigned i;
  
  	for (i = 0; i < ARRAY_SIZE(merrs); i++)
  		if (1 << i & err)
  			return merrs[i];
  
  	return "none";
  }
b18dfd05f   Nils Carlson   edac: i5100 clean...
409
  /* convert csrow index into a rank (per channel -- 0..5) */
d55c79ac8   Robert Richter   EDAC: Prefer 'uns...
410
411
  static unsigned int i5100_csrow_to_rank(const struct mem_ctl_info *mci,
  					unsigned int csrow)
8f421c595   Arthur Jones   edac: i5100 new i...
412
413
  {
  	const struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
414
  	return csrow % priv->ranksperchan;
8f421c595   Arthur Jones   edac: i5100 new i...
415
  }
b18dfd05f   Nils Carlson   edac: i5100 clean...
416
  /* convert csrow index into a channel (0..1) */
d55c79ac8   Robert Richter   EDAC: Prefer 'uns...
417
418
  static unsigned int i5100_csrow_to_chan(const struct mem_ctl_info *mci,
  					unsigned int csrow)
8f421c595   Arthur Jones   edac: i5100 new i...
419
420
  {
  	const struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
421
  	return csrow / priv->ranksperchan;
8f421c595   Arthur Jones   edac: i5100 new i...
422
  }
8f421c595   Arthur Jones   edac: i5100 new i...
423
  static void i5100_handle_ce(struct mem_ctl_info *mci,
b18dfd05f   Nils Carlson   edac: i5100 clean...
424
  			    int chan,
8f421c595   Arthur Jones   edac: i5100 new i...
425
426
427
428
429
430
431
  			    unsigned bank,
  			    unsigned rank,
  			    unsigned long syndrome,
  			    unsigned cas,
  			    unsigned ras,
  			    const char *msg)
  {
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
432
  	char detail[80];
084a4fcce   Mauro Carvalho Chehab   edac: move dimm p...
433

d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
434
435
436
437
438
  	/* Form out message */
  	snprintf(detail, sizeof(detail),
  		 "bank %u, cas %u, ras %u
  ",
  		 bank, cas, ras);
8f421c595   Arthur Jones   edac: i5100 new i...
439

9eb07a7fb   Mauro Carvalho Chehab   edac: edac_mc_han...
440
  	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
441
442
  			     0, 0, syndrome,
  			     chan, rank, -1,
03f7eae80   Mauro Carvalho Chehab   edac: remove arch...
443
  			     msg, detail);
8f421c595   Arthur Jones   edac: i5100 new i...
444
445
446
  }
  
  static void i5100_handle_ue(struct mem_ctl_info *mci,
b18dfd05f   Nils Carlson   edac: i5100 clean...
447
  			    int chan,
8f421c595   Arthur Jones   edac: i5100 new i...
448
449
450
451
452
453
454
  			    unsigned bank,
  			    unsigned rank,
  			    unsigned long syndrome,
  			    unsigned cas,
  			    unsigned ras,
  			    const char *msg)
  {
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
455
  	char detail[80];
8f421c595   Arthur Jones   edac: i5100 new i...
456

d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
457
458
459
460
461
  	/* Form out message */
  	snprintf(detail, sizeof(detail),
  		 "bank %u, cas %u, ras %u
  ",
  		 bank, cas, ras);
8f421c595   Arthur Jones   edac: i5100 new i...
462

9eb07a7fb   Mauro Carvalho Chehab   edac: edac_mc_han...
463
  	edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
464
465
  			     0, 0, syndrome,
  			     chan, rank, -1,
03f7eae80   Mauro Carvalho Chehab   edac: remove arch...
466
  			     msg, detail);
8f421c595   Arthur Jones   edac: i5100 new i...
467
  }
b18dfd05f   Nils Carlson   edac: i5100 clean...
468
  static void i5100_read_log(struct mem_ctl_info *mci, int chan,
8f421c595   Arthur Jones   edac: i5100 new i...
469
470
471
  			   u32 ferr, u32 nerr)
  {
  	struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
472
  	struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm;
8f421c595   Arthur Jones   edac: i5100 new i...
473
474
475
476
477
478
479
480
481
482
483
  	u32 dw;
  	u32 dw2;
  	unsigned syndrome = 0;
  	unsigned ecc_loc = 0;
  	unsigned merr;
  	unsigned bank;
  	unsigned rank;
  	unsigned cas;
  	unsigned ras;
  
  	pci_read_config_dword(pdev, I5100_VALIDLOG, &dw);
b238e5772   Arthur Jones   edac: i5100: cleanup
484
  	if (i5100_validlog_redmemvalid(dw)) {
8f421c595   Arthur Jones   edac: i5100 new i...
485
  		pci_read_config_dword(pdev, I5100_REDMEMA, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
486
  		syndrome = dw2;
8f421c595   Arthur Jones   edac: i5100 new i...
487
  		pci_read_config_dword(pdev, I5100_REDMEMB, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
488
  		ecc_loc = i5100_redmemb_ecc_locator(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
489
  	}
b238e5772   Arthur Jones   edac: i5100: cleanup
490
  	if (i5100_validlog_recmemvalid(dw)) {
8f421c595   Arthur Jones   edac: i5100 new i...
491
492
493
  		const char *msg;
  
  		pci_read_config_dword(pdev, I5100_RECMEMA, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
494
495
496
  		merr = i5100_recmema_merr(dw2);
  		bank = i5100_recmema_bank(dw2);
  		rank = i5100_recmema_rank(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
497
498
  
  		pci_read_config_dword(pdev, I5100_RECMEMB, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
499
500
  		cas = i5100_recmemb_cas(dw2);
  		ras = i5100_recmemb_ras(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
501
502
503
504
505
506
507
  
  		/* FIXME:  not really sure if this is what merr is...
  		 */
  		if (!merr)
  			msg = i5100_err_msg(ferr);
  		else
  			msg = i5100_err_msg(nerr);
b18dfd05f   Nils Carlson   edac: i5100 clean...
508
  		i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg);
8f421c595   Arthur Jones   edac: i5100 new i...
509
  	}
b238e5772   Arthur Jones   edac: i5100: cleanup
510
  	if (i5100_validlog_nrecmemvalid(dw)) {
8f421c595   Arthur Jones   edac: i5100 new i...
511
512
513
  		const char *msg;
  
  		pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
514
515
516
  		merr = i5100_nrecmema_merr(dw2);
  		bank = i5100_nrecmema_bank(dw2);
  		rank = i5100_nrecmema_rank(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
517
518
  
  		pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
519
520
  		cas = i5100_nrecmemb_cas(dw2);
  		ras = i5100_nrecmemb_ras(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
521
522
523
524
525
526
527
  
  		/* FIXME:  not really sure if this is what merr is...
  		 */
  		if (!merr)
  			msg = i5100_err_msg(ferr);
  		else
  			msg = i5100_err_msg(nerr);
b18dfd05f   Nils Carlson   edac: i5100 clean...
528
  		i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg);
8f421c595   Arthur Jones   edac: i5100 new i...
529
530
531
532
533
534
535
536
  	}
  
  	pci_write_config_dword(pdev, I5100_VALIDLOG, dw);
  }
  
  static void i5100_check_error(struct mem_ctl_info *mci)
  {
  	struct i5100_priv *priv = mci->pvt_info;
df95e42e1   Niklas Söderlund   edac: i5100 ack e...
537
  	u32 dw, dw2;
8f421c595   Arthur Jones   edac: i5100 new i...
538
539
  
  	pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw);
b238e5772   Arthur Jones   edac: i5100: cleanup
540
  	if (i5100_ferr_nf_mem_any(dw)) {
8f421c595   Arthur Jones   edac: i5100 new i...
541
542
  
  		pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
543

b238e5772   Arthur Jones   edac: i5100: cleanup
544
545
546
  		i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw),
  			       i5100_ferr_nf_mem_any(dw),
  			       i5100_nerr_nf_mem_any(dw2));
df95e42e1   Niklas Söderlund   edac: i5100 ack e...
547
548
  
  		pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
549
  	}
df95e42e1   Niklas Söderlund   edac: i5100 ack e...
550
  	pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
8f421c595   Arthur Jones   edac: i5100 new i...
551
  }
295439f2a   Nils Carlson   edac: i5100 add s...
552
553
554
555
556
557
558
559
  /* The i5100 chipset will scrub the entire memory once, then
   * set a done bit. Continuous scrubbing is achieved by enqueing
   * delayed work to a workqueue, checking every few minutes if
   * the scrubbing has completed and if so reinitiating it.
   */
  
  static void i5100_refresh_scrubbing(struct work_struct *work)
  {
1cac5503f   Geliang Tang   EDAC, i5100: Use ...
560
  	struct delayed_work *i5100_scrubbing = to_delayed_work(work);
295439f2a   Nils Carlson   edac: i5100 add s...
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
  	struct i5100_priv *priv = container_of(i5100_scrubbing,
  					       struct i5100_priv,
  					       i5100_scrubbing);
  	u32 dw;
  
  	pci_read_config_dword(priv->mc, I5100_MC, &dw);
  
  	if (priv->scrub_enable) {
  
  		pci_read_config_dword(priv->mc, I5100_MC, &dw);
  
  		if (i5100_mc_scrbdone(dw)) {
  			dw |= I5100_MC_SCRBEN_MASK;
  			pci_write_config_dword(priv->mc, I5100_MC, dw);
  			pci_read_config_dword(priv->mc, I5100_MC, &dw);
  		}
  
  		schedule_delayed_work(&(priv->i5100_scrubbing),
  				      I5100_SCRUB_REFRESH_RATE);
  	}
  }
  /*
   * The bandwidth is based on experimentation, feel free to refine it.
   */
eba042a81   Borislav Petkov   edac, mc: Improve...
585
  static int i5100_set_scrub_rate(struct mem_ctl_info *mci, u32 bandwidth)
295439f2a   Nils Carlson   edac: i5100 add s...
586
587
588
589
590
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	u32 dw;
  
  	pci_read_config_dword(priv->mc, I5100_MC, &dw);
eba042a81   Borislav Petkov   edac, mc: Improve...
591
  	if (bandwidth) {
295439f2a   Nils Carlson   edac: i5100 add s...
592
593
594
595
596
597
598
599
600
601
602
603
  		priv->scrub_enable = 1;
  		dw |= I5100_MC_SCRBEN_MASK;
  		schedule_delayed_work(&(priv->i5100_scrubbing),
  				      I5100_SCRUB_REFRESH_RATE);
  	} else {
  		priv->scrub_enable = 0;
  		dw &= ~I5100_MC_SCRBEN_MASK;
  		cancel_delayed_work(&(priv->i5100_scrubbing));
  	}
  	pci_write_config_dword(priv->mc, I5100_MC, dw);
  
  	pci_read_config_dword(priv->mc, I5100_MC, &dw);
eba042a81   Borislav Petkov   edac, mc: Improve...
604
  	bandwidth = 5900000 * i5100_mc_scrben(dw);
295439f2a   Nils Carlson   edac: i5100 add s...
605

390944439   Borislav Petkov   EDAC: Fixup scrub...
606
  	return bandwidth;
295439f2a   Nils Carlson   edac: i5100 add s...
607
  }
390944439   Borislav Petkov   EDAC: Fixup scrub...
608
  static int i5100_get_scrub_rate(struct mem_ctl_info *mci)
295439f2a   Nils Carlson   edac: i5100 add s...
609
610
611
612
613
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	u32 dw;
  
  	pci_read_config_dword(priv->mc, I5100_MC, &dw);
390944439   Borislav Petkov   EDAC: Fixup scrub...
614
  	return 5900000 * i5100_mc_scrben(dw);
295439f2a   Nils Carlson   edac: i5100 add s...
615
  }
8f421c595   Arthur Jones   edac: i5100 new i...
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
  static struct pci_dev *pci_get_device_func(unsigned vendor,
  					   unsigned device,
  					   unsigned func)
  {
  	struct pci_dev *ret = NULL;
  
  	while (1) {
  		ret = pci_get_device(vendor, device, ret);
  
  		if (!ret)
  			break;
  
  		if (PCI_FUNC(ret->devfn) == func)
  			break;
  	}
  
  	return ret;
  }
d55c79ac8   Robert Richter   EDAC: Prefer 'uns...
634
  static unsigned long i5100_npages(struct mem_ctl_info *mci, unsigned int csrow)
8f421c595   Arthur Jones   edac: i5100 new i...
635
636
  {
  	struct i5100_priv *priv = mci->pvt_info;
d55c79ac8   Robert Richter   EDAC: Prefer 'uns...
637
638
  	const unsigned int chan_rank = i5100_csrow_to_rank(mci, csrow);
  	const unsigned int chan = i5100_csrow_to_chan(mci, csrow);
8f421c595   Arthur Jones   edac: i5100 new i...
639
640
641
  	unsigned addr_lines;
  
  	/* dimm present? */
b18dfd05f   Nils Carlson   edac: i5100 clean...
642
  	if (!priv->mtr[chan][chan_rank].present)
8f421c595   Arthur Jones   edac: i5100 new i...
643
644
645
646
  		return 0ULL;
  
  	addr_lines =
  		I5100_DIMM_ADDR_LINES +
b18dfd05f   Nils Carlson   edac: i5100 clean...
647
648
649
  		priv->mtr[chan][chan_rank].numcol +
  		priv->mtr[chan][chan_rank].numrow +
  		priv->mtr[chan][chan_rank].numbank;
8f421c595   Arthur Jones   edac: i5100 new i...
650
651
652
653
  
  	return (unsigned long)
  		((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
654
  static void i5100_init_mtr(struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
655
656
657
658
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
659
  	for (i = 0; i < I5100_CHANNELS; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
660
661
  		int j;
  		struct pci_dev *pdev = mms[i];
b18dfd05f   Nils Carlson   edac: i5100 clean...
662
  		for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) {
8f421c595   Arthur Jones   edac: i5100 new i...
663
664
665
666
667
668
  			const unsigned addr =
  				(j < 4) ? I5100_MTR_0 + j * 2 :
  					  I5100_MTR_4 + (j - 4) * 2;
  			u16 w;
  
  			pci_read_config_word(pdev, addr, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
669
670
671
672
673
674
  			priv->mtr[i][j].present = i5100_mtr_present(w);
  			priv->mtr[i][j].ethrottle = i5100_mtr_ethrottle(w);
  			priv->mtr[i][j].width = 4 + 4 * i5100_mtr_width(w);
  			priv->mtr[i][j].numbank = 2 + i5100_mtr_numbank(w);
  			priv->mtr[i][j].numrow = 13 + i5100_mtr_numrow(w);
  			priv->mtr[i][j].numcol = 10 + i5100_mtr_numcol(w);
8f421c595   Arthur Jones   edac: i5100 new i...
675
676
677
678
679
680
681
682
683
684
685
686
687
  		}
  	}
  }
  
  /*
   * FIXME: make this into a real i2c adapter (so that dimm-decode
   * will work)?
   */
  static int i5100_read_spd_byte(const struct mem_ctl_info *mci,
  			       u8 ch, u8 slot, u8 addr, u8 *byte)
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	u16 w;
8f421c595   Arthur Jones   edac: i5100 new i...
688
689
690
  	unsigned long et;
  
  	pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
691
  	if (i5100_spddata_busy(w))
8f421c595   Arthur Jones   edac: i5100 new i...
692
  		return -1;
b238e5772   Arthur Jones   edac: i5100: cleanup
693
694
695
  	pci_write_config_dword(priv->mc, I5100_SPDCMD,
  			       i5100_spdcmd_create(0xa, 1, ch * 4 + slot, addr,
  						   0, 0));
8f421c595   Arthur Jones   edac: i5100 new i...
696
697
698
699
700
701
  
  	/* wait up to 100ms */
  	et = jiffies + HZ / 10;
  	udelay(100);
  	while (1) {
  		pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
702
  		if (!i5100_spddata_busy(w))
8f421c595   Arthur Jones   edac: i5100 new i...
703
704
705
  			break;
  		udelay(100);
  	}
b238e5772   Arthur Jones   edac: i5100: cleanup
706
  	if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w))
8f421c595   Arthur Jones   edac: i5100 new i...
707
  		return -1;
b238e5772   Arthur Jones   edac: i5100: cleanup
708
  	*byte = i5100_spddata_data(w);
8f421c595   Arthur Jones   edac: i5100 new i...
709
710
711
712
713
714
715
716
  
  	return 0;
  }
  
  /*
   * fill dimm chip select map
   *
   * FIXME:
8f421c595   Arthur Jones   edac: i5100 new i...
717
718
719
   *   o not the only way to may chip selects to dimm slots
   *   o investigate if there is some way to obtain this map from the bios
   */
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
720
  static void i5100_init_dimm_csmap(struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
721
722
723
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
724
  	for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
725
726
727
728
729
730
731
  		int j;
  
  		for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++)
  			priv->dimm_csmap[i][j] = -1; /* default NC */
  	}
  
  	/* only 2 chip selects per slot... */
bbead2104   Nils Carlson   edac: i5100 add 6...
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
  	if (priv->ranksperchan == 4) {
  		priv->dimm_csmap[0][0] = 0;
  		priv->dimm_csmap[0][1] = 3;
  		priv->dimm_csmap[1][0] = 1;
  		priv->dimm_csmap[1][1] = 2;
  		priv->dimm_csmap[2][0] = 2;
  		priv->dimm_csmap[3][0] = 3;
  	} else {
  		priv->dimm_csmap[0][0] = 0;
  		priv->dimm_csmap[0][1] = 1;
  		priv->dimm_csmap[1][0] = 2;
  		priv->dimm_csmap[1][1] = 3;
  		priv->dimm_csmap[2][0] = 4;
  		priv->dimm_csmap[2][1] = 5;
  	}
8f421c595   Arthur Jones   edac: i5100 new i...
747
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
748
749
  static void i5100_init_dimm_layout(struct pci_dev *pdev,
  				   struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
750
751
752
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
753
  	for (i = 0; i < I5100_CHANNELS; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
754
  		int j;
b18dfd05f   Nils Carlson   edac: i5100 clean...
755
  		for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) {
8f421c595   Arthur Jones   edac: i5100 new i...
756
757
758
759
760
761
762
763
764
765
766
  			u8 rank;
  
  			if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0)
  				priv->dimm_numrank[i][j] = 0;
  			else
  				priv->dimm_numrank[i][j] = (rank & 3) + 1;
  		}
  	}
  
  	i5100_init_dimm_csmap(mci);
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
767
768
  static void i5100_init_interleaving(struct pci_dev *pdev,
  				    struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
769
770
771
772
773
774
775
776
  {
  	u16 w;
  	u32 dw;
  	struct i5100_priv *priv = mci->pvt_info;
  	struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
  	int i;
  
  	pci_read_config_word(pdev, I5100_TOLM, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
777
  	priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024;
8f421c595   Arthur Jones   edac: i5100 new i...
778
779
  
  	pci_read_config_word(pdev, I5100_MIR0, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
780
781
782
  	priv->mir[0].limit = (u64) i5100_mir_limit(w) << 28;
  	priv->mir[0].way[1] = i5100_mir_way1(w);
  	priv->mir[0].way[0] = i5100_mir_way0(w);
8f421c595   Arthur Jones   edac: i5100 new i...
783
784
  
  	pci_read_config_word(pdev, I5100_MIR1, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
785
786
787
  	priv->mir[1].limit = (u64) i5100_mir_limit(w) << 28;
  	priv->mir[1].way[1] = i5100_mir_way1(w);
  	priv->mir[1].way[0] = i5100_mir_way0(w);
8f421c595   Arthur Jones   edac: i5100 new i...
788
789
790
791
792
  
  	pci_read_config_word(pdev, I5100_AMIR_0, &w);
  	priv->amir[0] = w;
  	pci_read_config_word(pdev, I5100_AMIR_1, &w);
  	priv->amir[1] = w;
b18dfd05f   Nils Carlson   edac: i5100 clean...
793
  	for (i = 0; i < I5100_CHANNELS; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
794
795
796
797
798
799
800
801
  		int j;
  
  		for (j = 0; j < 5; j++) {
  			int k;
  
  			pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw);
  
  			priv->dmir[i][j].limit =
b238e5772   Arthur Jones   edac: i5100: cleanup
802
  				(u64) i5100_dmir_limit(dw) << 28;
8f421c595   Arthur Jones   edac: i5100 new i...
803
804
  			for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++)
  				priv->dmir[i][j].rank[k] =
b238e5772   Arthur Jones   edac: i5100: cleanup
805
  					i5100_dmir_rank(dw, k);
8f421c595   Arthur Jones   edac: i5100 new i...
806
807
808
809
810
  		}
  	}
  
  	i5100_init_mtr(mci);
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
811
  static void i5100_init_csrows(struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
812
813
  {
  	int i;
8f421c595   Arthur Jones   edac: i5100 new i...
814
  	struct i5100_priv *priv = mci->pvt_info;
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
815
816
  	for (i = 0; i < mci->tot_dimms; i++) {
  		struct dimm_info *dimm;
8f421c595   Arthur Jones   edac: i5100 new i...
817
  		const unsigned long npages = i5100_npages(mci, i);
d55c79ac8   Robert Richter   EDAC: Prefer 'uns...
818
819
  		const unsigned int chan = i5100_csrow_to_chan(mci, i);
  		const unsigned int rank = i5100_csrow_to_rank(mci, i);
8f421c595   Arthur Jones   edac: i5100 new i...
820
821
822
  
  		if (!npages)
  			continue;
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
823
824
  		dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
  			       chan, rank, 0);
084a4fcce   Mauro Carvalho Chehab   edac: move dimm p...
825

a895bf8b1   Mauro Carvalho Chehab   edac: move nr_pag...
826
  		dimm->nr_pages = npages;
9d6c7cbe3   Dan Carpenter   i5100_edac: Remov...
827
828
829
830
831
832
833
  		dimm->grain = 32;
  		dimm->dtype = (priv->mtr[chan][rank].width == 4) ?
  				DEV_X4 : DEV_X8;
  		dimm->mtype = MEM_RDDR2;
  		dimm->edac_mode = EDAC_SECDED;
  		snprintf(dimm->label, sizeof(dimm->label), "DIMM%u",
  			 i5100_rank_to_slot(mci, chan, rank));
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
834

956b9ba15   Joe Perches   edac: Convert deb...
835
836
837
  		edac_dbg(2, "dimm channel %d, rank %d, size %ld
  ",
  			 chan, rank, (long)PAGES_TO_MiB(npages));
8f421c595   Arthur Jones   edac: i5100 new i...
838
839
  	}
  }
53ceafd6a   Niklas Söderlund   i5100_edac: add f...
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
  /****************************************************************************
   *                       Error injection routines
   ****************************************************************************/
  
  static void i5100_do_inject(struct mem_ctl_info *mci)
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	u32 mask0;
  	u16 mask1;
  
  	/* MEM[1:0]EINJMSK0
  	 * 31    - ADDRMATCHEN
  	 * 29:28 - HLINESEL
  	 *         00 Reserved
  	 *         01 Lower half of cache line
  	 *         10 Upper half of cache line
  	 *         11 Both upper and lower parts of cache line
  	 * 27    - EINJEN
  	 * 25:19 - XORMASK1 for deviceptr1
  	 * 9:5   - SEC2RAM for deviceptr2
  	 * 4:0   - FIR2RAM for deviceptr1
  	 */
  	mask0 = ((priv->inject_hlinesel & 0x3) << 28) |
  		I5100_MEMXEINJMSK0_EINJEN |
  		((priv->inject_eccmask1 & 0xffff) << 10) |
  		((priv->inject_deviceptr2 & 0x1f) << 5) |
  		(priv->inject_deviceptr1 & 0x1f);
  
  	/* MEM[1:0]EINJMSK1
  	 * 15:0  - XORMASK2 for deviceptr2
  	 */
  	mask1 = priv->inject_eccmask2;
  
  	if (priv->inject_channel == 0) {
  		pci_write_config_dword(priv->mc, I5100_MEM0EINJMSK0, mask0);
  		pci_write_config_word(priv->mc, I5100_MEM0EINJMSK1, mask1);
  	} else {
  		pci_write_config_dword(priv->mc, I5100_MEM1EINJMSK0, mask0);
  		pci_write_config_word(priv->mc, I5100_MEM1EINJMSK1, mask1);
  	}
  
  	/* Error Injection Response Function
  	 * Intel 5100 Memory Controller Hub Chipset (318378) datasheet
  	 * hints about this register but carry no data about them. All
  	 * data regarding device 19 is based on experimentation and the
  	 * Intel 7300 Chipset Memory Controller Hub (318082) datasheet
  	 * which appears to be accurate for the i5100 in this area.
  	 *
  	 * The injection code don't work without setting this register.
  	 * The register needs to be flipped off then on else the hardware
  	 * will only preform the first injection.
  	 *
  	 * Stop condition bits 7:4
  	 * 1010 - Stop after one injection
  	 * 1011 - Never stop injecting faults
  	 *
  	 * Start condition bits 3:0
  	 * 1010 - Never start
  	 * 1011 - Start immediately
  	 */
  	pci_write_config_byte(priv->einj, I5100_DINJ0, 0xaa);
  	pci_write_config_byte(priv->einj, I5100_DINJ0, 0xab);
  }
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
903
904
905
906
907
908
909
910
911
912
913
  #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
  static ssize_t inject_enable_write(struct file *file, const char __user *data,
  		size_t count, loff_t *ppos)
  {
  	struct device *dev = file->private_data;
  	struct mem_ctl_info *mci = to_mci(dev);
  
  	i5100_do_inject(mci);
  
  	return count;
  }
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
914
  static const struct file_operations i5100_inject_enable_fops = {
b0769891b   Wei Yongjun   i5100_edac: conve...
915
  	.open = simple_open,
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
916
917
918
919
920
921
922
923
924
925
  	.write = inject_enable_write,
  	.llseek = generic_file_llseek,
  };
  
  static int i5100_setup_debugfs(struct mem_ctl_info *mci)
  {
  	struct i5100_priv *priv = mci->pvt_info;
  
  	if (!i5100_debugfs)
  		return -ENODEV;
52019e406   Borislav Petkov   EDAC, i5100: Conv...
926
  	priv->debugfs = edac_debugfs_create_dir_at(mci->bus->name, i5100_debugfs);
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
927
928
929
  
  	if (!priv->debugfs)
  		return -ENOMEM;
52019e406   Borislav Petkov   EDAC, i5100: Conv...
930
931
932
933
934
935
936
937
938
939
940
941
942
943
  	edac_debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs,
  				&priv->inject_channel);
  	edac_debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs,
  				&priv->inject_hlinesel);
  	edac_debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs,
  				&priv->inject_deviceptr1);
  	edac_debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs,
  				&priv->inject_deviceptr2);
  	edac_debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs,
  				&priv->inject_eccmask1);
  	edac_debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs,
  				&priv->inject_eccmask2);
  	edac_debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs,
  				&mci->dev, &i5100_inject_enable_fops);
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
944
945
946
947
  
  	return 0;
  
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
948
  static int i5100_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
8f421c595   Arthur Jones   edac: i5100 new i...
949
950
951
  {
  	int rc;
  	struct mem_ctl_info *mci;
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
952
  	struct edac_mc_layer layers[2];
8f421c595   Arthur Jones   edac: i5100 new i...
953
  	struct i5100_priv *priv;
52608ba20   Niklas Söderlund   i5100_edac: probe...
954
  	struct pci_dev *ch0mm, *ch1mm, *einj;
8f421c595   Arthur Jones   edac: i5100 new i...
955
956
957
958
959
960
961
962
963
964
965
966
  	int ret = 0;
  	u32 dw;
  	int ranksperch;
  
  	if (PCI_FUNC(pdev->devfn) != 1)
  		return -ENODEV;
  
  	rc = pci_enable_device(pdev);
  	if (rc < 0) {
  		ret = rc;
  		goto bail;
  	}
43920a598   Arthur Jones   edac: i5100 fix e...
967
968
  	/* ECC enabled? */
  	pci_read_config_dword(pdev, I5100_MC, &dw);
b238e5772   Arthur Jones   edac: i5100: cleanup
969
  	if (!i5100_mc_errdeten(dw)) {
43920a598   Arthur Jones   edac: i5100 fix e...
970
971
972
  		printk(KERN_INFO "i5100_edac: ECC not enabled.
  ");
  		ret = -ENODEV;
b238e5772   Arthur Jones   edac: i5100: cleanup
973
  		goto bail_pdev;
43920a598   Arthur Jones   edac: i5100 fix e...
974
  	}
8f421c595   Arthur Jones   edac: i5100 new i...
975
976
977
  	/* figure out how many ranks, from strapped state of 48GB_Mode input */
  	pci_read_config_dword(pdev, I5100_MS, &dw);
  	ranksperch = !!(dw & (1 << 8)) * 2 + 4;
178d5a742   Arthur Jones   edac: i5100 fix u...
978
979
980
981
  	/* enable error reporting... */
  	pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw);
  	dw &= ~I5100_FERR_NF_MEM_ANY_MASK;
  	pci_write_config_dword(pdev, I5100_EMASK_MEM, dw);
8f421c595   Arthur Jones   edac: i5100 new i...
982
983
984
  	/* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */
  	ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
  				    PCI_DEVICE_ID_INTEL_5100_21, 0);
b238e5772   Arthur Jones   edac: i5100: cleanup
985
986
987
988
  	if (!ch0mm) {
  		ret = -ENODEV;
  		goto bail_pdev;
  	}
8f421c595   Arthur Jones   edac: i5100 new i...
989
990
991
992
993
994
995
996
997
998
999
1000
  
  	rc = pci_enable_device(ch0mm);
  	if (rc < 0) {
  		ret = rc;
  		goto bail_ch0;
  	}
  
  	/* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */
  	ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
  				    PCI_DEVICE_ID_INTEL_5100_22, 0);
  	if (!ch1mm) {
  		ret = -ENODEV;
b238e5772   Arthur Jones   edac: i5100: cleanup
1001
  		goto bail_disable_ch0;
8f421c595   Arthur Jones   edac: i5100 new i...
1002
1003
1004
1005
1006
1007
1008
  	}
  
  	rc = pci_enable_device(ch1mm);
  	if (rc < 0) {
  		ret = rc;
  		goto bail_ch1;
  	}
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
1009
1010
1011
1012
1013
1014
  	layers[0].type = EDAC_MC_LAYER_CHANNEL;
  	layers[0].size = 2;
  	layers[0].is_virt_csrow = false;
  	layers[1].type = EDAC_MC_LAYER_SLOT;
  	layers[1].size = ranksperch;
  	layers[1].is_virt_csrow = true;
ca0907b9e   Mauro Carvalho Chehab   edac: Remove the ...
1015
  	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
1016
  			    sizeof(*priv));
8f421c595   Arthur Jones   edac: i5100 new i...
1017
1018
  	if (!mci) {
  		ret = -ENOMEM;
b238e5772   Arthur Jones   edac: i5100: cleanup
1019
  		goto bail_disable_ch1;
8f421c595   Arthur Jones   edac: i5100 new i...
1020
  	}
52608ba20   Niklas Söderlund   i5100_edac: probe...
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
  
  	/* device 19, func 0, Error injection */
  	einj = pci_get_device_func(PCI_VENDOR_ID_INTEL,
  				    PCI_DEVICE_ID_INTEL_5100_19, 0);
  	if (!einj) {
  		ret = -ENODEV;
  		goto bail_einj;
  	}
  
  	rc = pci_enable_device(einj);
  	if (rc < 0) {
  		ret = rc;
  		goto bail_disable_einj;
  	}
fd687502d   Mauro Carvalho Chehab   edac: Rename the ...
1035
  	mci->pdev = &pdev->dev;
8f421c595   Arthur Jones   edac: i5100 new i...
1036
1037
  
  	priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
1038
  	priv->ranksperchan = ranksperch;
8f421c595   Arthur Jones   edac: i5100 new i...
1039
1040
1041
  	priv->mc = pdev;
  	priv->ch0mm = ch0mm;
  	priv->ch1mm = ch1mm;
52608ba20   Niklas Söderlund   i5100_edac: probe...
1042
  	priv->einj = einj;
8f421c595   Arthur Jones   edac: i5100 new i...
1043

295439f2a   Nils Carlson   edac: i5100 add s...
1044
1045
1046
1047
1048
1049
1050
1051
1052
  	INIT_DELAYED_WORK(&(priv->i5100_scrubbing), i5100_refresh_scrubbing);
  
  	/* If scrubbing was already enabled by the bios, start maintaining it */
  	pci_read_config_dword(pdev, I5100_MC, &dw);
  	if (i5100_mc_scrben(dw)) {
  		priv->scrub_enable = 1;
  		schedule_delayed_work(&(priv->i5100_scrubbing),
  				      I5100_SCRUB_REFRESH_RATE);
  	}
8f421c595   Arthur Jones   edac: i5100 new i...
1053
1054
1055
1056
1057
1058
1059
  	i5100_init_dimm_layout(pdev, mci);
  	i5100_init_interleaving(pdev, mci);
  
  	mci->mtype_cap = MEM_FLAG_FB_DDR2;
  	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
  	mci->edac_cap = EDAC_FLAG_SECDED;
  	mci->mod_name = "i5100_edac.c";
8f421c595   Arthur Jones   edac: i5100 new i...
1060
1061
  	mci->ctl_name = "i5100";
  	mci->dev_name = pci_name(pdev);
b238e5772   Arthur Jones   edac: i5100: cleanup
1062
  	mci->ctl_page_to_phys = NULL;
8f421c595   Arthur Jones   edac: i5100 new i...
1063
1064
  
  	mci->edac_check = i5100_check_error;
295439f2a   Nils Carlson   edac: i5100 add s...
1065
1066
  	mci->set_sdram_scrub_rate = i5100_set_scrub_rate;
  	mci->get_sdram_scrub_rate = i5100_get_scrub_rate;
8f421c595   Arthur Jones   edac: i5100 new i...
1067

53ceafd6a   Niklas Söderlund   i5100_edac: add f...
1068
1069
1070
1071
1072
1073
  	priv->inject_channel = 0;
  	priv->inject_hlinesel = 0;
  	priv->inject_deviceptr1 = 0;
  	priv->inject_deviceptr2 = 0;
  	priv->inject_eccmask1 = 0;
  	priv->inject_eccmask2 = 0;
8f421c595   Arthur Jones   edac: i5100 new i...
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
  	i5100_init_csrows(mci);
  
  	/* this strange construction seems to be in every driver, dunno why */
  	switch (edac_op_state) {
  	case EDAC_OPSTATE_POLL:
  	case EDAC_OPSTATE_NMI:
  		break;
  	default:
  		edac_op_state = EDAC_OPSTATE_POLL;
  		break;
  	}
  
  	if (edac_mc_add_mc(mci)) {
  		ret = -ENODEV;
295439f2a   Nils Carlson   edac: i5100 add s...
1088
  		goto bail_scrub;
8f421c595   Arthur Jones   edac: i5100 new i...
1089
  	}
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
1090
  	i5100_setup_debugfs(mci);
b238e5772   Arthur Jones   edac: i5100: cleanup
1091
  	return ret;
8f421c595   Arthur Jones   edac: i5100 new i...
1092

295439f2a   Nils Carlson   edac: i5100 add s...
1093
1094
1095
  bail_scrub:
  	priv->scrub_enable = 0;
  	cancel_delayed_work_sync(&(priv->i5100_scrubbing));
8f421c595   Arthur Jones   edac: i5100 new i...
1096
  	edac_mc_free(mci);
52608ba20   Niklas Söderlund   i5100_edac: probe...
1097
1098
1099
1100
1101
  bail_disable_einj:
  	pci_disable_device(einj);
  
  bail_einj:
  	pci_dev_put(einj);
b238e5772   Arthur Jones   edac: i5100: cleanup
1102
1103
  bail_disable_ch1:
  	pci_disable_device(ch1mm);
8f421c595   Arthur Jones   edac: i5100 new i...
1104
1105
  bail_ch1:
  	pci_dev_put(ch1mm);
b238e5772   Arthur Jones   edac: i5100: cleanup
1106
1107
  bail_disable_ch0:
  	pci_disable_device(ch0mm);
8f421c595   Arthur Jones   edac: i5100 new i...
1108
1109
  bail_ch0:
  	pci_dev_put(ch0mm);
b238e5772   Arthur Jones   edac: i5100: cleanup
1110
1111
  bail_pdev:
  	pci_disable_device(pdev);
8f421c595   Arthur Jones   edac: i5100 new i...
1112
1113
1114
  bail:
  	return ret;
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
1115
  static void i5100_remove_one(struct pci_dev *pdev)
8f421c595   Arthur Jones   edac: i5100 new i...
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
  {
  	struct mem_ctl_info *mci;
  	struct i5100_priv *priv;
  
  	mci = edac_mc_del_mc(&pdev->dev);
  
  	if (!mci)
  		return;
  
  	priv = mci->pvt_info;
295439f2a   Nils Carlson   edac: i5100 add s...
1126

52019e406   Borislav Petkov   EDAC, i5100: Conv...
1127
  	edac_debugfs_remove_recursive(priv->debugfs);
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
1128

295439f2a   Nils Carlson   edac: i5100 add s...
1129
1130
  	priv->scrub_enable = 0;
  	cancel_delayed_work_sync(&(priv->i5100_scrubbing));
b238e5772   Arthur Jones   edac: i5100: cleanup
1131
1132
1133
  	pci_disable_device(pdev);
  	pci_disable_device(priv->ch0mm);
  	pci_disable_device(priv->ch1mm);
52608ba20   Niklas Söderlund   i5100_edac: probe...
1134
  	pci_disable_device(priv->einj);
8f421c595   Arthur Jones   edac: i5100 new i...
1135
1136
  	pci_dev_put(priv->ch0mm);
  	pci_dev_put(priv->ch1mm);
52608ba20   Niklas Söderlund   i5100_edac: probe...
1137
  	pci_dev_put(priv->einj);
8f421c595   Arthur Jones   edac: i5100 new i...
1138
1139
1140
  
  	edac_mc_free(mci);
  }
ba935f409   Jingoo Han   EDAC: Remove DEFI...
1141
  static const struct pci_device_id i5100_pci_tbl[] = {
8f421c595   Arthur Jones   edac: i5100 new i...
1142
1143
1144
1145
1146
1147
1148
1149
1150
  	/* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */
  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) },
  	{ 0, }
  };
  MODULE_DEVICE_TABLE(pci, i5100_pci_tbl);
  
  static struct pci_driver i5100_driver = {
  	.name = KBUILD_BASENAME,
  	.probe = i5100_init_one,
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
1151
  	.remove = i5100_remove_one,
8f421c595   Arthur Jones   edac: i5100 new i...
1152
1153
1154
1155
1156
1157
  	.id_table = i5100_pci_tbl,
  };
  
  static int __init i5100_init(void)
  {
  	int pci_rc;
52019e406   Borislav Petkov   EDAC, i5100: Conv...
1158
  	i5100_debugfs = edac_debugfs_create_dir_at("i5100_edac", NULL);
8f421c595   Arthur Jones   edac: i5100 new i...
1159

9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
1160
  	pci_rc = pci_register_driver(&i5100_driver);
8f421c595   Arthur Jones   edac: i5100 new i...
1161
1162
1163
1164
1165
  	return (pci_rc < 0) ? pci_rc : 0;
  }
  
  static void __exit i5100_exit(void)
  {
52019e406   Borislav Petkov   EDAC, i5100: Conv...
1166
  	edac_debugfs_remove(i5100_debugfs);
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
1167

8f421c595   Arthur Jones   edac: i5100 new i...
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
  	pci_unregister_driver(&i5100_driver);
  }
  
  module_init(i5100_init);
  module_exit(i5100_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR
      ("Arthur Jones <ajones@riverbed.com>");
  MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers");