diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-04-16 14:10:12 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-05-28 18:13:42 -0400 |
commit | 0975c16f4fa901403708a0d3f19b33a726b10326 (patch) | |
tree | 622a5a9b66afc2205ef74c3064f8e2792ed26911 /drivers | |
parent | 70e2a8379b6e704b2e8cdfcf151954171c36e779 (diff) |
i7core_edac: convert driver to use the new edac ABI
The legacy edac ABI is going to be removed. Port the driver to use
and benefit from the new API functionality.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/edac/i7core_edac.c | 202 |
1 files changed, 50 insertions, 152 deletions
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 0e3cc34bcc22..bc2c04550b8b 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c | |||
@@ -257,7 +257,6 @@ struct i7core_pvt { | |||
257 | struct i7core_channel channel[NUM_CHANS]; | 257 | struct i7core_channel channel[NUM_CHANS]; |
258 | 258 | ||
259 | int ce_count_available; | 259 | int ce_count_available; |
260 | int csrow_map[NUM_CHANS][MAX_DIMMS]; | ||
261 | 260 | ||
262 | /* ECC corrected errors counts per udimm */ | 261 | /* ECC corrected errors counts per udimm */ |
263 | unsigned long udimm_ce_count[MAX_DIMMS]; | 262 | unsigned long udimm_ce_count[MAX_DIMMS]; |
@@ -492,113 +491,12 @@ static void free_i7core_dev(struct i7core_dev *i7core_dev) | |||
492 | /**************************************************************************** | 491 | /**************************************************************************** |
493 | Memory check routines | 492 | Memory check routines |
494 | ****************************************************************************/ | 493 | ****************************************************************************/ |
495 | static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot, | ||
496 | unsigned func) | ||
497 | { | ||
498 | struct i7core_dev *i7core_dev = get_i7core_dev(socket); | ||
499 | int i; | ||
500 | |||
501 | if (!i7core_dev) | ||
502 | return NULL; | ||
503 | |||
504 | for (i = 0; i < i7core_dev->n_devs; i++) { | ||
505 | if (!i7core_dev->pdev[i]) | ||
506 | continue; | ||
507 | |||
508 | if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot && | ||
509 | PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) { | ||
510 | return i7core_dev->pdev[i]; | ||
511 | } | ||
512 | } | ||
513 | |||
514 | return NULL; | ||
515 | } | ||
516 | |||
517 | /** | ||
518 | * i7core_get_active_channels() - gets the number of channels and csrows | ||
519 | * @socket: Quick Path Interconnect socket | ||
520 | * @channels: Number of channels that will be returned | ||
521 | * @csrows: Number of csrows found | ||
522 | * | ||
523 | * Since EDAC core needs to know in advance the number of available channels | ||
524 | * and csrows, in order to allocate memory for csrows/channels, it is needed | ||
525 | * to run two similar steps. At the first step, implemented on this function, | ||
526 | * it checks the number of csrows/channels present at one socket. | ||
527 | * this is used in order to properly allocate the size of mci components. | ||
528 | * | ||
529 | * It should be noticed that none of the current available datasheets explain | ||
530 | * or even mention how csrows are seen by the memory controller. So, we need | ||
531 | * to add a fake description for csrows. | ||
532 | * So, this driver is attributing one DIMM memory for one csrow. | ||
533 | */ | ||
534 | static int i7core_get_active_channels(const u8 socket, unsigned *channels, | ||
535 | unsigned *csrows) | ||
536 | { | ||
537 | struct pci_dev *pdev = NULL; | ||
538 | int i, j; | ||
539 | u32 status, control; | ||
540 | |||
541 | *channels = 0; | ||
542 | *csrows = 0; | ||
543 | |||
544 | pdev = get_pdev_slot_func(socket, 3, 0); | ||
545 | if (!pdev) { | ||
546 | i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n", | ||
547 | socket); | ||
548 | return -ENODEV; | ||
549 | } | ||
550 | |||
551 | /* Device 3 function 0 reads */ | ||
552 | pci_read_config_dword(pdev, MC_STATUS, &status); | ||
553 | pci_read_config_dword(pdev, MC_CONTROL, &control); | ||
554 | |||
555 | for (i = 0; i < NUM_CHANS; i++) { | ||
556 | u32 dimm_dod[3]; | ||
557 | /* Check if the channel is active */ | ||
558 | if (!(control & (1 << (8 + i)))) | ||
559 | continue; | ||
560 | |||
561 | /* Check if the channel is disabled */ | ||
562 | if (status & (1 << i)) | ||
563 | continue; | ||
564 | |||
565 | pdev = get_pdev_slot_func(socket, i + 4, 1); | ||
566 | if (!pdev) { | ||
567 | i7core_printk(KERN_ERR, "Couldn't find socket %d " | ||
568 | "fn %d.%d!!!\n", | ||
569 | socket, i + 4, 1); | ||
570 | return -ENODEV; | ||
571 | } | ||
572 | /* Devices 4-6 function 1 */ | ||
573 | pci_read_config_dword(pdev, | ||
574 | MC_DOD_CH_DIMM0, &dimm_dod[0]); | ||
575 | pci_read_config_dword(pdev, | ||
576 | MC_DOD_CH_DIMM1, &dimm_dod[1]); | ||
577 | pci_read_config_dword(pdev, | ||
578 | MC_DOD_CH_DIMM2, &dimm_dod[2]); | ||
579 | |||
580 | (*channels)++; | ||
581 | |||
582 | for (j = 0; j < 3; j++) { | ||
583 | if (!DIMM_PRESENT(dimm_dod[j])) | ||
584 | continue; | ||
585 | (*csrows)++; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | debugf0("Number of active channels on socket %d: %d\n", | ||
590 | socket, *channels); | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | 494 | ||
595 | static int get_dimm_config(struct mem_ctl_info *mci) | 495 | static int get_dimm_config(struct mem_ctl_info *mci) |
596 | { | 496 | { |
597 | struct i7core_pvt *pvt = mci->pvt_info; | 497 | struct i7core_pvt *pvt = mci->pvt_info; |
598 | struct csrow_info *csr; | ||
599 | struct pci_dev *pdev; | 498 | struct pci_dev *pdev; |
600 | int i, j; | 499 | int i, j; |
601 | int csrow = 0; | ||
602 | enum edac_type mode; | 500 | enum edac_type mode; |
603 | enum mem_type mtype; | 501 | enum mem_type mtype; |
604 | struct dimm_info *dimm; | 502 | struct dimm_info *dimm; |
@@ -696,6 +594,8 @@ static int get_dimm_config(struct mem_ctl_info *mci) | |||
696 | if (!DIMM_PRESENT(dimm_dod[j])) | 594 | if (!DIMM_PRESENT(dimm_dod[j])) |
697 | continue; | 595 | continue; |
698 | 596 | ||
597 | dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers, | ||
598 | i, j, 0); | ||
699 | banks = numbank(MC_DOD_NUMBANK(dimm_dod[j])); | 599 | banks = numbank(MC_DOD_NUMBANK(dimm_dod[j])); |
700 | ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j])); | 600 | ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j])); |
701 | rows = numrow(MC_DOD_NUMROW(dimm_dod[j])); | 601 | rows = numrow(MC_DOD_NUMROW(dimm_dod[j])); |
@@ -704,8 +604,6 @@ static int get_dimm_config(struct mem_ctl_info *mci) | |||
704 | /* DDR3 has 8 I/O banks */ | 604 | /* DDR3 has 8 I/O banks */ |
705 | size = (rows * cols * banks * ranks) >> (20 - 3); | 605 | size = (rows * cols * banks * ranks) >> (20 - 3); |
706 | 606 | ||
707 | pvt->channel[i].dimms++; | ||
708 | |||
709 | debugf0("\tdimm %d %d Mb offset: %x, " | 607 | debugf0("\tdimm %d %d Mb offset: %x, " |
710 | "bank: %d, rank: %d, row: %#x, col: %#x\n", | 608 | "bank: %d, rank: %d, row: %#x, col: %#x\n", |
711 | j, size, | 609 | j, size, |
@@ -714,11 +612,6 @@ static int get_dimm_config(struct mem_ctl_info *mci) | |||
714 | 612 | ||
715 | npages = MiB_TO_PAGES(size); | 613 | npages = MiB_TO_PAGES(size); |
716 | 614 | ||
717 | csr = &mci->csrows[csrow]; | ||
718 | |||
719 | pvt->csrow_map[i][j] = csrow; | ||
720 | |||
721 | dimm = csr->channels[0].dimm; | ||
722 | dimm->nr_pages = npages; | 615 | dimm->nr_pages = npages; |
723 | 616 | ||
724 | switch (banks) { | 617 | switch (banks) { |
@@ -741,7 +634,6 @@ static int get_dimm_config(struct mem_ctl_info *mci) | |||
741 | dimm->grain = 8; | 634 | dimm->grain = 8; |
742 | dimm->edac_mode = mode; | 635 | dimm->edac_mode = mode; |
743 | dimm->mtype = mtype; | 636 | dimm->mtype = mtype; |
744 | csrow++; | ||
745 | } | 637 | } |
746 | 638 | ||
747 | pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); | 639 | pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); |
@@ -1557,22 +1449,16 @@ error: | |||
1557 | /**************************************************************************** | 1449 | /**************************************************************************** |
1558 | Error check routines | 1450 | Error check routines |
1559 | ****************************************************************************/ | 1451 | ****************************************************************************/ |
1560 | static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, | 1452 | static void i7core_rdimm_update_errcount(struct mem_ctl_info *mci, |
1561 | const int chan, | 1453 | const int chan, |
1562 | const int dimm, | 1454 | const int dimm, |
1563 | const int add) | 1455 | const int add) |
1564 | { | 1456 | { |
1565 | char *msg; | 1457 | int i; |
1566 | struct i7core_pvt *pvt = mci->pvt_info; | ||
1567 | int row = pvt->csrow_map[chan][dimm], i; | ||
1568 | 1458 | ||
1569 | for (i = 0; i < add; i++) { | 1459 | for (i = 0; i < add; i++) { |
1570 | msg = kasprintf(GFP_KERNEL, "Corrected error " | 1460 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0, |
1571 | "(Socket=%d channel=%d dimm=%d)", | 1461 | chan, dimm, -1, "error", "", NULL); |
1572 | pvt->i7core_dev->socket, chan, dimm); | ||
1573 | |||
1574 | edac_mc_handle_fbd_ce(mci, row, 0, msg); | ||
1575 | kfree (msg); | ||
1576 | } | 1462 | } |
1577 | } | 1463 | } |
1578 | 1464 | ||
@@ -1613,11 +1499,11 @@ static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, | |||
1613 | 1499 | ||
1614 | /*updated the edac core */ | 1500 | /*updated the edac core */ |
1615 | if (add0 != 0) | 1501 | if (add0 != 0) |
1616 | i7core_rdimm_update_csrow(mci, chan, 0, add0); | 1502 | i7core_rdimm_update_errcount(mci, chan, 0, add0); |
1617 | if (add1 != 0) | 1503 | if (add1 != 0) |
1618 | i7core_rdimm_update_csrow(mci, chan, 1, add1); | 1504 | i7core_rdimm_update_errcount(mci, chan, 1, add1); |
1619 | if (add2 != 0) | 1505 | if (add2 != 0) |
1620 | i7core_rdimm_update_csrow(mci, chan, 2, add2); | 1506 | i7core_rdimm_update_errcount(mci, chan, 2, add2); |
1621 | 1507 | ||
1622 | } | 1508 | } |
1623 | 1509 | ||
@@ -1738,19 +1624,29 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, | |||
1738 | { | 1624 | { |
1739 | struct i7core_pvt *pvt = mci->pvt_info; | 1625 | struct i7core_pvt *pvt = mci->pvt_info; |
1740 | char *type, *optype, *err, *msg; | 1626 | char *type, *optype, *err, *msg; |
1627 | enum hw_event_mc_err_type tp_event; | ||
1741 | unsigned long error = m->status & 0x1ff0000l; | 1628 | unsigned long error = m->status & 0x1ff0000l; |
1629 | bool uncorrected_error = m->mcgstatus & 1ll << 61; | ||
1630 | bool ripv = m->mcgstatus & 1; | ||
1742 | u32 optypenum = (m->status >> 4) & 0x07; | 1631 | u32 optypenum = (m->status >> 4) & 0x07; |
1743 | u32 core_err_cnt = (m->status >> 38) & 0x7fff; | 1632 | u32 core_err_cnt = (m->status >> 38) & 0x7fff; |
1744 | u32 dimm = (m->misc >> 16) & 0x3; | 1633 | u32 dimm = (m->misc >> 16) & 0x3; |
1745 | u32 channel = (m->misc >> 18) & 0x3; | 1634 | u32 channel = (m->misc >> 18) & 0x3; |
1746 | u32 syndrome = m->misc >> 32; | 1635 | u32 syndrome = m->misc >> 32; |
1747 | u32 errnum = find_first_bit(&error, 32); | 1636 | u32 errnum = find_first_bit(&error, 32); |
1748 | int csrow; | ||
1749 | 1637 | ||
1750 | if (m->mcgstatus & 1) | 1638 | if (uncorrected_error) { |
1751 | type = "FATAL"; | 1639 | if (ripv) { |
1752 | else | 1640 | type = "FATAL"; |
1753 | type = "NON_FATAL"; | 1641 | tp_event = HW_EVENT_ERR_FATAL; |
1642 | } else { | ||
1643 | type = "NON_FATAL"; | ||
1644 | tp_event = HW_EVENT_ERR_UNCORRECTED; | ||
1645 | } | ||
1646 | } else { | ||
1647 | type = "CORRECTED"; | ||
1648 | tp_event = HW_EVENT_ERR_CORRECTED; | ||
1649 | } | ||
1754 | 1650 | ||
1755 | switch (optypenum) { | 1651 | switch (optypenum) { |
1756 | case 0: | 1652 | case 0: |
@@ -1805,25 +1701,23 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, | |||
1805 | err = "unknown"; | 1701 | err = "unknown"; |
1806 | } | 1702 | } |
1807 | 1703 | ||
1808 | /* FIXME: should convert addr into bank and rank information */ | ||
1809 | msg = kasprintf(GFP_ATOMIC, | 1704 | msg = kasprintf(GFP_ATOMIC, |
1810 | "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, " | 1705 | "addr=0x%08llx cpu=%d count=%d Err=%08llx:%08llx (%s: %s))\n", |
1811 | "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n", | 1706 | (long long) m->addr, m->cpu, core_err_cnt, |
1812 | type, (long long) m->addr, m->cpu, dimm, channel, | 1707 | (long long)m->status, (long long)m->misc, optype, err); |
1813 | syndrome, core_err_cnt, (long long)m->status, | ||
1814 | (long long)m->misc, optype, err); | ||
1815 | 1708 | ||
1816 | debugf0("%s", msg); | 1709 | /* |
1817 | 1710 | * Call the helper to output message | |
1818 | csrow = pvt->csrow_map[channel][dimm]; | 1711 | * FIXME: what to do if core_err_cnt > 1? Currently, it generates |
1819 | 1712 | * only one event | |
1820 | /* Call the helper to output message */ | 1713 | */ |
1821 | if (m->mcgstatus & 1) | 1714 | if (uncorrected_error || !pvt->is_registered) |
1822 | edac_mc_handle_fbd_ue(mci, csrow, 0, | 1715 | edac_mc_handle_error(tp_event, mci, |
1823 | 0 /* FIXME: should be channel here */, msg); | 1716 | m->addr >> PAGE_SHIFT, |
1824 | else if (!pvt->is_registered) | 1717 | m->addr & ~PAGE_MASK, |
1825 | edac_mc_handle_fbd_ce(mci, csrow, | 1718 | syndrome, |
1826 | 0 /* FIXME: should be channel here */, msg); | 1719 | channel, dimm, -1, |
1720 | err, msg, m); | ||
1827 | 1721 | ||
1828 | kfree(msg); | 1722 | kfree(msg); |
1829 | } | 1723 | } |
@@ -2242,15 +2136,19 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) | |||
2242 | { | 2136 | { |
2243 | struct mem_ctl_info *mci; | 2137 | struct mem_ctl_info *mci; |
2244 | struct i7core_pvt *pvt; | 2138 | struct i7core_pvt *pvt; |
2245 | int rc, channels, csrows; | 2139 | int rc; |
2246 | 2140 | struct edac_mc_layer layers[2]; | |
2247 | /* Check the number of active and not disabled channels */ | ||
2248 | rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows); | ||
2249 | if (unlikely(rc < 0)) | ||
2250 | return rc; | ||
2251 | 2141 | ||
2252 | /* allocate a new MC control structure */ | 2142 | /* allocate a new MC control structure */ |
2253 | mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket); | 2143 | |
2144 | layers[0].type = EDAC_MC_LAYER_CHANNEL; | ||
2145 | layers[0].size = NUM_CHANS; | ||
2146 | layers[0].is_virt_csrow = false; | ||
2147 | layers[1].type = EDAC_MC_LAYER_SLOT; | ||
2148 | layers[1].size = MAX_DIMMS; | ||
2149 | layers[1].is_virt_csrow = true; | ||
2150 | mci = new_edac_mc_alloc(i7core_dev->socket, ARRAY_SIZE(layers), layers, | ||
2151 | sizeof(*pvt)); | ||
2254 | if (unlikely(!mci)) | 2152 | if (unlikely(!mci)) |
2255 | return -ENOMEM; | 2153 | return -ENOMEM; |
2256 | 2154 | ||