aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Luck <tony.luck@intel.com>2018-03-12 14:24:30 -0400
committerBorislav Petkov <bp@suse.de>2018-03-14 19:33:55 -0400
commit58ca9ac1463d07d24b9fa8befe065192abca6f76 (patch)
tree8705aae2c20a8d7e2ce4bcb366a8dadfb1fe90de
parent6deae96b42eb1fa84938088087de0bd748f53093 (diff)
EDAC, skx_edac: Detect non-volatile DIMMs
This just covers the topology function of the EDAC driver. We locate which DIMM slots are populated with NVDIMMs and query the NFIT and SMBIOS tables to get the size. Reviewed-by: Jean Delvare <jdelvare@suse.de> Signed-off-by: Tony Luck <tony.luck@intel.com> Cc: Aristeu Rozanski <aris@redhat.com> Cc: Dan Williams <dan.j.williams@intel.com> Cc: Len Brown <lenb@kernel.org> Cc: Mauro Carvalho Chehab <mchehab@kernel.org> Cc: Qiuxu Zhuo <qiuxu.zhuo@intel.com> Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net> Cc: linux-acpi@vger.kernel.org Cc: linux-edac <linux-edac@vger.kernel.org> Cc: linux-nvdimm@lists.01.org Link: http://lkml.kernel.org/r/20180312182430.10335-6-tony.luck@intel.com Signed-off-by: Borislav Petkov <bp@suse.de>
-rw-r--r--drivers/edac/Kconfig5
-rw-r--r--drivers/edac/skx_edac.c67
2 files changed, 65 insertions, 7 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index cb4ff1cc6eb4..07d569d32b90 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -232,9 +232,12 @@ config EDAC_SBRIDGE
232config EDAC_SKX 232config EDAC_SKX
233 tristate "Intel Skylake server Integrated MC" 233 tristate "Intel Skylake server Integrated MC"
234 depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG 234 depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG
235 select DMI
235 help 236 help
236 Support for error detection and correction the Intel 237 Support for error detection and correction the Intel
237 Skylake server Integrated Memory Controllers. 238 Skylake server Integrated Memory Controllers. If your
239 system has non-volatile DIMMs you should also manually
240 select CONFIG_ACPI_NFIT.
238 241
239config EDAC_PND2 242config EDAC_PND2
240 tristate "Intel Pondicherry2" 243 tristate "Intel Pondicherry2"
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 @@
14 14
15#include <linux/module.h> 15#include <linux/module.h>
16#include <linux/init.h> 16#include <linux/init.h>
17#include <linux/acpi.h>
18#include <linux/dmi.h>
17#include <linux/pci.h> 19#include <linux/pci.h>
18#include <linux/pci_ids.h> 20#include <linux/pci_ids.h>
19#include <linux/slab.h> 21#include <linux/slab.h>
@@ -24,6 +26,7 @@
24#include <linux/bitmap.h> 26#include <linux/bitmap.h>
25#include <linux/math64.h> 27#include <linux/math64.h>
26#include <linux/mod_devicetable.h> 28#include <linux/mod_devicetable.h>
29#include <acpi/nfit.h>
27#include <asm/cpu_device_id.h> 30#include <asm/cpu_device_id.h>
28#include <asm/intel-family.h> 31#include <asm/intel-family.h>
29#include <asm/processor.h> 32#include <asm/processor.h>
@@ -302,6 +305,7 @@ static int get_dimm_attr(u32 reg, int lobit, int hibit, int add, int minval,
302} 305}
303 306
304#define IS_DIMM_PRESENT(mtr) GET_BITFIELD((mtr), 15, 15) 307#define IS_DIMM_PRESENT(mtr) GET_BITFIELD((mtr), 15, 15)
308#define IS_NVDIMM_PRESENT(mcddrtcfg, i) GET_BITFIELD((mcddrtcfg), (i), (i))
305 309
306#define numrank(reg) get_dimm_attr((reg), 12, 13, 0, 0, 2, "ranks") 310#define numrank(reg) get_dimm_attr((reg), 12, 13, 0, 0, 2, "ranks")
307#define numrow(reg) get_dimm_attr((reg), 2, 4, 12, 1, 6, "rows") 311#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 int banks = 16, ranks, rows, cols, npages; 354 int banks = 16, ranks, rows, cols, npages;
351 u64 size; 355 u64 size;
352 356
353 if (!IS_DIMM_PRESENT(mtr))
354 return 0;
355 ranks = numrank(mtr); 357 ranks = numrank(mtr);
356 rows = numrow(mtr); 358 rows = numrow(mtr);
357 cols = numcol(mtr); 359 cols = numcol(mtr);
@@ -383,6 +385,54 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
383 return 1; 385 return 1;
384} 386}
385 387
388static int get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc,
389 int chan, int dimmno)
390{
391 int smbios_handle;
392 u32 dev_handle;
393 u16 flags;
394 u64 size = 0;
395
396 dev_handle = ACPI_NFIT_BUILD_DEVICE_HANDLE(dimmno, chan, imc->lmc,
397 imc->src_id, 0);
398
399 smbios_handle = nfit_get_smbios_id(dev_handle, &flags);
400 if (smbios_handle == -EOPNOTSUPP) {
401 pr_warn_once(EDAC_MOD_STR ": Can't find size of NVDIMM. Try enabling CONFIG_ACPI_NFIT\n");
402 goto unknown_size;
403 }
404
405 if (smbios_handle < 0) {
406 skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=%x\n", dev_handle);
407 goto unknown_size;
408 }
409
410 if (flags & ACPI_NFIT_MEM_MAP_FAILED) {
411 skx_printk(KERN_ERR, "NVDIMM ADR=%x is not mapped\n", dev_handle);
412 goto unknown_size;
413 }
414
415 size = dmi_memdev_size(smbios_handle);
416 if (size == ~0ull)
417 skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=%x/SMBIOS=%x\n",
418 dev_handle, smbios_handle);
419
420unknown_size:
421 dimm->nr_pages = size >> PAGE_SHIFT;
422 dimm->grain = 32;
423 dimm->dtype = DEV_UNKNOWN;
424 dimm->mtype = MEM_NVDIMM;
425 dimm->edac_mode = EDAC_SECDED; /* likely better than this */
426
427 edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu Mb (%u pages)\n",
428 imc->mc, chan, dimmno, size >> 20, dimm->nr_pages);
429
430 snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u",
431 imc->src_id, imc->lmc, chan, dimmno);
432
433 return (size == 0 || size == ~0ull) ? 0 : 1;
434}
435
386#define SKX_GET_MTMTR(dev, reg) \ 436#define SKX_GET_MTMTR(dev, reg) \
387 pci_read_config_dword((dev), 0x87c, &reg) 437 pci_read_config_dword((dev), 0x87c, &reg)
388 438
@@ -399,20 +449,24 @@ static int skx_get_dimm_config(struct mem_ctl_info *mci)
399{ 449{
400 struct skx_pvt *pvt = mci->pvt_info; 450 struct skx_pvt *pvt = mci->pvt_info;
401 struct skx_imc *imc = pvt->imc; 451 struct skx_imc *imc = pvt->imc;
452 u32 mtr, amap, mcddrtcfg;
402 struct dimm_info *dimm; 453 struct dimm_info *dimm;
403 int i, j; 454 int i, j;
404 u32 mtr, amap;
405 int ndimms; 455 int ndimms;
406 456
407 for (i = 0; i < NUM_CHANNELS; i++) { 457 for (i = 0; i < NUM_CHANNELS; i++) {
408 ndimms = 0; 458 ndimms = 0;
409 pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap); 459 pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap);
460 pci_read_config_dword(imc->chan[i].cdev, 0x400, &mcddrtcfg);
410 for (j = 0; j < NUM_DIMMS; j++) { 461 for (j = 0; j < NUM_DIMMS; j++) {
411 dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, 462 dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
412 mci->n_layers, i, j, 0); 463 mci->n_layers, i, j, 0);
413 pci_read_config_dword(imc->chan[i].cdev, 464 pci_read_config_dword(imc->chan[i].cdev,
414 0x80 + 4*j, &mtr); 465 0x80 + 4*j, &mtr);
415 ndimms += get_dimm_info(mtr, amap, dimm, imc, i, j); 466 if (IS_DIMM_PRESENT(mtr))
467 ndimms += get_dimm_info(mtr, amap, dimm, imc, i, j);
468 else if (IS_NVDIMM_PRESENT(mcddrtcfg, j))
469 ndimms += get_nvdimm_info(dimm, imc, i, j);
416 } 470 }
417 if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) { 471 if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) {
418 skx_printk(KERN_ERR, "ECC is disabled on imc %d\n", imc->mc); 472 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 pvt = mci->pvt_info; 522 pvt = mci->pvt_info;
469 pvt->imc = imc; 523 pvt->imc = imc;
470 524
471 mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d", imc->node_id, imc->lmc); 525 mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d",
526 imc->node_id, imc->lmc);
472 if (!mci->ctl_name) { 527 if (!mci->ctl_name) {
473 rc = -ENOMEM; 528 rc = -ENOMEM;
474 goto fail0; 529 goto fail0;
475 } 530 }
476 531
477 mci->mtype_cap = MEM_FLAG_DDR4; 532 mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_NVDIMM;
478 mci->edac_ctl_cap = EDAC_FLAG_NONE; 533 mci->edac_ctl_cap = EDAC_FLAG_NONE;
479 mci->edac_cap = EDAC_FLAG_NONE; 534 mci->edac_cap = EDAC_FLAG_NONE;
480 mci->mod_name = EDAC_MOD_STR; 535 mci->mod_name = EDAC_MOD_STR;