Blame view

drivers/edac/i5100_edac.c 25.9 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.
8f421c595   Arthur Jones   edac: i5100 new i...
17
18
19
20
21
   */
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/pci.h>
  #include <linux/pci_ids.h>
8f421c595   Arthur Jones   edac: i5100 new i...
22
23
24
25
26
  #include <linux/edac.h>
  #include <linux/delay.h>
  #include <linux/mmzone.h>
  
  #include "edac_core.h"
b238e5772   Arthur Jones   edac: i5100: cleanup
27
  /* register addresses */
8f421c595   Arthur Jones   edac: i5100 new i...
28
29
  
  /* device 16, func 1 */
43920a598   Arthur Jones   edac: i5100 fix e...
30
  #define I5100_MC		0x40	/* Memory Control Register */
295439f2a   Nils Carlson   edac: i5100 add s...
31
32
  #define 	I5100_MC_SCRBEN_MASK	(1 << 7)
  #define 	I5100_MC_SCRBDONE_MASK	(1 << 4)
8f421c595   Arthur Jones   edac: i5100 new i...
33
34
  #define I5100_MS		0x44	/* Memory Status Register */
  #define I5100_SPDDATA		0x48	/* Serial Presence Detect Status Reg */
8f421c595   Arthur Jones   edac: i5100 new i...
35
  #define I5100_SPDCMD		0x4c	/* Serial Presence Detect Command Reg */
8f421c595   Arthur Jones   edac: i5100 new i...
36
  #define I5100_TOLM		0x6c	/* Top of Low Memory */
8f421c595   Arthur Jones   edac: i5100 new i...
37
38
39
40
  #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...
41
  #define I5100_FERR_NF_MEM	0xa0	/* MC First Non Fatal Errors */
8f421c595   Arthur Jones   edac: i5100 new i...
42
43
44
  #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...
45
46
47
48
49
50
51
  #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)
  #define		I5100_FERR_NF_MEM_M1ERR_MASK	1
8f421c595   Arthur Jones   edac: i5100 new i...
52
53
54
  #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...
55
56
57
58
59
60
61
62
  			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...
63
  #define	I5100_NERR_NF_MEM	0xa4	/* MC Next Non-Fatal Errors */
178d5a742   Arthur Jones   edac: i5100 fix u...
64
  #define I5100_EMASK_MEM		0xa8	/* MC Error Mask Register */
8f421c595   Arthur Jones   edac: i5100 new i...
65
66
67
68
  
  /* 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...
69
  #define	I5100_VALIDLOG	0x18c	/* Valid Log Markers */
8f421c595   Arthur Jones   edac: i5100 new i...
70
  #define	I5100_NRECMEMA	0x190	/* Non-Recoverable Memory Error Log Reg A */
8f421c595   Arthur Jones   edac: i5100 new i...
71
  #define	I5100_NRECMEMB	0x194	/* Non-Recoverable Memory Error Log Reg B */
8f421c595   Arthur Jones   edac: i5100 new i...
72
  #define	I5100_REDMEMA	0x198	/* Recoverable Memory Data Error Log Reg A */
8f421c595   Arthur Jones   edac: i5100 new i...
73
  #define	I5100_REDMEMB	0x19c	/* Recoverable Memory Data Error Log Reg B */
8f421c595   Arthur Jones   edac: i5100 new i...
74
  #define	I5100_RECMEMA	0x1a0	/* Recoverable Memory Error Log Reg A */
8f421c595   Arthur Jones   edac: i5100 new i...
75
  #define	I5100_RECMEMB	0x1a4	/* Recoverable Memory Error Log Reg B */
b238e5772   Arthur Jones   edac: i5100: cleanup
76
77
78
  #define I5100_MTR_4	0x1b0	/* Memory Technology Registers 4,5 */
  
  /* bit field accessors */
295439f2a   Nils Carlson   edac: i5100 add s...
79
80
81
82
  static inline u32 i5100_mc_scrben(u32 mc)
  {
  	return mc >> 7 & 1;
  }
b238e5772   Arthur Jones   edac: i5100: cleanup
83
84
85
86
  static inline u32 i5100_mc_errdeten(u32 mc)
  {
  	return mc >> 5 & 1;
  }
