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
32
  
  #include "edac_core.h"
b238e5772   Arthur Jones   edac: i5100: cleanup
33
  /* register addresses */
8f421c595   Arthur Jones   edac: i5100 new i...
34
35
  
  /* device 16, func 1 */
43920a598   Arthur Jones   edac: i5100 fix e...
36
  #define I5100_MC		0x40	/* Memory Control Register */
295439f2a   Nils Carlson   edac: i5100 add s...
37
38
  #define 	I5100_MC_SCRBEN_MASK	(1 << 7)
  #define 	I5100_MC_SCRBDONE_MASK	(1 << 4)
8f421c595   Arthur Jones   edac: i5100 new i...
39
40
  #define I5100_MS		0x44	/* Memory Status Register */
  #define I5100_SPDDATA		0x48	/* Serial Presence Detect Status Reg */
8f421c595   Arthur Jones   edac: i5100 new i...
41
  #define I5100_SPDCMD		0x4c	/* Serial Presence Detect Command Reg */
8f421c595   Arthur Jones   edac: i5100 new i...
42
  #define I5100_TOLM		0x6c	/* Top of Low Memory */
8f421c595   Arthur Jones   edac: i5100 new i...
43
44
45
46
  #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...
47
  #define I5100_FERR_NF_MEM	0xa0	/* MC First Non Fatal Errors */
8f421c595   Arthur Jones   edac: i5100 new i...
48
49
50
  #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...
51
52
53
54
55
56
  #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...
57
  #define		I5100_FERR_NF_MEM_M1ERR_MASK	(1 << 1)
8f421c595   Arthur Jones   edac: i5100 new i...
58
59
60
  #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...
61
62
63
64
65
66
67
68
  			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...
69
  #define	I5100_NERR_NF_MEM	0xa4	/* MC Next Non-Fatal Errors */
178d5a742   Arthur Jones   edac: i5100 fix u...
70
  #define I5100_EMASK_MEM		0xa8	/* MC Error Mask Register */
53ceafd6a   Niklas Söderlund   i5100_edac: add f...
71
72
73
74
75
76
77
78
  #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...
79
80
81
82
  
  /* 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...
83
  #define	I5100_VALIDLOG	0x18c	/* Valid Log Markers */
8f421c595   Arthur Jones   edac: i5100 new i...
84
  #define	I5100_NRECMEMA	0x190	/* Non-Recoverable Memory Error Log Reg A */
8f421c595   Arthur Jones   edac: i5100 new i...
85
  #define	I5100_NRECMEMB	0x194	/* Non-Recoverable Memory Error Log Reg B */
8f421c595   Arthur Jones   edac: i5100 new i...
86
  #define	I5100_REDMEMA	0x198	/* Recoverable Memory Data Error Log Reg A */
8f421c595   Arthur Jones   edac: i5100 new i...
87
  #define	I5100_REDMEMB	0x19c	/* Recoverable Memory Data Error Log Reg B */
8f421c595   Arthur Jones   edac: i5100 new i...
88
  #define	I5100_RECMEMA	0x1a0	/* Recoverable Memory Error Log Reg A */
8f421c595   Arthur Jones   edac: i5100 new i...
89
  #define	I5100_RECMEMB	0x1a4	/* Recoverable Memory Error Log Reg B */
b238e5772   Arthur Jones   edac: i5100: cleanup
90
91
92
  #define I5100_MTR_4	0x1b0	/* Memory Technology Registers 4,5 */
  
  /* bit field accessors */
295439f2a   Nils Carlson   edac: i5100 add s...
93
94
95
96
  static inline u32 i5100_mc_scrben(u32 mc)
  {
  	return mc >> 7 & 1;
  }
b238e5772   Arthur Jones   edac: i5100: cleanup
97
98
99
100
  static inline u32 i5100_mc_errdeten(u32 mc)
  {
  	return mc >> 5 & 1;
  }
295439f2a   Nils Carlson   edac: i5100 add s...
101
102
103
104
  static inline u32 i5100_mc_scrbdone(u32 mc)
  {
  	return mc >> 4 & 1;
  }
