Commit ff881842 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'edac_for_5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras

Pull EDAC updates from Borislav Petkov:
 "The new thing this time around is that we have three maintainers now
  and a new, old repo. New because it is new for the EDAC tree which is
  hosted there from now on and old because it is Tony's and mine's old
  RAS repo which we still use occasionally when the stuff isn't in tip.

  Summary:

   -  EDAC tree has three maintainers and one new designated reviewer
      now, so that the work can scale better.

   -  New driver for Mellanox' BlueField SoC DDR controller (Shravan
      Kumar Ramani)

   -  AMD Rome support in amd64_edac (Yazen Ghannam and Isaac Vaughn)

   -  Misc fixes, cleanups and code improvements"

* tag 'edac_for_5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras:
  EDAC/amd64: Add PCI device IDs for family 17h, model 70h
  MAINTAINERS: Add Robert as a EDAC reviewer
  EDAC/mc_sysfs: Make debug messages consistent
  EDAC/mc_sysfs: Remove pointless gotos
  EDAC: Prefer 'unsigned int' to bare use of 'unsigned'
  EDAC/amd64: Support asymmetric dual-rank DIMMs
  EDAC/amd64: Cache secondary Chip Select registers
  EDAC/amd64: Decode syndrome before translating address
  EDAC/amd64: Find Chip Select memory size using Address Mask
  EDAC/amd64: Initialize DIMM info for systems with more than two channels
  EDAC/amd64: Recognize DRAM device type ECC capability
  EDAC/amd64: Support more than two controllers for chip selects handling
  EDAC/mc: Cleanup _edac_mc_free() code
  EDAC, pnd2: Fix ioremap() size in dnv_rd_reg()
  EDAC, mellanox: Add ECC support for BlueField DDR4
  EDAC/altera: Use the proper type for the IRQ status bits
  EDAC/mc: Fix grain_bits calculation
  edac: altera: Move Stratix10 SDRAM ECC to peripheral
  MAINTAINERS: update EDAC entry to reflect current tree and maintainers