295439f2a   Nils Carlson   edac: i5100 add s...
87
88
89
90
  static inline u32 i5100_mc_scrbdone(u32 mc)
  {
  	return mc >> 4 & 1;
  }
b238e5772   Arthur Jones   edac: i5100: cleanup
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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
  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...
277
278
  
  /* some generic limits */
b18dfd05f   Nils Carlson   edac: i5100 clean...
279
280
  #define I5100_MAX_RANKS_PER_CHAN	6
  #define I5100_CHANNELS			    2
8f421c595   Arthur Jones   edac: i5100 new i...
281
282
  #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...
283
  #define I5100_MAX_DIMM_SLOTS_PER_CHAN	4
8f421c595   Arthur Jones   edac: i5100 new i...
284
285
  #define I5100_MAX_RANK_INTERLEAVE	4
  #define I5100_MAX_DMIRS			5
295439f2a   Nils Carlson   edac: i5100 add s...
286
  #define I5100_SCRUB_REFRESH_RATE	(5 * 60 * HZ)
8f421c595   Arthur Jones   edac: i5100 new i...
287
288
289
  
  struct i5100_priv {
  	/* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
b18dfd05f   Nils Carlson   edac: i5100 clean...
290
  	int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN];
8f421c595   Arthur Jones   edac: i5100 new i...
291
292
293
294
  
  	/*
  	 * 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...
295
296
  	 * 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...
297
298
299
  	 * map...
  	 *
  	 */
b18dfd05f   Nils Carlson   edac: i5100 clean...
300
  	int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM];
8f421c595   Arthur Jones   edac: i5100 new i...
301
302
303
304
305
  
  	/* memory interleave range */
  	struct {
  		u64	 limit;
  		unsigned way[2];
b18dfd05f   Nils Carlson   edac: i5100 clean...
306
  	} mir[I5100_CHANNELS];
8f421c595   Arthur Jones   edac: i5100 new i...
307
308
  
  	/* adjusted memory interleave range register */
b18dfd05f   Nils Carlson   edac: i5100 clean...
309
  	unsigned amir[I5100_CHANNELS];
8f421c595   Arthur Jones   edac: i5100 new i...
310
311
312
313
314
  
  	/* dimm interleave range */
  	struct {
  		unsigned rank[I5100_MAX_RANK_INTERLEAVE];
  		u64	 limit;
b18dfd05f   Nils Carlson   edac: i5100 clean...
315
  	} dmir[I5100_CHANNELS][I5100_MAX_DMIRS];
8f421c595   Arthur Jones   edac: i5100 new i...
316
317
318
319
320
321
322
323
324
  
  	/* 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...
325
  	} mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN];
8f421c595   Arthur Jones   edac: i5100 new i...
326
327
  
  	u64 tolm;		/* top of low memory in bytes */
b18dfd05f   Nils Carlson   edac: i5100 clean...
328
  	unsigned ranksperchan;	/* number of ranks per channel */
8f421c595   Arthur Jones   edac: i5100 new i...
329
330
331
332
  
  	struct pci_dev *mc;	/* device 16 func 1 */
  	struct pci_dev *ch0mm;	/* device 21 func 0 */
  	struct pci_dev *ch1mm;	/* device 22 func 0 */
295439f2a   Nils Carlson   edac: i5100 add s...
333
334
335
  
  	struct delayed_work i5100_scrubbing;
  	int scrub_enable;
8f421c595   Arthur Jones   edac: i5100 new i...
336
  };
b18dfd05f   Nils Carlson   edac: i5100 clean...
337
  /* map a rank/chan to a slot number on the mainboard */
8f421c595   Arthur Jones   edac: i5100 new i...
338
  static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
b18dfd05f   Nils Carlson   edac: i5100 clean...
339
  			      int chan, int rank)
8f421c595   Arthur Jones   edac: i5100 new i...
340
341
342
  {
  	const struct i5100_priv *priv = mci->pvt_info;
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
343
  	for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
344
  		int j;
b18dfd05f   Nils Carlson   edac: i5100 clean...
345
  		const int numrank = priv->dimm_numrank[chan][i];
8f421c595   Arthur Jones   edac: i5100 new i...
346
347
348
  
  		for (j = 0; j < numrank; j++)
  			if (priv->dimm_csmap[i][j] == rank)
b18dfd05f   Nils Carlson   edac: i5100 clean...
349
  				return i * 2 + chan;
8f421c595   Arthur Jones   edac: i5100 new i...
350
351
352
353
  	}
  
  	return -1;
  }
8f421c595   Arthur Jones   edac: i5100 new i...
354
355
  static const char *i5100_err_msg(unsigned err)
  {
b238e5772   Arthur Jones   edac: i5100: cleanup
356
  	static const char *merrs[] = {
8f421c595   Arthur Jones   edac: i5100 new i...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  		"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...
388
  /* convert csrow index into a rank (per channel -- 0..5) */
8f421c595   Arthur Jones   edac: i5100 new i...
389
390
391
  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...
392
  	return csrow % priv->ranksperchan;
8f421c595   Arthur Jones   edac: i5100 new i...
393
  }
b18dfd05f   Nils Carlson   edac: i5100 clean...
394
395
  /* 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...
396
397
  {
  	const struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
398
  	return csrow / priv->ranksperchan;
8f421c595   Arthur Jones   edac: i5100 new i...
399
400
401
  }
  
  static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci,
b18dfd05f   Nils Carlson   edac: i5100 clean...
402
  				    int chan, int rank)
8f421c595   Arthur Jones   edac: i5100 new i...
403
404
  {
  	const struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
405
  	return chan * priv->ranksperchan + rank;
8f421c595   Arthur Jones   edac: i5100 new i...
406
407
408
  }
  
  static void i5100_handle_ce(struct mem_ctl_info *mci,
b18dfd05f   Nils Carlson   edac: i5100 clean...
409
  			    int chan,
8f421c595   Arthur Jones   edac: i5100 new i...
410
411
412
413
414
415
416
  			    unsigned bank,
  			    unsigned rank,
  			    unsigned long syndrome,
  			    unsigned cas,
  			    unsigned ras,
  			    const char *msg)
  {
b18dfd05f   Nils Carlson   edac: i5100 clean...
417
  	const int csrow = i5100_rank_to_csrow(mci, chan, rank);
8f421c595   Arthur Jones   edac: i5100 new i...
418
419
  
  	printk(KERN_ERR
b18dfd05f   Nils Carlson   edac: i5100 clean...
420
  		"CE chan %d, bank %u, rank %u, syndrome 0x%lx, "
8f421c595   Arthur Jones   edac: i5100 new i...
421
422
  		"cas %u, ras %u, csrow %u, label \"%s\": %s
  ",
b18dfd05f   Nils Carlson   edac: i5100 clean...
423
  		chan, bank, rank, syndrome, cas, ras,
8f421c595   Arthur Jones   edac: i5100 new i...
424
425
426
427
428
429
430
431
  		csrow, mci->csrows[csrow].channels[0].label, msg);
  
  	mci->ce_count++;
  	mci->csrows[csrow].ce_count++;
  	mci->csrows[csrow].channels[0].ce_count++;
  }
  
  static void i5100_handle_ue(struct mem_ctl_info *mci,
b18dfd05f   Nils Carlson   edac: i5100 clean...
432
  			    int chan,
8f421c595   Arthur Jones   edac: i5100 new i...
433
434
435
436
437
438
439
  			    unsigned bank,
  			    unsigned rank,
  			    unsigned long syndrome,
  			    unsigned cas,
  			    unsigned ras,
  			    const char *msg)
  {
b18dfd05f   Nils Carlson   edac: i5100 clean...
440
  	const int csrow = i5100_rank_to_csrow(mci, chan, rank);
8f421c595   Arthur Jones   edac: i5100 new i...
441
442
  
  	printk(KERN_ERR
b18dfd05f   Nils Carlson   edac: i5100 clean...
443
  		"UE chan %d, bank %u, rank %u, syndrome 0x%lx, "
8f421c595   Arthur Jones   edac: i5100 new i...
444
445
  		"cas %u, ras %u, csrow %u, label \"%s\": %s
  ",
b18dfd05f   Nils Carlson   edac: i5100 clean...
446
  		chan, bank, rank, syndrome, cas, ras,
8f421c595   Arthur Jones   edac: i5100 new i...
447
448
449
450
451
  		csrow, mci->csrows[csrow].channels[0].label, msg);
  
  	mci->ue_count++;
  	mci->csrows[csrow].ue_count++;
  }
b18dfd05f   Nils Carlson   edac: i5100 clean...
452
  static void i5100_read_log(struct mem_ctl_info *mci, int chan,
8f421c595   Arthur Jones   edac: i5100 new i...
453
454
455
  			   u32 ferr, u32 nerr)
  {
  	struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
456
  	struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm;
8f421c595   Arthur Jones   edac: i5100 new i...
457
458
459
460
461
462
463
464
465
466
467
  	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
468
  	if (i5100_validlog_redmemvalid(dw)) {
8f421c595   Arthur Jones   edac: i5100 new i...
469
  		pci_read_config_dword(pdev, I5100_REDMEMA, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
470
  		syndrome = dw2;
8f421c595   Arthur Jones   edac: i5100 new i...
471
  		pci_read_config_dword(pdev, I5100_REDMEMB, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
472
  		ecc_loc = i5100_redmemb_ecc_locator(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
473
  	}
b238e5772   Arthur Jones   edac: i5100: cleanup
474
  	if (i5100_validlog_recmemvalid(dw)) {
8f421c595   Arthur Jones   edac: i5100 new i...
475
476
477
  		const char *msg;
  
  		pci_read_config_dword(pdev, I5100_RECMEMA, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
478
479
480
  		merr = i5100_recmema_merr(dw2);
  		bank = i5100_recmema_bank(dw2);
  		rank = i5100_recmema_rank(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
481
482
  
  		pci_read_config_dword(pdev, I5100_RECMEMB, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
483
484
  		cas = i5100_recmemb_cas(dw2);
  		ras = i5100_recmemb_ras(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
485
486
487
488
489
490
491
  
  		/* 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...
492
  		i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg);
8f421c595   Arthur Jones   edac: i5100 new i...
493
  	}
b238e5772   Arthur Jones   edac: i5100: cleanup
494
  	if (i5100_validlog_nrecmemvalid(dw)) {
8f421c595   Arthur Jones   edac: i5100 new i...
495
496
497
  		const char *msg;
  
  		pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
498
499
500
  		merr = i5100_nrecmema_merr(dw2);
  		bank = i5100_nrecmema_bank(dw2);
  		rank = i5100_nrecmema_rank(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
501
502
  
  		pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2);
b238e5772   Arthur Jones   edac: i5100: cleanup
503
504
  		cas = i5100_nrecmemb_cas(dw2);
  		ras = i5100_nrecmemb_ras(dw2);
8f421c595   Arthur Jones   edac: i5100 new i...
505
506
507
508
509
510
511
  
  		/* 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...
512
  		i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg);
8f421c595   Arthur Jones   edac: i5100 new i...
513
514
515
516
517
518
519
520
521
522
523
524
  	}
  
  	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;
  	u32 dw;
  
  
  	pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw);
b238e5772   Arthur Jones   edac: i5100: cleanup
525
  	if (i5100_ferr_nf_mem_any(dw)) {
8f421c595   Arthur Jones   edac: i5100 new i...
526
527
528
529
530
531
532
  		u32 dw2;
  
  		pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2);
  		if (dw2)
  			pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM,
  					       dw2);
  		pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
b238e5772   Arthur Jones   edac: i5100: cleanup
533
534
535
  		i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw),
  			       i5100_ferr_nf_mem_any(dw),
  			       i5100_nerr_nf_mem_any(dw2));
8f421c595   Arthur Jones   edac: i5100 new i...
536
537
  	}
  }
295439f2a   Nils Carlson   edac: i5100 add s...
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
  /* 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...
573
  static int i5100_set_scrub_rate(struct mem_ctl_info *mci, u32 bandwidth)
295439f2a   Nils Carlson   edac: i5100 add s...
574
575
576
577
578
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	u32 dw;
  
  	pci_read_config_dword(priv->mc, I5100_MC, &dw);
eba042a81   Borislav Petkov   edac, mc: Improve...
579
  	if (bandwidth) {
295439f2a   Nils Carlson   edac: i5100 add s...
580
581
582
583
584
585
586
587
588
589
590
591
  		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...
592
  	bandwidth = 5900000 * i5100_mc_scrben(dw);
295439f2a   Nils Carlson   edac: i5100 add s...
593

390944439   Borislav Petkov   EDAC: Fixup scrub...
594
  	return bandwidth;
295439f2a   Nils Carlson   edac: i5100 add s...
595
  }
390944439   Borislav Petkov   EDAC: Fixup scrub...
596
  static int i5100_get_scrub_rate(struct mem_ctl_info *mci)
295439f2a   Nils Carlson   edac: i5100 add s...
597
598
599
600
601
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	u32 dw;
  
  	pci_read_config_dword(priv->mc, I5100_MC, &dw);
390944439   Borislav Petkov   EDAC: Fixup scrub...
602
  	return 5900000 * i5100_mc_scrben(dw);
295439f2a   Nils Carlson   edac: i5100 add s...
603
  }
8f421c595   Arthur Jones   edac: i5100 new i...
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
  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;
  }
  
  static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci,
  					    int csrow)
  {
  	struct i5100_priv *priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
627
628
  	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...
629
630
631
  	unsigned addr_lines;
  
  	/* dimm present? */
b18dfd05f   Nils Carlson   edac: i5100 clean...
632
  	if (!priv->mtr[chan][chan_rank].present)
8f421c595   Arthur Jones   edac: i5100 new i...
633
634
635
636
  		return 0ULL;
  
  	addr_lines =
  		I5100_DIMM_ADDR_LINES +
b18dfd05f   Nils Carlson   edac: i5100 clean...
637
638
639
  		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...
640
641
642
643
644
645
646
647
648
649
  
  	return (unsigned long)
  		((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
  }
  
  static void __devinit i5100_init_mtr(struct mem_ctl_info *mci)
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
650
  	for (i = 0; i < I5100_CHANNELS; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
651
652
  		int j;
  		struct pci_dev *pdev = mms[i];
b18dfd05f   Nils Carlson   edac: i5100 clean...
653
  		for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) {
8f421c595   Arthur Jones   edac: i5100 new i...
654
655
656
657
658
659
  			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
660
661
662
663
664
665
  			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...
666
667
668
669
670
671
672
673
674
675
676
677
678
  		}
  	}
  }
  
  /*
   * 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...
679
680
681
  	unsigned long et;
  
  	pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
682
  	if (i5100_spddata_busy(w))
8f421c595   Arthur Jones   edac: i5100 new i...
683
  		return -1;
b238e5772   Arthur Jones   edac: i5100: cleanup
684
685
686
  	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...
687
688
689
690
691
692
  
  	/* 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
693
  		if (!i5100_spddata_busy(w))
8f421c595   Arthur Jones   edac: i5100 new i...
694
695
696
  			break;
  		udelay(100);
  	}
b238e5772   Arthur Jones   edac: i5100: cleanup
697
  	if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w))
8f421c595   Arthur Jones   edac: i5100 new i...
698
  		return -1;
b238e5772   Arthur Jones   edac: i5100: cleanup
699
  	*byte = i5100_spddata_data(w);
8f421c595   Arthur Jones   edac: i5100 new i...
700
701
702
703
704
705
706
707
  
  	return 0;
  }
  
  /*
   * fill dimm chip select map
   *
   * FIXME:
8f421c595   Arthur Jones   edac: i5100 new i...
708
709
710
711
712
713
714
   *   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
   */
  static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci)
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
715
  	for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