b238e5772   Arthur Jones   edac: i5100: cleanup
105
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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
  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);
  }
  
  static inline u32 i5100_recmema_dm_buf_id(u32 a)
  {
  	return i5100_nrecmema_dm_buf_id(a);
  }
  
  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...
291
292
  
  /* some generic limits */
b18dfd05f   Nils Carlson   edac: i5100 clean...
293
294
  #define I5100_MAX_RANKS_PER_CHAN	6
  #define I5100_CHANNELS			    2
8f421c595   Arthur Jones   edac: i5100 new i...
295
296
  #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...
297
  #define I5100_MAX_DIMM_SLOTS_PER_CHAN	4
8f421c595   Arthur Jones   edac: i5100 new i...
298
299
  #define I5100_MAX_RANK_INTERLEAVE	4
  #define I5100_MAX_DMIRS			5
295439f2a   Nils Carlson   edac: i5100 add s...
300
  #define I5100_SCRUB_REFRESH_RATE	(5 * 60 * HZ)
8f421c595   Arthur Jones   edac: i5100 new i...
301
302
303
  
  struct i5100_priv {
  	/* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
b18dfd05f   Nils Carlson   edac: i5100 clean...
304
  	int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN];
8f421c595   Arthur Jones   edac: i5100 new i...
305
306
307
308
  
  	/*
  	 * 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...
309
310
  	 * 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...
311
312
313
  	 * map...
  	 *
  	 */
b18dfd05f   Nils Carlson   edac: i5100 clean...
314
  	int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM];
8f421c595   Arthur Jones   edac: i5100 new i...
315
316
317
318
319
  
  	/* memory interleave range */
  	struct {
  		u64	 limit;
  		unsigned way[2];
b18dfd05f   Nils Carlson   edac: i5100 clean...
320
  	} mir[I5100_CHANNELS];
8f421c595   Arthur Jones   edac: i5100 new i...
321
322
  
  	/* adjusted memory interleave range register */
b18dfd05f   Nils Carlson   edac: i5100 clean...
323
  	unsigned amir[I5100_CHANNELS];
8f421c595   Arthur Jones   edac: i5100 new i...
324
325
326
327
328
  
  	/* dimm interleave range */
  	struct {
  		unsigned rank[I5100_MAX_RANK_INTERLEAVE];
  		u64	 limit;
b18dfd05f   Nils Carlson   edac: i5100 clean...
329
  	} dmir[I5100_CHANNELS][I5100_MAX_DMIRS];
8f421c595   Arthur Jones   edac: i5100 new i...
330
331
332
333
334
335
336
337
338
  
  	/* 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...
339
  	} mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN];
8f421c595   Arthur Jones   edac: i5100 new i...
340
341
  
  	u64 tolm;		/* top of low memory in bytes */
b18dfd05f   Nils Carlson   edac: i5100 clean...
342
  	unsigned ranksperchan;	/* number of ranks per channel */
8f421c595   Arthur Jones   edac: i5100 new i...
343
344
  
  	struct pci_dev *mc;	/* device 16 func 1 */
52608ba20   Niklas Söderlund   i5100_edac: probe...
345
  	struct pci_dev *einj;	/* device 19 func 0 */
8f421c595   Arthur Jones   edac: i5100 new i...
346
347
  	struct pci_dev *ch0mm;	/* device 21 func 0 */
  	struct pci_dev *ch1mm;	/* device 22 func 0 */
295439f2a   Nils Carlson   edac: i5100 add s...
348
349
350
  
  	struct delayed_work i5100_scrubbing;
  	int scrub_enable;
53ceafd6a   Niklas Söderlund   i5100_edac: add f...
351
352
353
354
355
356
357
358
  
  	/* 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...
359
360
  
  	struct dentry *debugfs;
8f421c595   Arthur Jones   edac: i5100 new i...
361
  };
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
362
  static struct dentry *i5100_debugfs;
b18dfd05f   Nils Carlson   edac: i5100 clean...
363
  /* map a rank/chan to a slot number on the mainboard */
8f421c595   Arthur Jones   edac: i5100 new i...
364
  static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
b18dfd05f   Nils Carlson   edac: i5100 clean...
365
  			      int chan, int rank)
8f421c595   Arthur Jones   edac: i5100 new i...
366
367
368
  {
  	const struct i5100_priv *priv = mci->pvt_info;
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
369
  	for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
370
  		int j;
b18dfd05f   Nils Carlson   edac: i5100 clean...
371
  		const int numrank = priv->dimm_numrank[chan][i];
8f421c595   Arthur Jones   edac: i5100 new i...
372
373
374
  
  		for (j = 0; j < numrank; j++)
  			if (priv->dimm_csmap[i][j] == rank)
b18dfd05f   Nils Carlson   edac: i5100 clean...
375
  				return i * 2 + chan;
8f421c595   Arthur Jones   edac: i5100 new i...
376
377
378
379
  	}
  
  	return -1;
  }
8f421c595   Arthur Jones   edac: i5100 new i...
380
381
  static const char *i5100_err_msg(unsigned err)
  {
b238e5772   Arthur Jones   edac: i5100: cleanup
382
  	static const char *merrs[] = {
8f421c595   Arthur Jones   edac: i5100 new i...
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
409
410
411
412
413
  		"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...
414
  /* convert csrow index into a rank (per channel -- 0..5) */
8f421c595   Arthur Jones   edac: i5100 new i...
415
416
417
  static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow)
  {
  	const struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
418
  	return csrow % priv->ranksperchan;
8f421c595   Arthur Jones   edac: i5100 new i...
419
  }
b18dfd05f   Nils Carlson   edac: i5100 clean...
420
421
  /* convert csrow index into a channel (0..1) */
  static int i5100_csrow_to_chan(const struct mem_ctl_info *mci, int csrow)
8f421c595   Arthur Jones   edac: i5100 new i...
422
423
  {
  	const struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
424
  	return csrow / priv->ranksperchan;
8f421c595   Arthur Jones   edac: i5100 new i...
425
  }
8f421c595   Arthur Jones   edac: i5100 new i...
426
  static void i5100_handle_ce(struct mem_ctl_info *mci,
b18dfd05f   Nils Carlson   edac: i5100 clean...
427
  			    int chan,
8f421c595   Arthur Jones   edac: i5100 new i...
428
429
430
431
432
433
434
  			    unsigned bank,
  			    unsigned rank,
  			    unsigned long syndrome,
  			    unsigned cas,
  			    unsigned ras,
  			    const char *msg)
  {
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
435
  	char detail[80];
084a4fcce   Mauro Carvalho Chehab   edac: move dimm p...
436

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

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

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

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

b238e5772   Arthur Jones   edac: i5100: cleanup
547
548
549
  		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...
550
551
  
  		pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
552
  	}
df95e42e1   Niklas Söderlund   edac: i5100 ack e...
553
  	pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
8f421c595   Arthur Jones   edac: i5100 new i...
554
  }
