aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac
diff options
context:
space:
mode:
authorDoug Thompson <dougthompson@xmission.com>2009-04-27 10:22:43 -0400
committerBorislav Petkov <borislav.petkov@amd.com>2009-06-10 06:18:56 -0400
commitf71d0a05001afd10e2be491ca002c55c7df42ed8 (patch)
tree6fc86e854d5ed4f32b3871f3668029410c3ce1c2 /drivers/edac
parent6163b5d4fb45d20e3eb92d627943f26572726889 (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.c269
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 */
1401static u32 f10_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 1405static 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
1448static inline u64 f10_determine_base_addr_offset(u64 sys_addr, int hi_range_sel, 1455/* See F10h BKDG, 2.8.10.2 DctSelBaseOffset Programming */
1456static 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. */
1574static 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
1655static 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 */
1687static 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 */
1740static 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 */
1754static 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 */
1799static 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