diff options
Diffstat (limited to 'drivers/edac/e7xxx_edac.c')
-rw-r--r-- | drivers/edac/e7xxx_edac.c | 86 |
1 files changed, 58 insertions, 28 deletions
diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index 68dea87b72e6..9a9c1a546797 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c | |||
@@ -10,6 +10,9 @@ | |||
10 | * Based on work by Dan Hollis <goemon at anime dot net> and others. | 10 | * Based on work by Dan Hollis <goemon at anime dot net> and others. |
11 | * http://www.anime.net/~goemon/linux-ecc/ | 11 | * http://www.anime.net/~goemon/linux-ecc/ |
12 | * | 12 | * |
13 | * Datasheet: | ||
14 | * http://www.intel.com/content/www/us/en/chipsets/e7501-chipset-memory-controller-hub-datasheet.html | ||
15 | * | ||
13 | * Contributors: | 16 | * Contributors: |
14 | * Eric Biederman (Linux Networx) | 17 | * Eric Biederman (Linux Networx) |
15 | * Tom Zimmerman (Linux Networx) | 18 | * Tom Zimmerman (Linux Networx) |
@@ -71,7 +74,7 @@ | |||
71 | #endif /* PCI_DEVICE_ID_INTEL_7505_1_ERR */ | 74 | #endif /* PCI_DEVICE_ID_INTEL_7505_1_ERR */ |
72 | 75 | ||
73 | #define E7XXX_NR_CSROWS 8 /* number of csrows */ | 76 | #define E7XXX_NR_CSROWS 8 /* number of csrows */ |
74 | #define E7XXX_NR_DIMMS 8 /* FIXME - is this correct? */ | 77 | #define E7XXX_NR_DIMMS 8 /* 2 channels, 4 dimms/channel */ |
75 | 78 | ||
76 | /* E7XXX register addresses - device 0 function 0 */ | 79 | /* E7XXX register addresses - device 0 function 0 */ |
77 | #define E7XXX_DRB 0x60 /* DRAM row boundary register (8b) */ | 80 | #define E7XXX_DRB 0x60 /* DRAM row boundary register (8b) */ |
@@ -216,13 +219,15 @@ static void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info) | |||
216 | row = edac_mc_find_csrow_by_page(mci, page); | 219 | row = edac_mc_find_csrow_by_page(mci, page); |
217 | /* convert syndrome to channel */ | 220 | /* convert syndrome to channel */ |
218 | channel = e7xxx_find_channel(syndrome); | 221 | channel = e7xxx_find_channel(syndrome); |
219 | edac_mc_handle_ce(mci, page, 0, syndrome, row, channel, "e7xxx CE"); | 222 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, page, 0, syndrome, |
223 | row, channel, -1, "e7xxx CE", "", NULL); | ||
220 | } | 224 | } |
221 | 225 | ||
222 | static void process_ce_no_info(struct mem_ctl_info *mci) | 226 | static void process_ce_no_info(struct mem_ctl_info *mci) |
223 | { | 227 | { |
224 | debugf3("%s()\n", __func__); | 228 | debugf3("%s()\n", __func__); |
225 | edac_mc_handle_ce_no_info(mci, "e7xxx CE log register overflow"); | 229 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1, |
230 | "e7xxx CE log register overflow", "", NULL); | ||
226 | } | 231 | } |
227 | 232 | ||
228 | static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info) | 233 | static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info) |
@@ -236,13 +241,17 @@ static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info) | |||
236 | /* FIXME - should use PAGE_SHIFT */ | 241 | /* FIXME - should use PAGE_SHIFT */ |
237 | block_page = error_2b >> 6; /* convert to 4k address */ | 242 | block_page = error_2b >> 6; /* convert to 4k address */ |
238 | row = edac_mc_find_csrow_by_page(mci, block_page); | 243 | row = edac_mc_find_csrow_by_page(mci, block_page); |
239 | edac_mc_handle_ue(mci, block_page, 0, row, "e7xxx UE"); | 244 | |
245 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, block_page, 0, 0, | ||
246 | row, -1, -1, "e7xxx UE", "", NULL); | ||
240 | } | 247 | } |
241 | 248 | ||
242 | static void process_ue_no_info(struct mem_ctl_info *mci) | 249 | static void process_ue_no_info(struct mem_ctl_info *mci) |
243 | { | 250 | { |
244 | debugf3("%s()\n", __func__); | 251 | debugf3("%s()\n", __func__); |
245 | edac_mc_handle_ue_no_info(mci, "e7xxx UE log register overflow"); | 252 | |
253 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1, | ||
254 | "e7xxx UE log register overflow", "", NULL); | ||
246 | } | 255 | } |
247 | 256 | ||
248 | static void e7xxx_get_error_info(struct mem_ctl_info *mci, | 257 | static void e7xxx_get_error_info(struct mem_ctl_info *mci, |
@@ -347,11 +356,12 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, | |||
347 | int dev_idx, u32 drc) | 356 | int dev_idx, u32 drc) |
348 | { | 357 | { |
349 | unsigned long last_cumul_size; | 358 | unsigned long last_cumul_size; |
350 | int index; | 359 | int index, j; |
351 | u8 value; | 360 | u8 value; |
352 | u32 dra, cumul_size; | 361 | u32 dra, cumul_size, nr_pages; |
353 | int drc_chan, drc_drbg, drc_ddim, mem_dev; | 362 | int drc_chan, drc_drbg, drc_ddim, mem_dev; |
354 | struct csrow_info *csrow; | 363 | struct csrow_info *csrow; |
364 | struct dimm_info *dimm; | ||
355 | 365 | ||
356 | pci_read_config_dword(pdev, E7XXX_DRA, &dra); | 366 | pci_read_config_dword(pdev, E7XXX_DRA, &dra); |
357 | drc_chan = dual_channel_active(drc, dev_idx); | 367 | drc_chan = dual_channel_active(drc, dev_idx); |
@@ -379,26 +389,32 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, | |||
379 | 389 | ||
380 | csrow->first_page = last_cumul_size; | 390 | csrow->first_page = last_cumul_size; |
381 | csrow->last_page = cumul_size - 1; | 391 | csrow->last_page = cumul_size - 1; |
382 | csrow->nr_pages = cumul_size - last_cumul_size; | 392 | nr_pages = cumul_size - last_cumul_size; |
383 | last_cumul_size = cumul_size; | 393 | last_cumul_size = cumul_size; |
384 | csrow->grain = 1 << 12; /* 4KiB - resolution of CELOG */ | 394 | |
385 | csrow->mtype = MEM_RDDR; /* only one type supported */ | 395 | for (j = 0; j < drc_chan + 1; j++) { |
386 | csrow->dtype = mem_dev ? DEV_X4 : DEV_X8; | 396 | dimm = csrow->channels[j].dimm; |
387 | 397 | ||
388 | /* | 398 | dimm->nr_pages = nr_pages / (drc_chan + 1); |
389 | * if single channel or x8 devices then SECDED | 399 | dimm->grain = 1 << 12; /* 4KiB - resolution of CELOG */ |
390 | * if dual channel and x4 then S4ECD4ED | 400 | dimm->mtype = MEM_RDDR; /* only one type supported */ |
391 | */ | 401 | dimm->dtype = mem_dev ? DEV_X4 : DEV_X8; |
392 | if (drc_ddim) { | 402 | |
393 | if (drc_chan && mem_dev) { | 403 | /* |
394 | csrow->edac_mode = EDAC_S4ECD4ED; | 404 | * if single channel or x8 devices then SECDED |
395 | mci->edac_cap |= EDAC_FLAG_S4ECD4ED; | 405 | * if dual channel and x4 then S4ECD4ED |
396 | } else { | 406 | */ |
397 | csrow->edac_mode = EDAC_SECDED; | 407 | if (drc_ddim) { |
398 | mci->edac_cap |= EDAC_FLAG_SECDED; | 408 | if (drc_chan && mem_dev) { |
399 | } | 409 | dimm->edac_mode = EDAC_S4ECD4ED; |
400 | } else | 410 | mci->edac_cap |= EDAC_FLAG_S4ECD4ED; |
401 | csrow->edac_mode = EDAC_NONE; | 411 | } else { |
412 | dimm->edac_mode = EDAC_SECDED; | ||
413 | mci->edac_cap |= EDAC_FLAG_SECDED; | ||
414 | } | ||
415 | } else | ||
416 | dimm->edac_mode = EDAC_NONE; | ||
417 | } | ||
402 | } | 418 | } |
403 | } | 419 | } |
404 | 420 | ||
@@ -406,6 +422,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx) | |||
406 | { | 422 | { |
407 | u16 pci_data; | 423 | u16 pci_data; |
408 | struct mem_ctl_info *mci = NULL; | 424 | struct mem_ctl_info *mci = NULL; |
425 | struct edac_mc_layer layers[2]; | ||
409 | struct e7xxx_pvt *pvt = NULL; | 426 | struct e7xxx_pvt *pvt = NULL; |
410 | u32 drc; | 427 | u32 drc; |
411 | int drc_chan; | 428 | int drc_chan; |
@@ -416,8 +433,21 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx) | |||
416 | pci_read_config_dword(pdev, E7XXX_DRC, &drc); | 433 | pci_read_config_dword(pdev, E7XXX_DRC, &drc); |
417 | 434 | ||
418 | drc_chan = dual_channel_active(drc, dev_idx); | 435 | drc_chan = dual_channel_active(drc, dev_idx); |
419 | mci = edac_mc_alloc(sizeof(*pvt), E7XXX_NR_CSROWS, drc_chan + 1, 0); | 436 | /* |
420 | 437 | * According with the datasheet, this device has a maximum of | |
438 | * 4 DIMMS per channel, either single-rank or dual-rank. So, the | ||
439 | * total amount of dimms is 8 (E7XXX_NR_DIMMS). | ||
440 | * That means that the DIMM is mapped as CSROWs, and the channel | ||
441 | * will map the rank. So, an error to either channel should be | ||
442 | * attributed to the same dimm. | ||
443 | */ | ||
444 | layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; | ||
445 | layers[0].size = E7XXX_NR_CSROWS; | ||
446 | layers[0].is_virt_csrow = true; | ||
447 | layers[1].type = EDAC_MC_LAYER_CHANNEL; | ||
448 | layers[1].size = drc_chan + 1; | ||
449 | layers[1].is_virt_csrow = false; | ||
450 | mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt)); | ||
421 | if (mci == NULL) | 451 | if (mci == NULL) |
422 | return -ENOMEM; | 452 | return -ENOMEM; |
423 | 453 | ||