* Add NVDIMM support to EDAC (Tony Luck)
* misc fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAlrDZ6EACgkQEsHwGGHe VUpWWA//RG9WqqdXxPa05TxiRFWMiNSH/HSGJQZsc3K7xm2TEGVdDv5thYsxXCaW kA0oiUJA3zd62HeUIWMeGypm+hLM8T73836aIKcfxMduxQ6DR9nk51IV4RmHsLvT rd4bUsrjCov6spapqS265LOxZVnpKvxZv0NVTj6pIk0pkeghWMZh63vZtrxVkLsS 6dHsdIDBr0fMhcFBVQ67SEDpMIyMAqziPOP9sv6NNElG4Q93RnZr6KYvQdE32Iev Z6lIUU/sjVJEuhgp6FjJrhvjc+FmGJGERsFqo3Z8RT9P26Cj3wq0ov8IY/KSBazG mfJFGbL+O3lppGKjjuAwMGr4R3fZMLQpgLxdYe7VNvOHB9ea7732q9MDGiNkN/u7 z8o+dtvmPWr0Y1ir8te+7LnSQXIAwxMkKP/lMStzzuFUETQDd0RWr7GAKlbrgPOU +DhiQRPBFS7qpslA7BdqV6Ybr3nhDVZciDYWsvNpDMwdem05TxlFwC5l/BzP9NtM FmQ3B0rQ5we6gib6UZWUjW3BH9wUgFUpu/WXKJl1unMHwYNe3CvdM8yD5W7mUylh 6SVYAmkZyuJ1Du40+YMeimroe77XV60IqoUFNEzMSFeB4/GxMG6+u2gUqNuTpHpZ aMmCRj9DxWGxZyDsTc49An0X/JbVyIE/Aq6heMTYScgBvCaD1VE= =K89j -----END PGP SIGNATURE----- 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
This commit is contained in:
Коммит
dd972f924d
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче