- A couple of fixes/improvements to amd64_edac:

- merge debugging and error injection functionality into the main driver
  - tone down info/error output
  - do not attempt to load it on F15h client hw
 
 - Misc fixes to other drivers
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAmAqOr4ACgkQEsHwGGHe
 VUq9kxAAu/Pxfl2XkKFXhjSpKjm0tFsnBKMgb+HDZpevmqp0blaf2rMRj0rapYjs
 img4VhxtTW00Iy0g+1OL38Lx2wYcEgLNPnsn5/NPGXTC2z59F7B2vbNwklr5LZyy
 81cNr5bOtXFZQq7aOt7Y0egvr6kJA8DEJ/pb3FeprocvYIYYYTLzGCQ3mRscc6dN
 nwp1ZC1v4tzJFH9xi0bdt3skxypgw9PwCjWaS93vYTUNrUWk0IHZPXziainoO3Ub
 H1tSeCM2mlFeN/OY6xeCiS+cKz6Xser9f5VJ3/fq3ZJMYh1FaWkAXAIDJqFrNsqd
 0C0S1sg1JusyDqgJuWIXiRKMplu5wfDnBxGJB+k4q1VfHvfmkD1RuUQxtKeoAIHW
 WSNNvHVdHrLepZufJ0sSXpEWIL+GqtWXVJvFW0aWITyGRpihzPHe3G7y1/dkMHjf
 iOk0Y/iw+TTbhsQAx9tn6WyqXzZm2+qO+Cq4C10OsB98iaQ2jLNAUUVZbCTxZA3C
 nUc7qM3E/zf3xoy5VNcG7ifK7os9b/0rgdpCy1VpJEi6JQQvuz7HM/a8rzyDbuct
 SI9AfiBxHaWHek67a5j+mf/uHC7nXBq+35FofWHHh9tCfr6WDU2Nf0MK2/s9B9mC
 yRyZt1iqQb0NvH9eS3NZdtlLfp3BsjzZDQUUB6vuVMJIZmu6QJY=
 =a4vX
 -----END PGP SIGNATURE-----

Merge tag 'edac_updates_for_v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras

Pull EDAC updates from Borislav Petkov:

 - a couple of fixes/improvements to amd64_edac:
    * merge debugging and error injection functionality into the main driver
    * tone down info/error output
    * do not attempt to load it on F15h client hw

 - misc fixes to other drivers

* tag 'edac_updates_for_v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras:
  EDAC/amd64: Issue probing messages only on properly detected hardware
  EDAC/xgene: Do not print a failure message to get an IRQ twice
  EDAC/ppc4xx: Convert comma to semicolon
  EDAC/amd64: Limit error injection functionality to supported hw
  EDAC/amd64: Merge error injection sysfs facilities
  EDAC/amd64: Merge sysfs debugging attributes setup code
  EDAC/amd64: Tone down messages about missing PCI IDs
  EDAC/amd64: Do not load on family 0x15, model 0x13
This commit is contained in:
Linus Torvalds 2021-02-20 19:02:28 -08:00
commit 8518496639
8 changed files with 319 additions and 340 deletions

View File

@ -81,14 +81,13 @@ config EDAC_AMD64
Support for error detection and correction of DRAM ECC errors on Support for error detection and correction of DRAM ECC errors on
the AMD64 families (>= K8) of memory controllers. the AMD64 families (>= K8) of memory controllers.
config EDAC_AMD64_ERROR_INJECTION When EDAC_DEBUG is enabled, hardware error injection facilities
bool "Sysfs HW Error injection facilities" through sysfs are available:
depends on EDAC_AMD64
help AMD CPUs up to and excluding family 0x17 provide for Memory
Recent Opterons (Family 10h and later) provide for Memory Error Error Injection into the ECC detection circuits. The amd64_edac
Injection into the ECC detection circuits. The amd64_edac module module allows the operator/user to inject Uncorrectable and
allows the operator/user to inject Uncorrectable and Correctable Correctable errors into DRAM.
errors into DRAM.
When enabled, in each of the respective memory controller directories When enabled, in each of the respective memory controller directories
(/sys/devices/system/edac/mc/mcX), there are 3 input files: (/sys/devices/system/edac/mc/mcX), there are 3 input files:

View File