parents a7bd4bcf 3e443eb3
......@@ -5761,6 +5761,11 @@ S: Supported
F: drivers/edac/aspeed_edac.c
F: Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt
EDAC-BLUEFIELD
M: Shravan Kumar Ramani <sramani@mellanox.com>
S: Supported
F: drivers/edac/bluefield_edac.c
EDAC-CALXEDA
M: Robert Richter <rric@kernel.org>
L: linux-edac@vger.kernel.org
......@@ -5785,10 +5790,11 @@ F: drivers/edac/thunderx_edac*
EDAC-CORE
M: Borislav Petkov <bp@alien8.de>
M: Mauro Carvalho Chehab <mchehab@kernel.org>
M: Tony Luck <tony.luck@intel.com>
R: James Morse <james.morse@arm.com>
R: Robert Richter <rrichter@marvell.com>
L: linux-edac@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git for-next
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac.git linux_next
T: git git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras.git edac-for-next
S: Supported
F: Documentation/admin-guide/ras.rst
F: Documentation/driver-api/edac.rst
......
......@@ -510,4 +510,11 @@ config EDAC_ASPEED
First, ECC must be configured in the bootloader. Then, this driver
will expose error counters via the EDAC kernel framework.
config EDAC_BLUEFIELD
tristate "Mellanox BlueField Memory ECC"
depends on ARM64 && ((MELLANOX_PLATFORM && ACPI) || COMPILE_TEST)
help
Support for error detection and correction on the
Mellanox BlueField SoCs.
endif # EDAC
......@@ -85,3 +85,4 @@ obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o
obj-$(CONFIG_EDAC_TI) += ti_edac.o
obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o
obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o
obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o
......@@ -222,7 +222,6 @@ static unsigned long get_total_mem(void)
static const struct of_device_id altr_sdram_ctrl_of_match[] = {
{ .compatible = "altr,sdram-edac", .data = &c5_data},
{ .compatible = "altr,sdram-edac-a10", .data = &a10_data},
{ .compatible = "altr,sdram-edac-s10", .data = &a10_data},
{},
};
MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match);
......@@ -1170,6 +1169,24 @@ static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat)
return 0;
}
/*********************** SDRAM EDAC Device Functions *********************/
#ifdef CONFIG_EDAC_ALTERA_SDRAM
static const struct edac_device_prv_data s10_sdramecc_data = {
.setup = altr_check_ecc_deps,
.ce_clear_mask = ALTR_S10_ECC_SERRPENA,
.ue_clear_mask = ALTR_S10_ECC_DERRPENA,
.ecc_enable_mask = ALTR_S10_ECC_EN,
.ecc_en_ofst = ALTR_S10_ECC_CTRL_SDRAM_OFST,
.ce_set_mask = ALTR_S10_ECC_TSERRA,
.ue_set_mask = ALTR_S10_ECC_TDERRA,
.set_err_ofst = ALTR_S10_ECC_INTTEST_OFST,
.ecc_irq_handler = altr_edac_a10_ecc_irq,
.inject_fops = &altr_edac_a10_device_inject_fops,
};
#endif /* CONFIG_EDAC_ALTERA_SDRAM */
/*********************** OCRAM EDAC Device Functions *********************/
#ifdef CONFIG_EDAC_ALTERA_OCRAM
......@@ -1758,6 +1775,9 @@ static const struct of_device_id altr_edac_a10_device_of_match[] = {
#endif
#ifdef CONFIG_EDAC_ALTERA_SDMMC
{ .compatible = "altr,socfpga-sdmmc-ecc", .data = &a10_sdmmcecca_data },
#endif
#ifdef CONFIG_EDAC_ALTERA_SDRAM
{ .compatible = "altr,sdram-edac-s10", .data = &s10_sdramecc_data },
#endif
{},
};
......@@ -1866,6 +1886,7 @@ static void altr_edac_a10_irq_handler(struct irq_desc *desc)
struct altr_arria10_edac *edac = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
int irq = irq_desc_get_irq(desc);
unsigned long bits;
dberr = (irq == edac->db_irq) ? 1 : 0;
sm_offset = dberr ? A10_SYSMGR_ECC_INTSTAT_DERR_OFST :
......@@ -1875,7 +1896,8 @@ static void altr_edac_a10_irq_handler(struct irq_desc *desc)
regmap_read(edac->ecc_mgr_map, sm_offset, &irq_status);
for_each_set_bit(bit, (unsigned long *)&irq_status, 32) {
bits = irq_status;
for_each_set_bit(bit, &bits, 32) {
irq = irq_linear_revmap(edac->domain, dberr * 32 + bit);
if (irq)
generic_handle_irq(irq);
......@@ -1889,6 +1911,10 @@ static int validate_parent_available(struct device_node *np)
struct device_node *parent;
int ret = 0;
/* SDRAM must be present for Linux (implied parent) */
if (of_device_is_compatible(np, "altr,sdram-edac-s10"))
return 0;
/* Ensure parent device is enabled if parent node exists */
parent = of_parse_phandle(np, "altr,ecc-parent", 0);
if (parent && !of_device_is_available(parent))
......@@ -1898,6 +1924,22 @@ static int validate_parent_available(struct device_node *np)
return ret;
}
static int get_s10_sdram_edac_resource(struct device_node *np,
struct resource *res)
{
struct device_node *parent;
int ret;
parent = of_parse_phandle(np, "altr,sdr-syscon", 0);
if (!parent)
return -ENODEV;
ret = of_address_to_resource(parent, 0, res);
of_node_put(parent);
return ret;
}
static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
struct device_node *np)
{
......@@ -1925,7 +1967,11 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
if (!devres_open_group(edac->dev, altr_edac_a10_device_add, GFP_KERNEL))
return -ENOMEM;
rc = of_address_to_resource(np, 0, &res);
if (of_device_is_compatible(np, "altr,sdram-edac-s10"))
rc = get_s10_sdram_edac_resource(np, &res);
else
rc = of_address_to_resource(np, 0, &res);
if (rc < 0) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"%s: no resource address\n", ecc_name);
......@@ -2231,13 +2277,15 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
of_device_is_compatible(child, "altr,socfpga-dma-ecc") ||
of_device_is_compatible(child, "altr,socfpga-usb-ecc") ||
of_device_is_compatible(child, "altr,socfpga-qspi-ecc") ||
#ifdef CONFIG_EDAC_ALTERA_SDRAM
of_device_is_compatible(child, "altr,sdram-edac-s10") ||
#endif
of_device_is_compatible(child, "altr,socfpga-sdmmc-ecc"))
altr_edac_a10_device_add(edac, child);
#ifdef CONFIG_EDAC_ALTERA_SDRAM
else if ((of_device_is_compatible(child, "altr,sdram-edac-a10")) ||
(of_device_is_compatible(child, "altr,sdram-edac-s10")))
else if (of_device_is_compatible(child, "altr,sdram-edac-a10"))
of_platform_populate(pdev->dev.of_node,
altr_sdram_ctrl_of_match,
NULL, &pdev->dev);
......
......@@ -289,6 +289,29 @@ struct altr_sdram_mc_data {
#define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000
/************* Stratix10 Defines **************/
#define ALTR_S10_ECC_CTRL_SDRAM_OFST 0x00
#define ALTR_S10_ECC_EN BIT(0)
#define ALTR_S10_ECC_ERRINTEN_OFST 0x10
#define ALTR_S10_ECC_ERRINTENS_OFST 0x14
#define ALTR_S10_ECC_ERRINTENR_OFST 0x18
#define ALTR_S10_ECC_SERRINTEN BIT(0)
#define ALTR_S10_ECC_INTMODE_OFST 0x1C
#define ALTR_S10_ECC_INTMODE BIT(0)
#define ALTR_S10_ECC_INTSTAT_OFST 0x20
#define ALTR_S10_ECC_SERRPENA BIT(0)
#define ALTR_S10_ECC_DERRPENA BIT(8)
#define ALTR_S10_ECC_ERRPENA_MASK (ALTR_S10_ECC_SERRPENA | \
ALTR_S10_ECC_DERRPENA)
#define ALTR_S10_ECC_INTTEST_OFST 0x24
#define ALTR_S10_ECC_TSERRA BIT(0)
#define ALTR_S10_ECC_TDERRA BIT(8)
#define ALTR_S10_ECC_TSERRB BIT(16)
#define ALTR_S10_ECC_TDERRB BIT(24)
#define ALTR_S10_DERR_ADDRA_OFST 0x2C
/* Stratix10 ECC Manager Defines */
......@@ -300,7 +323,7 @@ struct altr_sdram_mc_data {
#define S10_SYSMGR_UE_ADDR_OFST 0x224
#define S10_DDR0_IRQ_MASK BIT(16)
#define S10_DBE_IRQ_MASK 0x3FE
#define S10_DBE_IRQ_MASK 0x3FFFE
/* Define ECC Block Offsets for peripherals */
#define ECC_BLK_ADDRESS_OFST 0x40
......
......@@ -788,51 +788,45 @@ static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
(dclr & BIT(15)) ? "yes" : "no");
}
/*
* The Address Mask should be a contiguous set of bits in the non-interleaved
* case. So to check for CS interleaving, find the most- and least-significant
* bits of the mask, generate a contiguous bitmask, and compare the two.
*/
static bool f17_cs_interleaved(struct amd64_pvt *pvt, u8 ctrl, int cs)
#define CS_EVEN_PRIMARY BIT(0)
#define CS_ODD_PRIMARY BIT(1)
#define CS_EVEN_SECONDARY BIT(2)
#define CS_ODD_SECONDARY BIT(3)
#define CS_EVEN (CS_EVEN_PRIMARY | CS_EVEN_SECONDARY)
#define CS_ODD (CS_ODD_PRIMARY | CS_ODD_SECONDARY)
static int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
{
u32 mask = pvt->csels[ctrl].csmasks[cs >> 1];
u32 msb = fls(mask) - 1, lsb = ffs(mask) - 1;
u32 test_mask = GENMASK(msb, lsb);
int cs_mode = 0;
edac_dbg(1, "mask=0x%08x test_mask=0x%08x\n", mask, test_mask);
if (csrow_enabled(2 * dimm, ctrl, pvt))
cs_mode |= CS_EVEN_PRIMARY;
return mask ^ test_mask;
if (csrow_enabled(2 * dimm + 1, ctrl, pvt))
cs_mode |= CS_ODD_PRIMARY;
/* Asymmetric dual-rank DIMM support. */
if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt))
cs_mode |= CS_ODD_SECONDARY;
return cs_mode;
}
static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl)
{
int dimm, size0, size1, cs0, cs1;
int dimm, size0, size1, cs0, cs1, cs_mode;
edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl);
for (dimm = 0; dimm < 4; dimm++) {
size0 = 0;
for (dimm = 0; dimm < 2; dimm++) {
cs0 = dimm * 2;
if (csrow_enabled(cs0, ctrl, pvt))
size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, cs0);
size1 = 0;
cs1 = dimm * 2 + 1;
if (csrow_enabled(cs1, ctrl, pvt)) {
/*
* CS interleaving is only supported if both CSes have
* the same amount of memory. Because they are
* interleaved, it will look like both CSes have the
* full amount of memory. Save the size for both as
* half the amount we found on CS0, if interleaved.
*/
if (f17_cs_interleaved(pvt, ctrl, cs1))
size1 = size0 = (size0 >> 1);
else
size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, cs1);
}
cs_mode = f17_get_cs_mode(dimm, ctrl, pvt);
size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0);
size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1);
amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
cs0, size0,
......@@ -942,89 +936,119 @@ static void prep_chip_selects(struct amd64_pvt *pvt)
} else if (pvt->fam == 0x15 && pvt->model == 0x30) {
pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
} else if (pvt->fam >= 0x17) {
int umc;
for_each_umc(umc) {
pvt->csels[umc].b_cnt = 4;
pvt->csels[umc].m_cnt = 2;
}
} else {
pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
}
}
static void read_umc_base_mask(struct amd64_pvt *pvt)
{
u32 umc_base_reg, umc_base_reg_sec;
u32 umc_mask_reg, umc_mask_reg_sec;
u32 base_reg, base_reg_sec;
u32 mask_reg, mask_reg_sec;
u32 *base, *base_sec;
u32 *mask, *mask_sec;
int cs, umc;
for_each_umc(umc) {
umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR;
umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC;
for_each_chip_select(cs, umc, pvt) {
base = &pvt->csels[umc].csbases[cs];
base_sec = &pvt->csels[umc].csbases_sec[cs];
base_reg = umc_base_reg + (cs * 4);
base_reg_sec = umc_base_reg_sec + (cs * 4);
if (!amd_smn_read(pvt->mc_node_id, base_reg, base))
edac_dbg(0, " DCSB%d[%d]=0x%08x reg: 0x%x\n",
umc, cs, *base, base_reg);
if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec))
edac_dbg(0, " DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n",
umc, cs, *base_sec, base_reg_sec);
}
umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK;
umc_mask_reg_sec = get_umc_base(umc) + UMCCH_ADDR_MASK_SEC;
for_each_chip_select_mask(cs, umc, pvt) {
mask = &pvt->csels[umc].csmasks[cs];
mask_sec = &pvt->csels[umc].csmasks_sec[cs];
mask_reg = umc_mask_reg + (cs * 4);
mask_reg_sec = umc_mask_reg_sec + (cs * 4);
if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask))
edac_dbg(0, " DCSM%d[%d]=0x%08x reg: 0x%x\n",
umc, cs, *mask, mask_reg);
if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec))
edac_dbg(0, " DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n",
umc, cs, *mask_sec, mask_reg_sec);
}
}
}
/*
* Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
*/
static void read_dct_base_mask(struct amd64_pvt *pvt)
{
int base_reg0, base_reg1, mask_reg0, mask_reg1, cs;
int cs;
prep_chip_selects(pvt);
if (pvt->umc) {
base_reg0 = get_umc_base(0) + UMCCH_BASE_ADDR;
base_reg1 = get_umc_base(1) + UMCCH_BASE_ADDR;
mask_reg0 = get_umc_base(0) + UMCCH_ADDR_MASK;
mask_reg1 = get_umc_base(1) + UMCCH_ADDR_MASK;
} else {
base_reg0 = DCSB0;
base_reg1 = DCSB1;
mask_reg0 = DCSM0;
mask_reg1 = DCSM1;
}
if (pvt->umc)
return read_umc_base_mask(pvt);
for_each_chip_select(cs, 0, pvt) {
int reg0 = base_reg0 + (cs * 4);
int reg1 = base_reg1 + (cs * 4);
int reg0 = DCSB0 + (cs * 4);
int reg1 = DCSB1 + (cs * 4);
u32 *base0 = &pvt->csels[0].csbases[cs];
u32 *base1 = &pvt->csels[1].csbases[cs];
if (pvt->umc) {
if (!amd_smn_read(pvt->mc_node_id, reg0, base0))
edac_dbg(0, " DCSB0[%d]=0x%08x reg: 0x%x\n",
cs, *base0, reg0);
if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n",
cs, *base0, reg0);
if (!amd_smn_read(pvt->mc_node_id, reg1, base1))
edac_dbg(0, " DCSB1[%d]=0x%08x reg: 0x%x\n",
cs, *base1, reg1);
} else {
if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n",
cs, *base0, reg0);
if (pvt->fam == 0xf)
continue;
if (pvt->fam == 0xf)
continue;
if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n",
cs, *base1, (pvt->fam == 0x10) ? reg1
: reg0);
}
if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n",
cs, *base1, (pvt->fam == 0x10) ? reg1
: reg0);
}
for_each_chip_select_mask(cs, 0, pvt) {
int reg0 = mask_reg0 + (cs * 4);
int reg1 = mask_reg1 + (cs * 4);
int reg0 = DCSM0 + (cs * 4);
int reg1 = DCSM1 + (cs * 4);
u32 *mask0 = &pvt->csels[0].csmasks[cs];
u32 *mask1 = &pvt->csels[1].csmasks[cs];
if (pvt->umc) {
if (!amd_smn_read(pvt->mc_node_id, reg0, mask0))
edac_dbg(0, " DCSM0[%d]=0x%08x reg: 0x%x\n",
cs, *mask0, reg0);
if (!amd_smn_read(pvt->mc_node_id, reg1, mask1))
edac_dbg(0, " DCSM1[%d]=0x%08x reg: 0x%x\n",
cs, *mask1, reg1);
} else {
if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n",
cs, *mask0, reg0);
if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n",
cs, *mask0, reg0);
if (pvt->fam == 0xf)
continue;
if (pvt->fam == 0xf)
continue;
if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n",
cs, *mask1, (pvt->fam == 0x10) ? reg1
: reg0);
}
if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n",
cs, *mask1, (pvt->fam == 0x10) ? reg1
: reg0);
}
}
......@@ -1556,18 +1580,58 @@ static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
return ddr3_cs_size(cs_mode, false);
}
static int f17_base_addr_to_cs_size(struct amd64_pvt *pvt, u8 umc,
static int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
unsigned int cs_mode, int csrow_nr)
{
u32 base_addr = pvt->csels[umc].csbases[csrow_nr];
u32 addr_mask_orig, addr_mask_deinterleaved;
u32 msb, weight, num_zero_bits;
int dimm, size = 0;
/* Each mask is used for every two base addresses. */
u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr >> 1];
/* No Chip Selects are enabled. */
if (!cs_mode)
return size;
/* Register [31:1] = Address [39:9]. Size is in kBs here. */
u32 size = ((addr_mask >> 1) - (base_addr >> 1) + 1) >> 1;
/* Requested size of an even CS but none are enabled. */
if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1))
return size;
edac_dbg(1, "BaseAddr: 0x%x, AddrMask: 0x%x\n", base_addr, addr_mask);
/* Requested size of an odd CS but none are enabled. */
if (!(cs_mode & CS_ODD) && (csrow_nr & 1))
return size;
/*
* There is one mask per DIMM, and two Chip Selects per DIMM.
* CS0 and CS1 -> DIMM0
* CS2 and CS3 -> DIMM1
*/
dimm = csrow_nr >> 1;
/* Asymmetric dual-rank DIMM support. */
if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY))
addr_mask_orig = pvt->csels[umc].csmasks_sec[dimm];
else
addr_mask_orig = pvt->csels[umc].csmasks[dimm];
/*
* The number of zero bits in the mask is equal to the number of bits
* in a full mask minus the number of bits in the current mask.
*
* The MSB is the number of bits in the full mask because BIT[0] is
* always 0.
*/
msb = fls(addr_mask_orig) - 1;
weight = hweight_long(addr_mask_orig);
num_zero_bits = msb - weight;
/* Take the number of zero bits off from the top of the mask. */
addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1);
edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm);
edac_dbg(1, " Original AddrMask: 0x%x\n", addr_mask_orig);
edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved);
/* Register [31:1] = Address [39:9]. Size is in kBs here. */
size = (addr_mask_deinterleaved >> 2) + 1;
/* Return size in MBs. */
return size >> 10;
......@@ -2232,7 +2296,7 @@ static struct amd64_family_type family_types[] = {
.f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
.ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_base_addr_to_cs_size,
.dbam_to_cs = f17_addr_mask_to_cs_size,
}
},
[F17_M10H_CPUS] = {
......@@ -2241,7 +2305,7 @@ static struct amd64_family_type family_types[] = {
.f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6,
.ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_base_addr_to_cs_size,
.dbam_to_cs = f17_addr_mask_to_cs_size,
}
},
[F17_M30H_CPUS] = {
......@@ -2250,7 +2314,16 @@ static struct amd64_family_type family_types[] = {
.f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6,
.ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_base_addr_to_cs_size,
.dbam_to_cs = f17_addr_mask_to_cs_size,
}
},
[F17_M70H_CPUS] = {
.ctl_name = "F17h_M70h",
.f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0,
.f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6,
.ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_addr_mask_to_cs_size,
}
},
};
......@@ -2537,13 +2610,6 @@ static void decode_umc_error(int node_id, struct mce *m)
err.channel = find_umc_channel(m);
if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
err.err_code = ERR_NORM_ADDR;
goto log_error;
}
error_address_to_page_and_offset(sys_addr, &err);
if (!(m->status & MCI_STATUS_SYNDV)) {
err.err_code = ERR_SYND;
goto log_error;
......@@ -2560,6 +2626,13 @@ static void decode_umc_error(int node_id, struct mce *m)
err.csrow = m->synd & 0x7;
if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
err.err_code = ERR_NORM_ADDR;
goto log_error;
}
error_address_to_page_and_offset(sys_addr, &err);
log_error:
__log_ecc_error(mci, &err, ecc_type);
}
......@@ -2809,10 +2882,12 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
int csrow_nr = csrow_nr_orig;
u32 cs_mode, nr_pages;
if (!pvt->umc)
if (!pvt->umc) {
csrow_nr >>= 1;
cs_mode = DBAM_DIMM(csrow_nr, dbam);
cs_mode = DBAM_DIMM(csrow_nr, dbam);
} else {
cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt);
}
nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr);
nr_pages <<= 20 - PAGE_SHIFT;
......@@ -2824,6 +2899,49 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
return nr_pages;
}
static int init_csrows_df(struct mem_ctl_info *mci)
{
struct amd64_pvt *pvt = mci->pvt_info;
enum edac_type edac_mode = EDAC_NONE;
enum dev_type dev_type = DEV_UNKNOWN;
struct dimm_info *dimm;