295439f2a   Nils Carlson   edac: i5100 add s...
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
  /* 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)
  {
  	struct delayed_work *i5100_scrubbing = container_of(work,
  							    struct delayed_work,
  							    work);
  	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...
590
  static int i5100_set_scrub_rate(struct mem_ctl_info *mci, u32 bandwidth)
295439f2a   Nils Carlson   edac: i5100 add s...
591
592
593
594
595
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	u32 dw;
  
  	pci_read_config_dword(priv->mc, I5100_MC, &dw);
eba042a81   Borislav Petkov   edac, mc: Improve...
596
  	if (bandwidth) {
295439f2a   Nils Carlson   edac: i5100 add s...
597
598
599
600
601
602
603
604
605
606
607
608
  		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...
609
  	bandwidth = 5900000 * i5100_mc_scrben(dw);
295439f2a   Nils Carlson   edac: i5100 add s...
610

390944439   Borislav Petkov   EDAC: Fixup scrub...
611
  	return bandwidth;
295439f2a   Nils Carlson   edac: i5100 add s...
612
  }
390944439   Borislav Petkov   EDAC: Fixup scrub...
613
  static int i5100_get_scrub_rate(struct mem_ctl_info *mci)
295439f2a   Nils Carlson   edac: i5100 add s...
614
615
616
617
618
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	u32 dw;
  
  	pci_read_config_dword(priv->mc, I5100_MC, &dw);
390944439   Borislav Petkov   EDAC: Fixup scrub...
619
  	return 5900000 * i5100_mc_scrben(dw);
295439f2a   Nils Carlson   edac: i5100 add s...
620
  }
8f421c595   Arthur Jones   edac: i5100 new i...
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
  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;
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
639
  static unsigned long i5100_npages(struct mem_ctl_info *mci, int csrow)
8f421c595   Arthur Jones   edac: i5100 new i...
640
641
  {
  	struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
642
643
  	const unsigned chan_rank = i5100_csrow_to_rank(mci, csrow);
  	const unsigned chan = i5100_csrow_to_chan(mci, csrow);
8f421c595   Arthur Jones   edac: i5100 new i...
644
645
646
  	unsigned addr_lines;
  
  	/* dimm present? */
b18dfd05f   Nils Carlson   edac: i5100 clean...
647
  	if (!priv->mtr[chan][chan_rank].present)
8f421c595   Arthur Jones   edac: i5100 new i...
648
649
650
651
  		return 0ULL;
  
  	addr_lines =
  		I5100_DIMM_ADDR_LINES +
b18dfd05f   Nils Carlson   edac: i5100 clean...
652
653
654
  		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...
