blob: 9a933180b4342aaf979f65d2bcc48aee58eff984 [file] [log] [blame]
Arthur Jones8f421c592008-07-25 01:49:04 -07001/*
2 * Intel 5100 Memory Controllers kernel module
3 *
4 * This file may be distributed under the terms of the
5 * GNU General Public License.
6 *
7 * This module is based on the following document:
8 *
9 * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet
10 * http://download.intel.com/design/chipsets/datashts/318378.pdf
11 *
12 */
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/pci.h>
16#include <linux/pci_ids.h>
17#include <linux/slab.h>
18#include <linux/edac.h>
19#include <linux/delay.h>
20#include <linux/mmzone.h>
21
22#include "edac_core.h"
23
Arthur Jonesb238e572008-07-25 01:49:08 -070024/* register addresses */
Arthur Jones8f421c592008-07-25 01:49:04 -070025
26/* device 16, func 1 */
Arthur Jones43920a52008-07-25 01:49:06 -070027#define I5100_MC 0x40 /* Memory Control Register */
Nils Carlson295439f2009-12-15 16:47:42 -080028#define I5100_MC_SCRBEN_MASK (1 << 7)
29#define I5100_MC_SCRBDONE_MASK (1 << 4)
Arthur Jones8f421c592008-07-25 01:49:04 -070030#define I5100_MS 0x44 /* Memory Status Register */
31#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */
Arthur Jones8f421c592008-07-25 01:49:04 -070032#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */
Arthur Jones8f421c592008-07-25 01:49:04 -070033#define I5100_TOLM 0x6c /* Top of Low Memory */
Arthur Jones8f421c592008-07-25 01:49:04 -070034#define I5100_MIR0 0x80 /* Memory Interleave Range 0 */
35#define I5100_MIR1 0x84 /* Memory Interleave Range 1 */
36#define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */
37#define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */
Arthur Jones8f421c592008-07-25 01:49:04 -070038#define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */
Arthur Jones8f421c592008-07-25 01:49:04 -070039#define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16)
40#define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15)
41#define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14)
Arthur Jonesf7952ff2008-07-25 01:49:05 -070042#define I5100_FERR_NF_MEM_M12ERR_MASK (1 << 12)
43#define I5100_FERR_NF_MEM_M11ERR_MASK (1 << 11)
44#define I5100_FERR_NF_MEM_M10ERR_MASK (1 << 10)
45#define I5100_FERR_NF_MEM_M6ERR_MASK (1 << 6)
46#define I5100_FERR_NF_MEM_M5ERR_MASK (1 << 5)
47#define I5100_FERR_NF_MEM_M4ERR_MASK (1 << 4)
48#define I5100_FERR_NF_MEM_M1ERR_MASK 1
Arthur Jones8f421c592008-07-25 01:49:04 -070049#define I5100_FERR_NF_MEM_ANY_MASK \
50 (I5100_FERR_NF_MEM_M16ERR_MASK | \
51 I5100_FERR_NF_MEM_M15ERR_MASK | \
Arthur Jonesf7952ff2008-07-25 01:49:05 -070052 I5100_FERR_NF_MEM_M14ERR_MASK | \
53 I5100_FERR_NF_MEM_M12ERR_MASK | \
54 I5100_FERR_NF_MEM_M11ERR_MASK | \
55 I5100_FERR_NF_MEM_M10ERR_MASK | \
56 I5100_FERR_NF_MEM_M6ERR_MASK | \
57 I5100_FERR_NF_MEM_M5ERR_MASK | \
58 I5100_FERR_NF_MEM_M4ERR_MASK | \
59 I5100_FERR_NF_MEM_M1ERR_MASK)
Arthur Jones8f421c592008-07-25 01:49:04 -070060#define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */
Arthur Jones178d5a72008-07-25 01:49:06 -070061#define I5100_EMASK_MEM 0xa8 /* MC Error Mask Register */
Arthur Jones8f421c592008-07-25 01:49:04 -070062
63/* device 21 and 22, func 0 */
64#define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */
65#define I5100_DMIR 0x15c /* DIMM Interleave Range */
Arthur Jones8f421c592008-07-25 01:49:04 -070066#define I5100_VALIDLOG 0x18c /* Valid Log Markers */
Arthur Jones8f421c592008-07-25 01:49:04 -070067#define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */
Arthur Jones8f421c592008-07-25 01:49:04 -070068#define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */
Arthur Jones8f421c592008-07-25 01:49:04 -070069#define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */
Arthur Jones8f421c592008-07-25 01:49:04 -070070#define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */
Arthur Jones8f421c592008-07-25 01:49:04 -070071#define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */
Arthur Jones8f421c592008-07-25 01:49:04 -070072#define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */
Arthur Jonesb238e572008-07-25 01:49:08 -070073#define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */
74
75/* bit field accessors */
76
Nils Carlson295439f2009-12-15 16:47:42 -080077static inline u32 i5100_mc_scrben(u32 mc)
78{
79 return mc >> 7 & 1;
80}
81
Arthur Jonesb238e572008-07-25 01:49:08 -070082static inline u32 i5100_mc_errdeten(u32 mc)
83{
84 return mc >> 5 & 1;
85}
86
Nils Carlson295439f2009-12-15 16:47:42 -080087static inline u32 i5100_mc_scrbdone(u32 mc)
88{
89 return mc >> 4 & 1;
90}
91
Arthur Jonesb238e572008-07-25 01:49:08 -070092static inline u16 i5100_spddata_rdo(u16 a)
93{
94 return a >> 15 & 1;
95}
96
97static inline u16 i5100_spddata_sbe(u16 a)
98{
99 return a >> 13 & 1;
100}
101
102static inline u16 i5100_spddata_busy(u16 a)
103{
104 return a >> 12 & 1;
105}
106
107static inline u16 i5100_spddata_data(u16 a)
108{
109 return a & ((1 << 8) - 1);
110}
111
112static inline u32 i5100_spdcmd_create(u32 dti, u32 ckovrd, u32 sa, u32 ba,
113 u32 data, u32 cmd)
114{
115 return ((dti & ((1 << 4) - 1)) << 28) |
116 ((ckovrd & 1) << 27) |
117 ((sa & ((1 << 3) - 1)) << 24) |
118 ((ba & ((1 << 8) - 1)) << 16) |
119 ((data & ((1 << 8) - 1)) << 8) |
120 (cmd & 1);
121}
122
123static inline u16 i5100_tolm_tolm(u16 a)
124{
125 return a >> 12 & ((1 << 4) - 1);
126}
127
128static inline u16 i5100_mir_limit(u16 a)
129{
130 return a >> 4 & ((1 << 12) - 1);
131}
132
133static inline u16 i5100_mir_way1(u16 a)
134{
135 return a >> 1 & 1;
136}
137
138static inline u16 i5100_mir_way0(u16 a)
139{
140 return a & 1;
141}
142
143static inline u32 i5100_ferr_nf_mem_chan_indx(u32 a)
144{
145 return a >> 28 & 1;
146}
147
148static inline u32 i5100_ferr_nf_mem_any(u32 a)
149{
150 return a & I5100_FERR_NF_MEM_ANY_MASK;
151}
152
153static inline u32 i5100_nerr_nf_mem_any(u32 a)
154{
155 return i5100_ferr_nf_mem_any(a);
156}
157
158static inline u32 i5100_dmir_limit(u32 a)
159{
160 return a >> 16 & ((1 << 11) - 1);
161}
162
163static inline u32 i5100_dmir_rank(u32 a, u32 i)
164{
165 return a >> (4 * i) & ((1 << 2) - 1);
166}
167
168static inline u16 i5100_mtr_present(u16 a)
169{
170 return a >> 10 & 1;
171}
172
173static inline u16 i5100_mtr_ethrottle(u16 a)
174{
175 return a >> 9 & 1;
176}
177
178static inline u16 i5100_mtr_width(u16 a)
179{
180 return a >> 8 & 1;
181}
182
183static inline u16 i5100_mtr_numbank(u16 a)
184{
185 return a >> 6 & 1;
186}
187
188static inline u16 i5100_mtr_numrow(u16 a)
189{
190 return a >> 2 & ((1 << 2) - 1);
191}
192
193static inline u16 i5100_mtr_numcol(u16 a)
194{
195 return a & ((1 << 2) - 1);
196}
197
198
199static inline u32 i5100_validlog_redmemvalid(u32 a)
200{
201 return a >> 2 & 1;
202}
203
204static inline u32 i5100_validlog_recmemvalid(u32 a)
205{
206 return a >> 1 & 1;
207}
208
209static inline u32 i5100_validlog_nrecmemvalid(u32 a)
210{
211 return a & 1;
212}
213
214static inline u32 i5100_nrecmema_merr(u32 a)
215{
216 return a >> 15 & ((1 << 5) - 1);
217}
218
219static inline u32 i5100_nrecmema_bank(u32 a)
220{
221 return a >> 12 & ((1 << 3) - 1);
222}
223
224static inline u32 i5100_nrecmema_rank(u32 a)
225{
226 return a >> 8 & ((1 << 3) - 1);
227}
228
229static inline u32 i5100_nrecmema_dm_buf_id(u32 a)
230{
231 return a & ((1 << 8) - 1);
232}
233
234static inline u32 i5100_nrecmemb_cas(u32 a)
235{
236 return a >> 16 & ((1 << 13) - 1);
237}
238
239static inline u32 i5100_nrecmemb_ras(u32 a)
240{
241 return a & ((1 << 16) - 1);
242}
243
244static inline u32 i5100_redmemb_ecc_locator(u32 a)
245{
246 return a & ((1 << 18) - 1);
247}
248
249static inline u32 i5100_recmema_merr(u32 a)
250{
251 return i5100_nrecmema_merr(a);
252}
253
254static inline u32 i5100_recmema_bank(u32 a)
255{
256 return i5100_nrecmema_bank(a);
257}
258
259static inline u32 i5100_recmema_rank(u32 a)
260{
261 return i5100_nrecmema_rank(a);
262}
263
264static inline u32 i5100_recmema_dm_buf_id(u32 a)
265{
266 return i5100_nrecmema_dm_buf_id(a);
267}
268
269static inline u32 i5100_recmemb_cas(u32 a)
270{
271 return i5100_nrecmemb_cas(a);
272}
273
274static inline u32 i5100_recmemb_ras(u32 a)
275{
276 return i5100_nrecmemb_ras(a);
277}
Arthur Jones8f421c592008-07-25 01:49:04 -0700278
279/* some generic limits */
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800280#define I5100_MAX_RANKS_PER_CHAN 6
281#define I5100_CHANNELS 2
Arthur Jones8f421c592008-07-25 01:49:04 -0700282#define I5100_MAX_RANKS_PER_DIMM 4
283#define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800284#define I5100_MAX_DIMM_SLOTS_PER_CHAN 4
Arthur Jones8f421c592008-07-25 01:49:04 -0700285#define I5100_MAX_RANK_INTERLEAVE 4
286#define I5100_MAX_DMIRS 5
Nils Carlson295439f2009-12-15 16:47:42 -0800287#define I5100_SCRUB_REFRESH_RATE (5 * 60 * HZ)
Arthur Jones8f421c592008-07-25 01:49:04 -0700288
289struct i5100_priv {
290 /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800291 int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN];
Arthur Jones8f421c592008-07-25 01:49:04 -0700292
293 /*
294 * mainboard chip select map -- maps i5100 chip selects to
295 * DIMM slot chip selects. In the case of only 4 ranks per
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800296 * channel, the mapping is fairly obvious but not unique.
297 * we map -1 -> NC and assume both channels use the same
Arthur Jones8f421c592008-07-25 01:49:04 -0700298 * map...
299 *
300 */
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800301 int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM];
Arthur Jones8f421c592008-07-25 01:49:04 -0700302
303 /* memory interleave range */
304 struct {
305 u64 limit;
306 unsigned way[2];
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800307 } mir[I5100_CHANNELS];
Arthur Jones8f421c592008-07-25 01:49:04 -0700308
309 /* adjusted memory interleave range register */
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800310 unsigned amir[I5100_CHANNELS];
Arthur Jones8f421c592008-07-25 01:49:04 -0700311
312 /* dimm interleave range */
313 struct {
314 unsigned rank[I5100_MAX_RANK_INTERLEAVE];
315 u64 limit;
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800316 } dmir[I5100_CHANNELS][I5100_MAX_DMIRS];
Arthur Jones8f421c592008-07-25 01:49:04 -0700317
318 /* memory technology registers... */
319 struct {
320 unsigned present; /* 0 or 1 */
321 unsigned ethrottle; /* 0 or 1 */
322 unsigned width; /* 4 or 8 bits */
323 unsigned numbank; /* 2 or 3 lines */
324 unsigned numrow; /* 13 .. 16 lines */
325 unsigned numcol; /* 11 .. 12 lines */
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800326 } mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN];
Arthur Jones8f421c592008-07-25 01:49:04 -0700327
328 u64 tolm; /* top of low memory in bytes */
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800329 unsigned ranksperchan; /* number of ranks per channel */
Arthur Jones8f421c592008-07-25 01:49:04 -0700330
331 struct pci_dev *mc; /* device 16 func 1 */
332 struct pci_dev *ch0mm; /* device 21 func 0 */
333 struct pci_dev *ch1mm; /* device 22 func 0 */
Nils Carlson295439f2009-12-15 16:47:42 -0800334
335 struct delayed_work i5100_scrubbing;
336 int scrub_enable;
Arthur Jones8f421c592008-07-25 01:49:04 -0700337};
338
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800339/* map a rank/chan to a slot number on the mainboard */
Arthur Jones8f421c592008-07-25 01:49:04 -0700340static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800341 int chan, int rank)
Arthur Jones8f421c592008-07-25 01:49:04 -0700342{
343 const struct i5100_priv *priv = mci->pvt_info;
344 int i;
345
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800346 for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700347 int j;
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800348 const int numrank = priv->dimm_numrank[chan][i];
Arthur Jones8f421c592008-07-25 01:49:04 -0700349
350 for (j = 0; j < numrank; j++)
351 if (priv->dimm_csmap[i][j] == rank)
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800352 return i * 2 + chan;
Arthur Jones8f421c592008-07-25 01:49:04 -0700353 }
354
355 return -1;
356}
357
Arthur Jones8f421c592008-07-25 01:49:04 -0700358static const char *i5100_err_msg(unsigned err)
359{
Arthur Jonesb238e572008-07-25 01:49:08 -0700360 static const char *merrs[] = {
Arthur Jones8f421c592008-07-25 01:49:04 -0700361 "unknown", /* 0 */
362 "uncorrectable data ECC on replay", /* 1 */
363 "unknown", /* 2 */
364 "unknown", /* 3 */
365 "aliased uncorrectable demand data ECC", /* 4 */
366 "aliased uncorrectable spare-copy data ECC", /* 5 */
367 "aliased uncorrectable patrol data ECC", /* 6 */
368 "unknown", /* 7 */
369 "unknown", /* 8 */
370 "unknown", /* 9 */
371 "non-aliased uncorrectable demand data ECC", /* 10 */
372 "non-aliased uncorrectable spare-copy data ECC", /* 11 */
373 "non-aliased uncorrectable patrol data ECC", /* 12 */
374 "unknown", /* 13 */
375 "correctable demand data ECC", /* 14 */
376 "correctable spare-copy data ECC", /* 15 */
377 "correctable patrol data ECC", /* 16 */
378 "unknown", /* 17 */
379 "SPD protocol error", /* 18 */
380 "unknown", /* 19 */
381 "spare copy initiated", /* 20 */
382 "spare copy completed", /* 21 */
383 };
384 unsigned i;
385
386 for (i = 0; i < ARRAY_SIZE(merrs); i++)
387 if (1 << i & err)
388 return merrs[i];
389
390 return "none";
391}
392
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800393/* convert csrow index into a rank (per channel -- 0..5) */
Arthur Jones8f421c592008-07-25 01:49:04 -0700394static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow)
395{
396 const struct i5100_priv *priv = mci->pvt_info;
397
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800398 return csrow % priv->ranksperchan;
Arthur Jones8f421c592008-07-25 01:49:04 -0700399}
400
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800401/* convert csrow index into a channel (0..1) */
402static int i5100_csrow_to_chan(const struct mem_ctl_info *mci, int csrow)
Arthur Jones8f421c592008-07-25 01:49:04 -0700403{
404 const struct i5100_priv *priv = mci->pvt_info;
405
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800406 return csrow / priv->ranksperchan;
Arthur Jones8f421c592008-07-25 01:49:04 -0700407}
408
409static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci,
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800410 int chan, int rank)
Arthur Jones8f421c592008-07-25 01:49:04 -0700411{
412 const struct i5100_priv *priv = mci->pvt_info;
413
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800414 return chan * priv->ranksperchan + rank;
Arthur Jones8f421c592008-07-25 01:49:04 -0700415}
416
417static void i5100_handle_ce(struct mem_ctl_info *mci,
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800418 int chan,
Arthur Jones8f421c592008-07-25 01:49:04 -0700419 unsigned bank,
420 unsigned rank,
421 unsigned long syndrome,
422 unsigned cas,
423 unsigned ras,
424 const char *msg)
425{
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800426 const int csrow = i5100_rank_to_csrow(mci, chan, rank);
Arthur Jones8f421c592008-07-25 01:49:04 -0700427
428 printk(KERN_ERR
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800429 "CE chan %d, bank %u, rank %u, syndrome 0x%lx, "
Arthur Jones8f421c592008-07-25 01:49:04 -0700430 "cas %u, ras %u, csrow %u, label \"%s\": %s\n",
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800431 chan, bank, rank, syndrome, cas, ras,
Arthur Jones8f421c592008-07-25 01:49:04 -0700432 csrow, mci->csrows[csrow].channels[0].label, msg);
433
434 mci->ce_count++;
435 mci->csrows[csrow].ce_count++;
436 mci->csrows[csrow].channels[0].ce_count++;
437}
438
439static void i5100_handle_ue(struct mem_ctl_info *mci,
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800440 int chan,
Arthur Jones8f421c592008-07-25 01:49:04 -0700441 unsigned bank,
442 unsigned rank,
443 unsigned long syndrome,
444 unsigned cas,
445 unsigned ras,
446 const char *msg)
447{
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800448 const int csrow = i5100_rank_to_csrow(mci, chan, rank);
Arthur Jones8f421c592008-07-25 01:49:04 -0700449
450 printk(KERN_ERR
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800451 "UE chan %d, bank %u, rank %u, syndrome 0x%lx, "
Arthur Jones8f421c592008-07-25 01:49:04 -0700452 "cas %u, ras %u, csrow %u, label \"%s\": %s\n",
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800453 chan, bank, rank, syndrome, cas, ras,
Arthur Jones8f421c592008-07-25 01:49:04 -0700454 csrow, mci->csrows[csrow].channels[0].label, msg);
455
456 mci->ue_count++;
457 mci->csrows[csrow].ue_count++;
458}
459
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800460static void i5100_read_log(struct mem_ctl_info *mci, int chan,
Arthur Jones8f421c592008-07-25 01:49:04 -0700461 u32 ferr, u32 nerr)
462{
463 struct i5100_priv *priv = mci->pvt_info;
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800464 struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm;
Arthur Jones8f421c592008-07-25 01:49:04 -0700465 u32 dw;
466 u32 dw2;
467 unsigned syndrome = 0;
468 unsigned ecc_loc = 0;
469 unsigned merr;
470 unsigned bank;
471 unsigned rank;
472 unsigned cas;
473 unsigned ras;
474
475 pci_read_config_dword(pdev, I5100_VALIDLOG, &dw);
476
Arthur Jonesb238e572008-07-25 01:49:08 -0700477 if (i5100_validlog_redmemvalid(dw)) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700478 pci_read_config_dword(pdev, I5100_REDMEMA, &dw2);
Arthur Jonesb238e572008-07-25 01:49:08 -0700479 syndrome = dw2;
Arthur Jones8f421c592008-07-25 01:49:04 -0700480 pci_read_config_dword(pdev, I5100_REDMEMB, &dw2);
Arthur Jonesb238e572008-07-25 01:49:08 -0700481 ecc_loc = i5100_redmemb_ecc_locator(dw2);
Arthur Jones8f421c592008-07-25 01:49:04 -0700482 }
483
Arthur Jonesb238e572008-07-25 01:49:08 -0700484 if (i5100_validlog_recmemvalid(dw)) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700485 const char *msg;
486
487 pci_read_config_dword(pdev, I5100_RECMEMA, &dw2);
Arthur Jonesb238e572008-07-25 01:49:08 -0700488 merr = i5100_recmema_merr(dw2);
489 bank = i5100_recmema_bank(dw2);
490 rank = i5100_recmema_rank(dw2);
Arthur Jones8f421c592008-07-25 01:49:04 -0700491
492 pci_read_config_dword(pdev, I5100_RECMEMB, &dw2);
Arthur Jonesb238e572008-07-25 01:49:08 -0700493 cas = i5100_recmemb_cas(dw2);
494 ras = i5100_recmemb_ras(dw2);
Arthur Jones8f421c592008-07-25 01:49:04 -0700495
496 /* FIXME: not really sure if this is what merr is...
497 */
498 if (!merr)
499 msg = i5100_err_msg(ferr);
500 else
501 msg = i5100_err_msg(nerr);
502
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800503 i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg);
Arthur Jones8f421c592008-07-25 01:49:04 -0700504 }
505
Arthur Jonesb238e572008-07-25 01:49:08 -0700506 if (i5100_validlog_nrecmemvalid(dw)) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700507 const char *msg;
508
509 pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2);
Arthur Jonesb238e572008-07-25 01:49:08 -0700510 merr = i5100_nrecmema_merr(dw2);
511 bank = i5100_nrecmema_bank(dw2);
512 rank = i5100_nrecmema_rank(dw2);
Arthur Jones8f421c592008-07-25 01:49:04 -0700513
514 pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2);
Arthur Jonesb238e572008-07-25 01:49:08 -0700515 cas = i5100_nrecmemb_cas(dw2);
516 ras = i5100_nrecmemb_ras(dw2);
Arthur Jones8f421c592008-07-25 01:49:04 -0700517
518 /* FIXME: not really sure if this is what merr is...
519 */
520 if (!merr)
521 msg = i5100_err_msg(ferr);
522 else
523 msg = i5100_err_msg(nerr);
524
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800525 i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg);
Arthur Jones8f421c592008-07-25 01:49:04 -0700526 }
527
528 pci_write_config_dword(pdev, I5100_VALIDLOG, dw);
529}
530
531static void i5100_check_error(struct mem_ctl_info *mci)
532{
533 struct i5100_priv *priv = mci->pvt_info;
534 u32 dw;
535
536
537 pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw);
Arthur Jonesb238e572008-07-25 01:49:08 -0700538 if (i5100_ferr_nf_mem_any(dw)) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700539 u32 dw2;
540
541 pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2);
542 if (dw2)
543 pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM,
544 dw2);
545 pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
546
Arthur Jonesb238e572008-07-25 01:49:08 -0700547 i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw),
548 i5100_ferr_nf_mem_any(dw),
549 i5100_nerr_nf_mem_any(dw2));
Arthur Jones8f421c592008-07-25 01:49:04 -0700550 }
551}
552
Nils Carlson295439f2009-12-15 16:47:42 -0800553/* The i5100 chipset will scrub the entire memory once, then
554 * set a done bit. Continuous scrubbing is achieved by enqueing
555 * delayed work to a workqueue, checking every few minutes if
556 * the scrubbing has completed and if so reinitiating it.
557 */
558
559static void i5100_refresh_scrubbing(struct work_struct *work)
560{
561 struct delayed_work *i5100_scrubbing = container_of(work,
562 struct delayed_work,
563 work);
564 struct i5100_priv *priv = container_of(i5100_scrubbing,
565 struct i5100_priv,
566 i5100_scrubbing);
567 u32 dw;
568
569 pci_read_config_dword(priv->mc, I5100_MC, &dw);
570
571 if (priv->scrub_enable) {
572
573 pci_read_config_dword(priv->mc, I5100_MC, &dw);
574
575 if (i5100_mc_scrbdone(dw)) {
576 dw |= I5100_MC_SCRBEN_MASK;
577 pci_write_config_dword(priv->mc, I5100_MC, dw);
578 pci_read_config_dword(priv->mc, I5100_MC, &dw);
579 }
580
581 schedule_delayed_work(&(priv->i5100_scrubbing),
582 I5100_SCRUB_REFRESH_RATE);
583 }
584}
585/*
586 * The bandwidth is based on experimentation, feel free to refine it.
587 */
588static int i5100_set_scrub_rate(struct mem_ctl_info *mci,
589 u32 *bandwidth)
590{
591 struct i5100_priv *priv = mci->pvt_info;
592 u32 dw;
593
594 pci_read_config_dword(priv->mc, I5100_MC, &dw);
595 if (*bandwidth) {
596 priv->scrub_enable = 1;
597 dw |= I5100_MC_SCRBEN_MASK;
598 schedule_delayed_work(&(priv->i5100_scrubbing),
599 I5100_SCRUB_REFRESH_RATE);
600 } else {
601 priv->scrub_enable = 0;
602 dw &= ~I5100_MC_SCRBEN_MASK;
603 cancel_delayed_work(&(priv->i5100_scrubbing));
604 }
605 pci_write_config_dword(priv->mc, I5100_MC, dw);
606
607 pci_read_config_dword(priv->mc, I5100_MC, &dw);
608
609 *bandwidth = 5900000 * i5100_mc_scrben(dw);
610
611 return 0;
612}
613
614static int i5100_get_scrub_rate(struct mem_ctl_info *mci,
615 u32 *bandwidth)
616{
617 struct i5100_priv *priv = mci->pvt_info;
618 u32 dw;
619
620 pci_read_config_dword(priv->mc, I5100_MC, &dw);
621
622 *bandwidth = 5900000 * i5100_mc_scrben(dw);
623
624 return 0;
625}
626
Arthur Jones8f421c592008-07-25 01:49:04 -0700627static struct pci_dev *pci_get_device_func(unsigned vendor,
628 unsigned device,
629 unsigned func)
630{
631 struct pci_dev *ret = NULL;
632
633 while (1) {
634 ret = pci_get_device(vendor, device, ret);
635
636 if (!ret)
637 break;
638
639 if (PCI_FUNC(ret->devfn) == func)
640 break;
641 }
642
643 return ret;
644}
645
646static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci,
647 int csrow)
648{
649 struct i5100_priv *priv = mci->pvt_info;
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800650 const unsigned chan_rank = i5100_csrow_to_rank(mci, csrow);
651 const unsigned chan = i5100_csrow_to_chan(mci, csrow);
Arthur Jones8f421c592008-07-25 01:49:04 -0700652 unsigned addr_lines;
653
654 /* dimm present? */
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800655 if (!priv->mtr[chan][chan_rank].present)
Arthur Jones8f421c592008-07-25 01:49:04 -0700656 return 0ULL;
657
658 addr_lines =
659 I5100_DIMM_ADDR_LINES +
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800660 priv->mtr[chan][chan_rank].numcol +
661 priv->mtr[chan][chan_rank].numrow +
662 priv->mtr[chan][chan_rank].numbank;
Arthur Jones8f421c592008-07-25 01:49:04 -0700663
664 return (unsigned long)
665 ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
666}
667
668static void __devinit i5100_init_mtr(struct mem_ctl_info *mci)
669{
670 struct i5100_priv *priv = mci->pvt_info;
671 struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
672 int i;
673
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800674 for (i = 0; i < I5100_CHANNELS; i++) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700675 int j;
676 struct pci_dev *pdev = mms[i];
677
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800678 for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700679 const unsigned addr =
680 (j < 4) ? I5100_MTR_0 + j * 2 :
681 I5100_MTR_4 + (j - 4) * 2;
682 u16 w;
683
684 pci_read_config_word(pdev, addr, &w);
685
Arthur Jonesb238e572008-07-25 01:49:08 -0700686 priv->mtr[i][j].present = i5100_mtr_present(w);
687 priv->mtr[i][j].ethrottle = i5100_mtr_ethrottle(w);
688 priv->mtr[i][j].width = 4 + 4 * i5100_mtr_width(w);
689 priv->mtr[i][j].numbank = 2 + i5100_mtr_numbank(w);
690 priv->mtr[i][j].numrow = 13 + i5100_mtr_numrow(w);
691 priv->mtr[i][j].numcol = 10 + i5100_mtr_numcol(w);
Arthur Jones8f421c592008-07-25 01:49:04 -0700692 }
693 }
694}
695
696/*
697 * FIXME: make this into a real i2c adapter (so that dimm-decode
698 * will work)?
699 */
700static int i5100_read_spd_byte(const struct mem_ctl_info *mci,
701 u8 ch, u8 slot, u8 addr, u8 *byte)
702{
703 struct i5100_priv *priv = mci->pvt_info;
704 u16 w;
Arthur Jones8f421c592008-07-25 01:49:04 -0700705 unsigned long et;
706
707 pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
Arthur Jonesb238e572008-07-25 01:49:08 -0700708 if (i5100_spddata_busy(w))
Arthur Jones8f421c592008-07-25 01:49:04 -0700709 return -1;
710
Arthur Jonesb238e572008-07-25 01:49:08 -0700711 pci_write_config_dword(priv->mc, I5100_SPDCMD,
712 i5100_spdcmd_create(0xa, 1, ch * 4 + slot, addr,
713 0, 0));
Arthur Jones8f421c592008-07-25 01:49:04 -0700714
715 /* wait up to 100ms */
716 et = jiffies + HZ / 10;
717 udelay(100);
718 while (1) {
719 pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
Arthur Jonesb238e572008-07-25 01:49:08 -0700720 if (!i5100_spddata_busy(w))
Arthur Jones8f421c592008-07-25 01:49:04 -0700721 break;
722 udelay(100);
723 }
724
Arthur Jonesb238e572008-07-25 01:49:08 -0700725 if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w))
Arthur Jones8f421c592008-07-25 01:49:04 -0700726 return -1;
727
Arthur Jonesb238e572008-07-25 01:49:08 -0700728 *byte = i5100_spddata_data(w);
Arthur Jones8f421c592008-07-25 01:49:04 -0700729
730 return 0;
731}
732
733/*
734 * fill dimm chip select map
735 *
736 * FIXME:
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800737 * o only valid for 4 ranks per channel
Arthur Jones8f421c592008-07-25 01:49:04 -0700738 * o not the only way to may chip selects to dimm slots
739 * o investigate if there is some way to obtain this map from the bios
740 */
741static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci)
742{
743 struct i5100_priv *priv = mci->pvt_info;
744 int i;
745
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800746 WARN_ON(priv->ranksperchan != 4);
Arthur Jones8f421c592008-07-25 01:49:04 -0700747
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800748 for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700749 int j;
750
751 for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++)
752 priv->dimm_csmap[i][j] = -1; /* default NC */
753 }
754
755 /* only 2 chip selects per slot... */
756 priv->dimm_csmap[0][0] = 0;
757 priv->dimm_csmap[0][1] = 3;
758 priv->dimm_csmap[1][0] = 1;
759 priv->dimm_csmap[1][1] = 2;
760 priv->dimm_csmap[2][0] = 2;
761 priv->dimm_csmap[3][0] = 3;
762}
763
764static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev,
765 struct mem_ctl_info *mci)
766{
767 struct i5100_priv *priv = mci->pvt_info;
768 int i;
769
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800770 for (i = 0; i < I5100_CHANNELS; i++) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700771 int j;
772
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800773 for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700774 u8 rank;
775
776 if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0)
777 priv->dimm_numrank[i][j] = 0;
778 else
779 priv->dimm_numrank[i][j] = (rank & 3) + 1;
780 }
781 }
782
783 i5100_init_dimm_csmap(mci);
784}
785
786static void __devinit i5100_init_interleaving(struct pci_dev *pdev,
787 struct mem_ctl_info *mci)
788{
789 u16 w;
790 u32 dw;
791 struct i5100_priv *priv = mci->pvt_info;
792 struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
793 int i;
794
795 pci_read_config_word(pdev, I5100_TOLM, &w);
Arthur Jonesb238e572008-07-25 01:49:08 -0700796 priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024;
Arthur Jones8f421c592008-07-25 01:49:04 -0700797
798 pci_read_config_word(pdev, I5100_MIR0, &w);
Arthur Jonesb238e572008-07-25 01:49:08 -0700799 priv->mir[0].limit = (u64) i5100_mir_limit(w) << 28;
800 priv->mir[0].way[1] = i5100_mir_way1(w);
801 priv->mir[0].way[0] = i5100_mir_way0(w);
Arthur Jones8f421c592008-07-25 01:49:04 -0700802
803 pci_read_config_word(pdev, I5100_MIR1, &w);
Arthur Jonesb238e572008-07-25 01:49:08 -0700804 priv->mir[1].limit = (u64) i5100_mir_limit(w) << 28;
805 priv->mir[1].way[1] = i5100_mir_way1(w);
806 priv->mir[1].way[0] = i5100_mir_way0(w);
Arthur Jones8f421c592008-07-25 01:49:04 -0700807
808 pci_read_config_word(pdev, I5100_AMIR_0, &w);
809 priv->amir[0] = w;
810 pci_read_config_word(pdev, I5100_AMIR_1, &w);
811 priv->amir[1] = w;
812
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800813 for (i = 0; i < I5100_CHANNELS; i++) {
Arthur Jones8f421c592008-07-25 01:49:04 -0700814 int j;
815
816 for (j = 0; j < 5; j++) {
817 int k;
818
819 pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw);
820
821 priv->dmir[i][j].limit =
Arthur Jonesb238e572008-07-25 01:49:08 -0700822 (u64) i5100_dmir_limit(dw) << 28;
Arthur Jones8f421c592008-07-25 01:49:04 -0700823 for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++)
824 priv->dmir[i][j].rank[k] =
Arthur Jonesb238e572008-07-25 01:49:08 -0700825 i5100_dmir_rank(dw, k);
Arthur Jones8f421c592008-07-25 01:49:04 -0700826 }
827 }
828
829 i5100_init_mtr(mci);
830}
831
832static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
833{
834 int i;
835 unsigned long total_pages = 0UL;
836 struct i5100_priv *priv = mci->pvt_info;
837
838 for (i = 0; i < mci->nr_csrows; i++) {
839 const unsigned long npages = i5100_npages(mci, i);
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800840 const unsigned chan = i5100_csrow_to_chan(mci, i);
Arthur Jones8f421c592008-07-25 01:49:04 -0700841 const unsigned rank = i5100_csrow_to_rank(mci, i);
842
843 if (!npages)
844 continue;
845
846 /*
847 * FIXME: these two are totally bogus -- I don't see how to
848 * map them correctly to this structure...
849 */
850 mci->csrows[i].first_page = total_pages;
851 mci->csrows[i].last_page = total_pages + npages - 1;
852 mci->csrows[i].page_mask = 0UL;
853
854 mci->csrows[i].nr_pages = npages;
855 mci->csrows[i].grain = 32;
856 mci->csrows[i].csrow_idx = i;
857 mci->csrows[i].dtype =
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800858 (priv->mtr[chan][rank].width == 4) ? DEV_X4 : DEV_X8;
Arthur Jones8f421c592008-07-25 01:49:04 -0700859 mci->csrows[i].ue_count = 0;
860 mci->csrows[i].ce_count = 0;
861 mci->csrows[i].mtype = MEM_RDDR2;
862 mci->csrows[i].edac_mode = EDAC_SECDED;
863 mci->csrows[i].mci = mci;
864 mci->csrows[i].nr_channels = 1;
865 mci->csrows[i].channels[0].chan_idx = 0;
866 mci->csrows[i].channels[0].ce_count = 0;
867 mci->csrows[i].channels[0].csrow = mci->csrows + i;
868 snprintf(mci->csrows[i].channels[0].label,
869 sizeof(mci->csrows[i].channels[0].label),
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800870 "DIMM%u", i5100_rank_to_slot(mci, chan, rank));
Arthur Jones8f421c592008-07-25 01:49:04 -0700871
872 total_pages += npages;
873 }
874}
875
876static int __devinit i5100_init_one(struct pci_dev *pdev,
877 const struct pci_device_id *id)
878{
879 int rc;
880 struct mem_ctl_info *mci;
881 struct i5100_priv *priv;
882 struct pci_dev *ch0mm, *ch1mm;
883 int ret = 0;
884 u32 dw;
885 int ranksperch;
886
887 if (PCI_FUNC(pdev->devfn) != 1)
888 return -ENODEV;
889
890 rc = pci_enable_device(pdev);
891 if (rc < 0) {
892 ret = rc;
893 goto bail;
894 }
895
Arthur Jones43920a52008-07-25 01:49:06 -0700896 /* ECC enabled? */
897 pci_read_config_dword(pdev, I5100_MC, &dw);
Arthur Jonesb238e572008-07-25 01:49:08 -0700898 if (!i5100_mc_errdeten(dw)) {
Arthur Jones43920a52008-07-25 01:49:06 -0700899 printk(KERN_INFO "i5100_edac: ECC not enabled.\n");
900 ret = -ENODEV;
Arthur Jonesb238e572008-07-25 01:49:08 -0700901 goto bail_pdev;
Arthur Jones43920a52008-07-25 01:49:06 -0700902 }
903
Arthur Jones8f421c592008-07-25 01:49:04 -0700904 /* figure out how many ranks, from strapped state of 48GB_Mode input */
905 pci_read_config_dword(pdev, I5100_MS, &dw);
906 ranksperch = !!(dw & (1 << 8)) * 2 + 4;
907
908 if (ranksperch != 4) {
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800909 /* FIXME: get 6 ranks / channel to work - need hw... */
Arthur Jones8f421c592008-07-25 01:49:04 -0700910 printk(KERN_INFO "i5100_edac: unsupported configuration.\n");
911 ret = -ENODEV;
Arthur Jonesb238e572008-07-25 01:49:08 -0700912 goto bail_pdev;
Arthur Jones8f421c592008-07-25 01:49:04 -0700913 }
914
Arthur Jones178d5a72008-07-25 01:49:06 -0700915 /* enable error reporting... */
916 pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw);
917 dw &= ~I5100_FERR_NF_MEM_ANY_MASK;
918 pci_write_config_dword(pdev, I5100_EMASK_MEM, dw);
919
Arthur Jones8f421c592008-07-25 01:49:04 -0700920 /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */
921 ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
922 PCI_DEVICE_ID_INTEL_5100_21, 0);
Arthur Jonesb238e572008-07-25 01:49:08 -0700923 if (!ch0mm) {
924 ret = -ENODEV;
925 goto bail_pdev;
926 }
Arthur Jones8f421c592008-07-25 01:49:04 -0700927
928 rc = pci_enable_device(ch0mm);
929 if (rc < 0) {
930 ret = rc;
931 goto bail_ch0;
932 }
933
934 /* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */
935 ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
936 PCI_DEVICE_ID_INTEL_5100_22, 0);
937 if (!ch1mm) {
938 ret = -ENODEV;
Arthur Jonesb238e572008-07-25 01:49:08 -0700939 goto bail_disable_ch0;
Arthur Jones8f421c592008-07-25 01:49:04 -0700940 }
941
942 rc = pci_enable_device(ch1mm);
943 if (rc < 0) {
944 ret = rc;
945 goto bail_ch1;
946 }
947
948 mci = edac_mc_alloc(sizeof(*priv), ranksperch * 2, 1, 0);
949 if (!mci) {
950 ret = -ENOMEM;
Arthur Jonesb238e572008-07-25 01:49:08 -0700951 goto bail_disable_ch1;
Arthur Jones8f421c592008-07-25 01:49:04 -0700952 }
953
954 mci->dev = &pdev->dev;
955
956 priv = mci->pvt_info;
Nils Carlsonb18dfd02009-12-15 16:47:40 -0800957 priv->ranksperchan = ranksperch;
Arthur Jones8f421c592008-07-25 01:49:04 -0700958 priv->mc = pdev;
959 priv->ch0mm = ch0mm;
960 priv->ch1mm = ch1mm;
961
Nils Carlson295439f2009-12-15 16:47:42 -0800962 INIT_DELAYED_WORK(&(priv->i5100_scrubbing), i5100_refresh_scrubbing);
963
964 /* If scrubbing was already enabled by the bios, start maintaining it */
965 pci_read_config_dword(pdev, I5100_MC, &dw);
966 if (i5100_mc_scrben(dw)) {
967 priv->scrub_enable = 1;
968 schedule_delayed_work(&(priv->i5100_scrubbing),
969 I5100_SCRUB_REFRESH_RATE);
970 }
971
Arthur Jones8f421c592008-07-25 01:49:04 -0700972 i5100_init_dimm_layout(pdev, mci);
973 i5100_init_interleaving(pdev, mci);
974
975 mci->mtype_cap = MEM_FLAG_FB_DDR2;
976 mci->edac_ctl_cap = EDAC_FLAG_SECDED;
977 mci->edac_cap = EDAC_FLAG_SECDED;
978 mci->mod_name = "i5100_edac.c";
979 mci->mod_ver = "not versioned";
980 mci->ctl_name = "i5100";
981 mci->dev_name = pci_name(pdev);
Arthur Jonesb238e572008-07-25 01:49:08 -0700982 mci->ctl_page_to_phys = NULL;
Arthur Jones8f421c592008-07-25 01:49:04 -0700983
984 mci->edac_check = i5100_check_error;
Nils Carlson295439f2009-12-15 16:47:42 -0800985 mci->set_sdram_scrub_rate = i5100_set_scrub_rate;
986 mci->get_sdram_scrub_rate = i5100_get_scrub_rate;
Arthur Jones8f421c592008-07-25 01:49:04 -0700987
988 i5100_init_csrows(mci);
989
990 /* this strange construction seems to be in every driver, dunno why */
991 switch (edac_op_state) {
992 case EDAC_OPSTATE_POLL:
993 case EDAC_OPSTATE_NMI:
994 break;
995 default:
996 edac_op_state = EDAC_OPSTATE_POLL;
997 break;
998 }
999
1000 if (edac_mc_add_mc(mci)) {
1001 ret = -ENODEV;
Nils Carlson295439f2009-12-15 16:47:42 -08001002 goto bail_scrub;
Arthur Jones8f421c592008-07-25 01:49:04 -07001003 }
1004
Arthur Jonesb238e572008-07-25 01:49:08 -07001005 return ret;
Arthur Jones8f421c592008-07-25 01:49:04 -07001006
Nils Carlson295439f2009-12-15 16:47:42 -08001007bail_scrub:
1008 priv->scrub_enable = 0;
1009 cancel_delayed_work_sync(&(priv->i5100_scrubbing));
Arthur Jones8f421c592008-07-25 01:49:04 -07001010 edac_mc_free(mci);
1011
Arthur Jonesb238e572008-07-25 01:49:08 -07001012bail_disable_ch1:
1013 pci_disable_device(ch1mm);
1014
Arthur Jones8f421c592008-07-25 01:49:04 -07001015bail_ch1:
1016 pci_dev_put(ch1mm);
1017
Arthur Jonesb238e572008-07-25 01:49:08 -07001018bail_disable_ch0:
1019 pci_disable_device(ch0mm);
1020
Arthur Jones8f421c592008-07-25 01:49:04 -07001021bail_ch0:
1022 pci_dev_put(ch0mm);
1023
Arthur Jonesb238e572008-07-25 01:49:08 -07001024bail_pdev:
1025 pci_disable_device(pdev);
1026
Arthur Jones8f421c592008-07-25 01:49:04 -07001027bail:
1028 return ret;
1029}
1030
1031static void __devexit i5100_remove_one(struct pci_dev *pdev)
1032{
1033 struct mem_ctl_info *mci;
1034 struct i5100_priv *priv;
1035
1036 mci = edac_mc_del_mc(&pdev->dev);
1037
1038 if (!mci)
1039 return;
1040
1041 priv = mci->pvt_info;
Nils Carlson295439f2009-12-15 16:47:42 -08001042
1043 priv->scrub_enable = 0;
1044 cancel_delayed_work_sync(&(priv->i5100_scrubbing));
1045
Arthur Jonesb238e572008-07-25 01:49:08 -07001046 pci_disable_device(pdev);
1047 pci_disable_device(priv->ch0mm);
1048 pci_disable_device(priv->ch1mm);
Arthur Jones8f421c592008-07-25 01:49:04 -07001049 pci_dev_put(priv->ch0mm);
1050 pci_dev_put(priv->ch1mm);
1051
1052 edac_mc_free(mci);
1053}
1054
1055static const struct pci_device_id i5100_pci_tbl[] __devinitdata = {
1056 /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */
1057 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) },
1058 { 0, }
1059};
1060MODULE_DEVICE_TABLE(pci, i5100_pci_tbl);
1061
1062static struct pci_driver i5100_driver = {
1063 .name = KBUILD_BASENAME,
1064 .probe = i5100_init_one,
1065 .remove = __devexit_p(i5100_remove_one),
1066 .id_table = i5100_pci_tbl,
1067};
1068
1069static int __init i5100_init(void)
1070{
1071 int pci_rc;
1072
1073 pci_rc = pci_register_driver(&i5100_driver);
1074
1075 return (pci_rc < 0) ? pci_rc : 0;
1076}
1077
1078static void __exit i5100_exit(void)
1079{
1080 pci_unregister_driver(&i5100_driver);
1081}
1082
1083module_init(i5100_init);
1084module_exit(i5100_exit);
1085
1086MODULE_LICENSE("GPL");
1087MODULE_AUTHOR
1088 ("Arthur Jones <ajones@riverbed.com>");
1089MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers");