Commit 1ec16991 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'edac_for_4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp

Pull EDAC updates from Borislav Petkov:
 "The usual pile of bugfixes, cleanups and minor driver enhancements.

  Worth noting are the changes to ghes_edac to use a whitelist of
  known-good platforms on which GHES error reporting works relatively
  reliably. By Toshi Kani and Borislav Petkov"

* tag 'edac_for_4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp:
  EDAC, sb_edac: Fix missing break in switch
  MAINTAINERS: Split Cavium EDAC entry and add myself
  EDAC, sb_edac: Fix missing DIMM sysfs entries with KNL SNC2/SNC4 mode
  EDAC, skx_edac: Handle systems with segmented PCI busses
  EDAC, thunderx: Remove suspend/resume support
  EDAC, skx_edac: Fix detection of single-rank DIMMs
  EDAC, sb_edac: Don't create a second memory controller if HA1 is not present
  EDAC: Add owner check to the x86 platform drivers
  EDAC: Add helper which returns the loaded platform driver
  EDAC, ghes: Add platform check
  EDAC, ghes: Model a single, logical memory controller
  EDAC, ghes: Remove symbol exports
  EDAC: Handle return value of kasprintf()
parents 7832681b a8e9b186
...@@ -4906,13 +4906,19 @@ L: linux-edac@vger.kernel.org ...@@ -4906,13 +4906,19 @@ L: linux-edac@vger.kernel.org
S: Maintained S: Maintained
F: drivers/edac/highbank* F: drivers/edac/highbank*
EDAC-CAVIUM EDAC-CAVIUM OCTEON
M: Ralf Baechle <ralf@linux-mips.org> M: Ralf Baechle <ralf@linux-mips.org>
M: David Daney <david.daney@cavium.com> M: David Daney <david.daney@cavium.com>
L: linux-edac@vger.kernel.org L: linux-edac@vger.kernel.org
L: linux-mips@linux-mips.org L: linux-mips@linux-mips.org
S: Supported S: Supported
F: drivers/edac/octeon_edac* F: drivers/edac/octeon_edac*
EDAC-CAVIUM THUNDERX
M: David Daney <david.daney@cavium.com>
M: Jan Glauber <jglauber@cavium.com>
L: linux-edac@vger.kernel.org
S: Supported
F: drivers/edac/thunderx_edac* F: drivers/edac/thunderx_edac*
EDAC-CORE EDAC-CORE
......
...@@ -3434,9 +3434,14 @@ MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids); ...@@ -3434,9 +3434,14 @@ MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
static int __init amd64_edac_init(void) static int __init amd64_edac_init(void)
{ {
const char *owner;
int err = -ENODEV; int err = -ENODEV;
int i; int i;
owner = edac_get_owner();
if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
return -EBUSY;
if (!x86_match_cpu(amd64_cpuids)) if (!x86_match_cpu(amd64_cpuids))
return -ENODEV; return -ENODEV;
......
...@@ -53,7 +53,7 @@ static LIST_HEAD(mc_devices); ...@@ -53,7 +53,7 @@ static LIST_HEAD(mc_devices);
* Used to lock EDAC MC to just one module, avoiding two drivers e. g. * Used to lock EDAC MC to just one module, avoiding two drivers e. g.
* apei/ghes and i7core_edac to be used at the same time. * apei/ghes and i7core_edac to be used at the same time.
*/ */
static void const *edac_mc_owner; static const char *edac_mc_owner;
static struct bus_type mc_bus[EDAC_MAX_MCS]; static struct bus_type mc_bus[EDAC_MAX_MCS];
...@@ -701,6 +701,11 @@ struct mem_ctl_info *edac_mc_find(int idx) ...@@ -701,6 +701,11 @@ struct mem_ctl_info *edac_mc_find(int idx)
} }
EXPORT_SYMBOL(edac_mc_find); EXPORT_SYMBOL(edac_mc_find);
const char *edac_get_owner(void)
{
return edac_mc_owner;
}
EXPORT_SYMBOL_GPL(edac_get_owner);
/* FIXME - should a warning be printed if no error detection? correction? */ /* FIXME - should a warning be printed if no error detection? correction? */
int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci,
......
...@@ -128,6 +128,14 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, ...@@ -128,6 +128,14 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
unsigned sz_pvt); unsigned sz_pvt);
/** /**
* edac_get_owner - Return the owner's mod_name of EDAC MC
*
* Returns:
* Pointer to mod_name string when EDAC MC is owned. NULL otherwise.
*/
extern const char *edac_get_owner(void);
/*
* edac_mc_add_mc_with_groups() - Insert the @mci structure into the mci * edac_mc_add_mc_with_groups() - Insert the @mci structure into the mci
* global list and create sysfs entries associated with @mci structure. * global list and create sysfs entries associated with @mci structure.
* *
......
...@@ -28,10 +28,19 @@ struct ghes_edac_pvt { ...@@ -28,10 +28,19 @@ struct ghes_edac_pvt {
char msg[80]; char msg[80];
}; };
static LIST_HEAD(ghes_reglist); static atomic_t ghes_init = ATOMIC_INIT(0);
static DEFINE_MUTEX(ghes_edac_lock); static struct ghes_edac_pvt *ghes_pvt;
static int ghes_edac_mc_num;
/*
* Sync with other, potentially concurrent callers of
* ghes_edac_report_mem_error(). We don't know what the
* "inventive" firmware would do.
*/
static DEFINE_SPINLOCK(ghes_lock);
/* "ghes_edac.force_load=1" skips the platform check */
static bool __read_mostly force_load;
module_param(force_load, bool, 0);
/* Memory Device - Type 17 of SMBIOS spec */ /* Memory Device - Type 17 of SMBIOS spec */
struct memdev_dmi_entry { struct memdev_dmi_entry {
...@@ -169,18 +178,26 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev, ...@@ -169,18 +178,26 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
enum hw_event_mc_err_type type; enum hw_event_mc_err_type type;
struct edac_raw_error_desc *e; struct edac_raw_error_desc *e;
struct mem_ctl_info *mci; struct mem_ctl_info *mci;
struct ghes_edac_pvt *pvt = NULL; struct ghes_edac_pvt *pvt = ghes_pvt;
unsigned long flags;
char *p; char *p;
u8 grain_bits; u8 grain_bits;
list_for_each_entry(pvt, &ghes_reglist, list) {
if (ghes == pvt->ghes)
break;
}
if (!pvt) { if (!pvt) {
pr_err("Internal error: Can't find EDAC structure\n"); pr_err("Internal error: Can't find EDAC structure\n");
return; return;
} }
/*
* We can do the locking below because GHES defers error processing
* from NMI to IRQ context. Whenever that changes, we'd at least
* know.
*/
if (WARN_ON_ONCE(in_nmi()))
return;
spin_lock_irqsave(&ghes_lock, flags);
mci = pvt->mci; mci = pvt->mci;
e = &mci->error_desc; e = &mci->error_desc;
...@@ -398,10 +415,17 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev, ...@@ -398,10 +415,17 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
(e->page_frame_number << PAGE_SHIFT) | e->offset_in_page, (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page,
grain_bits, e->syndrome, pvt->detail_location); grain_bits, e->syndrome, pvt->detail_location);
/* Report the error via EDAC API */
edac_raw_mc_handle_error(type, mci, e); edac_raw_mc_handle_error(type, mci, e);
spin_unlock_irqrestore(&ghes_lock, flags);
} }
EXPORT_SYMBOL_GPL(ghes_edac_report_mem_error);
/*
* Known systems that are safe to enable this module.
*/
static struct acpi_platform_list plat_list[] = {
{"HPE ", "Server ", 0, ACPI_SIG_FADT, all_versions},
{ } /* End */
};
int ghes_edac_register(struct ghes *ghes, struct device *dev) int ghes_edac_register(struct ghes *ghes, struct device *dev)
{ {
...@@ -409,8 +433,19 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev) ...@@ -409,8 +433,19 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
int rc, num_dimm = 0; int rc, num_dimm = 0;
struct mem_ctl_info *mci; struct mem_ctl_info *mci;
struct edac_mc_layer layers[1]; struct edac_mc_layer layers[1];
struct ghes_edac_pvt *pvt;
struct ghes_edac_dimm_fill dimm_fill; struct ghes_edac_dimm_fill dimm_fill;
int idx;
/* Check if safe to enable on this system */
idx = acpi_match_platform_list(plat_list);
if (!force_load && idx < 0)
return 0;
/*
* We have only one logical memory controller to which all DIMMs belong.
*/
if (atomic_inc_return(&ghes_init) > 1)
return 0;
/* Get the number of DIMMs */ /* Get the number of DIMMs */
dmi_walk(ghes_edac_count_dimms, &num_dimm); dmi_walk(ghes_edac_count_dimms, &num_dimm);
...@@ -425,26 +460,17 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev) ...@@ -425,26 +460,17 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
layers[0].size = num_dimm; layers[0].size = num_dimm;
layers[0].is_virt_csrow = true; layers[0].is_virt_csrow = true;
/* mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(struct ghes_edac_pvt));
* We need to serialize edac_mc_alloc() and edac_mc_add_mc(),
* to avoid duplicated memory controller numbers
*/
mutex_lock(&ghes_edac_lock);
mci = edac_mc_alloc(ghes_edac_mc_num, ARRAY_SIZE(layers), layers,
sizeof(*pvt));
if (!mci) { if (!mci) {
pr_info("Can't allocate memory for EDAC data\n"); pr_info("Can't allocate memory for EDAC data\n");
mutex_unlock(&ghes_edac_lock);
return -ENOMEM; return -ENOMEM;
} }
pvt = mci->pvt_info; ghes_pvt = mci->pvt_info;
memset(pvt, 0, sizeof(*pvt)); ghes_pvt->ghes = ghes;
list_add_tail(&pvt->list, &ghes_reglist); ghes_pvt->mci = mci;
pvt->ghes = ghes;
pvt->mci = mci;
mci->pdev = dev;
mci->pdev = dev;
mci->mtype_cap = MEM_FLAG_EMPTY; mci->mtype_cap = MEM_FLAG_EMPTY;
mci->edac_ctl_cap = EDAC_FLAG_NONE; mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE;
...@@ -452,36 +478,23 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev) ...@@ -452,36 +478,23 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
mci->ctl_name = "ghes_edac"; mci->ctl_name = "ghes_edac";
mci->dev_name = "ghes"; mci->dev_name = "ghes";
if (!ghes_edac_mc_num) { if (fake) {
if (!fake) { pr_info("This system has a very crappy BIOS: It doesn't even list the DIMMS.\n");
pr_info("This EDAC driver relies on BIOS to enumerate memory and get error reports.\n"); pr_info("Its SMBIOS info is wrong. It is doubtful that the error report would\n");
pr_info("Unfortunately, not all BIOSes reflect the memory layout correctly.\n"); pr_info("work on such system. Use this driver with caution\n");
pr_info("So, the end result of using this driver varies from vendor to vendor.\n"); } else if (idx < 0) {
pr_info("If you find incorrect reports, please contact your hardware vendor\n"); pr_info("This EDAC driver relies on BIOS to enumerate memory and get error reports.\n");
pr_info("to correct its BIOS.\n"); pr_info("Unfortunately, not all BIOSes reflect the memory layout correctly.\n");
pr_info("This system has %d DIMM sockets.\n", pr_info("So, the end result of using this driver varies from vendor to vendor.\n");
num_dimm); pr_info("If you find incorrect reports, please contact your hardware vendor\n");
} else { pr_info("to correct its BIOS.\n");
pr_info("This system has a very crappy BIOS: It doesn't even list the DIMMS.\n"); pr_info("This system has %d DIMM sockets.\n", num_dimm);
pr_info("Its SMBIOS info is wrong. It is doubtful that the error report would\n");
pr_info("work on such system. Use this driver with caution\n");
}
} }
if (!fake) { if (!fake) {
/* dimm_fill.count = 0;
* Fill DIMM info from DMI for the memory controller #0 dimm_fill.mci = mci;
* dmi_walk(ghes_edac_dmidecode, &dimm_fill);
* Keep it in blank for the other memory controllers, as
* there's no reliable way to properly credit each DIMM to
* the memory controller, as different BIOSes fill the
* DMI bank location fields on different ways
*/
if (!ghes_edac_mc_num) {
dimm_fill.count = 0;
dimm_fill.mci = mci;
dmi_walk(ghes_edac_dmidecode, &dimm_fill);
}
} else { } else {
struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers, 0, 0, 0); mci->n_layers, 0, 0, 0);
...@@ -497,28 +510,16 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev) ...@@ -497,28 +510,16 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
if (rc < 0) { if (rc < 0) {
pr_info("Can't register at EDAC core\n"); pr_info("Can't register at EDAC core\n");
edac_mc_free(mci); edac_mc_free(mci);
mutex_unlock(&ghes_edac_lock);
return -ENODEV; return -ENODEV;
} }
ghes_edac_mc_num++;
mutex_unlock(&ghes_edac_lock);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ghes_edac_register);
void ghes_edac_unregister(struct ghes *ghes) void ghes_edac_unregister(struct ghes *ghes)
{ {
struct mem_ctl_info *mci; struct mem_ctl_info *mci;
struct ghes_edac_pvt *pvt, *tmp;
mci = ghes_pvt->mci;
list_for_each_entry_safe(pvt, tmp, &ghes_reglist, list) { edac_mc_del_mc(mci->pdev);
if (ghes == pvt->ghes) { edac_mc_free(mci);
mci = pvt->mci;
edac_mc_del_mc(mci->pdev);
edac_mc_free(mci);
list_del(&pvt->list);
}
}
} }
EXPORT_SYMBOL_GPL(ghes_edac_unregister);
...@@ -2159,8 +2159,13 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) ...@@ -2159,8 +2159,13 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
mci->edac_ctl_cap = EDAC_FLAG_NONE; mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = "i7core_edac.c"; mci->mod_name = "i7core_edac.c";
mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
i7core_dev->socket); mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d", i7core_dev->socket);
if (!mci->ctl_name) {
rc = -ENOMEM;
goto fail1;
}
mci->dev_name = pci_name(i7core_dev->pdev[0]); mci->dev_name = pci_name(i7core_dev->pdev[0]);
mci->ctl_page_to_phys = NULL; mci->ctl_page_to_phys = NULL;
...@@ -2214,6 +2219,8 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) ...@@ -2214,6 +2219,8 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
fail0: fail0:
kfree(mci->ctl_name); kfree(mci->ctl_name);
fail1:
edac_mc_free(mci); edac_mc_free(mci);
i7core_dev->mci = NULL; i7core_dev->mci = NULL;
return rc; return rc;
......
...@@ -45,6 +45,8 @@ ...@@ -45,6 +45,8 @@
#include "edac_module.h" #include "edac_module.h"
#include "pnd2_edac.h" #include "pnd2_edac.h"
#define EDAC_MOD_STR "pnd2_edac"
#define APL_NUM_CHANNELS 4 #define APL_NUM_CHANNELS 4
#define DNV_NUM_CHANNELS 2 #define DNV_NUM_CHANNELS 2
#define DNV_MAX_DIMMS 2 /* Max DIMMs per channel */ #define DNV_MAX_DIMMS 2 /* Max DIMMs per channel */
...@@ -1355,7 +1357,7 @@ static int pnd2_register_mci(struct mem_ctl_info **ppmci) ...@@ -1355,7 +1357,7 @@ static int pnd2_register_mci(struct mem_ctl_info **ppmci)
pvt = mci->pvt_info; pvt = mci->pvt_info;
memset(pvt, 0, sizeof(*pvt)); memset(pvt, 0, sizeof(*pvt));
mci->mod_name = "pnd2_edac.c"; mci->mod_name = EDAC_MOD_STR;
mci->dev_name = ops->name; mci->dev_name = ops->name;
mci->ctl_name = "Pondicherry2"; mci->ctl_name = "Pondicherry2";
...@@ -1547,10 +1549,15 @@ MODULE_DEVICE_TABLE(x86cpu, pnd2_cpuids); ...@@ -1547,10 +1549,15 @@ MODULE_DEVICE_TABLE(x86cpu, pnd2_cpuids);
static int __init pnd2_init(void) static int __init pnd2_init(void)
{ {
const struct x86_cpu_id *id; const struct x86_cpu_id *id;
const char *owner;
int rc; int rc;
edac_dbg(2, "\n"); edac_dbg(2, "\n");
owner = edac_get_owner();
if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
return -EBUSY;
id = x86_match_cpu(pnd2_cpuids); id = x86_match_cpu(pnd2_cpuids);
if (!id) if (!id)
return -ENODEV; return -ENODEV;
......
...@@ -36,7 +36,7 @@ static LIST_HEAD(sbridge_edac_list); ...@@ -36,7 +36,7 @@ static LIST_HEAD(sbridge_edac_list);
* Alter this version for the module when modifications are made * Alter this version for the module when modifications are made
*/ */
#define SBRIDGE_REVISION " Ver: 1.1.2 " #define SBRIDGE_REVISION " Ver: 1.1.2 "
#define EDAC_MOD_STR "sbridge_edac" #define EDAC_MOD_STR "sb_edac"
/* /*
* Debug macros * Debug macros
...@@ -462,6 +462,7 @@ static const struct pci_id_table pci_dev_descr_sbridge_table[] = { ...@@ -462,6 +462,7 @@ static const struct pci_id_table pci_dev_descr_sbridge_table[] = {
static const struct pci_id_descr pci_dev_descr_ibridge[] = { static const struct pci_id_descr pci_dev_descr_ibridge[] = {
/* Processor Home Agent */ /* Processor Home Agent */
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0, 0, IMC0) }, { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0, 0, IMC0) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1, 1, IMC1) },
/* Memory controller */ /* Memory controller */
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA, 0, IMC0) }, { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA, 0, IMC0) },
...@@ -472,7 +473,6 @@ static const struct pci_id_descr pci_dev_descr_ibridge[] = { ...@@ -472,7 +473,6 @@ static const struct pci_id_descr pci_dev_descr_ibridge[] = {
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3, 0, IMC0) }, { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3, 0, IMC0) },
/* Optional, mode 2HA */ /* Optional, mode 2HA */
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1, 1, IMC1) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TA, 1, IMC1) }, { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TA, 1, IMC1) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS, 1, IMC1) }, { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS, 1, IMC1) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0, 1, IMC1) }, { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0, 1, IMC1) },
...@@ -1318,9 +1318,7 @@ static int knl_get_dimm_capacity(struct sbridge_pvt *pvt, u64 *mc_sizes) ...@@ -1318,9 +1318,7 @@ static int knl_get_dimm_capacity(struct sbridge_pvt *pvt, u64 *mc_sizes)
int cur_reg_start; int cur_reg_start;
int mc; int mc;
int channel; int channel;
int way;
int participants[KNL_MAX_CHANNELS]; int participants[KNL_MAX_CHANNELS];
int participant_count = 0;
for (i = 0; i < KNL_MAX_CHANNELS; i++) for (i = 0; i < KNL_MAX_CHANNELS; i++)
mc_sizes[i] = 0; mc_sizes[i] = 0;
...@@ -1495,21 +1493,14 @@ static int knl_get_dimm_capacity(struct sbridge_pvt *pvt, u64 *mc_sizes) ...@@ -1495,21 +1493,14 @@ static int knl_get_dimm_capacity(struct sbridge_pvt *pvt, u64 *mc_sizes)
* this channel mapped to the given target? * this channel mapped to the given target?
*/ */
for (channel = 0; channel < KNL_MAX_CHANNELS; channel++) { for (channel = 0; channel < KNL_MAX_CHANNELS; channel++) {
for (way = 0; way < intrlv_ways; way++) { int target;
int target; int cha;
int cha;
if (KNL_MOD3(dram_rule))
target = way;
else
target = 0x7 & sad_pkg(
pvt->info.interleave_pkg, interleave_reg, way);
for (target = 0; target < KNL_MAX_CHANNELS; target++) {
for (cha = 0; cha < KNL_MAX_CHAS; cha++) { for (cha = 0; cha < KNL_MAX_CHAS; cha++) {
if (knl_get_mc_route(target, if (knl_get_mc_route(target,
mc_route_reg[cha]) == channel mc_route_reg[cha]) == channel
&& !participants[channel]) { && !participants[channel]) {
participant_count++;
participants[channel] = 1; participants[channel] = 1;
break; break;
} }
...@@ -1517,10 +1508,6 @@ static int knl_get_dimm_capacity(struct sbridge_pvt *pvt, u64 *mc_sizes) ...@@ -1517,10 +1508,6 @@ static int knl_get_dimm_capacity(struct sbridge_pvt *pvt, u64 *mc_sizes)
} }
} }
if (participant_count != intrlv_ways)
edac_dbg(0, "participant_count (%d) != interleave_ways (%d): DIMM size may be incorrect\n",
participant_count, intrlv_ways);
for (channel = 0; channel < KNL_MAX_CHANNELS; channel++) { for (channel = 0; channel < KNL_MAX_CHANNELS; channel++) {
mc = knl_channel_mc(channel); mc = knl_channel_mc(channel);
if (participants[channel]) { if (participants[channel]) {
...@@ -2291,6 +2278,13 @@ static int sbridge_get_onedevice(struct pci_dev **prev, ...@@ -2291,6 +2278,13 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
next_imc: next_imc:
sbridge_dev = get_sbridge_dev(bus, dev_descr->dom, multi_bus, sbridge_dev); sbridge_dev = get_sbridge_dev(bus, dev_descr->dom, multi_bus, sbridge_dev);
if (!sbridge_dev) { if (!sbridge_dev) {
/* If the HA1 wasn't found, don't create EDAC second memory controller */
if (dev_descr->dom == IMC1 && devno != 1) {
edac_dbg(0, "Skip IMC1: %04x:%04x (since HA1 was absent)\n",
PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
pci_dev_put(pdev);
return 0;
}
if (dev_descr->dom == SOCK) if (dev_descr->dom == SOCK)
goto out_imc; goto out_imc;
...@@ -2491,6 +2485,7 @@ static int ibridge_mci_bind_devs(struct mem_ctl_info *mci, ...@@ -2491,6 +2485,7 @@ static int ibridge_mci_bind_devs(struct mem_ctl_info *mci,
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA: case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA:
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TA: case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TA:
pvt->pci_ta = pdev; pvt->pci_ta = pdev;
break;
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS: case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS:
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS: case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS:
pvt->pci_ras = pdev; pvt->pci_ras = pdev;
...@@ -3155,7 +3150,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -3155,7 +3150,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
MEM_FLAG_DDR4 : MEM_FLAG_DDR3; MEM_FLAG_DDR4 : MEM_FLAG_DDR3;
mci->edac_ctl_cap = EDAC_FLAG_NONE; mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = "sb_edac.c"; mci->mod_name = EDAC_MOD_STR;
mci->dev_name = pci_name(pdev); mci->dev_name = pci_name(pdev);
mci->ctl_page_to_phys = NULL; mci->ctl_page_to_phys = NULL;
...@@ -3287,6 +3282,11 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) ...@@ -3287,6 +3282,11 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
break; break;
} }
if (!mci->ctl_name) {
rc = -ENOMEM;
goto fail0;
}
/* Get dimm basic config and the memory layout */ /* Get dimm basic config and the memory layout */
rc = get_dimm_config(mci); rc = get_dimm_config(mci);
if (rc < 0) { if (rc < 0) {
...@@ -3402,10 +3402,15 @@ static void sbridge_remove(void) ...@@ -3402,10 +3402,15 @@ static void sbridge_remove(void)
static int __init sbridge_init(void) static int __init sbridge_init(void)
{ {
const struct x86_cpu_id *id; const struct x86_cpu_id *id;
const char *owner;
int rc; int rc;
edac_dbg(2, "\n"); edac_dbg(2, "\n");
owner = edac_get_owner();
if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
return -EBUSY;
id = x86_match_cpu(sbridge_cpuids); id = x86_match_cpu(sbridge_cpuids);
if (!id) if (!id)
return -ENODEV; return -ENODEV;
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include "edac_module.h" #include "edac_module.h"
#define EDAC_MOD_STR "skx_edac"
/* /*
* Debug macros * Debug macros
*/ */
...@@ -65,6 +67,7 @@ static u64 skx_tolm, skx_tohm; ...@@ -65,6 +67,7 @@ static u64 skx_tolm, skx_tohm;
struct skx_dev { struct skx_dev {
struct list_head list; struct list_head list;
u8 bus[4]; u8 bus[4];
int seg;
struct pci_dev *sad_all; struct pci_dev *sad_all;
struct pci_dev *util_all; struct pci_dev *util_all;
u32 mcroute; u32 mcroute;
...@@ -110,12 +113,12 @@ struct decoded_addr { ...@@ -110,12 +113,12 @@ struct decoded_addr {
int bank_group; int bank_group;
}; };
static struct skx_dev *get_skx_dev(u8 bus, u8 idx) static struct skx_dev *get_skx_dev(struct pci_bus *bus, u8 idx)
{ {
struct skx_dev *d; struct skx_dev *d;
list_for_each_entry(d, &skx_edac_list, list) { list_for_each_entry(d, &skx_edac_list, list) {
if (d->bus[idx] == bus) if (d->seg == pci_domain_nr(bus) && d->bus[idx] == bus->number)
return d; return d;
} }
...@@ -172,6 +175,7 @@ static int get_all_bus_mappings(void) ...@@ -172,6 +175,7 @@ static int get_all_bus_mappings(void)
pci_dev_put(pdev); pci_dev_put(pdev);
return -ENOMEM; return -ENOMEM;
} }
d->seg = pci_domain_nr(pdev->bus);
pci_read_config_dword(pdev, 0xCC, &reg); pci_read_config_dword(pdev, 0xCC, &reg);
d->bus[0] = GET_BITFIELD(reg, 0, 7); d->bus[0] = GET_BITFIELD(reg, 0, 7);
d->bus[1] = GET_BITFIELD(reg, 8, 15); d->bus[1] = GET_BITFIELD(reg, 8, 15);
...@@ -207,7 +211,7 @@ static int get_all_munits(const struct munit *m) ...@@ -207,7 +211,7 @@ static int get_all_munits(const struct munit *m)
if (i == NUM_IMC) if (i == NUM_IMC)
goto fail; goto fail;
} }
d = get_skx_dev(pdev->bus->number, m->busidx); d = get_skx_dev(pdev->bus, m->busidx);
if (!d) if (!d)
goto fail; goto fail;
...@@ -299,7 +303,7 @@ static int get_dimm_attr(u32 reg, int lobit, int hibit, int add, int minval, ...@@ -299,7 +303,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_DIMM_PRESENT(mtr) GET_BITFIELD((mtr), 15, 15)
#define numrank(reg) get_dimm_attr((reg), 12, 13, 0, 1, 2, "ranks") #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") #define numrow(reg) get_dimm_attr((reg), 2, 4, 12, 1, 6, "rows")
#define numcol(reg) get_dimm_attr((reg), 0, 1, 10, 0, 2, "cols") #define numcol(reg) get_dimm_attr((reg), 0, 1, 10, 0, 2, "cols")
...@@ -360,7 +364,7 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm, ...@@ -360,7 +364,7 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n", edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
imc->mc, chan, dimmno, size, npages, imc->mc, chan, dimmno, size, npages,
banks, ranks, rows, cols); banks, 1 << ranks, rows, cols);
imc->chan[chan].dimms[dimmno].close_pg = GET_BITFIELD(mtr, 0, 0); imc->chan[chan].dimms[dimmno].close_pg = GET_BITFIELD(mtr, 0, 0);
imc->chan[chan].dimms[dimmno].bank_xor_enable = GET_BITFIELD(mtr, 9, 9); imc->chan[chan].dimms[dimmno].bank_xor_enable = GET_BITFIELD(mtr, 9, 9);
...@@ -464,12 +468,16 @@ static int skx_register_mci(struct skx_imc *imc) ...@@ -464,12 +468,16 @@ static int skx_register_mci(struct skx_imc *imc)
pvt = mci->pvt_info; pvt = mci->pvt_info;
pvt->imc = imc; pvt->imc = imc;
mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d", mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d", imc->node_id, imc->lmc);
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;
mci->edac_ctl_cap = EDAC_FLAG_NONE; mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = "skx_edac.c"; mci->mod_name = EDAC_MOD_STR;
mci->dev_name = pci_name(imc->chan[0].cdev); mci->dev_name = pci_name(imc->chan[0].cdev);
mci->ctl_page_to_phys = NULL; mci->ctl_page_to_phys = NULL;
...@@ -491,6 +499,7 @@ static int skx_register_mci(struct skx_imc *imc) ...@@ -491,6 +499,7 @@ static int skx_register_mci(struct skx_imc *imc)
fail: fail:
kfree(mci->ctl_name); kfree(mci->ctl_name);
fail0:
edac_mc_free(mci); edac_mc_free(mci);
imc->mci = NULL; imc->mci = NULL;
return rc; return rc;
...@@ -1039,12 +1048,17 @@ static int __init skx_init(void) ...@@ -1039,12 +1048,17 @@ static int __init skx_init(void)
{ {
const struct x86_cpu_id *id; const struct x86_cpu_id *id;
const struct munit *m; const struct munit *m;
const char *owner;
int rc = 0, i; int rc = 0, i;
u8 mc = 0, src_id, node_id; u8 mc = 0, src_id, node_id;
struct skx_dev *d; struct skx_dev *d;
edac_dbg(2, "\n"); edac_dbg(2, "\n");
owner = edac_get_owner();
if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
return -EBUSY;
id = x86_match_cpu(skx_cpuids); id = x86_match_cpu(skx_cpuids);
if (!id) if (!id)
return -ENODEV; return -ENODEV;
......
...@@ -639,27 +639,6 @@ static irqreturn_t thunderx_lmc_threaded_isr(int irq, void *dev_id) ...@@ -639,27 +639,6 @@ static irqreturn_t thunderx_lmc_threaded_isr(int irq, void *dev_id)
return ret; return ret;
} }
#ifdef CONFIG_PM
static int thunderx_lmc_suspend(struct pci_dev *pdev, pm_message_t state)
{
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
}
static int thunderx_lmc_resume(struct pci_dev *pdev)
{
pci_set_power_state(pdev, PCI_D0);
pci_enable_wake(pdev, PCI_D0, 0);
pci_restore_state(pdev);
return 0;
}
#endif
static const struct pci_device_id thunderx_lmc_pci_tbl[] = { static const struct pci_device_id thunderx_lmc_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_LMC) }, { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_LMC) },
{ 0, }, { 0, },
...@@ -834,10 +813,6 @@ static struct pci_driver thunderx_lmc_driver = { ...@@ -834,10 +813,6 @@ static struct pci_driver thunderx_lmc_driver = {
.name = "thunderx_lmc_edac", .name = "thunderx_lmc_edac",
.probe = thunderx_lmc_probe, .probe = thunderx_lmc_probe,
.remove = thunderx_lmc_remove, .remove = thunderx_lmc_remove,
#ifdef CONFIG_PM
.suspend = thunderx_lmc_suspend,
.resume = thunderx_lmc_resume,
#endif
.id_table = thunderx_lmc_pci_tbl, .id_table = thunderx_lmc_pci_tbl,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment