diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-05 17:21:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-05 17:21:13 -0400 |
commit | dd972f924df6bdbc0ab185a38d5d2361dbc26311 (patch) | |
tree | 181c24478ae55284f7baee88c3cf794794467aa2 | |
parent | 3c8ba0d61d04ced9f8d9ff93977995a9e4e96e91 (diff) | |
parent | 6fd052665274b4e7570491ba272f2dbb09cbbcd5 (diff) |
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
-rw-r--r-- | drivers/acpi/nfit/core.c | 27 | ||||
-rw-r--r-- | drivers/edac/Kconfig | 7 | ||||
-rw-r--r-- | drivers/edac/edac_mc.c | 41 | ||||
-rw-r--r-- | drivers/edac/edac_mc_sysfs.c | 26 | ||||
-rw-r--r-- | drivers/edac/sb_edac.c | 12 | ||||
-rw-r--r-- | drivers/edac/skx_edac.c | 67 | ||||
-rw-r--r-- | drivers/firmware/dmi_scan.c | 31 | ||||
-rw-r--r-- | include/acpi/nfit.h | 18 | ||||
-rw-r--r-- | include/linux/dmi.h | 2 | ||||
-rw-r--r-- | include/linux/edac.h | 3 |
10 files changed, 175 insertions, 59 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index eb09ef55c38a..22a112b4f4d8 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/nd.h> | 24 | #include <linux/nd.h> |
25 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
26 | #include <acpi/nfit.h> | ||
26 | #include "nfit.h" | 27 | #include "nfit.h" |
27 | 28 | ||
28 | /* | 29 | /* |
@@ -690,6 +691,32 @@ static bool add_memdev(struct acpi_nfit_desc *acpi_desc, | |||
690 | return true; | 691 | return true; |
691 | } | 692 | } |
692 | 693 | ||
694 | int nfit_get_smbios_id(u32 device_handle, u16 *flags) | ||
695 | { | ||
696 | struct acpi_nfit_memory_map *memdev; | ||
697 | struct acpi_nfit_desc *acpi_desc; | ||
698 | struct nfit_mem *nfit_mem; | ||
699 | |||
700 | mutex_lock(&acpi_desc_lock); | ||
701 | list_for_each_entry(acpi_desc, &acpi_descs, list) { | ||
702 | mutex_lock(&acpi_desc->init_mutex); | ||
703 | list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { | ||
704 | memdev = __to_nfit_memdev(nfit_mem); | ||
705 | if (memdev->device_handle == device_handle) { | ||
706 | mutex_unlock(&acpi_desc->init_mutex); | ||
707 | mutex_unlock(&acpi_desc_lock); | ||
708 | *flags = memdev->flags; | ||
709 | return memdev->physical_id; | ||
710 | } | ||
711 | } | ||
712 | mutex_unlock(&acpi_desc->init_mutex); | ||
713 | } | ||
714 | mutex_unlock(&acpi_desc_lock); | ||
715 | |||
716 | return -ENODEV; | ||
717 | } | ||
718 | EXPORT_SYMBOL_GPL(nfit_get_smbios_id); | ||
719 | |||
693 | /* | 720 | /* |
694 | * An implementation may provide a truncated control region if no block windows | 721 | * An implementation may provide a truncated control region if no block windows |
695 | * are defined. | 722 | * are defined. |
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index cfcb91056f23..da2da53bca6d 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig | |||
@@ -232,9 +232,12 @@ config EDAC_SBRIDGE | |||
232 | config EDAC_SKX | 232 | config 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 | ||
239 | config EDAC_PND2 | 242 | config EDAC_PND2 |
240 | tristate "Intel Pondicherry2" | 243 | tristate "Intel Pondicherry2" |
@@ -254,7 +257,7 @@ config EDAC_MPC85XX | |||
254 | 257 | ||
255 | config EDAC_LAYERSCAPE | 258 | config EDAC_LAYERSCAPE |
256 | tristate "Freescale Layerscape DDR" | 259 | tristate "Freescale Layerscape DDR" |
257 | depends on ARCH_LAYERSCAPE | 260 | depends on ARCH_LAYERSCAPE || SOC_LS1021A |
258 | help | 261 | help |
259 | Support for error detection and correction on Freescale memory | 262 | Support for error detection and correction on Freescale memory |
260 | controllers on Layerscape SoCs. | 263 | controllers on Layerscape SoCs. |
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 48193f5f3b56..3bb82e511eca 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c | |||
@@ -195,26 +195,27 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci) | |||
195 | #endif /* CONFIG_EDAC_DEBUG */ | 195 | #endif /* CONFIG_EDAC_DEBUG */ |
196 | 196 | ||
197 | const char * const edac_mem_types[] = { | 197 | const char * const edac_mem_types[] = { |
198 | [MEM_EMPTY] = "Empty csrow", | 198 | [MEM_EMPTY] = "Empty", |
199 | [MEM_RESERVED] = "Reserved csrow type", | 199 | [MEM_RESERVED] = "Reserved", |
200 | [MEM_UNKNOWN] = "Unknown csrow type", | 200 | [MEM_UNKNOWN] = "Unknown", |
201 | [MEM_FPM] = "Fast page mode RAM", | 201 | [MEM_FPM] = "FPM", |
202 | [MEM_EDO] = "Extended data out RAM", | 202 | [MEM_EDO] = "EDO", |
203 | [MEM_BEDO] = "Burst Extended data out RAM", | 203 | [MEM_BEDO] = "BEDO", |
204 | [MEM_SDR] = "Single data rate SDRAM", | 204 | [MEM_SDR] = "Unbuffered-SDR", |
205 | [MEM_RDR] = "Registered single data rate SDRAM", | 205 | [MEM_RDR] = "Registered-SDR", |
206 | [MEM_DDR] = "Double data rate SDRAM", | 206 | [MEM_DDR] = "Unbuffered-DDR", |
207 | [MEM_RDDR] = "Registered Double data rate SDRAM", | 207 | [MEM_RDDR] = "Registered-DDR", |
208 | [MEM_RMBS] = "Rambus DRAM", | 208 | [MEM_RMBS] = "RMBS", |
209 | [MEM_DDR2] = "Unbuffered DDR2 RAM", | 209 | [MEM_DDR2] = "Unbuffered-DDR2", |
210 | [MEM_FB_DDR2] = "Fully buffered DDR2", | 210 | [MEM_FB_DDR2] = "FullyBuffered-DDR2", |
211 | [MEM_RDDR2] = "Registered DDR2 RAM", | 211 | [MEM_RDDR2] = "Registered-DDR2", |
212 | [MEM_XDR] = "Rambus XDR", | 212 | [MEM_XDR] = "XDR", |
213 | [MEM_DDR3] = "Unbuffered DDR3 RAM", | 213 | [MEM_DDR3] = "Unbuffered-DDR3", |
214 | [MEM_RDDR3] = "Registered DDR3 RAM", | 214 | [MEM_RDDR3] = "Registered-DDR3", |
215 | [MEM_LRDDR3] = "Load-Reduced DDR3 RAM", | 215 | [MEM_LRDDR3] = "Load-Reduced-DDR3-RAM", |
216 | [MEM_DDR4] = "Unbuffered DDR4 RAM", | 216 | [MEM_DDR4] = "Unbuffered-DDR4", |
217 | [MEM_RDDR4] = "Registered DDR4 RAM", | 217 | [MEM_RDDR4] = "Registered-DDR4", |
218 | [MEM_NVDIMM] = "Non-volatile-RAM", | ||
218 | }; | 219 | }; |
219 | EXPORT_SYMBOL_GPL(edac_mem_types); | 220 | EXPORT_SYMBOL_GPL(edac_mem_types); |
220 | 221 | ||
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index c70ea82c815c..7481955160a4 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c | |||
@@ -91,28 +91,6 @@ static struct device *mci_pdev; | |||
91 | /* | 91 | /* |
92 | * various constants for Memory Controllers | 92 | * various constants for Memory Controllers |
93 | */ | 93 | */ |
94 | static const char * const mem_types[] = { | ||
95 | [MEM_EMPTY] = "Empty", | ||
96 | [MEM_RESERVED] = "Reserved", | ||
97 | [MEM_UNKNOWN] = "Unknown", | ||
98 | [MEM_FPM] = "FPM", | ||
99 | [MEM_EDO] = "EDO", | ||
100 | [MEM_BEDO] = "BEDO", | ||
101 | [MEM_SDR] = "Unbuffered-SDR", | ||
102 | [MEM_RDR] = "Registered-SDR", | ||
103 | [MEM_DDR] = "Unbuffered-DDR", | ||
104 | [MEM_RDDR] = "Registered-DDR", | ||
105 | [MEM_RMBS] = "RMBS", | ||
106 | [MEM_DDR2] = "Unbuffered-DDR2", | ||
107 | [MEM_FB_DDR2] = "FullyBuffered-DDR2", | ||
108 | [MEM_RDDR2] = "Registered-DDR2", | ||
109 | [MEM_XDR] = "XDR", | ||
110 | [MEM_DDR3] = "Unbuffered-DDR3", | ||
111 | [MEM_RDDR3] = "Registered-DDR3", | ||
112 | [MEM_DDR4] = "Unbuffered-DDR4", | ||
113 | [MEM_RDDR4] = "Registered-DDR4" | ||
114 | }; | ||
115 | |||
116 | static const char * const dev_types[] = { | 94 | static const char * const dev_types[] = { |
117 | [DEV_UNKNOWN] = "Unknown", | 95 | [DEV_UNKNOWN] = "Unknown", |
118 | [DEV_X1] = "x1", | 96 | [DEV_X1] = "x1", |
@@ -196,7 +174,7 @@ static ssize_t csrow_mem_type_show(struct device *dev, | |||
196 | { | 174 | { |
197 | struct csrow_info *csrow = to_csrow(dev); | 175 | struct csrow_info *csrow = to_csrow(dev); |
198 | 176 | ||
199 | return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]); | 177 | return sprintf(data, "%s\n", edac_mem_types[csrow->channels[0]->dimm->mtype]); |
200 | } | 178 | } |
201 | 179 | ||
202 | static ssize_t csrow_dev_type_show(struct device *dev, | 180 | static ssize_t csrow_dev_type_show(struct device *dev, |
@@ -549,7 +527,7 @@ static ssize_t dimmdev_mem_type_show(struct device *dev, | |||
549 | { | 527 | { |
550 | struct dimm_info *dimm = to_dimm(dev); | 528 | struct dimm_info *dimm = to_dimm(dev); |
551 | 529 | ||
552 | return sprintf(data, "%s\n", mem_types[dimm->mtype]); | 530 | return sprintf(data, "%s\n", edac_mem_types[dimm->mtype]); |
553 | } | 531 | } |
554 | 532 | ||
555 | static ssize_t dimmdev_dev_type_show(struct device *dev, | 533 | static ssize_t dimmdev_dev_type_show(struct device *dev, |
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 872100215ca0..4a89c8093307 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c | |||
@@ -110,6 +110,10 @@ static const u32 knl_interleave_list[] = { | |||
110 | 0xdc, 0xe4, 0xec, 0xf4, 0xfc, /* 15-19 */ | 110 | 0xdc, 0xe4, 0xec, 0xf4, 0xfc, /* 15-19 */ |
111 | 0x104, 0x10c, 0x114, 0x11c, /* 20-23 */ | 111 | 0x104, 0x10c, 0x114, 0x11c, /* 20-23 */ |
112 | }; | 112 | }; |
113 | #define MAX_INTERLEAVE \ | ||
114 | (max_t(unsigned int, ARRAY_SIZE(sbridge_interleave_list), \ | ||
115 | max_t(unsigned int, ARRAY_SIZE(ibridge_interleave_list), \ | ||
116 | ARRAY_SIZE(knl_interleave_list)))) | ||
113 | 117 | ||
114 | struct interleave_pkg { | 118 | struct interleave_pkg { |
115 | unsigned char start; | 119 | unsigned char start; |
@@ -321,7 +325,6 @@ struct sbridge_info { | |||
321 | const u32 *interleave_list; | 325 | const u32 *interleave_list; |
322 | const struct interleave_pkg *interleave_pkg; | 326 | const struct interleave_pkg *interleave_pkg; |
323 | u8 max_sad; | 327 | u8 max_sad; |
324 | u8 max_interleave; | ||
325 | u8 (*get_node_id)(struct sbridge_pvt *pvt); | 328 | u8 (*get_node_id)(struct sbridge_pvt *pvt); |
326 | enum mem_type (*get_memory_type)(struct sbridge_pvt *pvt); | 329 | enum mem_type (*get_memory_type)(struct sbridge_pvt *pvt); |
327 | enum dev_type (*get_width)(struct sbridge_pvt *pvt, u32 mtr); | 330 | 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, | |||
1899 | int n_rir, n_sads, n_tads, sad_way, sck_xch; | 1902 | int n_rir, n_sads, n_tads, sad_way, sck_xch; |
1900 | int sad_interl, idx, base_ch; | 1903 | int sad_interl, idx, base_ch; |
1901 | int interleave_mode, shiftup = 0; | 1904 | int interleave_mode, shiftup = 0; |
1902 | unsigned sad_interleave[pvt->info.max_interleave]; | 1905 | unsigned int sad_interleave[MAX_INTERLEAVE]; |
1903 | u32 reg, dram_rule; | 1906 | u32 reg, dram_rule; |
1904 | u8 ch_way, sck_way, pkg, sad_ha = 0; | 1907 | u8 ch_way, sck_way, pkg, sad_ha = 0; |
1905 | u32 tad_offset; | 1908 | u32 tad_offset; |
@@ -3169,7 +3172,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) | |||
3169 | pvt->info.dram_attr = dram_attr; | 3172 | pvt->info.dram_attr = dram_attr; |
3170 | pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); | 3173 | pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); |
3171 | pvt->info.interleave_list = ibridge_interleave_list; | 3174 | pvt->info.interleave_list = ibridge_interleave_list; |
3172 | pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); | ||
3173 | pvt->info.interleave_pkg = ibridge_interleave_pkg; | 3175 | pvt->info.interleave_pkg = ibridge_interleave_pkg; |
3174 | pvt->info.get_width = ibridge_get_width; | 3176 | pvt->info.get_width = ibridge_get_width; |
3175 | 3177 | ||
@@ -3194,7 +3196,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) | |||
3194 | pvt->info.dram_attr = dram_attr; | 3196 | pvt->info.dram_attr = dram_attr; |
3195 | pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule); | 3197 | pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule); |
3196 | pvt->info.interleave_list = sbridge_interleave_list; | 3198 | pvt->info.interleave_list = sbridge_interleave_list; |
3197 | pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list); | ||
3198 | pvt->info.interleave_pkg = sbridge_interleave_pkg; | 3199 | pvt->info.interleave_pkg = sbridge_interleave_pkg; |
3199 | pvt->info.get_width = sbridge_get_width; | 3200 | pvt->info.get_width = sbridge_get_width; |
3200 | 3201 | ||
@@ -3219,7 +3220,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) | |||
3219 | pvt->info.dram_attr = dram_attr; | 3220 | pvt->info.dram_attr = dram_attr; |
3220 | pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); | 3221 | pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); |
3221 | pvt->info.interleave_list = ibridge_interleave_list; | 3222 | pvt->info.interleave_list = ibridge_interleave_list; |
3222 | pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); | ||
3223 | pvt->info.interleave_pkg = ibridge_interleave_pkg; | 3223 | pvt->info.interleave_pkg = ibridge_interleave_pkg; |
3224 | pvt->info.get_width = ibridge_get_width; | 3224 | pvt->info.get_width = ibridge_get_width; |
3225 | 3225 | ||
@@ -3244,7 +3244,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) | |||
3244 | pvt->info.dram_attr = dram_attr; | 3244 | pvt->info.dram_attr = dram_attr; |
3245 | pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); | 3245 | pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule); |
3246 | pvt->info.interleave_list = ibridge_interleave_list; | 3246 | pvt->info.interleave_list = ibridge_interleave_list; |
3247 | pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); | ||
3248 | pvt->info.interleave_pkg = ibridge_interleave_pkg; | 3247 | pvt->info.interleave_pkg = ibridge_interleave_pkg; |
3249 | pvt->info.get_width = broadwell_get_width; | 3248 | pvt->info.get_width = broadwell_get_width; |
3250 | 3249 | ||
@@ -3269,7 +3268,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) | |||
3269 | pvt->info.dram_attr = dram_attr_knl; | 3268 | pvt->info.dram_attr = dram_attr_knl; |
3270 | pvt->info.max_sad = ARRAY_SIZE(knl_dram_rule); | 3269 | pvt->info.max_sad = ARRAY_SIZE(knl_dram_rule); |
3271 | pvt->info.interleave_list = knl_interleave_list; | 3270 | pvt->info.interleave_list = knl_interleave_list; |
3272 | pvt->info.max_interleave = ARRAY_SIZE(knl_interleave_list); | ||
3273 | pvt->info.interleave_pkg = ibridge_interleave_pkg; | 3271 | pvt->info.interleave_pkg = ibridge_interleave_pkg; |
3274 | pvt->info.get_width = knl_get_width; | 3272 | pvt->info.get_width = knl_get_width; |
3275 | 3273 | ||
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 | ||
388 | static 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 | |||
420 | unknown_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, ®) | 437 | pci_read_config_dword((dev), 0x87c, ®) |
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; |
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index e35c5e04c46a..6feeacbe4d97 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c | |||
@@ -32,6 +32,7 @@ static char dmi_ids_string[128] __initdata; | |||
32 | static struct dmi_memdev_info { | 32 | static struct dmi_memdev_info { |
33 | const char *device; | 33 | const char *device; |
34 | const char *bank; | 34 | const char *bank; |
35 | u64 size; /* bytes */ | ||
35 | u16 handle; | 36 | u16 handle; |
36 | } *dmi_memdev; | 37 | } *dmi_memdev; |
37 | static int dmi_memdev_nr; | 38 | static int dmi_memdev_nr; |
@@ -386,6 +387,8 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) | |||
386 | { | 387 | { |
387 | const char *d = (const char *)dm; | 388 | const char *d = (const char *)dm; |
388 | static int nr; | 389 | static int nr; |
390 | u64 bytes; | ||
391 | u16 size; | ||
389 | 392 | ||
390 | if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x12) | 393 | if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x12) |
391 | return; | 394 | return; |
@@ -396,6 +399,20 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) | |||
396 | dmi_memdev[nr].handle = get_unaligned(&dm->handle); | 399 | dmi_memdev[nr].handle = get_unaligned(&dm->handle); |
397 | dmi_memdev[nr].device = dmi_string(dm, d[0x10]); | 400 | dmi_memdev[nr].device = dmi_string(dm, d[0x10]); |
398 | dmi_memdev[nr].bank = dmi_string(dm, d[0x11]); | 401 | dmi_memdev[nr].bank = dmi_string(dm, d[0x11]); |
402 | |||
403 | size = get_unaligned((u16 *)&d[0xC]); | ||
404 | if (size == 0) | ||
405 | bytes = 0; | ||
406 | else if (size == 0xffff) | ||
407 | bytes = ~0ull; | ||
408 | else if (size & 0x8000) | ||
409 | bytes = (u64)(size & 0x7fff) << 10; | ||
410 | else if (size != 0x7fff) | ||
411 | bytes = (u64)size << 20; | ||
412 | else | ||
413 | bytes = (u64)get_unaligned((u32 *)&d[0x1C]) << 20; | ||
414 | |||
415 | dmi_memdev[nr].size = bytes; | ||
399 | nr++; | 416 | nr++; |
400 | } | 417 | } |
401 | 418 | ||
@@ -1085,3 +1102,17 @@ void dmi_memdev_name(u16 handle, const char **bank, const char **device) | |||
1085 | } | 1102 | } |
1086 | } | 1103 | } |
1087 | EXPORT_SYMBOL_GPL(dmi_memdev_name); | 1104 | EXPORT_SYMBOL_GPL(dmi_memdev_name); |
1105 | |||
1106 | u64 dmi_memdev_size(u16 handle) | ||
1107 | { | ||
1108 | int n; | ||
1109 | |||
1110 | if (dmi_memdev) { | ||
1111 | for (n = 0; n < dmi_memdev_nr; n++) { | ||
1112 | if (handle == dmi_memdev[n].handle) | ||
1113 | return dmi_memdev[n].size; | ||
1114 | } | ||
1115 | } | ||
1116 | return ~0ull; | ||
1117 | } | ||
1118 | EXPORT_SYMBOL_GPL(dmi_memdev_size); | ||
diff --git a/include/acpi/nfit.h b/include/acpi/nfit.h new file mode 100644 index 000000000000..86ed07c1200d --- /dev/null +++ b/include/acpi/nfit.h | |||
@@ -0,0 +1,18 @@ | |||
1 | /* | ||
2 | * SPDX-License-Identifier: GPL-2.0 | ||
3 | * Copyright (C) 2018 Intel Corporation | ||
4 | */ | ||
5 | |||
6 | #ifndef __ACPI_NFIT_H | ||
7 | #define __ACPI_NFIT_H | ||
8 | |||
9 | #if IS_ENABLED(CONFIG_ACPI_NFIT) | ||
10 | int nfit_get_smbios_id(u32 device_handle, u16 *flags); | ||
11 | #else | ||
12 | static inline int nfit_get_smbios_id(u32 device_handle, u16 *flags) | ||
13 | { | ||
14 | return -EOPNOTSUPP; | ||
15 | } | ||
16 | #endif | ||
17 | |||
18 | #endif /* __ACPI_NFIT_H */ | ||
diff --git a/include/linux/dmi.h b/include/linux/dmi.h index 6a86d8db16d9..c46fdb36700b 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h | |||
@@ -114,6 +114,7 @@ extern int dmi_walk(void (*decode)(const struct dmi_header *, void *), | |||
114 | void *private_data); | 114 | void *private_data); |
115 | extern bool dmi_match(enum dmi_field f, const char *str); | 115 | extern bool dmi_match(enum dmi_field f, const char *str); |
116 | extern void dmi_memdev_name(u16 handle, const char **bank, const char **device); | 116 | extern void dmi_memdev_name(u16 handle, const char **bank, const char **device); |
117 | extern u64 dmi_memdev_size(u16 handle); | ||
117 | 118 | ||
118 | #else | 119 | #else |
119 | 120 | ||
@@ -144,6 +145,7 @@ static inline bool dmi_match(enum dmi_field f, const char *str) | |||
144 | { return false; } | 145 | { return false; } |
145 | static inline void dmi_memdev_name(u16 handle, const char **bank, | 146 | static inline void dmi_memdev_name(u16 handle, const char **bank, |
146 | const char **device) { } | 147 | const char **device) { } |
148 | static inline u64 dmi_memdev_size(u16 handle) { return ~0ul; } | ||
147 | static inline const struct dmi_system_id * | 149 | static inline const struct dmi_system_id * |
148 | dmi_first_match(const struct dmi_system_id *list) { return NULL; } | 150 | dmi_first_match(const struct dmi_system_id *list) { return NULL; } |
149 | 151 | ||
diff --git a/include/linux/edac.h b/include/linux/edac.h index cd75c173fd00..bffb97828ed6 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h | |||
@@ -186,6 +186,7 @@ static inline char *mc_event_error_type(const unsigned int err_type) | |||
186 | * @MEM_RDDR4: Registered DDR4 RAM | 186 | * @MEM_RDDR4: Registered DDR4 RAM |
187 | * This is a variant of the DDR4 memories. | 187 | * This is a variant of the DDR4 memories. |
188 | * @MEM_LRDDR4: Load-Reduced DDR4 memory. | 188 | * @MEM_LRDDR4: Load-Reduced DDR4 memory. |
189 | * @MEM_NVDIMM: Non-volatile RAM | ||
189 | */ | 190 | */ |
190 | enum mem_type { | 191 | enum mem_type { |
191 | MEM_EMPTY = 0, | 192 | MEM_EMPTY = 0, |
@@ -209,6 +210,7 @@ enum mem_type { | |||
209 | MEM_DDR4, | 210 | MEM_DDR4, |
210 | MEM_RDDR4, | 211 | MEM_RDDR4, |
211 | MEM_LRDDR4, | 212 | MEM_LRDDR4, |
213 | MEM_NVDIMM, | ||
212 | }; | 214 | }; |
213 | 215 | ||
214 | #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) | 216 | #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) |
@@ -231,6 +233,7 @@ enum mem_type { | |||
231 | #define MEM_FLAG_DDR4 BIT(MEM_DDR4) | 233 | #define MEM_FLAG_DDR4 BIT(MEM_DDR4) |
232 | #define MEM_FLAG_RDDR4 BIT(MEM_RDDR4) | 234 | #define MEM_FLAG_RDDR4 BIT(MEM_RDDR4) |
233 | #define MEM_FLAG_LRDDR4 BIT(MEM_LRDDR4) | 235 | #define MEM_FLAG_LRDDR4 BIT(MEM_LRDDR4) |
236 | #define MEM_FLAG_NVDIMM BIT(MEM_NVDIMM) | ||
234 | 237 | ||
235 | /** | 238 | /** |
236 | * enum edac-type - Error Detection and Correction capabilities and mode | 239 | * enum edac-type - Error Detection and Correction capabilities and mode |