mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-23 20:53:53 +08:00
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. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAloIt2sACgkQEsHwGGHe VUpzmw/+PETRJYIwp9TnmLHsNjVA/TPFHmjQqYc+jR9sWzTdrsZqI1JbP6GNC4l6 0bMdcyCO5J57F8xLKkgYC5sv24m1vL8MKx0+sQSI+EXupCyqDhhclU3c2rfSj0Um +qpPV4MnewqJNZlenKKHC+R2wADSsijIpIiMeV682/8HCf0EB68P5m6NRMSqL+hz IVOD2xAyn+3dDw4ZazJgra5xSVt+xJdt+x0/bvovPapzvF81b+PCB4XXVm4J5d4f gi0AwDbmyLpMx2HK5vLT5JgcbGg6aUS7OhwcZLSwGuWJTTsYM5uEk+F+dj3pxNge hk0JSeCLm0S4f60p496VuiweIOrkk4feDbz+RAK9GJaR270K621Zb/06jCybB6aB htR/VQS50BgCyehYzSWw9VAOcsEvNPHKpYSGkrDwPRkFZOxfigdnKb4MN8XbGund bpQqx6VlgPhV32v0F1F893IIPHrcuc7qFo4KJPSGnP58+iBOeJ6iy2vlUdtMIfyL opjS0nqg7B6pXCcANLFRWV0iPTH4e1PWAMGSSxp64cqjuEmHPtlvQGinevBq56Ob HpxbtnOqen14xxMulkPPHBw3yKWQA0/teAGcSn2D5P/Qv0X3aTGsifr3Ruk/Cl0E wQaGBNcn+5xJ8nhBS7GdUDdyfE5o9zW8wjw3eZEphREoOM3N8ko= =XEah -----END PGP SIGNATURE----- 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()
This commit is contained in:
commit
1ec1699122
@ -4906,13 +4906,19 @@ L: linux-edac@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/edac/highbank*
|
||||
|
||||
EDAC-CAVIUM
|
||||
EDAC-CAVIUM OCTEON
|
||||
M: Ralf Baechle <ralf@linux-mips.org>
|
||||
M: David Daney <david.daney@cavium.com>
|
||||
L: linux-edac@vger.kernel.org
|
||||
L: linux-mips@linux-mips.org
|
||||
S: Supported
|
||||
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*
|
||||
|
||||
EDAC-CORE
|
||||
|
@ -3434,9 +3434,14 @@ MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
|
||||
|
||||
static int __init amd64_edac_init(void)
|
||||
{
|
||||
const char *owner;
|
||||
int err = -ENODEV;
|
||||
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))
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -53,7 +53,7 @@ static LIST_HEAD(mc_devices);
|
||||
* 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.
|
||||
*/
|
||||
static void const *edac_mc_owner;
|
||||
static const char *edac_mc_owner;
|
||||
|
||||
static struct bus_type mc_bus[EDAC_MAX_MCS];
|
||||
|
||||
@ -701,6 +701,11 @@ unlock:
|
||||
}
|
||||
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? */
|
||||
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,
|
||||
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
|
||||
* global list and create sysfs entries associated with @mci structure.
|
||||
*
|
||||
|
@ -28,10 +28,19 @@ struct ghes_edac_pvt {
|
||||
char msg[80];
|
||||
};
|
||||
|
||||
static LIST_HEAD(ghes_reglist);
|
||||
static DEFINE_MUTEX(ghes_edac_lock);
|
||||
static int ghes_edac_mc_num;
|
||||
static atomic_t ghes_init = ATOMIC_INIT(0);
|
||||
static struct ghes_edac_pvt *ghes_pvt;
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
struct memdev_dmi_entry {
|
||||
@ -169,18 +178,26 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
|
||||
enum hw_event_mc_err_type type;
|
||||
struct edac_raw_error_desc *e;
|
||||
struct mem_ctl_info *mci;
|
||||
struct ghes_edac_pvt *pvt = NULL;
|
||||
struct ghes_edac_pvt *pvt = ghes_pvt;
|
||||
unsigned long flags;
|
||||
char *p;
|
||||
u8 grain_bits;
|
||||
|
||||
list_for_each_entry(pvt, &ghes_reglist, list) {
|
||||
if (ghes == pvt->ghes)
|
||||
break;
|
||||
}
|
||||
if (!pvt) {
|
||||
pr_err("Internal error: Can't find EDAC structure\n");
|
||||
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;
|
||||
e = &mci->error_desc;
|
||||
|
||||
@ -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,
|
||||
grain_bits, e->syndrome, pvt->detail_location);
|
||||
|
||||
/* Report the error via EDAC API */
|
||||
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)
|
||||
{
|
||||
@ -409,8 +433,19 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
|
||||
int rc, num_dimm = 0;
|
||||
struct mem_ctl_info *mci;
|
||||
struct edac_mc_layer layers[1];
|
||||
struct ghes_edac_pvt *pvt;
|
||||
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 */
|
||||
dmi_walk(ghes_edac_count_dimms, &num_dimm);
|
||||
@ -425,26 +460,17 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
|
||||
layers[0].size = num_dimm;
|
||||
layers[0].is_virt_csrow = true;
|
||||
|
||||
/*
|
||||
* 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));
|
||||
mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(struct ghes_edac_pvt));
|
||||
if (!mci) {
|
||||
pr_info("Can't allocate memory for EDAC data\n");
|
||||
mutex_unlock(&ghes_edac_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pvt = mci->pvt_info;
|
||||
memset(pvt, 0, sizeof(*pvt));
|
||||
list_add_tail(&pvt->list, &ghes_reglist);
|
||||
pvt->ghes = ghes;
|
||||
pvt->mci = mci;
|
||||
mci->pdev = dev;
|
||||
ghes_pvt = mci->pvt_info;
|
||||
ghes_pvt->ghes = ghes;
|
||||
ghes_pvt->mci = mci;
|
||||
|
||||
mci->pdev = dev;
|
||||
mci->mtype_cap = MEM_FLAG_EMPTY;
|
||||
mci->edac_ctl_cap = EDAC_FLAG_NONE;
|
||||
mci->edac_cap = EDAC_FLAG_NONE;
|
||||
@ -452,36 +478,23 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
|
||||
mci->ctl_name = "ghes_edac";
|
||||
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("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");
|
||||
} else if (idx < 0) {
|
||||
pr_info("This EDAC driver relies on BIOS to enumerate memory and get error reports.\n");
|
||||
pr_info("Unfortunately, not all BIOSes reflect the memory layout correctly.\n");
|
||||
pr_info("So, the end result of using this driver varies from vendor to vendor.\n");
|
||||
pr_info("If you find incorrect reports, please contact your hardware vendor\n");
|
||||
pr_info("to correct its BIOS.\n");
|
||||
pr_info("This system has %d DIMM sockets.\n",
|
||||
num_dimm);
|
||||
} else {
|
||||
pr_info("This system has a very crappy BIOS: It doesn't even list the DIMMS.\n");
|
||||
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");
|
||||
}
|
||||
pr_info("This system has %d DIMM sockets.\n", num_dimm);
|
||||
}
|
||||
|
||||
if (!fake) {
|
||||
/*
|
||||
* Fill DIMM info from DMI for the memory controller #0
|
||||
*
|
||||
* 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 {
|
||||
struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
|
||||
mci->n_layers, 0, 0, 0);
|
||||
@ -497,28 +510,16 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
|
||||
if (rc < 0) {
|
||||
pr_info("Can't register at EDAC core\n");
|
||||
edac_mc_free(mci);
|
||||
mutex_unlock(&ghes_edac_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ghes_edac_mc_num++;
|
||||
mutex_unlock(&ghes_edac_lock);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ghes_edac_register);
|
||||
|
||||
void ghes_edac_unregister(struct ghes *ghes)
|
||||
{
|
||||
struct mem_ctl_info *mci;
|
||||
struct ghes_edac_pvt *pvt, *tmp;
|
||||
|
||||
list_for_each_entry_safe(pvt, tmp, &ghes_reglist, list) {
|
||||
if (ghes == pvt->ghes) {
|
||||
mci = pvt->mci;
|
||||
mci = ghes_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)
|
||||
mci->edac_ctl_cap = EDAC_FLAG_NONE;
|
||||
mci->edac_cap = EDAC_FLAG_NONE;
|
||||
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->ctl_page_to_phys = NULL;
|
||||
|
||||
@ -2214,6 +2219,8 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
|
||||
|
||||
fail0:
|
||||
kfree(mci->ctl_name);
|
||||
|
||||
fail1:
|
||||
edac_mc_free(mci);
|
||||
i7core_dev->mci = NULL;
|
||||
return rc;
|
||||
|
@ -45,6 +45,8 @@
|
||||
#include "edac_module.h"
|
||||
#include "pnd2_edac.h"
|
||||
|
||||
#define EDAC_MOD_STR "pnd2_edac"
|
||||
|
||||
#define APL_NUM_CHANNELS 4
|
||||
#define DNV_NUM_CHANNELS 2
|
||||
#define DNV_MAX_DIMMS 2 /* Max DIMMs per channel */
|
||||
@ -1355,7 +1357,7 @@ static int pnd2_register_mci(struct mem_ctl_info **ppmci)
|
||||
pvt = mci->pvt_info;
|
||||
memset(pvt, 0, sizeof(*pvt));
|
||||
|
||||
mci->mod_name = "pnd2_edac.c";
|
||||
mci->mod_name = EDAC_MOD_STR;
|
||||
mci->dev_name = ops->name;
|
||||
mci->ctl_name = "Pondicherry2";
|
||||
|
||||
@ -1547,10 +1549,15 @@ MODULE_DEVICE_TABLE(x86cpu, pnd2_cpuids);
|
||||
static int __init pnd2_init(void)
|
||||
{
|
||||
const struct x86_cpu_id *id;
|
||||
const char *owner;
|
||||
int rc;
|
||||
|
||||
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);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
@ -36,7 +36,7 @@ static LIST_HEAD(sbridge_edac_list);
|
||||
* Alter this version for the module when modifications are made
|
||||
*/
|
||||
#define SBRIDGE_REVISION " Ver: 1.1.2 "
|
||||
#define EDAC_MOD_STR "sbridge_edac"
|
||||
#define EDAC_MOD_STR "sb_edac"
|
||||
|
||||
/*
|
||||
* Debug macros
|
||||
@ -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[] = {
|
||||
/* Processor Home Agent */
|
||||
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0, 0, IMC0) },
|
||||
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1, 1, IMC1) },
|
||||
|
||||
/* Memory controller */
|
||||
{ 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[] = {
|
||||
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3, 0, IMC0) },
|
||||
|
||||
/* 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_RAS, 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)
|
||||
int cur_reg_start;
|
||||
int mc;
|
||||
int channel;
|
||||
int way;
|
||||
int participants[KNL_MAX_CHANNELS];
|
||||
int participant_count = 0;
|
||||
|
||||
for (i = 0; i < KNL_MAX_CHANNELS; i++)
|
||||
mc_sizes[i] = 0;
|
||||
@ -1495,21 +1493,14 @@ static int knl_get_dimm_capacity(struct sbridge_pvt *pvt, u64 *mc_sizes)
|
||||
* this channel mapped to the given target?
|
||||
*/
|
||||
for (channel = 0; channel < KNL_MAX_CHANNELS; channel++) {
|
||||
for (way = 0; way < intrlv_ways; way++) {
|
||||
int target;
|
||||
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++) {
|
||||
if (knl_get_mc_route(target,
|
||||
mc_route_reg[cha]) == channel
|
||||
&& !participants[channel]) {
|
||||
participant_count++;
|
||||
participants[channel] = 1;
|
||||
break;
|
||||
}
|
||||
@ -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++) {
|
||||
mc = knl_channel_mc(channel);
|
||||
if (participants[channel]) {
|
||||
@ -2291,6 +2278,13 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
|
||||
next_imc:
|
||||
sbridge_dev = get_sbridge_dev(bus, dev_descr->dom, multi_bus, 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)
|
||||
goto out_imc;
|
||||
@ -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_HA1_TA:
|
||||
pvt->pci_ta = pdev;
|
||||
break;
|
||||
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS:
|
||||
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS:
|
||||
pvt->pci_ras = pdev;
|
||||
@ -3155,7 +3150,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
|
||||
MEM_FLAG_DDR4 : MEM_FLAG_DDR3;
|
||||
mci->edac_ctl_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->ctl_page_to_phys = NULL;
|
||||
|
||||
@ -3287,6 +3282,11 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mci->ctl_name) {
|
||||
rc = -ENOMEM;
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
/* Get dimm basic config and the memory layout */
|
||||
rc = get_dimm_config(mci);
|
||||
if (rc < 0) {
|
||||
@ -3402,10 +3402,15 @@ static void sbridge_remove(void)
|
||||
static int __init sbridge_init(void)
|
||||
{
|
||||
const struct x86_cpu_id *id;
|
||||
const char *owner;
|
||||
int rc;
|
||||
|
||||
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);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
@ -31,6 +31,8 @@
|
||||
|
||||
#include "edac_module.h"
|
||||
|
||||
#define EDAC_MOD_STR "skx_edac"
|
||||
|
||||
/*
|
||||
* Debug macros
|
||||
*/
|
||||
@ -65,6 +67,7 @@ static u64 skx_tolm, skx_tohm;
|
||||
struct skx_dev {
|
||||
struct list_head list;
|
||||
u8 bus[4];
|
||||
int seg;
|
||||
struct pci_dev *sad_all;
|
||||
struct pci_dev *util_all;
|
||||
u32 mcroute;
|
||||
@ -110,12 +113,12 @@ struct decoded_addr {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -172,6 +175,7 @@ static int get_all_bus_mappings(void)
|
||||
pci_dev_put(pdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
d->seg = pci_domain_nr(pdev->bus);
|
||||
pci_read_config_dword(pdev, 0xCC, ®);
|
||||
d->bus[0] = GET_BITFIELD(reg, 0, 7);
|
||||
d->bus[1] = GET_BITFIELD(reg, 8, 15);
|
||||
@ -207,7 +211,7 @@ static int get_all_munits(const struct munit *m)
|
||||
if (i == NUM_IMC)
|
||||
goto fail;
|
||||
}
|
||||
d = get_skx_dev(pdev->bus->number, m->busidx);
|
||||
d = get_skx_dev(pdev->bus, m->busidx);
|
||||
if (!d)
|
||||
goto fail;
|
||||
|
||||
@ -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 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 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,
|
||||
|
||||
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,
|
||||
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].bank_xor_enable = GET_BITFIELD(mtr, 9, 9);
|
||||
@ -464,12 +468,16 @@ 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->edac_ctl_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->ctl_page_to_phys = NULL;
|
||||
|
||||
@ -491,6 +499,7 @@ static int skx_register_mci(struct skx_imc *imc)
|
||||
|
||||
fail:
|
||||
kfree(mci->ctl_name);
|
||||
fail0:
|
||||
edac_mc_free(mci);
|
||||
imc->mci = NULL;
|
||||
return rc;
|
||||
@ -1039,12 +1048,17 @@ static int __init skx_init(void)
|
||||
{
|
||||
const struct x86_cpu_id *id;
|
||||
const struct munit *m;
|
||||
const char *owner;
|
||||
int rc = 0, i;
|
||||
u8 mc = 0, src_id, node_id;
|
||||
struct skx_dev *d;
|
||||
|
||||
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);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
@ -639,27 +639,6 @@ err_free:
|
||||
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[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_LMC) },
|
||||
{ 0, },
|
||||
@ -834,10 +813,6 @@ static struct pci_driver thunderx_lmc_driver = {
|
||||
.name = "thunderx_lmc_edac",
|
||||
.probe = thunderx_lmc_probe,
|
||||
.remove = thunderx_lmc_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = thunderx_lmc_suspend,
|
||||
.resume = thunderx_lmc_resume,
|
||||
#endif
|
||||
.id_table = thunderx_lmc_pci_tbl,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user