@ -44,12 +44,7 @@ obj-$(CONFIG_EDAC_IE31200) += ie31200_edac.o
obj-$(CONFIG_EDAC_X38) += x38_edac.o obj-$(CONFIG_EDAC_X38) += x38_edac.o
obj-$(CONFIG_EDAC_I82860) += i82860_edac.o obj-$(CONFIG_EDAC_I82860) += i82860_edac.o
obj-$(CONFIG_EDAC_R82600) += r82600_edac.o obj-$(CONFIG_EDAC_R82600) += r82600_edac.o
obj-$(CONFIG_EDAC_AMD64) += amd64_edac.o
amd64_edac_mod-y := amd64_edac.o
amd64_edac_mod-$(CONFIG_EDAC_DEBUG) += amd64_edac_dbg.o
amd64_edac_mod-$(CONFIG_EDAC_AMD64_ERROR_INJECTION) += amd64_edac_inj.o
obj-$(CONFIG_EDAC_AMD64) += amd64_edac_mod.o
obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o

View File

@ -500,8 +500,8 @@ static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
* complete 32-bit values despite the fact that the bitfields in the DHAR * complete 32-bit values despite the fact that the bitfields in the DHAR
* only represent bits 31-24 of the base and offset values. * only represent bits 31-24 of the base and offset values.
*/ */
int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, static int get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
u64 *hole_offset, u64 *hole_size) u64 *hole_offset, u64 *hole_size)
{ {
struct amd64_pvt *pvt = mci->pvt_info; struct amd64_pvt *pvt = mci->pvt_info;
@ -554,7 +554,292 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
#ifdef CONFIG_EDAC_DEBUG
#define EDAC_DCT_ATTR_SHOW(reg) \
static ssize_t reg##_show(struct device *dev, \
struct device_attribute *mattr, char *data) \
{ \
struct mem_ctl_info *mci = to_mci(dev); \
struct amd64_pvt *pvt = mci->pvt_info; \
\
return sprintf(data, "0x%016llx\n", (u64)pvt->reg); \
}
EDAC_DCT_ATTR_SHOW(dhar);
EDAC_DCT_ATTR_SHOW(dbam0);
EDAC_DCT_ATTR_SHOW(top_mem);
EDAC_DCT_ATTR_SHOW(top_mem2);
static ssize_t hole_show(struct device *dev, struct device_attribute *mattr,
char *data)
{
struct mem_ctl_info *mci = to_mci(dev);
u64 hole_base = 0;
u64 hole_offset = 0;
u64 hole_size = 0;
get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size);
return sprintf(data, "%llx %llx %llx\n", hole_base, hole_offset,
hole_size);
}
/*
* update NUM_DBG_ATTRS in case you add new members
*/
static DEVICE_ATTR(dhar, S_IRUGO, dhar_show, NULL);
static DEVICE_ATTR(dbam, S_IRUGO, dbam0_show, NULL);
static DEVICE_ATTR(topmem, S_IRUGO, top_mem_show, NULL);
static DEVICE_ATTR(topmem2, S_IRUGO, top_mem2_show, NULL);
static DEVICE_ATTR(dram_hole, S_IRUGO, hole_show, NULL);
static struct attribute *dbg_attrs[] = {
&dev_attr_dhar.attr,
&dev_attr_dbam.attr,
&dev_attr_topmem.attr,
&dev_attr_topmem2.attr,
&dev_attr_dram_hole.attr,
NULL
};
static const struct attribute_group dbg_group = {
.attrs = dbg_attrs,
};
static ssize_t inject_section_show(struct device *dev,
struct device_attribute *mattr, char *buf)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
return sprintf(buf, "0x%x\n", pvt->injection.section);
}
/*
* store error injection section value which refers to one of 4 16-byte sections
* within a 64-byte cacheline
*
* range: 0..3
*/
static ssize_t inject_section_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
int ret;
ret = kstrtoul(data, 10, &value);
if (ret < 0)
return ret;
if (value > 3) {
amd64_warn("%s: invalid section 0x%lx\n", __func__, value);
return -EINVAL;
}
pvt->injection.section = (u32) value;
return count;
}
static ssize_t inject_word_show(struct device *dev,
struct device_attribute *mattr, char *buf)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
return sprintf(buf, "0x%x\n", pvt->injection.word);
}
/*
* store error injection word value which refers to one of 9 16-bit word of the
* 16-byte (128-bit + ECC bits) section
*
* range: 0..8
*/
static ssize_t inject_word_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
int ret;
ret = kstrtoul(data, 10, &value);
if (ret < 0)
return ret;
if (value > 8) {
amd64_warn("%s: invalid word 0x%lx\n", __func__, value);
return -EINVAL;
}
pvt->injection.word = (u32) value;
return count;
}
static ssize_t inject_ecc_vector_show(struct device *dev,
struct device_attribute *mattr,
char *buf)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
return sprintf(buf, "0x%x\n", pvt->injection.bit_map);
}
/*
* store 16 bit error injection vector which enables injecting errors to the
* corresponding bit within the error injection word above. When used during a
* DRAM ECC read, it holds the contents of the of the DRAM ECC bits.
*/
static ssize_t inject_ecc_vector_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
int ret;
ret = kstrtoul(data, 16, &value);
if (ret < 0)
return ret;
if (value & 0xFFFF0000) {
amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value);
return -EINVAL;
}
pvt->injection.bit_map = (u32) value;
return count;
}
/*
* Do a DRAM ECC read. Assemble staged values in the pvt area, format into
* fields needed by the injection registers and read the NB Array Data Port.
*/
static ssize_t inject_read_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
u32 section, word_bits;
int ret;
ret = kstrtoul(data, 10, &value);
if (ret < 0)
return ret;
/* Form value to choose 16-byte section of cacheline */
section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section);
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section);
word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection);
/* Issue 'word' and 'bit' along with the READ request */
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits);
return count;
}
/*
* Do a DRAM ECC write. Assemble staged values in the pvt area and format into
* fields needed by the injection registers.
*/
static ssize_t inject_write_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
u32 section, word_bits, tmp;
unsigned long value;
int ret;
ret = kstrtoul(data, 10, &value);
if (ret < 0)
return ret;
/* Form value to choose 16-byte section of cacheline */
section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section);
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section);
word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection);
pr_notice_once("Don't forget to decrease MCE polling interval in\n"
"/sys/bus/machinecheck/devices/machinecheck<CPUNUM>/check_interval\n"
"so that you can get the error report faster.\n");
on_each_cpu(disable_caches, NULL, 1);
/* Issue 'word' and 'bit' along with the READ request */
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
retry:
/* wait until injection happens */
amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp);
if (tmp & F10_NB_ARR_ECC_WR_REQ) {
cpu_relax();
goto retry;
}
on_each_cpu(enable_caches, NULL, 1);
edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits);
return count;
}
/*
* update NUM_INJ_ATTRS in case you add new members
*/
static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
inject_section_show, inject_section_store);
static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR,
inject_word_show, inject_word_store);
static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR,
inject_ecc_vector_show, inject_ecc_vector_store);
static DEVICE_ATTR(inject_write, S_IWUSR,
NULL, inject_write_store);
static DEVICE_ATTR(inject_read, S_IWUSR,
NULL, inject_read_store);
static struct attribute *inj_attrs[] = {
&dev_attr_inject_section.attr,
&dev_attr_inject_word.attr,
&dev_attr_inject_ecc_vector.attr,
&dev_attr_inject_write.attr,
&dev_attr_inject_read.attr,
NULL
};
static umode_t inj_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
{
struct device *dev = kobj_to_dev(kobj);
struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
struct amd64_pvt *pvt = mci->pvt_info;
/* Families which have that injection hw */
if (pvt->fam >= 0x10 && pvt->fam <= 0x16)
return attr->mode;
return 0;
}
static const struct attribute_group inj_group = {
.attrs = inj_attrs,
.is_visible = inj_is_visible,
};
#endif /* CONFIG_EDAC_DEBUG */
/* /*
* Return the DramAddr that the SysAddr given by @sys_addr maps to. It is * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is
@ -593,8 +878,7 @@ static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
dram_base = get_dram_base(pvt, pvt->mc_node_id); dram_base = get_dram_base(pvt, pvt->mc_node_id);
ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, ret = get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size);
&hole_size);
if (!ret) { if (!ret) {
if ((sys_addr >= (1ULL << 32)) && if ((sys_addr >= (1ULL << 32)) &&
(sys_addr < ((1ULL << 32) + hole_size))) { (sys_addr < ((1ULL << 32) + hole_size))) {
@ -2665,7 +2949,7 @@ reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
if (pvt->umc) { if (pvt->umc) {
pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
if (!pvt->F0) { if (!pvt->F0) {
amd64_err("F0 not found, device 0x%x (broken BIOS?)\n", pci_id1); edac_dbg(1, "F0 not found, device 0x%x\n", pci_id1);
return -ENODEV; return -ENODEV;
} }
@ -2674,7 +2958,7 @@ reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
pci_dev_put(pvt->F0); pci_dev_put(pvt->F0);
pvt->F0 = NULL; pvt->F0 = NULL;
amd64_err("F6 not found: device 0x%x (broken BIOS?)\n", pci_id2); edac_dbg(1, "F6 not found: device 0x%x\n", pci_id2);
return -ENODEV; return -ENODEV;
} }
@ -2691,7 +2975,7 @@ reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
/* Reserve the ADDRESS MAP Device */ /* Reserve the ADDRESS MAP Device */
pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
if (!pvt->F1) { if (!pvt->F1) {
amd64_err("F1 not found: device 0x%x (broken BIOS?)\n", pci_id1); edac_dbg(1, "F1 not found: device 0x%x\n", pci_id1);
return -ENODEV; return -ENODEV;
} }
@ -2701,7 +2985,7 @@ reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
pci_dev_put(pvt->F1); pci_dev_put(pvt->F1);
pvt->F1 = NULL; pvt->F1 = NULL;
amd64_err("F2 not found: device 0x%x (broken BIOS?)\n", pci_id2); edac_dbg(1, "F2 not found: device 0x%x\n", pci_id2);
return -ENODEV; return -ENODEV;
} }
@ -3244,8 +3528,7 @@ static bool ecc_enabled(struct amd64_pvt *pvt)
MSR_IA32_MCG_CTL, nid); MSR_IA32_MCG_CTL, nid);
} }
amd64_info("Node %d: DRAM ECC %s.\n", edac_dbg(3, "Node %d: DRAM ECC %s.\n", nid, (ecc_en ? "enabled" : "disabled"));
nid, (ecc_en ? "enabled" : "disabled"));
if (!ecc_en || !nb_mce_en) if (!ecc_en || !nb_mce_en)
return false; return false;
@ -3342,10 +3625,13 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
fam_type = &family_types[F15_M60H_CPUS]; fam_type = &family_types[F15_M60H_CPUS];
pvt->ops = &family_types[F15_M60H_CPUS].ops; pvt->ops = &family_types[F15_M60H_CPUS].ops;
break; break;
/* Richland is only client */
} else if (pvt->model == 0x13) {
return NULL;
} else {
fam_type = &family_types[F15_CPUS];
pvt->ops = &family_types[F15_CPUS].ops;
} }
fam_type = &family_types[F15_CPUS];
pvt->ops = &family_types[F15_CPUS].ops;
break; break;
case 0x16: case 0x16:
@ -3402,20 +3688,13 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
return NULL; return NULL;
} }
amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
(pvt->fam == 0xf ?
(pvt->ext_model >= K8_REV_F ? "revF or later "
: "revE or earlier ")
: ""), pvt->mc_node_id);
return fam_type; return fam_type;
} }
static const struct attribute_group *amd64_edac_attr_groups[] = { static const struct attribute_group *amd64_edac_attr_groups[] = {
#ifdef CONFIG_EDAC_DEBUG #ifdef CONFIG_EDAC_DEBUG
&amd64_edac_dbg_group, &dbg_group,
#endif &inj_group,
#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
&amd64_edac_inj_group,
#endif #endif
NULL NULL
}; };
@ -3539,6 +3818,7 @@ static int probe_one_instance(unsigned int nid)
pvt->mc_node_id = nid; pvt->mc_node_id = nid;
pvt->F3 = F3; pvt->F3 = F3;
ret = -ENODEV;
fam_type = per_family_init(pvt); fam_type = per_family_init(pvt);
if (!fam_type) if (!fam_type)
goto err_enable; goto err_enable;
@ -3579,6 +3859,12 @@ static int probe_one_instance(unsigned int nid)
goto err_enable; goto err_enable;
} }
amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
(pvt->fam == 0xf ?
(pvt->ext_model >= K8_REV_F ? "revF or later "
: "revE or earlier ")
: ""), pvt->mc_node_id);
dump_misc_regs(pvt); dump_misc_regs(pvt);
return ret; return ret;

View File

@ -462,14 +462,6 @@ struct ecc_settings {
} flags; } flags;
}; };
#ifdef CONFIG_EDAC_DEBUG
extern const struct attribute_group amd64_edac_dbg_group;
#endif
#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
extern const struct attribute_group amd64_edac_inj_group;
#endif
/* /*
* Each of the PCI Device IDs types have their own set of hardware accessor * Each of the PCI Device IDs types have their own set of hardware accessor
* functions and per device encoding/decoding logic. * functions and per device encoding/decoding logic.
@ -501,9 +493,6 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
#define amd64_write_pci_cfg(pdev, offset, val) \ #define amd64_write_pci_cfg(pdev, offset, val) \
__amd64_write_pci_cfg_dword(pdev, offset, val, __func__) __amd64_write_pci_cfg_dword(pdev, offset, val, __func__)
int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
u64 *hole_offset, u64 *hole_size);
#define to_mci(k) container_of(k, struct mem_ctl_info, dev) #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
/* Injection helpers */ /* Injection helpers */

View File

@ -1,55 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include "amd64_edac.h"
#define EDAC_DCT_ATTR_SHOW(reg) \
static ssize_t amd64_##reg##_show(struct device *dev, \
struct device_attribute *mattr, \
char *data) \
{ \
struct mem_ctl_info *mci = to_mci(dev); \
struct amd64_pvt *pvt = mci->pvt_info; \
return sprintf(data, "0x%016llx\n", (u64)pvt->reg); \
}
EDAC_DCT_ATTR_SHOW(dhar);
EDAC_DCT_ATTR_SHOW(dbam0);
EDAC_DCT_ATTR_SHOW(top_mem);
EDAC_DCT_ATTR_SHOW(top_mem2);
static ssize_t amd64_hole_show(struct device *dev,
struct device_attribute *mattr,
char *data)
{
struct mem_ctl_info *mci = to_mci(dev);
u64 hole_base = 0;
u64 hole_offset = 0;
u64 hole_size = 0;
amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size);
return sprintf(data, "%llx %llx %llx\n", hole_base, hole_offset,
hole_size);
}
/*
* update NUM_DBG_ATTRS in case you add new members
*/
static DEVICE_ATTR(dhar, S_IRUGO, amd64_dhar_show, NULL);
static DEVICE_ATTR(dbam, S_IRUGO, amd64_dbam0_show, NULL);
static DEVICE_ATTR(topmem, S_IRUGO, amd64_top_mem_show, NULL);
static DEVICE_ATTR(topmem2, S_IRUGO, amd64_top_mem2_show, NULL);
static DEVICE_ATTR(dram_hole, S_IRUGO, amd64_hole_show, NULL);
static struct attribute *amd64_edac_dbg_attrs[] = {
&dev_attr_dhar.attr,
&dev_attr_dbam.attr,
&dev_attr_topmem.attr,
&dev_attr_topmem2.attr,
&dev_attr_dram_hole.attr,
NULL
};
const struct attribute_group amd64_edac_dbg_group = {
.attrs = amd64_edac_dbg_attrs,
};