655
656
657
658
  
  	return (unsigned long)
  		((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
659
  static void i5100_init_mtr(struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
660
661
662
663
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
664
  	for (i = 0; i < I5100_CHANNELS; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
665
666
  		int j;
  		struct pci_dev *pdev = mms[i];
b18dfd05f   Nils Carlson   edac: i5100 clean...
667
  		for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) {
8f421c595   Arthur Jones   edac: i5100 new i...
668
669
670
671
672
673
  			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
674
675
676
677
678
679
  			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...
680
681
682
683
684
685
686
687
688
689
690
691
692
  		}
  	}
  }
  
  /*
   * 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...
693
694
695
  	unsigned long et;
  
  	pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
696
  	if (i5100_spddata_busy(w))
8f421c595   Arthur Jones   edac: i5100 new i...
697
  		return -1;
b238e5772   Arthur Jones   edac: i5100: cleanup
698
699
700
  	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...
701
702
703
704
705
706
  
  	/* 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
707
  		if (!i5100_spddata_busy(w))
8f421c595   Arthur Jones   edac: i5100 new i...
708
709
710
  			break;
  		udelay(100);
  	}
b238e5772   Arthur Jones   edac: i5100: cleanup
711
  	if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w))
8f421c595   Arthur Jones   edac: i5100 new i...
712
  		return -1;
b238e5772   Arthur Jones   edac: i5100: cleanup
713
  	*byte = i5100_spddata_data(w);
8f421c595   Arthur Jones   edac: i5100 new i...
714
715
716
717
718
719
720
721
  
  	return 0;
  }
  
  /*
   * fill dimm chip select map
   *
   * FIXME:
8f421c595   Arthur Jones   edac: i5100 new i...
722
723
724
   *   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...
725
  static void i5100_init_dimm_csmap(struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
726
727
728
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
729
  	for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
730
731
732
733
734
735
736
  		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...
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
  	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...
752
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
753
754
  static void i5100_init_dimm_layout(struct pci_dev *pdev,
  				   struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
755
756
757
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
758
  	for (i = 0; i < I5100_CHANNELS; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
759
  		int j;
b18dfd05f   Nils Carlson   edac: i5100 clean...
760
  		for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) {
8f421c595   Arthur Jones   edac: i5100 new i...
761
762
763
764
765
766
767
768
769
770
771
  			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...
772
773
  static void i5100_init_interleaving(struct pci_dev *pdev,
  				    struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
774
775
776
777
778
779
780
781
  {
  	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
782
  	priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024;
8f421c595   Arthur Jones   edac: i5100 new i...
783
784
  
  	pci_read_config_word(pdev, I5100_MIR0, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
785
786
787
  	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...
788
789
  
  	pci_read_config_word(pdev, I5100_MIR1, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
790
791
792
  	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...
793
794
795
796
797
  
  	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...
798
  	for (i = 0; i < I5100_CHANNELS; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
799
800
801
802
803
804
805
806
  		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
807
  				(u64) i5100_dmir_limit(dw) << 28;
8f421c595   Arthur Jones   edac: i5100 new i...
808
809
  			for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++)
  				priv->dmir[i][j].rank[k] =
b238e5772   Arthur Jones   edac: i5100: cleanup
810
  					i5100_dmir_rank(dw, k);
8f421c595   Arthur Jones   edac: i5100 new i...
811
812
813
814
815
  		}
  	}
  
  	i5100_init_mtr(mci);
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
816
  static void i5100_init_csrows(struct mem_ctl_info *mci)
8f421c595   Arthur Jones   edac: i5100 new i...
817
818
  {
  	int i;
8f421c595   Arthur Jones   edac: i5100 new i...
819
  	struct i5100_priv *priv = mci->pvt_info;
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
820
821
  	for (i = 0; i < mci->tot_dimms; i++) {
  		struct dimm_info *dimm;
8f421c595   Arthur Jones   edac: i5100 new i...
822
  		const unsigned long npages = i5100_npages(mci, i);
b18dfd05f   Nils Carlson   edac: i5100 clean...
823
  		const unsigned chan = i5100_csrow_to_chan(mci, i);
8f421c595   Arthur Jones   edac: i5100 new i...
824
825
826
827
  		const unsigned rank = i5100_csrow_to_rank(mci, i);
  
  		if (!npages)
  			continue;
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
828
829
  		dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
  			       chan, rank, 0);
084a4fcce   Mauro Carvalho Chehab   edac: move dimm p...
830

a895bf8b1   Mauro Carvalho Chehab   edac: move nr_pag...
831
  		dimm->nr_pages = npages;
9d6c7cbe3   Dan Carpenter   i5100_edac: Remov...
832
833
834
835
836
837
838
  		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...
839

956b9ba15   Joe Perches   edac: Convert deb...
840
841
842
  		edac_dbg(2, "dimm channel %d, rank %d, size %ld
  ",
  			 chan, rank, (long)PAGES_TO_MiB(npages));
8f421c595   Arthur Jones   edac: i5100 new i...
843
844
  	}
  }
53ceafd6a   Niklas Söderlund   i5100_edac: add f...
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
903
904
905
906
907
  /****************************************************************************
   *                       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...
908
909
910
911
912
913
914
915
916
917
918
  #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...
919
  static const struct file_operations i5100_inject_enable_fops = {
b0769891b   Wei Yongjun   i5100_edac: conve...
920
  	.open = simple_open,
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
921
922
923
924
925
926
927
928
929
930
  	.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;
88d84ac97   Borislav Petkov   EDAC: Fix lockdep...
931
  	priv->debugfs = debugfs_create_dir(mci->bus->name, i5100_debugfs);
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
  
  	if (!priv->debugfs)
  		return -ENOMEM;
  
  	debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs,
  			&priv->inject_channel);
  	debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs,
  			&priv->inject_hlinesel);
  	debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs,
  			&priv->inject_deviceptr1);
  	debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs,
  			&priv->inject_deviceptr2);
  	debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs,
  			&priv->inject_eccmask1);
  	debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs,
  			&priv->inject_eccmask2);
  	debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs,
  			&mci->dev, &i5100_inject_enable_fops);
  
  	return 0;
  
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
954
  static int i5100_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
8f421c595   Arthur Jones   edac: i5100 new i...
955
956
957
  {
  	int rc;
  	struct mem_ctl_info *mci;
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
958
  	struct edac_mc_layer layers[2];
8f421c595   Arthur Jones   edac: i5100 new i...
959
  	struct i5100_priv *priv;
52608ba20   Niklas Söderlund   i5100_edac: probe...
960
  	struct pci_dev *ch0mm, *ch1mm, *einj;
8f421c595   Arthur Jones   edac: i5100 new i...
961
962
963
964
965
966
967
968
969
970
971
972
  	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...
973
974
  	/* ECC enabled? */
  	pci_read_config_dword(pdev, I5100_MC, &dw);
b238e5772   Arthur Jones   edac: i5100: cleanup
975
  	if (!i5100_mc_errdeten(dw)) {
43920a598   Arthur Jones   edac: i5100 fix e...
976
977
978
  		printk(KERN_INFO "i5100_edac: ECC not enabled.
  ");
  		ret = -ENODEV;
b238e5772   Arthur Jones   edac: i5100: cleanup
979
  		goto bail_pdev;
43920a598   Arthur Jones   edac: i5100 fix e...
980
  	}
8f421c595   Arthur Jones   edac: i5100 new i...
981
982
983
  	/* 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...
984
985
986
987
  	/* 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...
988
989
990
  	/* 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
991
992
993
994
  	if (!ch0mm) {
  		ret = -ENODEV;
  		goto bail_pdev;
  	}
8f421c595   Arthur Jones   edac: i5100 new i...
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
  
  	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
1007
  		goto bail_disable_ch0;
8f421c595   Arthur Jones   edac: i5100 new i...
1008
1009
1010
1011
1012
1013
1014
  	}
  
  	rc = pci_enable_device(ch1mm);
  	if (rc < 0) {
  		ret = rc;
  		goto bail_ch1;
  	}
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
1015
1016
1017
1018
1019
1020
  	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 ...
1021
  	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
d1afaa0a6   Mauro Carvalho Chehab   i5100_edac: conve...
1022
  			    sizeof(*priv));
8f421c595   Arthur Jones   edac: i5100 new i...
1023
1024
  	if (!mci) {
  		ret = -ENOMEM;
b238e5772   Arthur Jones   edac: i5100: cleanup
1025
  		goto bail_disable_ch1;
8f421c595   Arthur Jones   edac: i5100 new i...
1026
  	}
52608ba20   Niklas Söderlund   i5100_edac: probe...
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
  
  	/* 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 ...
1041
  	mci->pdev = &pdev->dev;
8f421c595   Arthur Jones   edac: i5100 new i...
1042
1043
  
  	priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
1044
  	priv->ranksperchan = ranksperch;
8f421c595   Arthur Jones   edac: i5100 new i...
1045
1046
1047
  	priv->mc = pdev;
  	priv->ch0mm = ch0mm;
  	priv->ch1mm = ch1mm;
52608ba20   Niklas Söderlund   i5100_edac: probe...
1048
  	priv->einj = einj;
8f421c595   Arthur Jones   edac: i5100 new i...
1049

295439f2a   Nils Carlson   edac: i5100 add s...
1050
1051
1052
1053
1054
1055
1056
1057
1058
  	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...
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
  	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";
  	mci->mod_ver = "not versioned";
  	mci->ctl_name = "i5100";
  	mci->dev_name = pci_name(pdev);
b238e5772   Arthur Jones   edac: i5100: cleanup
1069
  	mci->ctl_page_to_phys = NULL;
8f421c595   Arthur Jones   edac: i5100 new i...
1070
1071
  
  	mci->edac_check = i5100_check_error;
295439f2a   Nils Carlson   edac: i5100 add s...
1072
1073
  	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...
1074

53ceafd6a   Niklas Söderlund   i5100_edac: add f...
1075
1076
1077
1078
1079
1080
  	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...
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
  	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...
1095
  		goto bail_scrub;
8f421c595   Arthur Jones   edac: i5100 new i...
1096
  	}
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
1097
  	i5100_setup_debugfs(mci);
b238e5772   Arthur Jones   edac: i5100: cleanup
1098
  	return ret;
8f421c595   Arthur Jones   edac: i5100 new i...
1099

295439f2a   Nils Carlson   edac: i5100 add s...
1100
1101
1102
  bail_scrub:
  	priv->scrub_enable = 0;
  	cancel_delayed_work_sync(&(priv->i5100_scrubbing));
8f421c595   Arthur Jones   edac: i5100 new i...
1103
  	edac_mc_free(mci);
52608ba20   Niklas Söderlund   i5100_edac: probe...
1104
1105
1106
1107
1108
  bail_disable_einj:
  	pci_disable_device(einj);
  
  bail_einj:
  	pci_dev_put(einj);
b238e5772   Arthur Jones   edac: i5100: cleanup
1109
1110
  bail_disable_ch1:
  	pci_disable_device(ch1mm);
8f421c595   Arthur Jones   edac: i5100 new i...
1111
1112
  bail_ch1:
  	pci_dev_put(ch1mm);
b238e5772   Arthur Jones   edac: i5100: cleanup
1113
1114
  bail_disable_ch0:
  	pci_disable_device(ch0mm);
8f421c595   Arthur Jones   edac: i5100 new i...
1115
1116
  bail_ch0:
  	pci_dev_put(ch0mm);
b238e5772   Arthur Jones   edac: i5100: cleanup
1117
1118
  bail_pdev:
  	pci_disable_device(pdev);
8f421c595   Arthur Jones   edac: i5100 new i...
1119
1120
1121
  bail:
  	return ret;
  }
9b3c6e85c   Greg Kroah-Hartman   Drivers: edac: re...
1122
  static void i5100_remove_one(struct pci_dev *pdev)
8f421c595   Arthur Jones   edac: i5100 new i...
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
  {
  	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...
1133

59b9796d1   Mauro Carvalho Chehab   i5100_edac: Remov...
1134
  	debugfs_remove_recursive(priv->debugfs);
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
1135

295439f2a   Nils Carlson   edac: i5100 add s...
1136
1137
  	priv->scrub_enable = 0;
  	cancel_delayed_work_sync(&(priv->i5100_scrubbing));
b238e5772   Arthur Jones   edac: i5100: cleanup
1138
1139
1140
  	pci_disable_device(pdev);
  	pci_disable_device(priv->ch0mm);
  	pci_disable_device(priv->ch1mm);
52608ba20   Niklas Söderlund   i5100_edac: probe...
1141
  	pci_disable_device(priv->einj);
8f421c595   Arthur Jones   edac: i5100 new i...
1142
1143
  	pci_dev_put(priv->ch0mm);
  	pci_dev_put(priv->ch1mm);
52608ba20   Niklas Söderlund   i5100_edac: probe...
1144
  	pci_dev_put(priv->einj);
8f421c595   Arthur Jones   edac: i5100 new i...
1145
1146
1147
  
  	edac_mc_free(mci);
  }
ba935f409   Jingoo Han   EDAC: Remove DEFI...
1148
  static const struct pci_device_id i5100_pci_tbl[] = {
8f421c595   Arthur Jones   edac: i5100 new i...
1149
1150
1151
1152
1153
1154
1155
1156
1157
  	/* 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...
1158
  	.remove = i5100_remove_one,
8f421c595   Arthur Jones   edac: i5100 new i...
1159
1160
1161
1162
1163
1164
  	.id_table = i5100_pci_tbl,
  };
  
  static int __init i5100_init(void)
  {
  	int pci_rc;
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
1165
  	i5100_debugfs = debugfs_create_dir("i5100_edac", NULL);
8f421c595   Arthur Jones   edac: i5100 new i...
1166

9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
1167
  	pci_rc = pci_register_driver(&i5100_driver);
8f421c595   Arthur Jones   edac: i5100 new i...
1168
1169
1170
1171
1172
  	return (pci_rc < 0) ? pci_rc : 0;
  }
  
  static void __exit i5100_exit(void)
  {
59b9796d1   Mauro Carvalho Chehab   i5100_edac: Remov...
1173
  	debugfs_remove(i5100_debugfs);
9cbc6d38f   Niklas Söderlund   i5100_edac: conne...
1174

8f421c595   Arthur Jones   edac: i5100 new i...
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
  	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");