#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>
 #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>
 }
 
 #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")
        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);
        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, ®)
 
 {
        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);
        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;