diff options
author | Doug Thompson <dougthompson@xmission.com> | 2009-04-27 10:22:43 -0400 |
---|---|---|
committer | Borislav Petkov <borislav.petkov@amd.com> | 2009-06-10 06:18:56 -0400 |
commit | f71d0a05001afd10e2be491ca002c55c7df42ed8 (patch) | |
tree | 6fc86e854d5ed4f32b3871f3668029410c3ce1c2 /drivers/edac | |
parent | 6163b5d4fb45d20e3eb92d627943f26572726889 (diff) |
amd64_edac: add F10h-and-later methods-p3
Borislav:
- compute dct_sel_base_off in f10_match_to_this_node() correctly since
it cannot be assumed that the Reserved bits are zero and they have to be
masked out instead.
- cleanup, remove StinkyIdentifiers, simplify logic
- fix function return value patterns
- cleanup debug calls
Reviewed-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Signed-off-by: Doug Thompson <dougthompson@xmission.com>
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/amd64_edac.c | 269 |
1 files changed, 265 insertions, 4 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 744a49ac9f5c..c2e2c3c37f5c 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c | |||
@@ -1398,6 +1398,10 @@ static void f10_read_dram_ctl_register(struct amd64_pvt *pvt) | |||
1398 | debugf0("Reading F10_DCTL_SEL_HIGH failed\n"); | 1398 | debugf0("Reading F10_DCTL_SEL_HIGH failed\n"); |
1399 | } | 1399 | } |
1400 | 1400 | ||
1401 | /* | ||
1402 | * determine channel based on the interleaving mode: F10h BKDG, 2.8.9 Memory | ||
1403 | * Interleaving Modes. | ||
1404 | */ | ||
1401 | static u32 f10_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, | 1405 | static u32 f10_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, |
1402 | int hi_range_sel, u32 intlv_en) | 1406 | int hi_range_sel, u32 intlv_en) |
1403 | { | 1407 | { |
@@ -1408,6 +1412,9 @@ static u32 f10_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, | |||
1408 | else if (hi_range_sel) | 1412 | else if (hi_range_sel) |
1409 | cs = dct_sel_high; | 1413 | cs = dct_sel_high; |
1410 | else if (dct_interleave_enabled(pvt)) { | 1414 | else if (dct_interleave_enabled(pvt)) { |
1415 | /* | ||
1416 | * see F2x110[DctSelIntLvAddr] - channel interleave mode | ||
1417 | */ | ||
1411 | if (dct_sel_interleave_addr(pvt) == 0) | 1418 | if (dct_sel_interleave_addr(pvt) == 0) |
1412 | cs = sys_addr >> 6 & 1; | 1419 | cs = sys_addr >> 6 & 1; |
1413 | else if ((dct_sel_interleave_addr(pvt) >> 1) & 1) { | 1420 | else if ((dct_sel_interleave_addr(pvt) >> 1) & 1) { |
@@ -1445,22 +1452,23 @@ static inline u32 f10_map_intlv_en_to_shift(u32 intlv_en) | |||
1445 | return 0; | 1452 | return 0; |
1446 | } | 1453 | } |
1447 | 1454 | ||
1448 | static inline u64 f10_determine_base_addr_offset(u64 sys_addr, int hi_range_sel, | 1455 | /* See F10h BKDG, 2.8.10.2 DctSelBaseOffset Programming */ |
1456 | static inline u64 f10_get_base_addr_offset(u64 sys_addr, int hi_range_sel, | ||
1449 | u32 dct_sel_base_addr, | 1457 | u32 dct_sel_base_addr, |
1450 | u64 dct_sel_base_off, | 1458 | u64 dct_sel_base_off, |
1451 | u32 hole_en, u32 hole_off, | 1459 | u32 hole_valid, u32 hole_off, |
1452 | u64 dram_base) | 1460 | u64 dram_base) |
1453 | { | 1461 | { |
1454 | u64 chan_off; | 1462 | u64 chan_off; |
1455 | 1463 | ||
1456 | if (hi_range_sel) { | 1464 | if (hi_range_sel) { |
1457 | if (!(dct_sel_base_addr & 0xFFFFF800) && | 1465 | if (!(dct_sel_base_addr & 0xFFFFF800) && |
1458 | (hole_en & 1) && (sys_addr >= 0x100000000ULL)) | 1466 | hole_valid && (sys_addr >= 0x100000000ULL)) |
1459 | chan_off = hole_off << 16; | 1467 | chan_off = hole_off << 16; |
1460 | else | 1468 | else |
1461 | chan_off = dct_sel_base_off; | 1469 | chan_off = dct_sel_base_off; |
1462 | } else { | 1470 | } else { |
1463 | if ((hole_en & 1) && (sys_addr >= 0x100000000ULL)) | 1471 | if (hole_valid && (sys_addr >= 0x100000000ULL)) |
1464 | chan_off = hole_off << 16; | 1472 | chan_off = hole_off << 16; |
1465 | else | 1473 | else |
1466 | chan_off = dram_base & 0xFFFFF8000000ULL; | 1474 | chan_off = dram_base & 0xFFFFF8000000ULL; |
@@ -1562,4 +1570,257 @@ static int f10_lookup_addr_in_dct(u32 in_addr, u32 nid, u32 cs) | |||
1562 | return cs_found; | 1570 | return cs_found; |
1563 | } | 1571 | } |
1564 | 1572 | ||
1573 | /* For a given @dram_range, check if @sys_addr falls within it. */ | ||
1574 | static int f10_match_to_this_node(struct amd64_pvt *pvt, int dram_range, | ||
1575 | u64 sys_addr, int *nid, int *chan_sel) | ||
1576 | { | ||
1577 | int node_id, cs_found = -EINVAL, high_range = 0; | ||
1578 | u32 intlv_en, intlv_sel, intlv_shift, hole_off; | ||
1579 | u32 hole_valid, tmp, dct_sel_base, channel; | ||
1580 | u64 dram_base, chan_addr, dct_sel_base_off; | ||
1581 | |||
1582 | dram_base = pvt->dram_base[dram_range]; | ||
1583 | intlv_en = pvt->dram_IntlvEn[dram_range]; | ||
1584 | |||
1585 | node_id = pvt->dram_DstNode[dram_range]; | ||
1586 | intlv_sel = pvt->dram_IntlvSel[dram_range]; | ||
1587 | |||
1588 | debugf1("(dram=%d) Base=0x%llx SystemAddr= 0x%llx Limit=0x%llx\n", | ||
1589 | dram_range, dram_base, sys_addr, pvt->dram_limit[dram_range]); | ||
1590 | |||
1591 | /* | ||
1592 | * This assumes that one node's DHAR is the same as all the other | ||
1593 | * nodes' DHAR. | ||
1594 | */ | ||
1595 | hole_off = (pvt->dhar & 0x0000FF80); | ||
1596 | hole_valid = (pvt->dhar & 0x1); | ||
1597 | dct_sel_base_off = (pvt->dram_ctl_select_high & 0xFFFFFC00) << 16; | ||
1598 | |||
1599 | debugf1(" HoleOffset=0x%x HoleValid=0x%x IntlvSel=0x%x\n", | ||
1600 | hole_off, hole_valid, intlv_sel); | ||
1601 | |||
1602 | if (intlv_en || | ||
1603 | (intlv_sel != ((sys_addr >> 12) & intlv_en))) | ||
1604 | return -EINVAL; | ||
1605 | |||
1606 | dct_sel_base = dct_sel_baseaddr(pvt); | ||
1607 | |||
1608 | /* | ||
1609 | * check whether addresses >= DctSelBaseAddr[47:27] are to be used to | ||
1610 | * select between DCT0 and DCT1. | ||
1611 | */ | ||
1612 | if (dct_high_range_enabled(pvt) && | ||
1613 | !dct_ganging_enabled(pvt) && | ||
1614 | ((sys_addr >> 27) >= (dct_sel_base >> 11))) | ||
1615 | high_range = 1; | ||
1616 | |||
1617 | channel = f10_determine_channel(pvt, sys_addr, high_range, intlv_en); | ||
1618 | |||
1619 | chan_addr = f10_get_base_addr_offset(sys_addr, high_range, dct_sel_base, | ||
1620 | dct_sel_base_off, hole_valid, | ||
1621 | hole_off, dram_base); | ||
1622 | |||
1623 | intlv_shift = f10_map_intlv_en_to_shift(intlv_en); | ||
1624 | |||
1625 | /* remove Node ID (in case of memory interleaving) */ | ||
1626 | tmp = chan_addr & 0xFC0; | ||
1627 | |||
1628 | chan_addr = ((chan_addr >> intlv_shift) & 0xFFFFFFFFF000ULL) | tmp; | ||
1629 | |||
1630 | /* remove channel interleave and hash */ | ||
1631 | if (dct_interleave_enabled(pvt) && | ||
1632 | !dct_high_range_enabled(pvt) && | ||
1633 | !dct_ganging_enabled(pvt)) { | ||
1634 | if (dct_sel_interleave_addr(pvt) != 1) | ||
1635 | chan_addr = (chan_addr >> 1) & 0xFFFFFFFFFFFFFFC0ULL; | ||
1636 | else { | ||
1637 | tmp = chan_addr & 0xFC0; | ||
1638 | chan_addr = ((chan_addr & 0xFFFFFFFFFFFFC000ULL) >> 1) | ||
1639 | | tmp; | ||
1640 | } | ||
1641 | } | ||
1642 | |||
1643 | debugf1(" (ChannelAddrLong=0x%llx) >> 8 becomes InputAddr=0x%x\n", | ||
1644 | chan_addr, (u32)(chan_addr >> 8)); | ||
1645 | |||
1646 | cs_found = f10_lookup_addr_in_dct(chan_addr >> 8, node_id, channel); | ||
1647 | |||
1648 | if (cs_found >= 0) { | ||
1649 | *nid = node_id; | ||
1650 | *chan_sel = channel; | ||
1651 | } | ||
1652 | return cs_found; | ||
1653 | } | ||
1654 | |||
1655 | static int f10_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr, | ||
1656 | int *node, int *chan_sel) | ||
1657 | { | ||
1658 | int dram_range, cs_found = -EINVAL; | ||
1659 | u64 dram_base, dram_limit; | ||
1660 | |||
1661 | for (dram_range = 0; dram_range < DRAM_REG_COUNT; dram_range++) { | ||
1662 | |||
1663 | if (!pvt->dram_rw_en[dram_range]) | ||
1664 | continue; | ||
1665 | |||
1666 | dram_base = pvt->dram_base[dram_range]; | ||
1667 | dram_limit = pvt->dram_limit[dram_range]; | ||
1668 | |||
1669 | if ((dram_base <= sys_addr) && (sys_addr <= dram_limit)) { | ||
1670 | |||
1671 | cs_found = f10_match_to_this_node(pvt, dram_range, | ||
1672 | sys_addr, node, | ||
1673 | chan_sel); | ||
1674 | if (cs_found >= 0) | ||
1675 | break; | ||
1676 | } | ||
1677 | } | ||
1678 | return cs_found; | ||
1679 | } | ||
1680 | |||
1681 | /* | ||
1682 | * This the F10h reference code from AMD to map a @sys_addr to NodeID, | ||
1683 | * CSROW, Channel. | ||
1684 | * | ||
1685 | * The @sys_addr is usually an error address received from the hardware. | ||
1686 | */ | ||
1687 | static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci, | ||
1688 | struct amd64_error_info_regs *info, | ||
1689 | u64 sys_addr) | ||
1690 | { | ||
1691 | struct amd64_pvt *pvt = mci->pvt_info; | ||
1692 | u32 page, offset; | ||
1693 | unsigned short syndrome; | ||
1694 | int nid, csrow, chan = 0; | ||
1695 | |||
1696 | csrow = f10_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan); | ||
1697 | |||
1698 | if (csrow >= 0) { | ||
1699 | error_address_to_page_and_offset(sys_addr, &page, &offset); | ||
1700 | |||
1701 | syndrome = EXTRACT_HIGH_SYNDROME(info->nbsl) << 8; | ||
1702 | syndrome |= EXTRACT_LOW_SYNDROME(info->nbsh); | ||
1703 | |||
1704 | /* | ||
1705 | * Is CHIPKILL on? If so, then we can attempt to use the | ||
1706 | * syndrome to isolate which channel the error was on. | ||
1707 | */ | ||
1708 | if (pvt->nbcfg & K8_NBCFG_CHIPKILL) | ||
1709 | chan = get_channel_from_ecc_syndrome(syndrome); | ||
1710 | |||
1711 | if (chan >= 0) { | ||
1712 | edac_mc_handle_ce(mci, page, offset, syndrome, | ||
1713 | csrow, chan, EDAC_MOD_STR); | ||
1714 | } else { | ||
1715 | /* | ||
1716 | * Channel unknown, report all channels on this | ||
1717 | * CSROW as failed. | ||
1718 | */ | ||
1719 | for (chan = 0; chan < mci->csrows[csrow].nr_channels; | ||
1720 | chan++) { | ||
1721 | edac_mc_handle_ce(mci, page, offset, | ||
1722 | syndrome, | ||
1723 | csrow, chan, | ||
1724 | EDAC_MOD_STR); | ||
1725 | } | ||
1726 | } | ||
1727 | |||
1728 | } else { | ||
1729 | edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR); | ||
1730 | } | ||
1731 | } | ||
1732 | |||
1733 | /* | ||
1734 | * Input (@index) is the DBAM DIMM value (1 of 4) used as an index into a shift | ||
1735 | * table (revf_quad_ddr2_shift) which starts at 128MB DIMM size. Index of 0 | ||
1736 | * indicates an empty DIMM slot, as reported by Hardware on empty slots. | ||
1737 | * | ||
1738 | * Normalize to 128MB by subracting 27 bit shift. | ||
1739 | */ | ||
1740 | static int map_dbam_to_csrow_size(int index) | ||
1741 | { | ||
1742 | int mega_bytes = 0; | ||
1743 | |||
1744 | if (index > 0 && index <= DBAM_MAX_VALUE) | ||
1745 | mega_bytes = ((128 << (revf_quad_ddr2_shift[index]-27))); | ||
1746 | |||
1747 | return mega_bytes; | ||
1748 | } | ||
1749 | |||
1750 | /* | ||
1751 | * debug routine to display the memory sizes of a DIMM (ganged or not) and it | ||
1752 | * CSROWs as well | ||
1753 | */ | ||
1754 | static void f10_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt, | ||
1755 | int ganged) | ||
1756 | { | ||
1757 | int dimm, size0, size1; | ||
1758 | u32 dbam; | ||
1759 | u32 *dcsb; | ||
1760 | |||
1761 | debugf1(" dbam%d: 0x%8.08x CSROW is %s\n", ctrl, | ||
1762 | ctrl ? pvt->dbam1 : pvt->dbam0, | ||
1763 | ganged ? "GANGED - dbam1 not used" : "NON-GANGED"); | ||
1764 | |||
1765 | dbam = ctrl ? pvt->dbam1 : pvt->dbam0; | ||
1766 | dcsb = ctrl ? pvt->dcsb1 : pvt->dcsb0; | ||
1767 | |||
1768 | /* Dump memory sizes for DIMM and its CSROWs */ | ||
1769 | for (dimm = 0; dimm < 4; dimm++) { | ||
1770 | |||
1771 | size0 = 0; | ||
1772 | if (dcsb[dimm*2] & K8_DCSB_CS_ENABLE) | ||
1773 | size0 = map_dbam_to_csrow_size(DBAM_DIMM(dimm, dbam)); | ||
1774 | |||
1775 | size1 = 0; | ||
1776 | if (dcsb[dimm*2 + 1] & K8_DCSB_CS_ENABLE) | ||
1777 | size1 = map_dbam_to_csrow_size(DBAM_DIMM(dimm, dbam)); | ||
1778 | |||
1779 | debugf1(" CTRL-%d DIMM-%d=%5dMB CSROW-%d=%5dMB " | ||
1780 | "CSROW-%d=%5dMB\n", | ||
1781 | ctrl, | ||
1782 | dimm, | ||
1783 | size0 + size1, | ||
1784 | dimm * 2, | ||
1785 | size0, | ||
1786 | dimm * 2 + 1, | ||
1787 | size1); | ||
1788 | } | ||
1789 | } | ||
1790 | |||
1791 | /* | ||
1792 | * Very early hardware probe on pci_probe thread to determine if this module | ||
1793 | * supports the hardware. | ||
1794 | * | ||
1795 | * Return: | ||
1796 | * 0 for OK | ||
1797 | * 1 for error | ||
1798 | */ | ||
1799 | static int f10_probe_valid_hardware(struct amd64_pvt *pvt) | ||
1800 | { | ||
1801 | int ret = 0; | ||
1802 | |||
1803 | /* | ||
1804 | * If we are on a DDR3 machine, we don't know yet if | ||
1805 | * we support that properly at this time | ||
1806 | */ | ||
1807 | if ((pvt->dchr0 & F10_DCHR_Ddr3Mode) || | ||
1808 | (pvt->dchr1 & F10_DCHR_Ddr3Mode)) { | ||
1809 | |||
1810 | amd64_printk(KERN_WARNING, | ||
1811 | "%s() This machine is running with DDR3 memory. " | ||
1812 | "This is not currently supported. " | ||
1813 | "DCHR0=0x%x DCHR1=0x%x\n", | ||
1814 | __func__, pvt->dchr0, pvt->dchr1); | ||
1815 | |||
1816 | amd64_printk(KERN_WARNING, | ||
1817 | " Contact '%s' module MAINTAINER to help add" | ||
1818 | " support.\n", | ||
1819 | EDAC_MOD_STR); | ||
1820 | |||
1821 | ret = 1; | ||
1822 | |||
1823 | } | ||
1824 | return ret; | ||
1825 | } | ||
1565 | 1826 | ||