716
717
718
719
720
721
722
  		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...
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
  	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...
738
739
740
741
742
743
744
  }
  
  static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev,
  					     struct mem_ctl_info *mci)
  {
  	struct i5100_priv *priv = mci->pvt_info;
  	int i;
b18dfd05f   Nils Carlson   edac: i5100 clean...
745
  	for (i = 0; i < I5100_CHANNELS; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
746
  		int j;
b18dfd05f   Nils Carlson   edac: i5100 clean...
747
  		for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) {
8f421c595   Arthur Jones   edac: i5100 new i...
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
  			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);
  }
  
  static void __devinit i5100_init_interleaving(struct pci_dev *pdev,
  					      struct mem_ctl_info *mci)
  {
  	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
770
  	priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024;
8f421c595   Arthur Jones   edac: i5100 new i...
771
772
  
  	pci_read_config_word(pdev, I5100_MIR0, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
773
774
775
  	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...
776
777
  
  	pci_read_config_word(pdev, I5100_MIR1, &w);
b238e5772   Arthur Jones   edac: i5100: cleanup
778
779
780
  	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...
781
782
783
784
785
  
  	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...
786
  	for (i = 0; i < I5100_CHANNELS; i++) {
8f421c595   Arthur Jones   edac: i5100 new i...
787
788
789
790
791
792
793
794
  		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
795
  				(u64) i5100_dmir_limit(dw) << 28;
8f421c595   Arthur Jones   edac: i5100 new i...
796
797
  			for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++)
  				priv->dmir[i][j].rank[k] =
b238e5772   Arthur Jones   edac: i5100: cleanup
798
  					i5100_dmir_rank(dw, k);
8f421c595   Arthur Jones   edac: i5100 new i...
799
800
801
802
803
804
805
806
807
808
809
810
811
812
  		}
  	}
  
  	i5100_init_mtr(mci);
  }
  
  static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
  {
  	int i;
  	unsigned long total_pages = 0UL;
  	struct i5100_priv *priv = mci->pvt_info;
  
  	for (i = 0; i < mci->nr_csrows; i++) {
  		const unsigned long npages = i5100_npages(mci, i);
b18dfd05f   Nils Carlson   edac: i5100 clean...
813
  		const unsigned chan = i5100_csrow_to_chan(mci, i);
8f421c595   Arthur Jones   edac: i5100 new i...
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
  		const unsigned rank = i5100_csrow_to_rank(mci, i);
  
  		if (!npages)
  			continue;
  
  		/*
  		 * FIXME: these two are totally bogus -- I don't see how to
  		 * map them correctly to this structure...
  		 */
  		mci->csrows[i].first_page = total_pages;
  		mci->csrows[i].last_page = total_pages + npages - 1;
  		mci->csrows[i].page_mask = 0UL;
  
  		mci->csrows[i].nr_pages = npages;
  		mci->csrows[i].grain = 32;
  		mci->csrows[i].csrow_idx = i;
  		mci->csrows[i].dtype =
b18dfd05f   Nils Carlson   edac: i5100 clean...
831
  			(priv->mtr[chan][rank].width == 4) ? DEV_X4 : DEV_X8;
8f421c595   Arthur Jones   edac: i5100 new i...
832
833
834
835
836
837
838
839
840
841
842
  		mci->csrows[i].ue_count = 0;
  		mci->csrows[i].ce_count = 0;
  		mci->csrows[i].mtype = MEM_RDDR2;
  		mci->csrows[i].edac_mode = EDAC_SECDED;
  		mci->csrows[i].mci = mci;
  		mci->csrows[i].nr_channels = 1;
  		mci->csrows[i].channels[0].chan_idx = 0;
  		mci->csrows[i].channels[0].ce_count = 0;
  		mci->csrows[i].channels[0].csrow = mci->csrows + i;
  		snprintf(mci->csrows[i].channels[0].label,
  			 sizeof(mci->csrows[i].channels[0].label),
b18dfd05f   Nils Carlson   edac: i5100 clean...
843
  			 "DIMM%u", i5100_rank_to_slot(mci, chan, rank));
8f421c595   Arthur Jones   edac: i5100 new i...
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
  
  		total_pages += npages;
  	}
  }
  
  static int __devinit i5100_init_one(struct pci_dev *pdev,
  				    const struct pci_device_id *id)
  {
  	int rc;
  	struct mem_ctl_info *mci;
  	struct i5100_priv *priv;
  	struct pci_dev *ch0mm, *ch1mm;
  	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...
868
869
  	/* ECC enabled? */
  	pci_read_config_dword(pdev, I5100_MC, &dw);
b238e5772   Arthur Jones   edac: i5100: cleanup
870
  	if (!i5100_mc_errdeten(dw)) {
43920a598   Arthur Jones   edac: i5100 fix e...
871
872
873
  		printk(KERN_INFO "i5100_edac: ECC not enabled.
  ");
  		ret = -ENODEV;
b238e5772   Arthur Jones   edac: i5100: cleanup
874
  		goto bail_pdev;
43920a598   Arthur Jones   edac: i5100 fix e...
875
  	}
8f421c595   Arthur Jones   edac: i5100 new i...
876
877
878
  	/* 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...
879
880
881
882
  	/* 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...
883
884
885
  	/* 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
886
887
888
889
  	if (!ch0mm) {
  		ret = -ENODEV;
  		goto bail_pdev;
  	}
8f421c595   Arthur Jones   edac: i5100 new i...
890
891
892
893
894
895
896
897
898
899
900
901
  
  	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
902
  		goto bail_disable_ch0;
8f421c595   Arthur Jones   edac: i5100 new i...
903
904
905
906
907
908
909
910
911
912
913
  	}
  
  	rc = pci_enable_device(ch1mm);
  	if (rc < 0) {
  		ret = rc;
  		goto bail_ch1;
  	}
  
  	mci = edac_mc_alloc(sizeof(*priv), ranksperch * 2, 1, 0);
  	if (!mci) {
  		ret = -ENOMEM;
b238e5772   Arthur Jones   edac: i5100: cleanup
914
  		goto bail_disable_ch1;
8f421c595   Arthur Jones   edac: i5100 new i...
915
916
917
918
919
  	}
  
  	mci->dev = &pdev->dev;
  
  	priv = mci->pvt_info;
b18dfd05f   Nils Carlson   edac: i5100 clean...
920
  	priv->ranksperchan = ranksperch;
8f421c595   Arthur Jones   edac: i5100 new i...
921
922
923
  	priv->mc = pdev;
  	priv->ch0mm = ch0mm;
  	priv->ch1mm = ch1mm;
295439f2a   Nils Carlson   edac: i5100 add s...
924
925
926
927
928
929
930
931
932
  	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...
933
934
935
936
937
938
939
940
941
942
  	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
943
  	mci->ctl_page_to_phys = NULL;
8f421c595   Arthur Jones   edac: i5100 new i...
944
945
  
  	mci->edac_check = i5100_check_error;
295439f2a   Nils Carlson   edac: i5100 add s...
946
947
  	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...
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
  
  	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...
963
  		goto bail_scrub;
8f421c595   Arthur Jones   edac: i5100 new i...
964
  	}
b238e5772   Arthur Jones   edac: i5100: cleanup
965
  	return ret;
8f421c595   Arthur Jones   edac: i5100 new i...
966

295439f2a   Nils Carlson   edac: i5100 add s...
967
968
969
  bail_scrub:
  	priv->scrub_enable = 0;
  	cancel_delayed_work_sync(&(priv->i5100_scrubbing));
8f421c595   Arthur Jones   edac: i5100 new i...
970
  	edac_mc_free(mci);
b238e5772   Arthur Jones   edac: i5100: cleanup
971
972
  bail_disable_ch1:
  	pci_disable_device(ch1mm);
8f421c595   Arthur Jones   edac: i5100 new i...
973
974
  bail_ch1:
  	pci_dev_put(ch1mm);
b238e5772   Arthur Jones   edac: i5100: cleanup
975
976
  bail_disable_ch0:
  	pci_disable_device(ch0mm);
8f421c595   Arthur Jones   edac: i5100 new i...
977
978
  bail_ch0:
  	pci_dev_put(ch0mm);
b238e5772   Arthur Jones   edac: i5100: cleanup
979
980
  bail_pdev:
  	pci_disable_device(pdev);
8f421c595   Arthur Jones   edac: i5100 new i...
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
  bail:
  	return ret;
  }
  
  static void __devexit i5100_remove_one(struct pci_dev *pdev)
  {
  	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...
996
997
998
  
  	priv->scrub_enable = 0;
  	cancel_delayed_work_sync(&(priv->i5100_scrubbing));
b238e5772   Arthur Jones   edac: i5100: cleanup
999
1000
1001
  	pci_disable_device(pdev);
  	pci_disable_device(priv->ch0mm);
  	pci_disable_device(priv->ch1mm);
8f421c595   Arthur Jones   edac: i5100 new i...
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
  	pci_dev_put(priv->ch0mm);
  	pci_dev_put(priv->ch1mm);
  
  	edac_mc_free(mci);
  }
  
  static const struct pci_device_id i5100_pci_tbl[] __devinitdata = {
  	/* 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,
  	.remove = __devexit_p(i5100_remove_one),
  	.id_table = i5100_pci_tbl,
  };
  
  static int __init i5100_init(void)
  {
  	int pci_rc;
  
  	pci_rc = pci_register_driver(&i5100_driver);
  
  	return (pci_rc < 0) ? pci_rc : 0;
  }
  
  static void __exit i5100_exit(void)
  {
  	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");