summaryrefslogtreecommitdiffstats
path: root/drivers/edac
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-04-05 14:21:13 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2018-04-05 14:21:13 -0700
commitdd972f924df6bdbc0ab185a38d5d2361dbc26311 (patch)
tree181c24478ae55284f7baee88c3cf794794467aa2 /drivers/edac
parent3c8ba0d61d04ced9f8d9ff93977995a9e4e96e91 (diff)
parent6fd052665274b4e7570491ba272f2dbb09cbbcd5 (diff)
downloadlinux-dd972f924df6bdbc0ab185a38d5d2361dbc26311.tar.gz
linux-dd972f924df6bdbc0ab185a38d5d2361dbc26311.tar.bz2
linux-dd972f924df6bdbc0ab185a38d5d2361dbc26311.zip
Merge tag 'edac_for_4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
Pull EDAC updates from Borislav Petkov: "Noteworthy is the NVDIMM support: - NVDIMM support to EDAC (Tony Luck) - misc fixes" * tag 'edac_for_4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp: EDAC, sb_edac: Remove variable length array usage EDAC, skx_edac: Detect non-volatile DIMMs firmware, DMI: Add function to look up a handle and return DIMM size acpi, nfit: Add function to look up nvdimm device and provide SMBIOS handle EDAC: Add new memory type for non-volatile DIMMs EDAC: Drop duplicated array of strings for memory type names EDAC, layerscape: Allow building for LS1021A
Diffstat (limited to 'drivers/edac')
-rw-r--r--drivers/edac/Kconfig7
-rw-r--r--drivers/edac/edac_mc.c41
-rw-r--r--drivers/edac/edac_mc_sysfs.c26
-rw-r--r--drivers/edac/sb_edac.c12
-rw-r--r--drivers/edac/skx_edac.c67
5 files changed, 94 insertions, 59 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index cfcb91056f23..da2da53bca6d 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -232,9 +232,12 @@ config EDAC_SBRIDGE
config EDAC_SKX
tristate "Intel Skylake server Integrated MC"
depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG
+ select DMI
help
Support for error detection and correction the Intel
- Skylake server Integrated Memory Controllers.
+ Skylake server Integrated Memory Controllers. If your
+ system has non-volatile DIMMs you should also manually
+ select CONFIG_ACPI_NFIT.
config EDAC_PND2
tristate "Intel Pondicherry2"
@@ -254,7 +257,7 @@ config EDAC_MPC85XX
config EDAC_LAYERSCAPE
tristate "Freescale Layerscape DDR"
- depends on ARCH_LAYERSCAPE
+ depends on ARCH_LAYERSCAPE || SOC_LS1021A
help
Support for error detection and correction on Freescale memory
controllers on Layerscape SoCs.
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 48193f5f3b56..3bb82e511eca 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -195,26 +195,27 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci)
#endif /* CONFIG_EDAC_DEBUG */
const char * const edac_mem_types[] = {
- [MEM_EMPTY] = "Empty csrow",
- [MEM_RESERVED] = "Reserved csrow type",
- [MEM_UNKNOWN] = "Unknown csrow type",
- [MEM_FPM] = "Fast page mode RAM",
- [MEM_EDO] = "Extended data out RAM",
- [MEM_BEDO] = "Burst Extended data out RAM",
- [MEM_SDR] = "Single data rate SDRAM",
- [MEM_RDR] = "Registered single data rate SDRAM",
- [MEM_DDR] = "Double data rate SDRAM",
- [MEM_RDDR] = "Registered Double data rate SDRAM",
- [MEM_RMBS] = "Rambus DRAM",
- [MEM_DDR2] = "Unbuffered DDR2 RAM",
- [MEM_FB_DDR2] = "Fully buffered DDR2",
- [MEM_RDDR2] = "Registered DDR2 RAM",
- [MEM_XDR] = "Rambus XDR",
- [MEM_DDR3] = "Unbuffered DDR3 RAM",
- [MEM_RDDR3] = "Registered DDR3 RAM",
- [MEM_LRDDR3] = "Load-Reduced DDR3 RAM",
- [MEM_DDR4] = "Unbuffered DDR4 RAM",
- [MEM_RDDR4] = "Registered DDR4 RAM",
+ [MEM_EMPTY] = "Empty",
+ [MEM_RESERVED] = "Reserved",
+ [MEM_UNKNOWN] = "Unknown",
+ [MEM_FPM] = "FPM",
+ [MEM_EDO] = "EDO",
+ [MEM_BEDO] = "BEDO",
+ [MEM_SDR] = "Unbuffered-SDR",
+ [MEM_RDR] = "Registered-SDR",
+ [MEM_DDR] = "Unbuffered-DDR",
+ [MEM_RDDR] = "Registered-DDR",
+ [MEM_RMBS] = "RMBS",
+ [MEM_DDR2] = "Unbuffered-DDR2",
+ [MEM_FB_DDR2] = "FullyBuffered-DDR2",
+ [MEM_RDDR2] = "Registered-DDR2",
+ [MEM_XDR] = "XDR",
+ [MEM_DDR3] = "Unbuffered-DDR3",
+ [MEM_RDDR3] = "Registered-DDR3",
+ [MEM_LRDDR3] = "Load-Reduced-DDR3-RAM",
+ [MEM_DDR4] = "Unbuffered-DDR4",
+ [MEM_RDDR4] = "Registered-DDR4",
+ [MEM_NVDIMM] = "Non-volatile-RAM",
};
EXPORT_SYMBOL_GPL(edac_mem_types);
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index c70ea82c815c..7481955160a4 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -91,28 +91,6 @@ static struct device *mci_pdev;
/*
* various constants for Memory Controllers
*/
-static const char * const mem_types[] = {
- [MEM_EMPTY] = "Empty",
- [MEM_RESERVED] = "Reserved",
- [MEM_UNKNOWN] = "Unknown",
- [MEM_FPM] = "FPM",
- [MEM_EDO] = "EDO",
- [MEM_BEDO] = "BEDO",
- [MEM_SDR] = "Unbuffered-SDR",
- [MEM_RDR] = "Registered-SDR",
- [MEM_DDR] = "Unbuffered-DDR",
- [MEM_RDDR] = "Registered-DDR",
- [MEM_RMBS] = "RMBS",
- [MEM_DDR2] = "Unbuffered-DDR2",
- [MEM_FB_DDR2] = "FullyBuffered-DDR2",
- [MEM_RDDR2] = "Registered-DDR2",
- [MEM_XDR] = "XDR",
- [MEM_DDR3] = "Unbuffered-DDR3",
- [MEM_RDDR3] = "Registered-DDR3",
- [MEM_DDR4] = "Unbuffered-DDR4",
- [MEM_RDDR4] = "Registered-DDR4"
-};
-
static const char * const dev_types[] = {
[DEV_UNKNOWN] = "Unknown",
[DEV_X1] = "x1",
@@ -196,7 +174,7 @@ static ssize_t csrow_mem_type_show(struct device *dev,
{
struct csrow_info *csrow = to_csrow(dev);
- return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]);
+ return sprintf(data, "%s\n", edac_mem_types[csrow->channels[0]->dimm->mtype]);
}
static ssize_t csrow_dev_type_show(struct device *dev,
@@ -549,7 +527,7 @@ static ssize_t dimmdev_mem_type_show(struct device *dev,
{
struct dimm_info *dimm = to_dimm(dev);
- return sprintf(data, "%s\n", mem_types[dimm->mtype]);
+ return sprintf(data, "%s\n", edac_mem_types[dimm->mtype]);
}
static ssize_t dimmdev_dev_type_show(struct device *dev,
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 872100215ca0..4a89c8093307 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -110,6 +110,10 @@ static const u32 knl_interleave_list[] = {
0xdc, 0xe4, 0xec, 0xf4, 0xfc, /* 15-19 */
0x104, 0x10c, 0x114, 0x11c, /* 20-23 */
};
+#define MAX_INTERLEAVE \
+ (max_t(unsigned int, ARRAY_SIZE(sbridge_interleave_list), \
+ max_t(unsigned int, ARRAY_SIZE(ibridge_interleave_list), \
+ ARRAY_SIZE(knl_interleave_list))))
struct interleave_pkg {
unsigned char start;
@@ -321,7 +325,6 @@ struct sbridge_info {
const u32 *interleave_list;
const struct interleave_pkg *interleave_pkg;
u8 max_sad;
- u8 max_interleave;
u8 (*get_node_id)(struct sbridge_pvt *pvt);
enum mem_type (*get_memory_type)(struct sbridge_pvt *pvt);
enum dev_type (*get_width)(struct sbridge_pvt *pvt, u32 mtr);
@@ -1899,7 +1902,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
int n_rir, n_sads, n_tads, sad_way, sck_xch;
int sad_interl, idx, base_ch;
int interleave_mode, shiftup = 0;
- unsigned sad_interleave[pvt->info.max_interleave];
+ unsigned int sad_interleave[MAX_INTERLEAVE];
u32 reg, dram_rule;
u8 ch_way, sck_way, pkg, sad_ha = 0;
u32 tad_offset;
@@ -3169,7 +3172,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
pvt->info.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = ibridge_get_width;
@@ -3194,7 +3196,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule);
pvt->info.interleave_list = sbridge_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list);
pvt->info.interleave_pkg = sbridge_interleave_pkg;
pvt->info.get_width = sbridge_get_width;
@@ -3219,7 +3220,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
pvt->info.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = ibridge_get_width;
@@ -3244,7 +3244,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
pvt->info.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = broadwell_get_width;
@@ -3269,7 +3268,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr_knl;
pvt->info.max_sad = ARRAY_SIZE(knl_dram_rule);
pvt->info.interleave_list = knl_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(knl_interleave_list);
pvt->info.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = knl_get_width;
diff --git a/drivers/edac/skx_edac.c b/drivers/edac/skx_edac.c
index 912c4930c9ef..fae095162c01 100644
--- a/drivers/edac/skx_edac.c
+++ b/drivers/edac/skx_edac.c
@@ -14,6 +14,8 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
@@ -24,6 +26,7 @@
#include <linux/bitmap.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
+#include <acpi/nfit.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/processor.h>
@@ -302,6 +305,7 @@ static int get_dimm_attr(u32 reg, int lobit, int hibit, int add, int minval,
}
#define IS_DIMM_PRESENT(mtr) GET_BITFIELD((mtr), 15, 15)
+#define IS_NVDIMM_PRESENT(mcddrtcfg, i) GET_BITFIELD((mcddrtcfg), (i), (i))
#define numrank(reg) get_dimm_attr((reg), 12, 13, 0, 0, 2, "ranks")
#define numrow(reg) get_dimm_attr((reg), 2, 4, 12, 1, 6, "rows")
@@ -350,8 +354,6 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
int banks = 16, ranks, rows, cols, npages;
u64 size;
- if (!IS_DIMM_PRESENT(mtr))
- return 0;
ranks = numrank(mtr);
rows = numrow(mtr);
cols = numcol(mtr);
@@ -383,6 +385,54 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
return 1;
}
+static int get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc,
+ int chan, int dimmno)
+{
+ int smbios_handle;
+ u32 dev_handle;
+ u16 flags;
+ u64 size = 0;
+
+ dev_handle = ACPI_NFIT_BUILD_DEVICE_HANDLE(dimmno, chan, imc->lmc,
+ imc->src_id, 0);
+
+ smbios_handle = nfit_get_smbios_id(dev_handle, &flags);
+ if (smbios_handle == -EOPNOTSUPP) {
+ pr_warn_once(EDAC_MOD_STR ": Can't find size of NVDIMM. Try enabling CONFIG_ACPI_NFIT\n");
+ goto unknown_size;
+ }
+
+ if (smbios_handle < 0) {
+ skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=%x\n", dev_handle);
+ goto unknown_size;
+ }
+
+ if (flags & ACPI_NFIT_MEM_MAP_FAILED) {
+ skx_printk(KERN_ERR, "NVDIMM ADR=%x is not mapped\n", dev_handle);
+ goto unknown_size;
+ }
+
+ size = dmi_memdev_size(smbios_handle);
+ if (size == ~0ull)
+ skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=%x/SMBIOS=%x\n",
+ dev_handle, smbios_handle);
+
+unknown_size:
+ dimm->nr_pages = size >> PAGE_SHIFT;
+ dimm->grain = 32;
+ dimm->dtype = DEV_UNKNOWN;
+ dimm->mtype = MEM_NVDIMM;
+ dimm->edac_mode = EDAC_SECDED; /* likely better than this */
+
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu Mb (%u pages)\n",
+ imc->mc, chan, dimmno, size >> 20, dimm->nr_pages);
+
+ snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u",
+ imc->src_id, imc->lmc, chan, dimmno);
+
+ return (size == 0 || size == ~0ull) ? 0 : 1;
+}
+
#define SKX_GET_MTMTR(dev, reg) \
pci_read_config_dword((dev), 0x87c, &reg)
@@ -399,20 +449,24 @@ static int skx_get_dimm_config(struct mem_ctl_info *mci)
{
struct skx_pvt *pvt = mci->pvt_info;
struct skx_imc *imc = pvt->imc;
+ u32 mtr, amap, mcddrtcfg;
struct dimm_info *dimm;
int i, j;
- u32 mtr, amap;
int ndimms;
for (i = 0; i < NUM_CHANNELS; i++) {
ndimms = 0;
pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap);
+ pci_read_config_dword(imc->chan[i].cdev, 0x400, &mcddrtcfg);
for (j = 0; j < NUM_DIMMS; j++) {
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers, i, j, 0);
pci_read_config_dword(imc->chan[i].cdev,
0x80 + 4*j, &mtr);
- ndimms += get_dimm_info(mtr, amap, dimm, imc, i, j);
+ if (IS_DIMM_PRESENT(mtr))
+ ndimms += get_dimm_info(mtr, amap, dimm, imc, i, j);
+ else if (IS_NVDIMM_PRESENT(mcddrtcfg, j))
+ ndimms += get_nvdimm_info(dimm, imc, i, j);
}
if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) {
skx_printk(KERN_ERR, "ECC is disabled on imc %d\n", imc->mc);
@@ -468,13 +522,14 @@ static int skx_register_mci(struct skx_imc *imc)
pvt = mci->pvt_info;
pvt->imc = imc;
- mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d", imc->node_id, imc->lmc);
+ mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d",
+ imc->node_id, imc->lmc);
if (!mci->ctl_name) {
rc = -ENOMEM;
goto fail0;
}
- mci->mtype_cap = MEM_FLAG_DDR4;
+ mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_NVDIMM;
mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = EDAC_MOD_STR;