diff options
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/amd64_edac.c | 46 | ||||
-rw-r--r-- | drivers/edac/i5100_edac.c | 252 |
2 files changed, 199 insertions, 99 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 5fdd6daa40ea..df5b68433f34 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c | |||
@@ -13,6 +13,8 @@ module_param(report_gart_errors, int, 0644); | |||
13 | static int ecc_enable_override; | 13 | static int ecc_enable_override; |
14 | module_param(ecc_enable_override, int, 0644); | 14 | module_param(ecc_enable_override, int, 0644); |
15 | 15 | ||
16 | static struct msr *msrs; | ||
17 | |||
16 | /* Lookup table for all possible MC control instances */ | 18 | /* Lookup table for all possible MC control instances */ |
17 | struct amd64_pvt; | 19 | struct amd64_pvt; |
18 | static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES]; | 20 | static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES]; |
@@ -2495,8 +2497,7 @@ static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, int nid) | |||
2495 | static bool amd64_nb_mce_bank_enabled_on_node(int nid) | 2497 | static bool amd64_nb_mce_bank_enabled_on_node(int nid) |
2496 | { | 2498 | { |
2497 | cpumask_var_t mask; | 2499 | cpumask_var_t mask; |
2498 | struct msr *msrs; | 2500 | int cpu, nbe; |
2499 | int cpu, nbe, idx = 0; | ||
2500 | bool ret = false; | 2501 | bool ret = false; |
2501 | 2502 | ||
2502 | if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { | 2503 | if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { |
@@ -2507,32 +2508,22 @@ static bool amd64_nb_mce_bank_enabled_on_node(int nid) | |||
2507 | 2508 | ||
2508 | get_cpus_on_this_dct_cpumask(mask, nid); | 2509 | get_cpus_on_this_dct_cpumask(mask, nid); |
2509 | 2510 | ||
2510 | msrs = kzalloc(sizeof(struct msr) * cpumask_weight(mask), GFP_KERNEL); | ||
2511 | if (!msrs) { | ||
2512 | amd64_printk(KERN_WARNING, "%s: error allocating msrs\n", | ||
2513 | __func__); | ||
2514 | free_cpumask_var(mask); | ||
2515 | return false; | ||
2516 | } | ||
2517 | |||
2518 | rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); | 2511 | rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); |
2519 | 2512 | ||
2520 | for_each_cpu(cpu, mask) { | 2513 | for_each_cpu(cpu, mask) { |
2521 | nbe = msrs[idx].l & K8_MSR_MCGCTL_NBE; | 2514 | struct msr *reg = per_cpu_ptr(msrs, cpu); |
2515 | nbe = reg->l & K8_MSR_MCGCTL_NBE; | ||
2522 | 2516 | ||
2523 | debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", | 2517 | debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", |
2524 | cpu, msrs[idx].q, | 2518 | cpu, reg->q, |
2525 | (nbe ? "enabled" : "disabled")); | 2519 | (nbe ? "enabled" : "disabled")); |
2526 | 2520 | ||
2527 | if (!nbe) | 2521 | if (!nbe) |
2528 | goto out; | 2522 | goto out; |
2529 | |||
2530 | idx++; | ||
2531 | } | 2523 | } |
2532 | ret = true; | 2524 | ret = true; |
2533 | 2525 | ||
2534 | out: | 2526 | out: |
2535 | kfree(msrs); | ||
2536 | free_cpumask_var(mask); | 2527 | free_cpumask_var(mask); |
2537 | return ret; | 2528 | return ret; |
2538 | } | 2529 | } |
@@ -2540,8 +2531,7 @@ out: | |||
2540 | static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on) | 2531 | static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on) |
2541 | { | 2532 | { |
2542 | cpumask_var_t cmask; | 2533 | cpumask_var_t cmask; |
2543 | struct msr *msrs = NULL; | 2534 | int cpu; |
2544 | int cpu, idx = 0; | ||
2545 | 2535 | ||
2546 | if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { | 2536 | if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { |
2547 | amd64_printk(KERN_WARNING, "%s: error allocating mask\n", | 2537 | amd64_printk(KERN_WARNING, "%s: error allocating mask\n", |
@@ -2551,34 +2541,27 @@ static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on) | |||
2551 | 2541 | ||
2552 | get_cpus_on_this_dct_cpumask(cmask, pvt->mc_node_id); | 2542 | get_cpus_on_this_dct_cpumask(cmask, pvt->mc_node_id); |
2553 | 2543 | ||
2554 | msrs = kzalloc(sizeof(struct msr) * cpumask_weight(cmask), GFP_KERNEL); | ||
2555 | if (!msrs) { | ||
2556 | amd64_printk(KERN_WARNING, "%s: error allocating msrs\n", | ||
2557 | __func__); | ||
2558 | return -ENOMEM; | ||
2559 | } | ||
2560 | |||
2561 | rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); | 2544 | rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); |
2562 | 2545 | ||
2563 | for_each_cpu(cpu, cmask) { | 2546 | for_each_cpu(cpu, cmask) { |
2564 | 2547 | ||
2548 | struct msr *reg = per_cpu_ptr(msrs, cpu); | ||
2549 | |||
2565 | if (on) { | 2550 | if (on) { |
2566 | if (msrs[idx].l & K8_MSR_MCGCTL_NBE) | 2551 | if (reg->l & K8_MSR_MCGCTL_NBE) |
2567 | pvt->flags.ecc_report = 1; | 2552 | pvt->flags.ecc_report = 1; |
2568 | 2553 | ||
2569 | msrs[idx].l |= K8_MSR_MCGCTL_NBE; | 2554 | reg->l |= K8_MSR_MCGCTL_NBE; |
2570 | } else { | 2555 | } else { |
2571 | /* | 2556 | /* |
2572 | * Turn off ECC reporting only when it was off before | 2557 | * Turn off ECC reporting only when it was off before |
2573 | */ | 2558 | */ |
2574 | if (!pvt->flags.ecc_report) | 2559 | if (!pvt->flags.ecc_report) |
2575 | msrs[idx].l &= ~K8_MSR_MCGCTL_NBE; | 2560 | reg->l &= ~K8_MSR_MCGCTL_NBE; |
2576 | } | 2561 | } |
2577 | idx++; | ||
2578 | } | 2562 | } |
2579 | wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); | 2563 | wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); |
2580 | 2564 | ||
2581 | kfree(msrs); | ||
2582 | free_cpumask_var(cmask); | 2565 | free_cpumask_var(cmask); |
2583 | 2566 | ||
2584 | return 0; | 2567 | return 0; |
@@ -3036,6 +3019,8 @@ static int __init amd64_edac_init(void) | |||
3036 | if (cache_k8_northbridges() < 0) | 3019 | if (cache_k8_northbridges() < 0) |
3037 | return err; | 3020 | return err; |
3038 | 3021 | ||
3022 | msrs = msrs_alloc(); | ||
3023 | |||
3039 | err = pci_register_driver(&amd64_pci_driver); | 3024 | err = pci_register_driver(&amd64_pci_driver); |
3040 | if (err) | 3025 | if (err) |
3041 | return err; | 3026 | return err; |
@@ -3071,6 +3056,9 @@ static void __exit amd64_edac_exit(void) | |||
3071 | edac_pci_release_generic_ctl(amd64_ctl_pci); | 3056 | edac_pci_release_generic_ctl(amd64_ctl_pci); |
3072 | 3057 | ||
3073 | pci_unregister_driver(&amd64_pci_driver); | 3058 | pci_unregister_driver(&amd64_pci_driver); |
3059 | |||
3060 | msrs_free(msrs); | ||
3061 | msrs = NULL; | ||
3074 | } | 3062 | } |
3075 | 3063 | ||
3076 | module_init(amd64_edac_init); | 3064 | module_init(amd64_edac_init); |
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index 22db05a67bfb..7785d8ffa404 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c | |||
@@ -9,6 +9,11 @@ | |||
9 | * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet | 9 | * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet |
10 | * http://download.intel.com/design/chipsets/datashts/318378.pdf | 10 | * http://download.intel.com/design/chipsets/datashts/318378.pdf |
11 | * | 11 | * |
12 | * The intel 5100 has two independent channels. EDAC core currently | ||
13 | * can not reflect this configuration so instead the chip-select | ||
14 | * rows for each respective channel are layed out one after another, | ||
15 | * the first half belonging to channel 0, the second half belonging | ||
16 | * to channel 1. | ||
12 | */ | 17 | */ |
13 | #include <linux/module.h> | 18 | #include <linux/module.h> |
14 | #include <linux/init.h> | 19 | #include <linux/init.h> |
@@ -25,6 +30,8 @@ | |||
25 | 30 | ||
26 | /* device 16, func 1 */ | 31 | /* device 16, func 1 */ |
27 | #define I5100_MC 0x40 /* Memory Control Register */ | 32 | #define I5100_MC 0x40 /* Memory Control Register */ |
33 | #define I5100_MC_SCRBEN_MASK (1 << 7) | ||
34 | #define I5100_MC_SCRBDONE_MASK (1 << 4) | ||
28 | #define I5100_MS 0x44 /* Memory Status Register */ | 35 | #define I5100_MS 0x44 /* Memory Status Register */ |
29 | #define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */ | 36 | #define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */ |
30 | #define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */ | 37 | #define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */ |
@@ -72,11 +79,21 @@ | |||
72 | 79 | ||
73 | /* bit field accessors */ | 80 | /* bit field accessors */ |
74 | 81 | ||
82 | static inline u32 i5100_mc_scrben(u32 mc) | ||
83 | { | ||
84 | return mc >> 7 & 1; | ||
85 | } | ||
86 | |||
75 | static inline u32 i5100_mc_errdeten(u32 mc) | 87 | static inline u32 i5100_mc_errdeten(u32 mc) |
76 | { | 88 | { |
77 | return mc >> 5 & 1; | 89 | return mc >> 5 & 1; |
78 | } | 90 | } |
79 | 91 | ||
92 | static inline u32 i5100_mc_scrbdone(u32 mc) | ||
93 | { | ||
94 | return mc >> 4 & 1; | ||
95 | } | ||
96 | |||
80 | static inline u16 i5100_spddata_rdo(u16 a) | 97 | static inline u16 i5100_spddata_rdo(u16 a) |
81 | { | 98 | { |
82 | return a >> 15 & 1; | 99 | return a >> 15 & 1; |
@@ -265,42 +282,43 @@ static inline u32 i5100_recmemb_ras(u32 a) | |||
265 | } | 282 | } |
266 | 283 | ||
267 | /* some generic limits */ | 284 | /* some generic limits */ |
268 | #define I5100_MAX_RANKS_PER_CTLR 6 | 285 | #define I5100_MAX_RANKS_PER_CHAN 6 |
269 | #define I5100_MAX_CTLRS 2 | 286 | #define I5100_CHANNELS 2 |
270 | #define I5100_MAX_RANKS_PER_DIMM 4 | 287 | #define I5100_MAX_RANKS_PER_DIMM 4 |
271 | #define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */ | 288 | #define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */ |
272 | #define I5100_MAX_DIMM_SLOTS_PER_CTLR 4 | 289 | #define I5100_MAX_DIMM_SLOTS_PER_CHAN 4 |
273 | #define I5100_MAX_RANK_INTERLEAVE 4 | 290 | #define I5100_MAX_RANK_INTERLEAVE 4 |
274 | #define I5100_MAX_DMIRS 5 | 291 | #define I5100_MAX_DMIRS 5 |
292 | #define I5100_SCRUB_REFRESH_RATE (5 * 60 * HZ) | ||
275 | 293 | ||
276 | struct i5100_priv { | 294 | struct i5100_priv { |
277 | /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */ | 295 | /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */ |
278 | int dimm_numrank[I5100_MAX_CTLRS][I5100_MAX_DIMM_SLOTS_PER_CTLR]; | 296 | int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN]; |
279 | 297 | ||
280 | /* | 298 | /* |
281 | * mainboard chip select map -- maps i5100 chip selects to | 299 | * mainboard chip select map -- maps i5100 chip selects to |
282 | * DIMM slot chip selects. In the case of only 4 ranks per | 300 | * DIMM slot chip selects. In the case of only 4 ranks per |
283 | * controller, the mapping is fairly obvious but not unique. | 301 | * channel, the mapping is fairly obvious but not unique. |
284 | * we map -1 -> NC and assume both controllers use the same | 302 | * we map -1 -> NC and assume both channels use the same |
285 | * map... | 303 | * map... |
286 | * | 304 | * |
287 | */ | 305 | */ |
288 | int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CTLR][I5100_MAX_RANKS_PER_DIMM]; | 306 | int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM]; |
289 | 307 | ||
290 | /* memory interleave range */ | 308 | /* memory interleave range */ |
291 | struct { | 309 | struct { |
292 | u64 limit; | 310 | u64 limit; |
293 | unsigned way[2]; | 311 | unsigned way[2]; |
294 | } mir[I5100_MAX_CTLRS]; | 312 | } mir[I5100_CHANNELS]; |
295 | 313 | ||
296 | /* adjusted memory interleave range register */ | 314 | /* adjusted memory interleave range register */ |
297 | unsigned amir[I5100_MAX_CTLRS]; | 315 | unsigned amir[I5100_CHANNELS]; |
298 | 316 | ||
299 | /* dimm interleave range */ | 317 | /* dimm interleave range */ |
300 | struct { | 318 | struct { |
301 | unsigned rank[I5100_MAX_RANK_INTERLEAVE]; | 319 | unsigned rank[I5100_MAX_RANK_INTERLEAVE]; |
302 | u64 limit; | 320 | u64 limit; |
303 | } dmir[I5100_MAX_CTLRS][I5100_MAX_DMIRS]; | 321 | } dmir[I5100_CHANNELS][I5100_MAX_DMIRS]; |
304 | 322 | ||
305 | /* memory technology registers... */ | 323 | /* memory technology registers... */ |
306 | struct { | 324 | struct { |
@@ -310,30 +328,33 @@ struct i5100_priv { | |||
310 | unsigned numbank; /* 2 or 3 lines */ | 328 | unsigned numbank; /* 2 or 3 lines */ |
311 | unsigned numrow; /* 13 .. 16 lines */ | 329 | unsigned numrow; /* 13 .. 16 lines */ |
312 | unsigned numcol; /* 11 .. 12 lines */ | 330 | unsigned numcol; /* 11 .. 12 lines */ |
313 | } mtr[I5100_MAX_CTLRS][I5100_MAX_RANKS_PER_CTLR]; | 331 | } mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN]; |
314 | 332 | ||
315 | u64 tolm; /* top of low memory in bytes */ | 333 | u64 tolm; /* top of low memory in bytes */ |
316 | unsigned ranksperctlr; /* number of ranks per controller */ | 334 | unsigned ranksperchan; /* number of ranks per channel */ |
317 | 335 | ||
318 | struct pci_dev *mc; /* device 16 func 1 */ | 336 | struct pci_dev *mc; /* device 16 func 1 */ |
319 | struct pci_dev *ch0mm; /* device 21 func 0 */ | 337 | struct pci_dev *ch0mm; /* device 21 func 0 */ |
320 | struct pci_dev *ch1mm; /* device 22 func 0 */ | 338 | struct pci_dev *ch1mm; /* device 22 func 0 */ |
339 | |||
340 | struct delayed_work i5100_scrubbing; | ||
341 | int scrub_enable; | ||
321 | }; | 342 | }; |
322 | 343 | ||
323 | /* map a rank/ctlr to a slot number on the mainboard */ | 344 | /* map a rank/chan to a slot number on the mainboard */ |
324 | static int i5100_rank_to_slot(const struct mem_ctl_info *mci, | 345 | static int i5100_rank_to_slot(const struct mem_ctl_info *mci, |
325 | int ctlr, int rank) | 346 | int chan, int rank) |
326 | { | 347 | { |
327 | const struct i5100_priv *priv = mci->pvt_info; | 348 | const struct i5100_priv *priv = mci->pvt_info; |
328 | int i; | 349 | int i; |
329 | 350 | ||
330 | for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) { | 351 | for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) { |
331 | int j; | 352 | int j; |
332 | const int numrank = priv->dimm_numrank[ctlr][i]; | 353 | const int numrank = priv->dimm_numrank[chan][i]; |
333 | 354 | ||
334 | for (j = 0; j < numrank; j++) | 355 | for (j = 0; j < numrank; j++) |
335 | if (priv->dimm_csmap[i][j] == rank) | 356 | if (priv->dimm_csmap[i][j] == rank) |
336 | return i * 2 + ctlr; | 357 | return i * 2 + chan; |
337 | } | 358 | } |
338 | 359 | ||
339 | return -1; | 360 | return -1; |
@@ -374,32 +395,32 @@ static const char *i5100_err_msg(unsigned err) | |||
374 | return "none"; | 395 | return "none"; |
375 | } | 396 | } |
376 | 397 | ||
377 | /* convert csrow index into a rank (per controller -- 0..5) */ | 398 | /* convert csrow index into a rank (per channel -- 0..5) */ |
378 | static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow) | 399 | static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow) |
379 | { | 400 | { |
380 | const struct i5100_priv *priv = mci->pvt_info; | 401 | const struct i5100_priv *priv = mci->pvt_info; |
381 | 402 | ||
382 | return csrow % priv->ranksperctlr; | 403 | return csrow % priv->ranksperchan; |
383 | } | 404 | } |
384 | 405 | ||
385 | /* convert csrow index into a controller (0..1) */ | 406 | /* convert csrow index into a channel (0..1) */ |
386 | static int i5100_csrow_to_cntlr(const struct mem_ctl_info *mci, int csrow) | 407 | static int i5100_csrow_to_chan(const struct mem_ctl_info *mci, int csrow) |
387 | { | 408 | { |
388 | const struct i5100_priv *priv = mci->pvt_info; | 409 | const struct i5100_priv *priv = mci->pvt_info; |
389 | 410 | ||
390 | return csrow / priv->ranksperctlr; | 411 | return csrow / priv->ranksperchan; |
391 | } | 412 | } |
392 | 413 | ||
393 | static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci, | 414 | static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci, |
394 | int ctlr, int rank) | 415 | int chan, int rank) |
395 | { | 416 | { |
396 | const struct i5100_priv *priv = mci->pvt_info; | 417 | const struct i5100_priv *priv = mci->pvt_info; |
397 | 418 | ||
398 | return ctlr * priv->ranksperctlr + rank; | 419 | return chan * priv->ranksperchan + rank; |
399 | } | 420 | } |
400 | 421 | ||
401 | static void i5100_handle_ce(struct mem_ctl_info *mci, | 422 | static void i5100_handle_ce(struct mem_ctl_info *mci, |
402 | int ctlr, | 423 | int chan, |
403 | unsigned bank, | 424 | unsigned bank, |
404 | unsigned rank, | 425 | unsigned rank, |
405 | unsigned long syndrome, | 426 | unsigned long syndrome, |
@@ -407,12 +428,12 @@ static void i5100_handle_ce(struct mem_ctl_info *mci, | |||
407 | unsigned ras, | 428 | unsigned ras, |
408 | const char *msg) | 429 | const char *msg) |
409 | { | 430 | { |
410 | const int csrow = i5100_rank_to_csrow(mci, ctlr, rank); | 431 | const int csrow = i5100_rank_to_csrow(mci, chan, rank); |
411 | 432 | ||
412 | printk(KERN_ERR | 433 | printk(KERN_ERR |
413 | "CE ctlr %d, bank %u, rank %u, syndrome 0x%lx, " | 434 | "CE chan %d, bank %u, rank %u, syndrome 0x%lx, " |
414 | "cas %u, ras %u, csrow %u, label \"%s\": %s\n", | 435 | "cas %u, ras %u, csrow %u, label \"%s\": %s\n", |
415 | ctlr, bank, rank, syndrome, cas, ras, | 436 | chan, bank, rank, syndrome, cas, ras, |
416 | csrow, mci->csrows[csrow].channels[0].label, msg); | 437 | csrow, mci->csrows[csrow].channels[0].label, msg); |
417 | 438 | ||
418 | mci->ce_count++; | 439 | mci->ce_count++; |
@@ -421,7 +442,7 @@ static void i5100_handle_ce(struct mem_ctl_info *mci, | |||
421 | } | 442 | } |
422 | 443 | ||
423 | static void i5100_handle_ue(struct mem_ctl_info *mci, | 444 | static void i5100_handle_ue(struct mem_ctl_info *mci, |
424 | int ctlr, | 445 | int chan, |
425 | unsigned bank, | 446 | unsigned bank, |
426 | unsigned rank, | 447 | unsigned rank, |
427 | unsigned long syndrome, | 448 | unsigned long syndrome, |
@@ -429,23 +450,23 @@ static void i5100_handle_ue(struct mem_ctl_info *mci, | |||
429 | unsigned ras, | 450 | unsigned ras, |
430 | const char *msg) | 451 | const char *msg) |
431 | { | 452 | { |
432 | const int csrow = i5100_rank_to_csrow(mci, ctlr, rank); | 453 | const int csrow = i5100_rank_to_csrow(mci, chan, rank); |
433 | 454 | ||
434 | printk(KERN_ERR | 455 | printk(KERN_ERR |
435 | "UE ctlr %d, bank %u, rank %u, syndrome 0x%lx, " | 456 | "UE chan %d, bank %u, rank %u, syndrome 0x%lx, " |
436 | "cas %u, ras %u, csrow %u, label \"%s\": %s\n", | 457 | "cas %u, ras %u, csrow %u, label \"%s\": %s\n", |
437 | ctlr, bank, rank, syndrome, cas, ras, | 458 | chan, bank, rank, syndrome, cas, ras, |
438 | csrow, mci->csrows[csrow].channels[0].label, msg); | 459 | csrow, mci->csrows[csrow].channels[0].label, msg); |
439 | 460 | ||
440 | mci->ue_count++; | 461 | mci->ue_count++; |
441 | mci->csrows[csrow].ue_count++; | 462 | mci->csrows[csrow].ue_count++; |
442 | } | 463 | } |
443 | 464 | ||
444 | static void i5100_read_log(struct mem_ctl_info *mci, int ctlr, | 465 | static void i5100_read_log(struct mem_ctl_info *mci, int chan, |
445 | u32 ferr, u32 nerr) | 466 | u32 ferr, u32 nerr) |
446 | { | 467 | { |
447 | struct i5100_priv *priv = mci->pvt_info; | 468 | struct i5100_priv *priv = mci->pvt_info; |
448 | struct pci_dev *pdev = (ctlr) ? priv->ch1mm : priv->ch0mm; | 469 | struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm; |
449 | u32 dw; | 470 | u32 dw; |
450 | u32 dw2; | 471 | u32 dw2; |
451 | unsigned syndrome = 0; | 472 | unsigned syndrome = 0; |
@@ -484,7 +505,7 @@ static void i5100_read_log(struct mem_ctl_info *mci, int ctlr, | |||
484 | else | 505 | else |
485 | msg = i5100_err_msg(nerr); | 506 | msg = i5100_err_msg(nerr); |
486 | 507 | ||
487 | i5100_handle_ce(mci, ctlr, bank, rank, syndrome, cas, ras, msg); | 508 | i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg); |
488 | } | 509 | } |
489 | 510 | ||
490 | if (i5100_validlog_nrecmemvalid(dw)) { | 511 | if (i5100_validlog_nrecmemvalid(dw)) { |
@@ -506,7 +527,7 @@ static void i5100_read_log(struct mem_ctl_info *mci, int ctlr, | |||
506 | else | 527 | else |
507 | msg = i5100_err_msg(nerr); | 528 | msg = i5100_err_msg(nerr); |
508 | 529 | ||
509 | i5100_handle_ue(mci, ctlr, bank, rank, syndrome, cas, ras, msg); | 530 | i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg); |
510 | } | 531 | } |
511 | 532 | ||
512 | pci_write_config_dword(pdev, I5100_VALIDLOG, dw); | 533 | pci_write_config_dword(pdev, I5100_VALIDLOG, dw); |
@@ -534,6 +555,80 @@ static void i5100_check_error(struct mem_ctl_info *mci) | |||
534 | } | 555 | } |
535 | } | 556 | } |
536 | 557 | ||
558 | /* The i5100 chipset will scrub the entire memory once, then | ||
559 | * set a done bit. Continuous scrubbing is achieved by enqueing | ||
560 | * delayed work to a workqueue, checking every few minutes if | ||
561 | * the scrubbing has completed and if so reinitiating it. | ||
562 | */ | ||
563 | |||
564 | static void i5100_refresh_scrubbing(struct work_struct *work) | ||
565 | { | ||
566 | struct delayed_work *i5100_scrubbing = container_of(work, | ||
567 | struct delayed_work, | ||
568 | work); | ||
569 | struct i5100_priv *priv = container_of(i5100_scrubbing, | ||
570 | struct i5100_priv, | ||
571 | i5100_scrubbing); | ||
572 | u32 dw; | ||
573 | |||
574 | pci_read_config_dword(priv->mc, I5100_MC, &dw); | ||
575 | |||
576 | if (priv->scrub_enable) { | ||
577 | |||
578 | pci_read_config_dword(priv->mc, I5100_MC, &dw); | ||
579 | |||
580 | if (i5100_mc_scrbdone(dw)) { | ||
581 | dw |= I5100_MC_SCRBEN_MASK; | ||
582 | pci_write_config_dword(priv->mc, I5100_MC, dw); | ||
583 | pci_read_config_dword(priv->mc, I5100_MC, &dw); | ||
584 | } | ||
585 | |||
586 | schedule_delayed_work(&(priv->i5100_scrubbing), | ||
587 | I5100_SCRUB_REFRESH_RATE); | ||
588 | } | ||
589 | } | ||
590 | /* | ||
591 | * The bandwidth is based on experimentation, feel free to refine it. | ||
592 | */ | ||
593 | static int i5100_set_scrub_rate(struct mem_ctl_info *mci, | ||
594 | u32 *bandwidth) | ||
595 | { | ||
596 | struct i5100_priv *priv = mci->pvt_info; | ||
597 | u32 dw; | ||
598 | |||
599 | pci_read_config_dword(priv->mc, I5100_MC, &dw); | ||
600 | if (*bandwidth) { | ||
601 | priv->scrub_enable = 1; | ||
602 | dw |= I5100_MC_SCRBEN_MASK; | ||
603 | schedule_delayed_work(&(priv->i5100_scrubbing), | ||
604 | I5100_SCRUB_REFRESH_RATE); | ||
605 | } else { | ||
606 | priv->scrub_enable = 0; | ||
607 | dw &= ~I5100_MC_SCRBEN_MASK; | ||
608 | cancel_delayed_work(&(priv->i5100_scrubbing)); | ||
609 | } | ||
610 | pci_write_config_dword(priv->mc, I5100_MC, dw); | ||
611 | |||
612 | pci_read_config_dword(priv->mc, I5100_MC, &dw); | ||
613 | |||
614 | *bandwidth = 5900000 * i5100_mc_scrben(dw); | ||
615 | |||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | static int i5100_get_scrub_rate(struct mem_ctl_info *mci, | ||
620 | u32 *bandwidth) | ||
621 | { | ||
622 | struct i5100_priv *priv = mci->pvt_info; | ||
623 | u32 dw; | ||
624 | |||
625 | pci_read_config_dword(priv->mc, I5100_MC, &dw); | ||
626 | |||
627 | *bandwidth = 5900000 * i5100_mc_scrben(dw); | ||
628 | |||
629 | return 0; | ||
630 | } | ||
631 | |||
537 | static struct pci_dev *pci_get_device_func(unsigned vendor, | 632 | static struct pci_dev *pci_get_device_func(unsigned vendor, |
538 | unsigned device, | 633 | unsigned device, |
539 | unsigned func) | 634 | unsigned func) |
@@ -557,19 +652,19 @@ static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci, | |||
557 | int csrow) | 652 | int csrow) |
558 | { | 653 | { |
559 | struct i5100_priv *priv = mci->pvt_info; | 654 | struct i5100_priv *priv = mci->pvt_info; |
560 | const unsigned ctlr_rank = i5100_csrow_to_rank(mci, csrow); | 655 | const unsigned chan_rank = i5100_csrow_to_rank(mci, csrow); |
561 | const unsigned ctlr = i5100_csrow_to_cntlr(mci, csrow); | 656 | const unsigned chan = i5100_csrow_to_chan(mci, csrow); |
562 | unsigned addr_lines; | 657 | unsigned addr_lines; |
563 | 658 | ||
564 | /* dimm present? */ | 659 | /* dimm present? */ |
565 | if (!priv->mtr[ctlr][ctlr_rank].present) | 660 | if (!priv->mtr[chan][chan_rank].present) |
566 | return 0ULL; | 661 | return 0ULL; |
567 | 662 | ||
568 | addr_lines = | 663 | addr_lines = |
569 | I5100_DIMM_ADDR_LINES + | 664 | I5100_DIMM_ADDR_LINES + |
570 | priv->mtr[ctlr][ctlr_rank].numcol + | 665 | priv->mtr[chan][chan_rank].numcol + |
571 | priv->mtr[ctlr][ctlr_rank].numrow + | 666 | priv->mtr[chan][chan_rank].numrow + |
572 | priv->mtr[ctlr][ctlr_rank].numbank; | 667 | priv->mtr[chan][chan_rank].numbank; |
573 | 668 | ||
574 | return (unsigned long) | 669 | return (unsigned long) |
575 | ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE); | 670 | ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE); |
@@ -581,11 +676,11 @@ static void __devinit i5100_init_mtr(struct mem_ctl_info *mci) | |||
581 | struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; | 676 | struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; |
582 | int i; | 677 | int i; |
583 | 678 | ||
584 | for (i = 0; i < I5100_MAX_CTLRS; i++) { | 679 | for (i = 0; i < I5100_CHANNELS; i++) { |
585 | int j; | 680 | int j; |
586 | struct pci_dev *pdev = mms[i]; | 681 | struct pci_dev *pdev = mms[i]; |
587 | 682 | ||
588 | for (j = 0; j < I5100_MAX_RANKS_PER_CTLR; j++) { | 683 | for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) { |
589 | const unsigned addr = | 684 | const unsigned addr = |
590 | (j < 4) ? I5100_MTR_0 + j * 2 : | 685 | (j < 4) ? I5100_MTR_0 + j * 2 : |
591 | I5100_MTR_4 + (j - 4) * 2; | 686 | I5100_MTR_4 + (j - 4) * 2; |
@@ -644,7 +739,6 @@ static int i5100_read_spd_byte(const struct mem_ctl_info *mci, | |||
644 | * fill dimm chip select map | 739 | * fill dimm chip select map |
645 | * | 740 | * |
646 | * FIXME: | 741 | * FIXME: |
647 | * o only valid for 4 ranks per controller | ||
648 | * o not the only way to may chip selects to dimm slots | 742 | * o not the only way to may chip selects to dimm slots |
649 | * o investigate if there is some way to obtain this map from the bios | 743 | * o investigate if there is some way to obtain this map from the bios |
650 | */ | 744 | */ |
@@ -653,9 +747,7 @@ static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci) | |||
653 | struct i5100_priv *priv = mci->pvt_info; | 747 | struct i5100_priv *priv = mci->pvt_info; |
654 | int i; | 748 | int i; |
655 | 749 | ||
656 | WARN_ON(priv->ranksperctlr != 4); | 750 | for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) { |
657 | |||
658 | for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) { | ||
659 | int j; | 751 | int j; |
660 | 752 | ||
661 | for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++) | 753 | for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++) |
@@ -663,12 +755,21 @@ static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci) | |||
663 | } | 755 | } |
664 | 756 | ||
665 | /* only 2 chip selects per slot... */ | 757 | /* only 2 chip selects per slot... */ |
666 | priv->dimm_csmap[0][0] = 0; | 758 | if (priv->ranksperchan == 4) { |
667 | priv->dimm_csmap[0][1] = 3; | 759 | priv->dimm_csmap[0][0] = 0; |
668 | priv->dimm_csmap[1][0] = 1; | 760 | priv->dimm_csmap[0][1] = 3; |
669 | priv->dimm_csmap[1][1] = 2; | 761 | priv->dimm_csmap[1][0] = 1; |
670 | priv->dimm_csmap[2][0] = 2; | 762 | priv->dimm_csmap[1][1] = 2; |
671 | priv->dimm_csmap[3][0] = 3; | 763 | priv->dimm_csmap[2][0] = 2; |
764 | priv->dimm_csmap[3][0] = 3; | ||
765 | } else { | ||
766 | priv->dimm_csmap[0][0] = 0; | ||
767 | priv->dimm_csmap[0][1] = 1; | ||
768 | priv->dimm_csmap[1][0] = 2; | ||
769 | priv->dimm_csmap[1][1] = 3; | ||
770 | priv->dimm_csmap[2][0] = 4; | ||
771 | priv->dimm_csmap[2][1] = 5; | ||
772 | } | ||
672 | } | 773 | } |
673 | 774 | ||
674 | static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev, | 775 | static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev, |
@@ -677,10 +778,10 @@ static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev, | |||
677 | struct i5100_priv *priv = mci->pvt_info; | 778 | struct i5100_priv *priv = mci->pvt_info; |
678 | int i; | 779 | int i; |
679 | 780 | ||
680 | for (i = 0; i < I5100_MAX_CTLRS; i++) { | 781 | for (i = 0; i < I5100_CHANNELS; i++) { |
681 | int j; | 782 | int j; |
682 | 783 | ||
683 | for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CTLR; j++) { | 784 | for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) { |
684 | u8 rank; | 785 | u8 rank; |
685 | 786 | ||
686 | if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0) | 787 | if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0) |
@@ -720,7 +821,7 @@ static void __devinit i5100_init_interleaving(struct pci_dev *pdev, | |||
720 | pci_read_config_word(pdev, I5100_AMIR_1, &w); | 821 | pci_read_config_word(pdev, I5100_AMIR_1, &w); |
721 | priv->amir[1] = w; | 822 | priv->amir[1] = w; |
722 | 823 | ||
723 | for (i = 0; i < I5100_MAX_CTLRS; i++) { | 824 | for (i = 0; i < I5100_CHANNELS; i++) { |
724 | int j; | 825 | int j; |
725 | 826 | ||
726 | for (j = 0; j < 5; j++) { | 827 | for (j = 0; j < 5; j++) { |
@@ -747,7 +848,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci) | |||
747 | 848 | ||
748 | for (i = 0; i < mci->nr_csrows; i++) { | 849 | for (i = 0; i < mci->nr_csrows; i++) { |
749 | const unsigned long npages = i5100_npages(mci, i); | 850 | const unsigned long npages = i5100_npages(mci, i); |
750 | const unsigned cntlr = i5100_csrow_to_cntlr(mci, i); | 851 | const unsigned chan = i5100_csrow_to_chan(mci, i); |
751 | const unsigned rank = i5100_csrow_to_rank(mci, i); | 852 | const unsigned rank = i5100_csrow_to_rank(mci, i); |
752 | 853 | ||
753 | if (!npages) | 854 | if (!npages) |
@@ -765,7 +866,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci) | |||
765 | mci->csrows[i].grain = 32; | 866 | mci->csrows[i].grain = 32; |
766 | mci->csrows[i].csrow_idx = i; | 867 | mci->csrows[i].csrow_idx = i; |
767 | mci->csrows[i].dtype = | 868 | mci->csrows[i].dtype = |
768 | (priv->mtr[cntlr][rank].width == 4) ? DEV_X4 : DEV_X8; | 869 | (priv->mtr[chan][rank].width == 4) ? DEV_X4 : DEV_X8; |
769 | mci->csrows[i].ue_count = 0; | 870 | mci->csrows[i].ue_count = 0; |
770 | mci->csrows[i].ce_count = 0; | 871 | mci->csrows[i].ce_count = 0; |
771 | mci->csrows[i].mtype = MEM_RDDR2; | 872 | mci->csrows[i].mtype = MEM_RDDR2; |
@@ -777,7 +878,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci) | |||
777 | mci->csrows[i].channels[0].csrow = mci->csrows + i; | 878 | mci->csrows[i].channels[0].csrow = mci->csrows + i; |
778 | snprintf(mci->csrows[i].channels[0].label, | 879 | snprintf(mci->csrows[i].channels[0].label, |
779 | sizeof(mci->csrows[i].channels[0].label), | 880 | sizeof(mci->csrows[i].channels[0].label), |
780 | "DIMM%u", i5100_rank_to_slot(mci, cntlr, rank)); | 881 | "DIMM%u", i5100_rank_to_slot(mci, chan, rank)); |
781 | 882 | ||
782 | total_pages += npages; | 883 | total_pages += npages; |
783 | } | 884 | } |
@@ -815,13 +916,6 @@ static int __devinit i5100_init_one(struct pci_dev *pdev, | |||
815 | pci_read_config_dword(pdev, I5100_MS, &dw); | 916 | pci_read_config_dword(pdev, I5100_MS, &dw); |
816 | ranksperch = !!(dw & (1 << 8)) * 2 + 4; | 917 | ranksperch = !!(dw & (1 << 8)) * 2 + 4; |
817 | 918 | ||
818 | if (ranksperch != 4) { | ||
819 | /* FIXME: get 6 ranks / controller to work - need hw... */ | ||
820 | printk(KERN_INFO "i5100_edac: unsupported configuration.\n"); | ||
821 | ret = -ENODEV; | ||
822 | goto bail_pdev; | ||
823 | } | ||
824 | |||
825 | /* enable error reporting... */ | 919 | /* enable error reporting... */ |
826 | pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw); | 920 | pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw); |
827 | dw &= ~I5100_FERR_NF_MEM_ANY_MASK; | 921 | dw &= ~I5100_FERR_NF_MEM_ANY_MASK; |
@@ -864,11 +958,21 @@ static int __devinit i5100_init_one(struct pci_dev *pdev, | |||
864 | mci->dev = &pdev->dev; | 958 | mci->dev = &pdev->dev; |
865 | 959 | ||
866 | priv = mci->pvt_info; | 960 | priv = mci->pvt_info; |
867 | priv->ranksperctlr = ranksperch; | 961 | priv->ranksperchan = ranksperch; |
868 | priv->mc = pdev; | 962 | priv->mc = pdev; |
869 | priv->ch0mm = ch0mm; | 963 | priv->ch0mm = ch0mm; |
870 | priv->ch1mm = ch1mm; | 964 | priv->ch1mm = ch1mm; |
871 | 965 | ||
966 | INIT_DELAYED_WORK(&(priv->i5100_scrubbing), i5100_refresh_scrubbing); | ||
967 | |||
968 | /* If scrubbing was already enabled by the bios, start maintaining it */ | ||
969 | pci_read_config_dword(pdev, I5100_MC, &dw); | ||
970 | if (i5100_mc_scrben(dw)) { | ||
971 | priv->scrub_enable = 1; | ||
972 | schedule_delayed_work(&(priv->i5100_scrubbing), | ||
973 | I5100_SCRUB_REFRESH_RATE); | ||
974 | } | ||
975 | |||
872 | i5100_init_dimm_layout(pdev, mci); | 976 | i5100_init_dimm_layout(pdev, mci); |
873 | i5100_init_interleaving(pdev, mci); | 977 | i5100_init_interleaving(pdev, mci); |
874 | 978 | ||
@@ -882,6 +986,8 @@ static int __devinit i5100_init_one(struct pci_dev *pdev, | |||
882 | mci->ctl_page_to_phys = NULL; | 986 | mci->ctl_page_to_phys = NULL; |
883 | 987 | ||
884 | mci->edac_check = i5100_check_error; | 988 | mci->edac_check = i5100_check_error; |
989 | mci->set_sdram_scrub_rate = i5100_set_scrub_rate; | ||
990 | mci->get_sdram_scrub_rate = i5100_get_scrub_rate; | ||
885 | 991 | ||
886 | i5100_init_csrows(mci); | 992 | i5100_init_csrows(mci); |
887 | 993 | ||
@@ -897,12 +1003,14 @@ static int __devinit i5100_init_one(struct pci_dev *pdev, | |||
897 | 1003 | ||
898 | if (edac_mc_add_mc(mci)) { | 1004 | if (edac_mc_add_mc(mci)) { |
899 | ret = -ENODEV; | 1005 | ret = -ENODEV; |
900 | goto bail_mc; | 1006 | goto bail_scrub; |
901 | } | 1007 | } |
902 | 1008 | ||
903 | return ret; | 1009 | return ret; |
904 | 1010 | ||
905 | bail_mc: | 1011 | bail_scrub: |
1012 | priv->scrub_enable = 0; | ||
1013 | cancel_delayed_work_sync(&(priv->i5100_scrubbing)); | ||
906 | edac_mc_free(mci); | 1014 | edac_mc_free(mci); |
907 | 1015 | ||
908 | bail_disable_ch1: | 1016 | bail_disable_ch1: |
@@ -935,6 +1043,10 @@ static void __devexit i5100_remove_one(struct pci_dev *pdev) | |||
935 | return; | 1043 | return; |
936 | 1044 | ||
937 | priv = mci->pvt_info; | 1045 | priv = mci->pvt_info; |
1046 | |||
1047 | priv->scrub_enable = 0; | ||
1048 | cancel_delayed_work_sync(&(priv->i5100_scrubbing)); | ||
1049 | |||
938 | pci_disable_device(pdev); | 1050 | pci_disable_device(pdev); |
939 | pci_disable_device(priv->ch0mm); | 1051 | pci_disable_device(priv->ch0mm); |
940 | pci_disable_device(priv->ch1mm); | 1052 | pci_disable_device(priv->ch1mm); |