aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac/i5100_edac.c
diff options
context:
space:
mode:
authorArthur Jones <ajones@riverbed.com>2008-07-25 04:49:04 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-25 13:53:48 -0400
commit8f421c595a9145959d8aab09172743132abdffdb (patch)
tree05c374ad506a47c127007dcab4dc7018b7f93c1c /drivers/edac/i5100_edac.c
parent48e90761b570ff57f58b726229d229729949c5bb (diff)
edac: i5100 new intel chipset driver
Preliminary support for the Intel 5100 MCH. CE and UE errors are reported along with the current DIMM label information and other memory parameters. Reasons why this is preliminary: 1) This chip has 2 independent memory controllers which, for best perforance, use interleaved accesses to the DDR2 memory. This architecture does not map very well to the current edac data structures which depend on symmetric channel access to the interleaved data. Without core changes, the best I could do for now is to map both memory controllers to different csrows (first all ranks of controller 0, then all ranks of controller 1). Someone much more familiar with the edac core than I will probably need to come up with a more general data structure to handle the interleaving and de-interleaving of the two memory controllers. 2) I have not yet tackled the de-interleaving of the rank/controller address space into the physical address space of the CPU. There is nothing fundamentally missing, it is just ending up to be a lot of code, and I'd rather keep it separate for now, esp since it doesn't work yet... 3) The code depends on a particular i5100 chip select to DIMM mainboard chip select mapping. This mapping seems obvious to me in order to support dual and single ranked memory, but it is not unique and DIMM labels could be wrong on other mainboards. There is no way to query this mapping that I know of. 4) The code requires that the i5100 is in 32GB mode. Only 4 ranks per controller, 2 ranks per DIMM are supported. I do not have hardware (nor do I expect to have hardware anytime soon) for the 48GB (6 ranks per controller) mode. 5) The serial presence detect code should be broken out into a "real" i2c driver so that decode-dimms.pl can work. Signed-off-by: Arthur Jones <ajones@riverbed.com> Signed-off-by: Doug Thompson <dougthompson@xmission.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/edac/i5100_edac.c')
-rw-r--r--drivers/edac/i5100_edac.c827
1 files changed, 827 insertions, 0 deletions
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
new file mode 100644
index 000000000000..43430bf70181
--- /dev/null
+++ b/drivers/edac/i5100_edac.c
@@ -0,0 +1,827 @@
1/*
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
24/* register addresses and bit field accessors... */
25
26/* device 16, func 1 */
27#define I5100_MS 0x44 /* Memory Status Register */
28#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */
29#define I5100_SPDDATA_RDO(a) ((a) >> 15 & 1)
30#define I5100_SPDDATA_SBE(a) ((a) >> 13 & 1)
31#define I5100_SPDDATA_BUSY(a) ((a) >> 12 & 1)
32#define I5100_SPDDATA_DATA(a) ((a) & ((1 << 8) - 1))
33#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */
34#define I5100_SPDCMD_DTI(a) (((a) & ((1 << 4) - 1)) << 28)
35#define I5100_SPDCMD_CKOVRD(a) (((a) & 1) << 27)
36#define I5100_SPDCMD_SA(a) (((a) & ((1 << 3) - 1)) << 24)
37#define I5100_SPDCMD_BA(a) (((a) & ((1 << 8) - 1)) << 16)
38#define I5100_SPDCMD_DATA(a) (((a) & ((1 << 8) - 1)) << 8)
39#define I5100_SPDCMD_CMD(a) ((a) & 1)
40#define I5100_TOLM 0x6c /* Top of Low Memory */
41#define I5100_TOLM_TOLM(a) ((a) >> 12 & ((1 << 4) - 1))
42#define I5100_MIR0 0x80 /* Memory Interleave Range 0 */
43#define I5100_MIR1 0x84 /* Memory Interleave Range 1 */
44#define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */
45#define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */
46#define I5100_MIR_LIMIT(a) ((a) >> 4 & ((1 << 12) - 1))
47#define I5100_MIR_WAY1(a) ((a) >> 1 & 1)
48#define I5100_MIR_WAY0(a) ((a) & 1)
49#define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */
50#define I5100_FERR_NF_MEM_CHAN_INDX(a) ((a) >> 28 & 1)
51#define I5100_FERR_NF_MEM_SPD_MASK (1 << 18)
52#define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16)
53#define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15)
54#define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14)
55#define I5100_FERR_NF_MEM_
56#define I5100_FERR_NF_MEM_
57#define I5100_FERR_NF_MEM_ANY_MASK \
58 (I5100_FERR_NF_MEM_M16ERR_MASK | \
59 I5100_FERR_NF_MEM_M15ERR_MASK | \
60 I5100_FERR_NF_MEM_M14ERR_MASK)
61#define I5100_FERR_NF_MEM_ANY(a) ((a) & I5100_FERR_NF_MEM_ANY_MASK)
62#define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */
63#define I5100_NERR_NF_MEM_ANY(a) I5100_FERR_NF_MEM_ANY(a)
64
65/* device 21 and 22, func 0 */
66#define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */
67#define I5100_DMIR 0x15c /* DIMM Interleave Range */
68#define I5100_DMIR_LIMIT(a) ((a) >> 16 & ((1 << 11) - 1))
69#define I5100_DMIR_RANK(a, i) ((a) >> (4 * i) & ((1 << 2) - 1))
70#define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */
71#define I5100_MTR_PRESENT(a) ((a) >> 10 & 1)
72#define I5100_MTR_ETHROTTLE(a) ((a) >> 9 & 1)
73#define I5100_MTR_WIDTH(a) ((a) >> 8 & 1)
74#define I5100_MTR_NUMBANK(a) ((a) >> 6 & 1)
75#define I5100_MTR_NUMROW(a) ((a) >> 2 & ((1 << 2) - 1))
76#define I5100_MTR_NUMCOL(a) ((a) & ((1 << 2) - 1))
77#define I5100_VALIDLOG 0x18c /* Valid Log Markers */
78#define I5100_VALIDLOG_REDMEMVALID(a) ((a) >> 2 & 1)
79#define I5100_VALIDLOG_RECMEMVALID(a) ((a) >> 1 & 1)
80#define I5100_VALIDLOG_NRECMEMVALID(a) ((a) & 1)
81#define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */
82#define I5100_NRECMEMA_MERR(a) ((a) >> 15 & ((1 << 5) - 1))
83#define I5100_NRECMEMA_BANK(a) ((a) >> 12 & ((1 << 3) - 1))
84#define I5100_NRECMEMA_RANK(a) ((a) >> 8 & ((1 << 3) - 1))
85#define I5100_NRECMEMA_DM_BUF_ID(a) ((a) & ((1 << 8) - 1))
86#define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */
87#define I5100_NRECMEMB_CAS(a) ((a) >> 16 & ((1 << 13) - 1))
88#define I5100_NRECMEMB_RAS(a) ((a) & ((1 << 16) - 1))
89#define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */
90#define I5100_REDMEMA_SYNDROME(a) (a)
91#define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */
92#define I5100_REDMEMB_ECC_LOCATOR(a) ((a) & ((1 << 18) - 1))
93#define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */
94#define I5100_RECMEMA_MERR(a) I5100_NRECMEMA_MERR(a)
95#define I5100_RECMEMA_BANK(a) I5100_NRECMEMA_BANK(a)
96#define I5100_RECMEMA_RANK(a) I5100_NRECMEMA_RANK(a)
97#define I5100_RECMEMA_DM_BUF_ID(a) I5100_NRECMEMA_DM_BUF_ID(a)
98#define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */
99#define I5100_RECMEMB_CAS(a) I5100_NRECMEMB_CAS(a)
100#define I5100_RECMEMB_RAS(a) I5100_NRECMEMB_RAS(a)
101
102/* some generic limits */
103#define I5100_MAX_RANKS_PER_CTLR 6
104#define I5100_MAX_CTLRS 2
105#define I5100_MAX_RANKS_PER_DIMM 4
106#define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */
107#define I5100_MAX_DIMM_SLOTS_PER_CTLR 4
108#define I5100_MAX_RANK_INTERLEAVE 4
109#define I5100_MAX_DMIRS 5
110
111struct i5100_priv {
112 /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
113 int dimm_numrank[I5100_MAX_CTLRS][I5100_MAX_DIMM_SLOTS_PER_CTLR];
114
115 /*
116 * mainboard chip select map -- maps i5100 chip selects to
117 * DIMM slot chip selects. In the case of only 4 ranks per
118 * controller, the mapping is fairly obvious but not unique.
119 * we map -1 -> NC and assume both controllers use the same
120 * map...
121 *
122 */
123 int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CTLR][I5100_MAX_RANKS_PER_DIMM];
124
125 /* memory interleave range */
126 struct {
127 u64 limit;
128 unsigned way[2];
129 } mir[I5100_MAX_CTLRS];
130
131 /* adjusted memory interleave range register */
132 unsigned amir[I5100_MAX_CTLRS];
133
134 /* dimm interleave range */
135 struct {
136 unsigned rank[I5100_MAX_RANK_INTERLEAVE];
137 u64 limit;
138 } dmir[I5100_MAX_CTLRS][I5100_MAX_DMIRS];
139
140 /* memory technology registers... */
141 struct {
142 unsigned present; /* 0 or 1 */
143 unsigned ethrottle; /* 0 or 1 */
144 unsigned width; /* 4 or 8 bits */
145 unsigned numbank; /* 2 or 3 lines */
146 unsigned numrow; /* 13 .. 16 lines */
147 unsigned numcol; /* 11 .. 12 lines */
148 } mtr[I5100_MAX_CTLRS][I5100_MAX_RANKS_PER_CTLR];
149
150 u64 tolm; /* top of low memory in bytes */
151 unsigned ranksperctlr; /* number of ranks per controller */
152
153 struct pci_dev *mc; /* device 16 func 1 */
154 struct pci_dev *ch0mm; /* device 21 func 0 */
155 struct pci_dev *ch1mm; /* device 22 func 0 */
156};
157
158/* map a rank/ctlr to a slot number on the mainboard */
159static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
160 int ctlr, int rank)
161{
162 const struct i5100_priv *priv = mci->pvt_info;
163 int i;
164
165 for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) {
166 int j;
167 const int numrank = priv->dimm_numrank[ctlr][i];
168
169 for (j = 0; j < numrank; j++)
170 if (priv->dimm_csmap[i][j] == rank)
171 return i * 2 + ctlr;
172 }
173
174 return -1;
175}
176
177/*
178 * The processor bus memory addresses are broken into three
179 * pieces, whereas the controller addresses are contiguous.
180 *
181 * here we map from the controller address space to the
182 * processor address space:
183 *
184 * Processor Address Space
185 * +-----------------------------+
186 * | |
187 * | "high" memory addresses |
188 * | |
189 * +-----------------------------+ <- 4GB on the i5100
190 * | |
191 * | other non-memory addresses |
192 * | |
193 * +-----------------------------+ <- top of low memory
194 * | |
195 * | "low" memory addresses |
196 * | |
197 * +-----------------------------+
198 */
199static unsigned long i5100_ctl_page_to_phys(struct mem_ctl_info *mci,
200 unsigned long cntlr_addr)
201{
202 const struct i5100_priv *priv = mci->pvt_info;
203
204 if (cntlr_addr < priv->tolm)
205 return cntlr_addr;
206
207 return (1ULL << 32) + (cntlr_addr - priv->tolm);
208}
209
210static const char *i5100_err_msg(unsigned err)
211{
212 const char *merrs[] = {
213 "unknown", /* 0 */
214 "uncorrectable data ECC on replay", /* 1 */
215 "unknown", /* 2 */
216 "unknown", /* 3 */
217 "aliased uncorrectable demand data ECC", /* 4 */
218 "aliased uncorrectable spare-copy data ECC", /* 5 */
219 "aliased uncorrectable patrol data ECC", /* 6 */
220 "unknown", /* 7 */
221 "unknown", /* 8 */
222 "unknown", /* 9 */
223 "non-aliased uncorrectable demand data ECC", /* 10 */
224 "non-aliased uncorrectable spare-copy data ECC", /* 11 */
225 "non-aliased uncorrectable patrol data ECC", /* 12 */
226 "unknown", /* 13 */
227 "correctable demand data ECC", /* 14 */
228 "correctable spare-copy data ECC", /* 15 */
229 "correctable patrol data ECC", /* 16 */
230 "unknown", /* 17 */
231 "SPD protocol error", /* 18 */
232 "unknown", /* 19 */
233 "spare copy initiated", /* 20 */
234 "spare copy completed", /* 21 */
235 };
236 unsigned i;
237
238 for (i = 0; i < ARRAY_SIZE(merrs); i++)
239 if (1 << i & err)
240 return merrs[i];
241
242 return "none";
243}
244
245/* convert csrow index into a rank (per controller -- 0..5) */
246static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow)
247{
248 const struct i5100_priv *priv = mci->pvt_info;
249
250 return csrow % priv->ranksperctlr;
251}
252
253/* convert csrow index into a controller (0..1) */
254static int i5100_csrow_to_cntlr(const struct mem_ctl_info *mci, int csrow)
255{
256 const struct i5100_priv *priv = mci->pvt_info;
257
258 return csrow / priv->ranksperctlr;
259}
260
261static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci,
262 int ctlr, int rank)
263{
264 const struct i5100_priv *priv = mci->pvt_info;
265
266 return ctlr * priv->ranksperctlr + rank;
267}
268
269static void i5100_handle_ce(struct mem_ctl_info *mci,
270 int ctlr,
271 unsigned bank,
272 unsigned rank,
273 unsigned long syndrome,
274 unsigned cas,
275 unsigned ras,
276 const char *msg)
277{
278 const int csrow = i5100_rank_to_csrow(mci, ctlr, rank);
279
280 printk(KERN_ERR
281 "CE ctlr %d, bank %u, rank %u, syndrome 0x%lx, "
282 "cas %u, ras %u, csrow %u, label \"%s\": %s\n",
283 ctlr, bank, rank, syndrome, cas, ras,
284 csrow, mci->csrows[csrow].channels[0].label, msg);
285
286 mci->ce_count++;
287 mci->csrows[csrow].ce_count++;
288 mci->csrows[csrow].channels[0].ce_count++;
289}
290
291static void i5100_handle_ue(struct mem_ctl_info *mci,
292 int ctlr,
293 unsigned bank,
294 unsigned rank,
295 unsigned long syndrome,
296 unsigned cas,
297 unsigned ras,
298 const char *msg)
299{
300 const int csrow = i5100_rank_to_csrow(mci, ctlr, rank);
301
302 printk(KERN_ERR
303 "UE ctlr %d, bank %u, rank %u, syndrome 0x%lx, "
304 "cas %u, ras %u, csrow %u, label \"%s\": %s\n",
305 ctlr, bank, rank, syndrome, cas, ras,
306 csrow, mci->csrows[csrow].channels[0].label, msg);
307
308 mci->ue_count++;
309 mci->csrows[csrow].ue_count++;
310}
311
312static void i5100_read_log(struct mem_ctl_info *mci, int ctlr,
313 u32 ferr, u32 nerr)
314{
315 struct i5100_priv *priv = mci->pvt_info;
316 struct pci_dev *pdev = (ctlr) ? priv->ch1mm : priv->ch0mm;
317 u32 dw;
318 u32 dw2;
319 unsigned syndrome = 0;
320 unsigned ecc_loc = 0;
321 unsigned merr;
322 unsigned bank;
323 unsigned rank;
324 unsigned cas;
325 unsigned ras;
326
327 pci_read_config_dword(pdev, I5100_VALIDLOG, &dw);
328
329 if (I5100_VALIDLOG_REDMEMVALID(dw)) {
330 pci_read_config_dword(pdev, I5100_REDMEMA, &dw2);
331 syndrome = I5100_REDMEMA_SYNDROME(dw2);
332 pci_read_config_dword(pdev, I5100_REDMEMB, &dw2);
333 ecc_loc = I5100_REDMEMB_ECC_LOCATOR(dw2);
334 }
335
336 if (I5100_VALIDLOG_RECMEMVALID(dw)) {
337 const char *msg;
338
339 pci_read_config_dword(pdev, I5100_RECMEMA, &dw2);
340 merr = I5100_RECMEMA_MERR(dw2);
341 bank = I5100_RECMEMA_BANK(dw2);
342 rank = I5100_RECMEMA_RANK(dw2);
343
344 pci_read_config_dword(pdev, I5100_RECMEMB, &dw2);
345 cas = I5100_RECMEMB_CAS(dw2);
346 ras = I5100_RECMEMB_RAS(dw2);
347
348 /* FIXME: not really sure if this is what merr is...
349 */
350 if (!merr)
351 msg = i5100_err_msg(ferr);
352 else
353 msg = i5100_err_msg(nerr);
354
355 i5100_handle_ce(mci, ctlr, bank, rank, syndrome, cas, ras, msg);
356 }
357
358 if (I5100_VALIDLOG_NRECMEMVALID(dw)) {
359 const char *msg;
360
361 pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2);
362 merr = I5100_NRECMEMA_MERR(dw2);
363 bank = I5100_NRECMEMA_BANK(dw2);
364 rank = I5100_NRECMEMA_RANK(dw2);
365
366 pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2);
367 cas = I5100_NRECMEMB_CAS(dw2);
368 ras = I5100_NRECMEMB_RAS(dw2);
369
370 /* FIXME: not really sure if this is what merr is...
371 */
372 if (!merr)
373 msg = i5100_err_msg(ferr);
374 else
375 msg = i5100_err_msg(nerr);
376
377 i5100_handle_ue(mci, ctlr, bank, rank, syndrome, cas, ras, msg);
378 }
379
380 pci_write_config_dword(pdev, I5100_VALIDLOG, dw);
381}
382
383static void i5100_check_error(struct mem_ctl_info *mci)
384{
385 struct i5100_priv *priv = mci->pvt_info;
386 u32 dw;
387
388
389 pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw);
390 if (I5100_FERR_NF_MEM_ANY(dw)) {
391 u32 dw2;
392
393 pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2);
394 if (dw2)
395 pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM,
396 dw2);
397 pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
398
399 i5100_read_log(mci, I5100_FERR_NF_MEM_CHAN_INDX(dw),
400 I5100_FERR_NF_MEM_ANY(dw),
401 I5100_NERR_NF_MEM_ANY(dw2));
402 }
403}
404
405static struct pci_dev *pci_get_device_func(unsigned vendor,
406 unsigned device,
407 unsigned func)
408{
409 struct pci_dev *ret = NULL;
410
411 while (1) {
412 ret = pci_get_device(vendor, device, ret);
413
414 if (!ret)
415 break;
416
417 if (PCI_FUNC(ret->devfn) == func)
418 break;
419 }
420
421 return ret;
422}
423
424static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci,
425 int csrow)
426{
427 struct i5100_priv *priv = mci->pvt_info;
428 const unsigned ctlr_rank = i5100_csrow_to_rank(mci, csrow);
429 const unsigned ctlr = i5100_csrow_to_cntlr(mci, csrow);
430 unsigned addr_lines;
431
432 /* dimm present? */
433 if (!priv->mtr[ctlr][ctlr_rank].present)
434 return 0ULL;
435
436 addr_lines =
437 I5100_DIMM_ADDR_LINES +
438 priv->mtr[ctlr][ctlr_rank].numcol +
439 priv->mtr[ctlr][ctlr_rank].numrow +
440 priv->mtr[ctlr][ctlr_rank].numbank;
441
442 return (unsigned long)
443 ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
444}
445
446static void __devinit i5100_init_mtr(struct mem_ctl_info *mci)
447{
448 struct i5100_priv *priv = mci->pvt_info;
449 struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
450 int i;
451
452 for (i = 0; i < I5100_MAX_CTLRS; i++) {
453 int j;
454 struct pci_dev *pdev = mms[i];
455
456 for (j = 0; j < I5100_MAX_RANKS_PER_CTLR; j++) {
457 const unsigned addr =
458 (j < 4) ? I5100_MTR_0 + j * 2 :
459 I5100_MTR_4 + (j - 4) * 2;
460 u16 w;
461
462 pci_read_config_word(pdev, addr, &w);
463
464 priv->mtr[i][j].present = I5100_MTR_PRESENT(w);
465 priv->mtr[i][j].ethrottle = I5100_MTR_ETHROTTLE(w);
466 priv->mtr[i][j].width = 4 + 4 * I5100_MTR_WIDTH(w);
467 priv->mtr[i][j].numbank = 2 + I5100_MTR_NUMBANK(w);
468 priv->mtr[i][j].numrow = 13 + I5100_MTR_NUMROW(w);
469 priv->mtr[i][j].numcol = 10 + I5100_MTR_NUMCOL(w);
470 }
471 }
472}
473
474/*
475 * FIXME: make this into a real i2c adapter (so that dimm-decode
476 * will work)?
477 */
478static int i5100_read_spd_byte(const struct mem_ctl_info *mci,
479 u8 ch, u8 slot, u8 addr, u8 *byte)
480{
481 struct i5100_priv *priv = mci->pvt_info;
482 u16 w;
483 u32 dw;
484 unsigned long et;
485
486 pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
487 if (I5100_SPDDATA_BUSY(w))
488 return -1;
489
490 dw = I5100_SPDCMD_DTI(0xa) |
491 I5100_SPDCMD_CKOVRD(1) |
492 I5100_SPDCMD_SA(ch * 4 + slot) |
493 I5100_SPDCMD_BA(addr) |
494 I5100_SPDCMD_DATA(0) |
495 I5100_SPDCMD_CMD(0);
496 pci_write_config_dword(priv->mc, I5100_SPDCMD, dw);
497
498 /* wait up to 100ms */
499 et = jiffies + HZ / 10;
500 udelay(100);
501 while (1) {
502 pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
503 if (!I5100_SPDDATA_BUSY(w))
504 break;
505 udelay(100);
506 }
507
508 if (!I5100_SPDDATA_RDO(w) || I5100_SPDDATA_SBE(w))
509 return -1;
510
511 *byte = I5100_SPDDATA_DATA(w);
512
513 return 0;
514}
515
516/*
517 * fill dimm chip select map
518 *
519 * FIXME:
520 * o only valid for 4 ranks per controller
521 * o not the only way to may chip selects to dimm slots
522 * o investigate if there is some way to obtain this map from the bios
523 */
524static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci)
525{
526 struct i5100_priv *priv = mci->pvt_info;
527 int i;
528
529 WARN_ON(priv->ranksperctlr != 4);
530
531 for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) {
532 int j;
533
534 for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++)
535 priv->dimm_csmap[i][j] = -1; /* default NC */
536 }
537
538 /* only 2 chip selects per slot... */
539 priv->dimm_csmap[0][0] = 0;
540 priv->dimm_csmap[0][1] = 3;
541 priv->dimm_csmap[1][0] = 1;
542 priv->dimm_csmap[1][1] = 2;
543 priv->dimm_csmap[2][0] = 2;
544 priv->dimm_csmap[3][0] = 3;
545}
546
547static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev,
548 struct mem_ctl_info *mci)
549{
550 struct i5100_priv *priv = mci->pvt_info;
551 int i;
552
553 for (i = 0; i < I5100_MAX_CTLRS; i++) {
554 int j;
555
556 for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CTLR; j++) {
557 u8 rank;
558
559 if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0)
560 priv->dimm_numrank[i][j] = 0;
561 else
562 priv->dimm_numrank[i][j] = (rank & 3) + 1;
563 }
564 }
565
566 i5100_init_dimm_csmap(mci);
567}
568
569static void __devinit i5100_init_interleaving(struct pci_dev *pdev,
570 struct mem_ctl_info *mci)
571{
572 u16 w;
573 u32 dw;
574 struct i5100_priv *priv = mci->pvt_info;
575 struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
576 int i;
577
578 pci_read_config_word(pdev, I5100_TOLM, &w);
579 priv->tolm = (u64) I5100_TOLM_TOLM(w) * 256 * 1024 * 1024;
580
581 pci_read_config_word(pdev, I5100_MIR0, &w);
582 priv->mir[0].limit = (u64) I5100_MIR_LIMIT(w) << 28;
583 priv->mir[0].way[1] = I5100_MIR_WAY1(w);
584 priv->mir[0].way[0] = I5100_MIR_WAY0(w);
585
586 pci_read_config_word(pdev, I5100_MIR1, &w);
587 priv->mir[1].limit = (u64) I5100_MIR_LIMIT(w) << 28;
588 priv->mir[1].way[1] = I5100_MIR_WAY1(w);
589 priv->mir[1].way[0] = I5100_MIR_WAY0(w);
590
591 pci_read_config_word(pdev, I5100_AMIR_0, &w);
592 priv->amir[0] = w;
593 pci_read_config_word(pdev, I5100_AMIR_1, &w);
594 priv->amir[1] = w;
595
596 for (i = 0; i < I5100_MAX_CTLRS; i++) {
597 int j;
598
599 for (j = 0; j < 5; j++) {
600 int k;
601
602 pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw);
603
604 priv->dmir[i][j].limit =
605 (u64) I5100_DMIR_LIMIT(dw) << 28;
606 for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++)
607 priv->dmir[i][j].rank[k] =
608 I5100_DMIR_RANK(dw, k);
609 }
610 }
611
612 i5100_init_mtr(mci);
613}
614
615static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
616{
617 int i;
618 unsigned long total_pages = 0UL;
619 struct i5100_priv *priv = mci->pvt_info;
620
621 for (i = 0; i < mci->nr_csrows; i++) {
622 const unsigned long npages = i5100_npages(mci, i);
623 const unsigned cntlr = i5100_csrow_to_cntlr(mci, i);
624 const unsigned rank = i5100_csrow_to_rank(mci, i);
625
626 if (!npages)
627 continue;
628
629 /*
630 * FIXME: these two are totally bogus -- I don't see how to
631 * map them correctly to this structure...
632 */
633 mci->csrows[i].first_page = total_pages;
634 mci->csrows[i].last_page = total_pages + npages - 1;
635 mci->csrows[i].page_mask = 0UL;
636
637 mci->csrows[i].nr_pages = npages;
638 mci->csrows[i].grain = 32;
639 mci->csrows[i].csrow_idx = i;
640 mci->csrows[i].dtype =
641 (priv->mtr[cntlr][rank].width == 4) ? DEV_X4 : DEV_X8;
642 mci->csrows[i].ue_count = 0;
643 mci->csrows[i].ce_count = 0;
644 mci->csrows[i].mtype = MEM_RDDR2;
645 mci->csrows[i].edac_mode = EDAC_SECDED;
646 mci->csrows[i].mci = mci;
647 mci->csrows[i].nr_channels = 1;
648 mci->csrows[i].channels[0].chan_idx = 0;
649 mci->csrows[i].channels[0].ce_count = 0;
650 mci->csrows[i].channels[0].csrow = mci->csrows + i;
651 snprintf(mci->csrows[i].channels[0].label,
652 sizeof(mci->csrows[i].channels[0].label),
653 "DIMM%u", i5100_rank_to_slot(mci, cntlr, rank));
654
655 total_pages += npages;
656 }
657}
658
659static int __devinit i5100_init_one(struct pci_dev *pdev,
660 const struct pci_device_id *id)
661{
662 int rc;
663 struct mem_ctl_info *mci;
664 struct i5100_priv *priv;
665 struct pci_dev *ch0mm, *ch1mm;
666 int ret = 0;
667 u32 dw;
668 int ranksperch;
669
670 if (PCI_FUNC(pdev->devfn) != 1)
671 return -ENODEV;
672
673 rc = pci_enable_device(pdev);
674 if (rc < 0) {
675 ret = rc;
676 goto bail;
677 }
678
679 /* figure out how many ranks, from strapped state of 48GB_Mode input */
680 pci_read_config_dword(pdev, I5100_MS, &dw);
681 ranksperch = !!(dw & (1 << 8)) * 2 + 4;
682
683 if (ranksperch != 4) {
684 /* FIXME: get 6 ranks / controller to work - need hw... */
685 printk(KERN_INFO "i5100_edac: unsupported configuration.\n");
686 ret = -ENODEV;
687 goto bail;
688 }
689
690 /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */
691 ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
692 PCI_DEVICE_ID_INTEL_5100_21, 0);
693 if (!ch0mm)
694 return -ENODEV;
695
696 rc = pci_enable_device(ch0mm);
697 if (rc < 0) {
698 ret = rc;
699 goto bail_ch0;
700 }
701
702 /* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */
703 ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
704 PCI_DEVICE_ID_INTEL_5100_22, 0);
705 if (!ch1mm) {
706 ret = -ENODEV;
707 goto bail_ch0;
708 }
709
710 rc = pci_enable_device(ch1mm);
711 if (rc < 0) {
712 ret = rc;
713 goto bail_ch1;
714 }
715
716 mci = edac_mc_alloc(sizeof(*priv), ranksperch * 2, 1, 0);
717 if (!mci) {
718 ret = -ENOMEM;
719 goto bail_ch1;
720 }
721
722 mci->dev = &pdev->dev;
723
724 priv = mci->pvt_info;
725 priv->ranksperctlr = ranksperch;
726 priv->mc = pdev;
727 priv->ch0mm = ch0mm;
728 priv->ch1mm = ch1mm;
729
730 i5100_init_dimm_layout(pdev, mci);
731 i5100_init_interleaving(pdev, mci);
732
733 mci->mtype_cap = MEM_FLAG_FB_DDR2;
734 mci->edac_ctl_cap = EDAC_FLAG_SECDED;
735 mci->edac_cap = EDAC_FLAG_SECDED;
736 mci->mod_name = "i5100_edac.c";
737 mci->mod_ver = "not versioned";
738 mci->ctl_name = "i5100";
739 mci->dev_name = pci_name(pdev);
740 mci->ctl_page_to_phys = i5100_ctl_page_to_phys;
741
742 mci->edac_check = i5100_check_error;
743
744 i5100_init_csrows(mci);
745
746 /* this strange construction seems to be in every driver, dunno why */
747 switch (edac_op_state) {
748 case EDAC_OPSTATE_POLL:
749 case EDAC_OPSTATE_NMI:
750 break;
751 default:
752 edac_op_state = EDAC_OPSTATE_POLL;
753 break;
754 }
755
756 if (edac_mc_add_mc(mci)) {
757 ret = -ENODEV;
758 goto bail_mc;
759 }
760
761 goto bail;
762
763bail_mc:
764 edac_mc_free(mci);
765
766bail_ch1:
767 pci_dev_put(ch1mm);
768
769bail_ch0:
770 pci_dev_put(ch0mm);
771
772bail:
773 return ret;
774}
775
776static void __devexit i5100_remove_one(struct pci_dev *pdev)
777{
778 struct mem_ctl_info *mci;
779 struct i5100_priv *priv;
780
781 mci = edac_mc_del_mc(&pdev->dev);
782
783 if (!mci)
784 return;
785
786 priv = mci->pvt_info;
787 pci_dev_put(priv->ch0mm);
788 pci_dev_put(priv->ch1mm);
789
790 edac_mc_free(mci);
791}
792
793static const struct pci_device_id i5100_pci_tbl[] __devinitdata = {
794 /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */
795 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) },
796 { 0, }
797};
798MODULE_DEVICE_TABLE(pci, i5100_pci_tbl);
799
800static struct pci_driver i5100_driver = {
801 .name = KBUILD_BASENAME,
802 .probe = i5100_init_one,
803 .remove = __devexit_p(i5100_remove_one),
804 .id_table = i5100_pci_tbl,
805};
806
807static int __init i5100_init(void)
808{
809 int pci_rc;
810
811 pci_rc = pci_register_driver(&i5100_driver);
812
813 return (pci_rc < 0) ? pci_rc : 0;
814}
815
816static void __exit i5100_exit(void)
817{
818 pci_unregister_driver(&i5100_driver);
819}
820
821module_init(i5100_init);
822module_exit(i5100_exit);
823
824MODULE_LICENSE("GPL");
825MODULE_AUTHOR
826 ("Arthur Jones <ajones@riverbed.com>");
827MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers");