diff options
Diffstat (limited to 'drivers/edac/i82975x_edac.c')
-rw-r--r-- | drivers/edac/i82975x_edac.c | 69 |
1 files changed, 44 insertions, 25 deletions
diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 3218819b7286..92e65e7038e9 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c | |||
@@ -160,8 +160,8 @@ NOTE: Only ONE of the three must be enabled | |||
160 | * 3:2 Rank 1 architecture | 160 | * 3:2 Rank 1 architecture |
161 | * 1:0 Rank 0 architecture | 161 | * 1:0 Rank 0 architecture |
162 | * | 162 | * |
163 | * 00 => x16 devices; i.e 4 banks | 163 | * 00 => 4 banks |
164 | * 01 => x8 devices; i.e 8 banks | 164 | * 01 => 8 banks |
165 | */ | 165 | */ |
166 | #define I82975X_C0BNKARC 0x10e | 166 | #define I82975X_C0BNKARC 0x10e |
167 | #define I82975X_C1BNKARC 0x18e | 167 | #define I82975X_C1BNKARC 0x18e |
@@ -278,6 +278,7 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci, | |||
278 | struct i82975x_error_info *info, int handle_errors) | 278 | struct i82975x_error_info *info, int handle_errors) |
279 | { | 279 | { |
280 | int row, multi_chan, chan; | 280 | int row, multi_chan, chan; |
281 | unsigned long offst, page; | ||
281 | 282 | ||
282 | multi_chan = mci->csrows[0].nr_channels - 1; | 283 | multi_chan = mci->csrows[0].nr_channels - 1; |
283 | 284 | ||
@@ -292,17 +293,19 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci, | |||
292 | info->errsts = info->errsts2; | 293 | info->errsts = info->errsts2; |
293 | } | 294 | } |
294 | 295 | ||
295 | chan = info->eap & 1; | 296 | page = (unsigned long) info->eap; |
296 | info->eap >>= 1; | 297 | if (info->xeap & 1) |
297 | if (info->xeap ) | 298 | page |= 0x100000000ul; |
298 | info->eap |= 0x80000000; | 299 | chan = page & 1; |
299 | info->eap >>= PAGE_SHIFT; | 300 | page >>= 1; |
300 | row = edac_mc_find_csrow_by_page(mci, info->eap); | 301 | offst = page & ((1 << PAGE_SHIFT) - 1); |
302 | page >>= PAGE_SHIFT; | ||
303 | row = edac_mc_find_csrow_by_page(mci, page); | ||
301 | 304 | ||
302 | if (info->errsts & 0x0002) | 305 | if (info->errsts & 0x0002) |
303 | edac_mc_handle_ue(mci, info->eap, 0, row, "i82975x UE"); | 306 | edac_mc_handle_ue(mci, page, offst , row, "i82975x UE"); |
304 | else | 307 | else |
305 | edac_mc_handle_ce(mci, info->eap, 0, info->derrsyn, row, | 308 | edac_mc_handle_ce(mci, page, offst, info->derrsyn, row, |
306 | multi_chan ? chan : 0, | 309 | multi_chan ? chan : 0, |
307 | "i82975x CE"); | 310 | "i82975x CE"); |
308 | 311 | ||
@@ -344,11 +347,7 @@ static int dual_channel_active(void __iomem *mch_window) | |||
344 | static enum dev_type i82975x_dram_type(void __iomem *mch_window, int rank) | 347 | static enum dev_type i82975x_dram_type(void __iomem *mch_window, int rank) |
345 | { | 348 | { |
346 | /* | 349 | /* |
347 | * ASUS P5W DH either does not program this register or programs | 350 | * ECC is possible on i92975x ONLY with DEV_X8 |
348 | * it wrong! | ||
349 | * ECC is possible on i92975x ONLY with DEV_X8 which should mean 'val' | ||
350 | * for each rank should be 01b - the LSB of the word should be 0x55; | ||
351 | * but it reads 0! | ||
352 | */ | 351 | */ |
353 | return DEV_X8; | 352 | return DEV_X8; |
354 | } | 353 | } |
@@ -356,11 +355,15 @@ static enum dev_type i82975x_dram_type(void __iomem *mch_window, int rank) | |||
356 | static void i82975x_init_csrows(struct mem_ctl_info *mci, | 355 | static void i82975x_init_csrows(struct mem_ctl_info *mci, |
357 | struct pci_dev *pdev, void __iomem *mch_window) | 356 | struct pci_dev *pdev, void __iomem *mch_window) |
358 | { | 357 | { |
358 | static const char *labels[4] = { | ||
359 | "DIMM A1", "DIMM A2", | ||
360 | "DIMM B1", "DIMM B2" | ||
361 | }; | ||
359 | struct csrow_info *csrow; | 362 | struct csrow_info *csrow; |
360 | unsigned long last_cumul_size; | 363 | unsigned long last_cumul_size; |
361 | u8 value; | 364 | u8 value; |
362 | u32 cumul_size; | 365 | u32 cumul_size; |
363 | int index; | 366 | int index, chan; |
364 | 367 | ||
365 | last_cumul_size = 0; | 368 | last_cumul_size = 0; |
366 | 369 | ||
@@ -369,11 +372,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, | |||
369 | * The dram row boundary (DRB) reg values are boundary address | 372 | * The dram row boundary (DRB) reg values are boundary address |
370 | * for each DRAM row with a granularity of 32 or 64MB (single/dual | 373 | * for each DRAM row with a granularity of 32 or 64MB (single/dual |
371 | * channel operation). DRB regs are cumulative; therefore DRB7 will | 374 | * channel operation). DRB regs are cumulative; therefore DRB7 will |
372 | * contain the total memory contained in all eight rows. | 375 | * contain the total memory contained in all rows. |
373 | * | ||
374 | * FIXME: | ||
375 | * EDAC currently works for Dual-channel Interleaved configuration. | ||
376 | * Other configurations, which the chip supports, need fixing/testing. | ||
377 | * | 376 | * |
378 | */ | 377 | */ |
379 | 378 | ||
@@ -384,8 +383,26 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, | |||
384 | ((index >= 4) ? 0x80 : 0)); | 383 | ((index >= 4) ? 0x80 : 0)); |
385 | cumul_size = value; | 384 | cumul_size = value; |
386 | cumul_size <<= (I82975X_DRB_SHIFT - PAGE_SHIFT); | 385 | cumul_size <<= (I82975X_DRB_SHIFT - PAGE_SHIFT); |
386 | /* | ||
387 | * Adjust cumul_size w.r.t number of channels | ||
388 | * | ||
389 | */ | ||
390 | if (csrow->nr_channels > 1) | ||
391 | cumul_size <<= 1; | ||
387 | debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index, | 392 | debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index, |
388 | cumul_size); | 393 | cumul_size); |
394 | |||
395 | /* | ||
396 | * Initialise dram labels | ||
397 | * index values: | ||
398 | * [0-7] for single-channel; i.e. csrow->nr_channels = 1 | ||
399 | * [0-3] for dual-channel; i.e. csrow->nr_channels = 2 | ||
400 | */ | ||
401 | for (chan = 0; chan < csrow->nr_channels; chan++) | ||
402 | strncpy(csrow->channels[chan].label, | ||
403 | labels[(index >> 1) + (chan * 2)], | ||
404 | EDAC_MC_LABEL_LEN); | ||
405 | |||
389 | if (cumul_size == last_cumul_size) | 406 | if (cumul_size == last_cumul_size) |
390 | continue; /* not populated */ | 407 | continue; /* not populated */ |
391 | 408 | ||
@@ -393,8 +410,8 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, | |||
393 | csrow->last_page = cumul_size - 1; | 410 | csrow->last_page = cumul_size - 1; |
394 | csrow->nr_pages = cumul_size - last_cumul_size; | 411 | csrow->nr_pages = cumul_size - last_cumul_size; |
395 | last_cumul_size = cumul_size; | 412 | last_cumul_size = cumul_size; |
396 | csrow->grain = 1 << 7; /* I82975X_EAP has 128B resolution */ | 413 | csrow->grain = 1 << 6; /* I82975X_EAP has 64B resolution */ |
397 | csrow->mtype = MEM_DDR; /* i82975x supports only DDR2 */ | 414 | csrow->mtype = MEM_DDR2; /* I82975x supports only DDR2 */ |
398 | csrow->dtype = i82975x_dram_type(mch_window, index); | 415 | csrow->dtype = i82975x_dram_type(mch_window, index); |
399 | csrow->edac_mode = EDAC_SECDED; /* only supported */ | 416 | csrow->edac_mode = EDAC_SECDED; /* only supported */ |
400 | } | 417 | } |
@@ -515,18 +532,20 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx) | |||
515 | 532 | ||
516 | debugf3("%s(): init mci\n", __func__); | 533 | debugf3("%s(): init mci\n", __func__); |
517 | mci->dev = &pdev->dev; | 534 | mci->dev = &pdev->dev; |
518 | mci->mtype_cap = MEM_FLAG_DDR; | 535 | mci->mtype_cap = MEM_FLAG_DDR2; |
519 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; | 536 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; |
520 | mci->edac_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; | 537 | mci->edac_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; |
521 | mci->mod_name = EDAC_MOD_STR; | 538 | mci->mod_name = EDAC_MOD_STR; |
522 | mci->mod_ver = I82975X_REVISION; | 539 | mci->mod_ver = I82975X_REVISION; |
523 | mci->ctl_name = i82975x_devs[dev_idx].ctl_name; | 540 | mci->ctl_name = i82975x_devs[dev_idx].ctl_name; |
541 | mci->dev_name = pci_name(pdev); | ||
524 | mci->edac_check = i82975x_check; | 542 | mci->edac_check = i82975x_check; |
525 | mci->ctl_page_to_phys = NULL; | 543 | mci->ctl_page_to_phys = NULL; |
526 | debugf3("%s(): init pvt\n", __func__); | 544 | debugf3("%s(): init pvt\n", __func__); |
527 | pvt = (struct i82975x_pvt *) mci->pvt_info; | 545 | pvt = (struct i82975x_pvt *) mci->pvt_info; |
528 | pvt->mch_window = mch_window; | 546 | pvt->mch_window = mch_window; |
529 | i82975x_init_csrows(mci, pdev, mch_window); | 547 | i82975x_init_csrows(mci, pdev, mch_window); |
548 | mci->scrub_mode = SCRUB_HW_SRC; | ||
530 | i82975x_get_error_info(mci, &discard); /* clear counters */ | 549 | i82975x_get_error_info(mci, &discard); /* clear counters */ |
531 | 550 | ||
532 | /* finalize this instance of memory controller with edac core */ | 551 | /* finalize this instance of memory controller with edac core */ |
@@ -664,7 +683,7 @@ module_init(i82975x_init); | |||
664 | module_exit(i82975x_exit); | 683 | module_exit(i82975x_exit); |
665 | 684 | ||
666 | MODULE_LICENSE("GPL"); | 685 | MODULE_LICENSE("GPL"); |
667 | MODULE_AUTHOR("Arvind R. <arvind@acarlab.com>"); | 686 | MODULE_AUTHOR("Arvind R. <arvino55@gmail.com>"); |
668 | MODULE_DESCRIPTION("MC support for Intel 82975 memory hub controllers"); | 687 | MODULE_DESCRIPTION("MC support for Intel 82975 memory hub controllers"); |
669 | 688 | ||
670 | module_param(edac_op_state, int, 0444); | 689 | module_param(edac_op_state, int, 0444); |