View File

@ -1,235 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include "amd64_edac.h"
static ssize_t amd64_inject_section_show(struct device *dev,
struct device_attribute *mattr,
char *buf)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
return sprintf(buf, "0x%x\n", pvt->injection.section);
}
/*
* store error injection section value which refers to one of 4 16-byte sections
* within a 64-byte cacheline
*
* range: 0..3
*/
static ssize_t amd64_inject_section_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
int ret;
ret = kstrtoul(data, 10, &value);
if (ret < 0)
return ret;
if (value > 3) {
amd64_warn("%s: invalid section 0x%lx\n", __func__, value);
return -EINVAL;
}
pvt->injection.section = (u32) value;
return count;
}
static ssize_t amd64_inject_word_show(struct device *dev,
struct device_attribute *mattr,
char *buf)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
return sprintf(buf, "0x%x\n", pvt->injection.word);
}
/*
* store error injection word value which refers to one of 9 16-bit word of the
* 16-byte (128-bit + ECC bits) section
*
* range: 0..8
*/
static ssize_t amd64_inject_word_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
int ret;
ret = kstrtoul(data, 10, &value);
if (ret < 0)
return ret;
if (value > 8) {
amd64_warn("%s: invalid word 0x%lx\n", __func__, value);
return -EINVAL;
}
pvt->injection.word = (u32) value;
return count;
}
static ssize_t amd64_inject_ecc_vector_show(struct device *dev,
struct device_attribute *mattr,
char *buf)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
return sprintf(buf, "0x%x\n", pvt->injection.bit_map);
}
/*
* store 16 bit error injection vector which enables injecting errors to the
* corresponding bit within the error injection word above. When used during a
* DRAM ECC read, it holds the contents of the of the DRAM ECC bits.
*/
static ssize_t amd64_inject_ecc_vector_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
int ret;
ret = kstrtoul(data, 16, &value);
if (ret < 0)
return ret;
if (value & 0xFFFF0000) {
amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value);
return -EINVAL;
}
pvt->injection.bit_map = (u32) value;
return count;
}
/*
* Do a DRAM ECC read. Assemble staged values in the pvt area, format into
* fields needed by the injection registers and read the NB Array Data Port.
*/
static ssize_t amd64_inject_read_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
u32 section, word_bits;
int ret;
ret = kstrtoul(data, 10, &value);
if (ret < 0)
return ret;
/* Form value to choose 16-byte section of cacheline */
section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section);
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section);
word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection);
/* Issue 'word' and 'bit' along with the READ request */
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits);
return count;
}
/*
* Do a DRAM ECC write. Assemble staged values in the pvt area and format into
* fields needed by the injection registers.
*/
static ssize_t amd64_inject_write_store(struct device *dev,
struct device_attribute *mattr,
const char *data, size_t count)
{
struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
u32 section, word_bits, tmp;
unsigned long value;
int ret;
ret = kstrtoul(data, 10, &value);
if (ret < 0)
return ret;
/* Form value to choose 16-byte section of cacheline */
section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section);
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section);
word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection);
pr_notice_once("Don't forget to decrease MCE polling interval in\n"
"/sys/bus/machinecheck/devices/machinecheck<CPUNUM>/check_interval\n"
"so that you can get the error report faster.\n");
on_each_cpu(disable_caches, NULL, 1);
/* Issue 'word' and 'bit' along with the READ request */
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
retry:
/* wait until injection happens */
amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp);
if (tmp & F10_NB_ARR_ECC_WR_REQ) {
cpu_relax();
goto retry;
}
on_each_cpu(enable_caches, NULL, 1);
edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits);
return count;
}
/*
* update NUM_INJ_ATTRS in case you add new members
*/
static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
amd64_inject_section_show, amd64_inject_section_store);
static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR,
amd64_inject_word_show, amd64_inject_word_store);
static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR,
amd64_inject_ecc_vector_show, amd64_inject_ecc_vector_store);
static DEVICE_ATTR(inject_write, S_IWUSR,
NULL, amd64_inject_write_store);
static DEVICE_ATTR(inject_read, S_IWUSR,
NULL, amd64_inject_read_store);
static struct attribute *amd64_edac_inj_attrs[] = {
&dev_attr_inject_section.attr,
&dev_attr_inject_word.attr,
&dev_attr_inject_ecc_vector.attr,
&dev_attr_inject_write.attr,
&dev_attr_inject_read.attr,
NULL
};
static umode_t amd64_edac_inj_is_visible(struct kobject *kobj,
struct attribute *attr, int idx)
{
struct device *dev = kobj_to_dev(kobj);
struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
struct amd64_pvt *pvt = mci->pvt_info;
if (pvt->fam < 0x10)
return 0;
return attr->mode;
}
const struct attribute_group amd64_edac_inj_group = {
.attrs = amd64_edac_inj_attrs,
.is_visible = amd64_edac_inj_is_visible,
};

View File

@ -1058,7 +1058,7 @@ static int ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
/* Initialize strings */ /* Initialize strings */
mci->mod_name = PPC4XX_EDAC_MODULE_NAME; mci->mod_name = PPC4XX_EDAC_MODULE_NAME;
mci->ctl_name = ppc4xx_edac_match->compatible, mci->ctl_name = ppc4xx_edac_match->compatible;
mci->dev_name = np->full_name; mci->dev_name = np->full_name;
/* Initialize callbacks */ /* Initialize callbacks */

View File

@ -1916,7 +1916,7 @@ static int xgene_edac_probe(struct platform_device *pdev)
int i; int i;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
irq = platform_get_irq(pdev, i); irq = platform_get_irq_optional(pdev, i);
if (irq < 0) { if (irq < 0) {
dev_err(&pdev->dev, "No IRQ resource\n"); dev_err(&pdev->dev, "No IRQ resource\n");
rc = -EINVAL; rc = -EINVAL;