Commit dd972f92 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 3c8ba0d6 6fd05266
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/nd.h> #include <linux/nd.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <acpi/nfit.h>
#include "nfit.h" #include "nfit.h"
/* /*
...@@ -690,6 +691,32 @@ static bool add_memdev(struct acpi_nfit_desc *acpi_desc, ...@@ -690,6 +691,32 @@ static bool add_memdev(struct acpi_nfit_desc *acpi_desc,
return true; return true;
} }
int nfit_get_smbios_id(u32 device_handle, u16 *flags)
{
struct acpi_nfit_memory_map *memdev;
struct acpi_nfit_desc *acpi_desc;
struct nfit_mem *nfit_mem;
mutex_lock(&acpi_desc_lock);
list_for_each_entry(acpi_desc, &acpi_descs, list) {
mutex_lock(&acpi_desc->init_mutex);
list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
memdev = __to_nfit_memdev(nfit_mem);
if (memdev->device_handle == device_handle) {
mutex_unlock(&acpi_desc->init_mutex);
mutex_unlock(&acpi_desc_lock);
*flags = memdev->flags;
return memdev->physical_id;
}
}
mutex_unlock(&acpi_desc->init_mutex);
}
mutex_unlock(&acpi_desc_lock);
return -ENODEV;
}
EXPORT_SYMBOL_GPL(nfit_get_smbios_id);
/* /*
* An implementation may provide a truncated control region if no block windows * An implementation may provide a truncated control region if no block windows
* are defined. * are defined.
......
...@@ -232,9 +232,12 @@ config EDAC_SBRIDGE ...@@ -232,9 +232,12 @@ config EDAC_SBRIDGE
config EDAC_SKX config EDAC_SKX
tristate "Intel Skylake server Integrated MC" tristate "Intel Skylake server Integrated MC"
depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG
select DMI
help help
Support for error detection and correction the Intel 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 config EDAC_PND2
tristate "Intel Pondicherry2" tristate "Intel Pondicherry2"
...@@ -254,7 +257,7 @@ config EDAC_MPC85XX ...@@ -254,7 +257,7 @@ config EDAC_MPC85XX
config EDAC_LAYERSCAPE config EDAC_LAYERSCAPE
tristate "Freescale Layerscape DDR" tristate "Freescale Layerscape DDR"
depends on ARCH_LAYERSCAPE depends on ARCH_LAYERSCAPE || SOC_LS1021A
help help
Support for error detection and correction on Freescale memory Support for error detection and correction on Freescale memory
controllers on Layerscape SoCs. controllers on Layerscape SoCs.
......
...@@ -195,26 +195,27 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci) ...@@ -195,26 +195,27 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci)
#endif /* CONFIG_EDAC_DEBUG */ #endif /* CONFIG_EDAC_DEBUG */
const char * const edac_mem_types[] = { const char * const edac_mem_types[] = {
[MEM_EMPTY] = "Empty csrow", [MEM_EMPTY] = "Empty",
[MEM_RESERVED] = "Reserved csrow type", [MEM_RESERVED] = "Reserved",
[MEM_UNKNOWN] = "Unknown csrow type", [MEM_UNKNOWN] = "Unknown",
[MEM_FPM] = "Fast page mode RAM", [MEM_FPM] = "FPM",
[MEM_EDO] = "Extended data out RAM", [MEM_EDO] = "EDO",
[MEM_BEDO] = "Burst Extended data out RAM", [MEM_BEDO] = "BEDO",
[MEM_SDR] = "Single data rate SDRAM", [MEM_SDR] = "Unbuffered-SDR",
[MEM_RDR] = "Registered single data rate SDRAM", [MEM_RDR] = "Registered-SDR",
[MEM_DDR] = "Double data rate SDRAM", [MEM_DDR] = "Unbuffered-DDR",
[MEM_RDDR] = "Registered Double data rate SDRAM", [MEM_RDDR] = "Registered-DDR",
[MEM_RMBS] = "Rambus DRAM", [MEM_RMBS] = "RMBS",
[MEM_DDR2] = "Unbuffered DDR2 RAM", [MEM_DDR2] = "Unbuffered-DDR2",
[MEM_FB_DDR2] = "Fully buffered DDR2", [MEM_FB_DDR2] = "FullyBuffered-DDR2",
[MEM_RDDR2] = "Registered DDR2 RAM", [MEM_RDDR2] = "Registered-DDR2",
[MEM_XDR] = "Rambus XDR", [MEM_XDR] = "XDR",
[MEM_DDR3] = "Unbuffered DDR3 RAM", [MEM_DDR3] = "Unbuffered-DDR3",
[MEM_RDDR3] = "Registered DDR3 RAM", [MEM_RDDR3] = "Registered-DDR3",
[MEM_LRDDR3] = "Load-Reduced DDR3 RAM", [MEM_LRDDR3] = "Load-Reduced-DDR3-RAM",
[MEM_DDR4] = "Unbuffered DDR4 RAM", [MEM_DDR4] = "Unbuffered-DDR4",
[MEM_RDDR4] = "Registered DDR4 RAM", [MEM_RDDR4] = "Registered-DDR4",
[MEM_NVDIMM] = "Non-volatile-RAM",
}; };
EXPORT_SYMBOL_GPL(edac_mem_types); EXPORT_SYMBOL_GPL(edac_mem_types);
......
...@@ -91,28 +91,6 @@ static struct device *mci_pdev; ...@@ -91,28 +91,6 @@ static struct device *mci_pdev;
/* /*
* various constants for Memory Controllers * 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[] = { static const char * const dev_types[] = {
[DEV_UNKNOWN] = "Unknown", [DEV_UNKNOWN] = "Unknown",
[DEV_X1] = "x1", [DEV_X1] = "x1",
...@@ -196,7 +174,7 @@ static ssize_t csrow_mem_type_show(struct device *dev, ...@@ -196,7 +174,7 @@ static ssize_t csrow_mem_type_show(struct device *dev,
{ {
struct csrow_info *csrow = to_csrow(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, static ssize_t csrow_dev_type_show(struct device *dev,
...@@ -549,7 +527,7 @@ static ssize_t dimmdev_mem_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); 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, static ssize_t dimmdev_dev_type_show(struct device *dev,
......
...@@ -110,6 +110,10 @@ static const u32 knl_interleave_list[] = { ...@@ -110,6 +110,10 @@ static const u32 knl_interleave_list[] = {
0xdc, 0xe4, 0xec, 0xf4, 0xfc, /* 15-19 */ 0xdc, 0xe4, 0xec, 0xf4, 0xfc, /* 15-19 */
0x104, 0x10c, 0x114, 0x11c, /* 20-23 */ 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 { struct interleave_pkg {
unsigned char start; unsigned char start;
...@@ -321,7 +325,6 @@ struct sbridge_info { ...@@ -321,7 +325,6 @@ struct sbridge_info {
const u32 *interleave_list; const u32 *interleave_list;
const struct interleave_pkg *interleave_pkg; const struct interleave_pkg *interleave_pkg;
u8 max_sad; u8 max_sad;
u8 max_interleave;
u8 (*get_node_id)(struct sbridge_pvt *pvt); u8 (*get_node_id)(struct sbridge_pvt *pvt);
enum mem_type (*get_memory_type)(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); 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, ...@@ -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 n_rir, n_sads, n_tads, sad_way, sck_xch;
int sad_interl, idx, base_ch; int sad_interl, idx, base_ch;
int interleave_mode, shiftup = 0; int interleave_mode, shiftup = 0;
unsigned sad_interleave[pvt->info.max_interleave]; unsigned int sad_interleave[MAX_INTERLEAVE];
u32 reg, dram_rule; u32 reg, dram_rule;
u8 ch_way, sck_way, pkg, sad_ha = 0; u8 ch_way, sck_way, pkg, sad_ha = 0;
u32 tad_offset; u32 tad_offset;
...@@ -3169,7 +3172,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -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.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list; 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.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = ibridge_get_width; pvt->info.get_width = ibridge_get_width;
...@@ -3194,7 +3196,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -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.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule); pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule);
pvt->info.interleave_list = sbridge_interleave_list; 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.interleave_pkg = sbridge_interleave_pkg;
pvt->info.get_width = sbridge_get_width; pvt->info.get_width = sbridge_get_width;
...@@ -3219,7 +3220,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -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.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list; 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.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = ibridge_get_width; pvt->info.get_width = ibridge_get_width;
...@@ -3244,7 +3244,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -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.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list; 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.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = broadwell_get_width; pvt->info.get_width = broadwell_get_width;
...@@ -3269,7 +3268,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -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.dram_attr = dram_attr_knl;
pvt->info.max_sad = ARRAY_SIZE(knl_dram_rule); pvt->info.max_sad = ARRAY_SIZE(knl_dram_rule);
pvt->info.interleave_list = knl_interleave_list; 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.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = knl_get_width; pvt->info.get_width = knl_get_width;
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pci_ids.h> #include <linux/pci_ids.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -24,6 +26,7 @@ ...@@ -24,6 +26,7 @@
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/math64.h> #include <linux/math64.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <acpi/nfit.h>
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/intel-family.h> #include <asm/intel-family.h>
#include <asm/processor.h> #include <asm/processor.h>
...@@ -302,6 +305,7 @@ static int get_dimm_attr(u32 reg, int lobit, int hibit, int add, int minval, ...@@ -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_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 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") #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, ...@@ -350,8 +354,6 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
int banks = 16, ranks, rows, cols, npages; int banks = 16, ranks, rows, cols, npages;
u64 size; u64 size;
if (!IS_DIMM_PRESENT(mtr))
return 0;
ranks = numrank(mtr); ranks = numrank(mtr);
rows = numrow(mtr); rows = numrow(mtr);
cols = numcol(mtr); cols = numcol(mtr);
...@@ -383,6 +385,54 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm, ...@@ -383,6 +385,54 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
return 1; 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) \ #define SKX_GET_MTMTR(dev, reg) \
pci_read_config_dword((dev), 0x87c, &reg) pci_read_config_dword((dev), 0x87c, &reg)
...@@ -399,20 +449,24 @@ static int skx_get_dimm_config(struct mem_ctl_info *mci) ...@@ -399,20 +449,24 @@ static int skx_get_dimm_config(struct mem_ctl_info *mci)
{ {
struct skx_pvt *pvt = mci->pvt_info; struct skx_pvt *pvt = mci->pvt_info;
struct skx_imc *imc = pvt->imc; struct skx_imc *imc = pvt->imc;
u32 mtr, amap, mcddrtcfg;
struct dimm_info *dimm; struct dimm_info *dimm;
int i, j; int i, j;
u32 mtr, amap;
int ndimms; int ndimms;
for (i = 0; i < NUM_CHANNELS; i++) { for (i = 0; i < NUM_CHANNELS; i++) {
ndimms = 0; ndimms = 0;
pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap); 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++) { for (j = 0; j < NUM_DIMMS; j++) {
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers, i, j, 0); mci->n_layers, i, j, 0);
pci_read_config_dword(imc->chan[i].cdev, pci_read_config_dword(imc->chan[i].cdev,
0x80 + 4*j, &mtr); 0x80 + 4*j, &mtr);
if (IS_DIMM_PRESENT(mtr))
ndimms += get_dimm_info(mtr, amap, dimm, imc, i, j); 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)) { if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) {
skx_printk(KERN_ERR, "ECC is disabled on imc %d\n", imc->mc); 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) ...@@ -468,13 +522,14 @@ static int skx_register_mci(struct skx_imc *imc)
pvt = mci->pvt_info; pvt = mci->pvt_info;
pvt->imc = imc; 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) { if (!mci->ctl_name) {
rc = -ENOMEM; rc = -ENOMEM;
goto fail0; 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_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = EDAC_MOD_STR; mci->mod_name = EDAC_MOD_STR;
......
...@@ -32,6 +32,7 @@ static char dmi_ids_string[128] __initdata; ...@@ -32,6 +32,7 @@ static char dmi_ids_string[128] __initdata;
static struct dmi_memdev_info { static struct dmi_memdev_info {
const char *device; const char *device;
const char *bank; const char *bank;
u64 size; /* bytes */
u16 handle; u16 handle;
} *dmi_memdev; } *dmi_memdev;
static int dmi_memdev_nr; static int dmi_memdev_nr;
...@@ -386,6 +387,8 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) ...@@ -386,6 +387,8 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v)
{ {
const char *d = (const char *)dm; const char *d = (const char *)dm;
static int nr; static int nr;
u64 bytes;
u16 size;
if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x12) if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x12)
return; return;
...@@ -396,6 +399,20 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) ...@@ -396,6 +399,20 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v)
dmi_memdev[nr].handle = get_unaligned(&dm->handle); dmi_memdev[nr].handle = get_unaligned(&dm->handle);
dmi_memdev[nr].device = dmi_string(dm, d[0x10]); dmi_memdev[nr].device = dmi_string(dm, d[0x10]);
dmi_memdev[nr].bank = dmi_string(dm, d[0x11]); dmi_memdev[nr].bank = dmi_string(dm, d[0x11]);
size = get_unaligned((u16 *)&d[0xC]);
if (size == 0)
bytes = 0;
else if (size == 0xffff)
bytes = ~0ull;
else if (size & 0x8000)
bytes = (u64)(size & 0x7fff) << 10;
else if (size != 0x7fff)
bytes = (u64)size << 20;
else
bytes = (u64)get_unaligned((u32 *)&d[0x1C]) << 20;
dmi_memdev[nr].size = bytes;
nr++; nr++;
} }
...@@ -1085,3 +1102,17 @@ void dmi_memdev_name(u16 handle, const char **bank, const char **device) ...@@ -1085,3 +1102,17 @@ void dmi_memdev_name(u16 handle, const char **bank, const char **device)
} }
} }
EXPORT_SYMBOL_GPL(dmi_memdev_name); EXPORT_SYMBOL_GPL(dmi_memdev_name);
u64 dmi_memdev_size(u16 handle)
{
int n;
if (dmi_memdev) {
for (n = 0; n < dmi_memdev_nr; n++) {
if (handle == dmi_memdev[n].handle)
return dmi_memdev[n].size;
}
}
return ~0ull;
}
EXPORT_SYMBOL_GPL(dmi_memdev_size);
/*
* SPDX-License-Identifier: GPL-2.0
* Copyright (C) 2018 Intel Corporation
*/
#ifndef __ACPI_NFIT_H
#define __ACPI_NFIT_H
#if IS_ENABLED(CONFIG_ACPI_NFIT)
int nfit_get_smbios_id(u32 device_handle, u16 *flags);
#else
static inline int nfit_get_smbios_id(u32 device_handle, u16 *flags)
{
return -EOPNOTSUPP;
}
#endif
#endif /* __ACPI_NFIT_H */
...@@ -114,6 +114,7 @@ extern int dmi_walk(void (*decode)(const struct dmi_header *, void *), ...@@ -114,6 +114,7 @@ extern int dmi_walk(void (*decode)(const struct dmi_header *, void *),
void *private_data); void *private_data);
extern bool dmi_match(enum dmi_field f, const char *str); extern bool dmi_match(enum dmi_field f, const char *str);
extern void dmi_memdev_name(u16 handle, const char **bank, const char **device); extern void dmi_memdev_name(u16 handle, const char **bank, const char **device);
extern u64 dmi_memdev_size(u16 handle);
#else #else
...@@ -144,6 +145,7 @@ static inline bool dmi_match(enum dmi_field f, const char *str) ...@@ -144,6 +145,7 @@ static inline bool dmi_match(enum dmi_field f, const char *str)
{ return false; } { return false; }
static inline void dmi_memdev_name(u16 handle, const char **bank, static inline void dmi_memdev_name(u16 handle, const char **bank,
const char **device) { } const char **device) { }
static inline u64 dmi_memdev_size(u16 handle) { return ~0ul; }
static inline const struct dmi_system_id * static inline const struct dmi_system_id *
dmi_first_match(const struct dmi_system_id *list) { return NULL; } dmi_first_match(const struct dmi_system_id *list) { return NULL; }
......
...@@ -186,6 +186,7 @@ static inline char *mc_event_error_type(const unsigned int err_type) ...@@ -186,6 +186,7 @@ static inline char *mc_event_error_type(const unsigned int err_type)
* @MEM_RDDR4: Registered DDR4 RAM * @MEM_RDDR4: Registered DDR4 RAM
* This is a variant of the DDR4 memories. * This is a variant of the DDR4 memories.
* @MEM_LRDDR4: Load-Reduced DDR4 memory. * @MEM_LRDDR4: Load-Reduced DDR4 memory.
* @MEM_NVDIMM: Non-volatile RAM
*/ */
enum mem_type { enum mem_type {
MEM_EMPTY = 0, MEM_EMPTY = 0,
...@@ -209,6 +210,7 @@ enum mem_type { ...@@ -209,6 +210,7 @@ enum mem_type {
MEM_DDR4, MEM_DDR4,
MEM_RDDR4, MEM_RDDR4,
MEM_LRDDR4, MEM_LRDDR4,
MEM_NVDIMM,
}; };
#define MEM_FLAG_EMPTY BIT(MEM_EMPTY) #define MEM_FLAG_EMPTY BIT(MEM_EMPTY)
...@@ -231,6 +233,7 @@ enum mem_type { ...@@ -231,6 +233,7 @@ enum mem_type {
#define MEM_FLAG_DDR4 BIT(MEM_DDR4) #define MEM_FLAG_DDR4 BIT(MEM_DDR4)
#define MEM_FLAG_RDDR4 BIT(MEM_RDDR4) #define MEM_FLAG_RDDR4 BIT(MEM_RDDR4)
#define MEM_FLAG_LRDDR4 BIT(MEM_LRDDR4) #define MEM_FLAG_LRDDR4 BIT(MEM_LRDDR4)
#define MEM_FLAG_NVDIMM BIT(MEM_NVDIMM)
/** /**
* enum edac-type - Error Detection and Correction capabilities and mode * enum edac-type - Error Detection and Correction capabilities and mode
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment