diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-05 14:21:13 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-05 14:21:13 -0700 |
commit | dd972f924df6bdbc0ab185a38d5d2361dbc26311 (patch) | |
tree | 181c24478ae55284f7baee88c3cf794794467aa2 /drivers/edac/skx_edac.c | |
parent | 3c8ba0d61d04ced9f8d9ff93977995a9e4e96e91 (diff) | |
parent | 6fd052665274b4e7570491ba272f2dbb09cbbcd5 (diff) | |
download | linux-stable-dd972f924df6bdbc0ab185a38d5d2361dbc26311.tar.gz |
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/skx_edac.c')
-rw-r--r-- | drivers/edac/skx_edac.c | 67 |
1 files changed, 61 insertions, 6 deletions
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, ®) @@ -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; |