Merge tag 'edac_for_4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 5 Apr 2018 21:21:13 +0000 (14:21 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 5 Apr 2018 21:21:13 +0000 (14:21 -0700)
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

drivers/acpi/nfit/core.c
drivers/edac/Kconfig
drivers/edac/edac_mc.c
drivers/edac/edac_mc_sysfs.c
drivers/edac/sb_edac.c
drivers/edac/skx_edac.c
drivers/firmware/dmi_scan.c
include/acpi/nfit.h [new file with mode: 0644]
include/linux/dmi.h
include/linux/edac.h

index eb09ef55c38a2779c046241c337ea7be3cf75b79..22a112b4f4d84cf120d21a95b2722ccf04c9f7e6 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/io.h>
 #include <linux/nd.h>
 #include <asm/cacheflush.h>
+#include <acpi/nfit.h>
 #include "nfit.h"
 
 /*
@@ -690,6 +691,32 @@ static bool add_memdev(struct acpi_nfit_desc *acpi_desc,
        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
  * are defined.
index cfcb91056f238d4299e3a2564fe4a73d3e08ec1c..da2da53bca6d07cbb14d76f6586fa304d9c3485c 100644 (file)
@@ -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.
index 48193f5f3b56eb874505480a0c0a4ea12d039a64..3bb82e511eca9c799c3435841cb7986f475bfc71 100644 (file)
@@ -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);
 
index c70ea82c815c90966e1cf22c3781d16f5eed6ce6..7481955160a44e418ee7ff3dbde28d1ef6d030fa 100644 (file)
@@ -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,
index 872100215ca00f0f4703c025b8e52e637c85c710..4a89c8093307fc4d921781720a8c6f891e6e9b3d 100644 (file)
@@ -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;
 
index 912c4930c9efb8e58116731c9711d53311515574..fae095162c0175d860bfb9669b6036e4773282fd 100644 (file)
@@ -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;
index e35c5e04c46adcbdf1a20b3fe5f090b4651a0ee4..6feeacbe4d9784aa3bfaa742da5a46660b78fd34 100644 (file)
@@ -32,6 +32,7 @@ static char dmi_ids_string[128] __initdata;
 static struct dmi_memdev_info {
        const char *device;
        const char *bank;
+       u64 size;               /* bytes */
        u16 handle;
 } *dmi_memdev;
 static int dmi_memdev_nr;
@@ -386,6 +387,8 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v)
 {
        const char *d = (const char *)dm;
        static int nr;
+       u64 bytes;
+       u16 size;
 
        if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x12)
                return;
@@ -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].device = dmi_string(dm, d[0x10]);
        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++;
 }
 
@@ -1085,3 +1102,17 @@ void dmi_memdev_name(u16 handle, const char **bank, const char **device)
        }
 }
 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);
diff --git a/include/acpi/nfit.h b/include/acpi/nfit.h
new file mode 100644 (file)
index 0000000..86ed07c
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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 */
index 6a86d8db16d9274e3e82ed0522bc47170d4e23cc..c46fdb36700bc2d83115245e660853ffc5a47141 100644 (file)
@@ -114,6 +114,7 @@ extern int dmi_walk(void (*decode)(const struct dmi_header *, void *),
        void *private_data);
 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 u64 dmi_memdev_size(u16 handle);
 
 #else
 
@@ -144,6 +145,7 @@ static inline bool dmi_match(enum dmi_field f, const char *str)
        { return false; }
 static inline void dmi_memdev_name(u16 handle, const char **bank,
                const char **device) { }
+static inline u64 dmi_memdev_size(u16 handle) { return ~0ul; }
 static inline const struct dmi_system_id *
        dmi_first_match(const struct dmi_system_id *list) { return NULL; }
 
index cd75c173fd00bf6945a8bfe17b280354b8e3308c..bffb97828ed67711dffb24099c26f4580e4e654e 100644 (file)
@@ -186,6 +186,7 @@ static inline char *mc_event_error_type(const unsigned int err_type)
  * @MEM_RDDR4:         Registered DDR4 RAM
  *                     This is a variant of the DDR4 memories.
  * @MEM_LRDDR4:                Load-Reduced DDR4 memory.
+ * @MEM_NVDIMM:                Non-volatile RAM
  */
 enum mem_type {
        MEM_EMPTY = 0,
@@ -209,6 +210,7 @@ enum mem_type {
        MEM_DDR4,
        MEM_RDDR4,
        MEM_LRDDR4,
+       MEM_NVDIMM,
 };
 
 #define MEM_FLAG_EMPTY         BIT(MEM_EMPTY)
@@ -231,6 +233,7 @@ enum mem_type {
 #define MEM_FLAG_DDR4           BIT(MEM_DDR4)
 #define MEM_FLAG_RDDR4          BIT(MEM_RDDR4)
 #define MEM_FLAG_LRDDR4         BIT(MEM_LRDDR4)
+#define MEM_FLAG_NVDIMM         BIT(MEM_NVDIMM)
 
 /**
  * enum edac-type - Error Detection and Correction capabilities and mode