Commit 9ff3ca58 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull EDAC updates from Borislav Petkov:
 "A bunch of fixes all over the place and some hw enablement this time.

   - Convert EDAC to debugfs wrappers and make drivers use those
     (Borislav Petkov)

   - L3 and SoC support for xgene_edac (Loc Ho)

   - AMD F15h, models 0x60-6f support to amd64_edac (Aravind
     Gopalakrishnan)

   - Fixes and cleanups all over the place"

* tag 'edac_for_4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp: (22 commits)
  EDAC: Fix PAGES_TO_MiB macro misuse
  EDAC, altera: SoCFPGA EDAC should not look for ECC_CORR_EN
  EDAC: Use edac_debugfs_remove_recursive()
  EDAC, ppc4xx_edac: Fix module autoload for OF platform driver
  Documentation/EDAC: Add reference documents section for amd64_edac
  EDAC, amd64_edac: Update copyright and remove changelog
  EDAC, amd64_edac: Extend scrub rate support to F15hM60h
  EDAC: Don't allow empty DIMM labels
  EDAC: Fix sysfs dimm_label store operation
  EDAC: Fix sysfs dimm_label show operation
  arm64, EDAC: Add L3/SoC DT subnodes to the APM X-Gene SoC EDAC node
  EDAC, xgene: Add SoC support
  EDAC, xgene: Fix possible sprintf() overflow issue
  EDAC, xgene: Add L3 support
  EDAC, Documentation: Update X-Gene EDAC binding for L3/SoC subnodes
  EDAC, sb_edac: Fix TAD presence check for sbridge_mci_bind_devs()
  EDAC, ghes_edac: Remove redundant memory_type array
  EDAC, xgene: Convert to debugfs wrappers
  EDAC, i5100: Convert to debugfs wrappers
  EDAC, altera: Convert to debugfs wrappers
  ...
parents 17a13590 990995ba
...@@ -5,6 +5,8 @@ The follow error types are supported: ...@@ -5,6 +5,8 @@ The follow error types are supported:
memory controller - Memory controller memory controller - Memory controller
PMD (L1/L2) - Processor module unit (PMD) L1/L2 cache PMD (L1/L2) - Processor module unit (PMD) L1/L2 cache
L3 - L3 cache controller
SoC - SoC IP's such as Ethernet, SATA, and etc
The following section describes the EDAC DT node binding. The following section describes the EDAC DT node binding.
...@@ -30,6 +32,17 @@ Required properties for PMD subnode: ...@@ -30,6 +32,17 @@ Required properties for PMD subnode:
- reg : First resource shall be the PMD resource. - reg : First resource shall be the PMD resource.
- pmd-controller : Instance number of the PMD controller. - pmd-controller : Instance number of the PMD controller.
Required properties for L3 subnode:
- compatible : Shall be "apm,xgene-edac-l3" or
"apm,xgene-edac-l3-v2".
- reg : First resource shall be the L3 EDAC resource.
Required properties for SoC subnode:
- compatible : Shall be "apm,xgene-edac-soc-v1" for revision 1 or
"apm,xgene-edac-l3-soc" for general value reporting
only.
- reg : First resource shall be the SoC EDAC resource.
Example: Example:
csw: csw@7e200000 { csw: csw@7e200000 {
compatible = "apm,xgene-csw", "syscon"; compatible = "apm,xgene-csw", "syscon";
...@@ -76,4 +89,14 @@ Example: ...@@ -76,4 +89,14 @@ Example:
reg = <0x0 0x7c000000 0x0 0x200000>; reg = <0x0 0x7c000000 0x0 0x200000>;
pmd-controller = <0>; pmd-controller = <0>;
}; };
edacl3@7e600000 {
compatible = "apm,xgene-edac-l3";
reg = <0x0 0x7e600000 0x0 0x1000>;
};
edacsoc@7e930000 {
compatible = "apm,xgene-edac-soc-v1";
reg = <0x0 0x7e930000 0x0 0x1000>;
};
}; };
...@@ -744,6 +744,52 @@ exports one ...@@ -744,6 +744,52 @@ exports one
possible that some errors could be lost. With rdimm's, they display the possible that some errors could be lost. With rdimm's, they display the
contents of the registers contents of the registers
AMD64_EDAC REFERENCE DOCUMENTS USED
-----------------------------------
amd64_edac module is based on the following documents
(available from http://support.amd.com/en-us/search/tech-docs):
1. Title: BIOS and Kernel Developer's Guide for AMD Athlon 64 and AMD
Opteron Processors
AMD publication #: 26094
Revision: 3.26
Link: http://support.amd.com/TechDocs/26094.PDF
2. Title: BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh
Processors
AMD publication #: 32559
Revision: 3.00
Issue Date: May 2006
Link: http://support.amd.com/TechDocs/32559.pdf
3. Title: BIOS and Kernel Developer's Guide (BKDG) For AMD Family 10h
Processors
AMD publication #: 31116
Revision: 3.00
Issue Date: September 07, 2007
Link: http://support.amd.com/TechDocs/31116.pdf
4. Title: BIOS and Kernel Developer's Guide (BKDG) for AMD Family 15h
Models 30h-3Fh Processors
AMD publication #: 49125
Revision: 3.06
Issue Date: 2/12/2015 (latest release)
Link: http://support.amd.com/TechDocs/49125_15h_Models_30h-3Fh_BKDG.pdf
5. Title: BIOS and Kernel Developer's Guide (BKDG) for AMD Family 15h
Models 60h-6Fh Processors
AMD publication #: 50742
Revision: 3.01
Issue Date: 7/23/2015 (latest release)
Link: http://support.amd.com/TechDocs/50742_15h_Models_60h-6Fh_BKDG.pdf
6. Title: BIOS and Kernel Developer's Guide (BKDG) for AMD Family 16h
Models 00h-0Fh Processors
AMD publication #: 48751
Revision: 3.03
Issue Date: 2/23/2015 (latest release)
Link: http://support.amd.com/TechDocs/48751_16h_bkdg.pdf
CREDITS: CREDITS:
======== ========
......
...@@ -477,6 +477,16 @@ edacpmd@7c600000 { ...@@ -477,6 +477,16 @@ edacpmd@7c600000 {
reg = <0x0 0x7c600000 0x0 0x200000>; reg = <0x0 0x7c600000 0x0 0x200000>;
pmd-controller = <3>; pmd-controller = <3>;
}; };
edacl3@7e600000 {
compatible = "apm,xgene-edac-l3";
reg = <0x0 0x7e600000 0x0 0x1000>;
};
edacsoc@7e930000 {
compatible = "apm,xgene-edac-soc-v1";
reg = <0x0 0x7e930000 0x0 0x1000>;
};
}; };
pcie0: pcie@1f2b0000 { pcie0: pcie@1f2b0000 {
......
...@@ -12,6 +12,8 @@ obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o ...@@ -12,6 +12,8 @@ obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o
edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o
edac_core-y += edac_module.o edac_device_sysfs.o edac_core-y += edac_module.o edac_device_sysfs.o
edac_core-$(CONFIG_EDAC_DEBUG) += debugfs.o
ifdef CONFIG_PCI ifdef CONFIG_PCI
edac_core-y += edac_pci.o edac_pci_sysfs.o edac_core-y += edac_pci.o edac_pci_sysfs.o
endif endif
......
...@@ -51,11 +51,9 @@ static const struct altr_sdram_prv_data c5_data = { ...@@ -51,11 +51,9 @@ static const struct altr_sdram_prv_data c5_data = {
.ecc_irq_clr_mask = (CV_DRAMINTR_INTRCLR | CV_DRAMINTR_INTREN), .ecc_irq_clr_mask = (CV_DRAMINTR_INTRCLR | CV_DRAMINTR_INTREN),
.ecc_cnt_rst_offset = CV_DRAMINTR_OFST, .ecc_cnt_rst_offset = CV_DRAMINTR_OFST,
.ecc_cnt_rst_mask = CV_DRAMINTR_INTRCLR, .ecc_cnt_rst_mask = CV_DRAMINTR_INTRCLR,
#ifdef CONFIG_EDAC_DEBUG
.ce_ue_trgr_offset = CV_CTLCFG_OFST, .ce_ue_trgr_offset = CV_CTLCFG_OFST,
.ce_set_mask = CV_CTLCFG_GEN_SB_ERR, .ce_set_mask = CV_CTLCFG_GEN_SB_ERR,
.ue_set_mask = CV_CTLCFG_GEN_DB_ERR, .ue_set_mask = CV_CTLCFG_GEN_DB_ERR,
#endif
}; };
static const struct altr_sdram_prv_data a10_data = { static const struct altr_sdram_prv_data a10_data = {
...@@ -72,11 +70,9 @@ static const struct altr_sdram_prv_data a10_data = { ...@@ -72,11 +70,9 @@ static const struct altr_sdram_prv_data a10_data = {
.ecc_irq_clr_mask = (A10_INTSTAT_SBEERR | A10_INTSTAT_DBEERR), .ecc_irq_clr_mask = (A10_INTSTAT_SBEERR | A10_INTSTAT_DBEERR),
.ecc_cnt_rst_offset = A10_ECCCTRL1_OFST, .ecc_cnt_rst_offset = A10_ECCCTRL1_OFST,
.ecc_cnt_rst_mask = A10_ECC_CNT_RESET_MASK, .ecc_cnt_rst_mask = A10_ECC_CNT_RESET_MASK,
#ifdef CONFIG_EDAC_DEBUG
.ce_ue_trgr_offset = A10_DIAGINTTEST_OFST, .ce_ue_trgr_offset = A10_DIAGINTTEST_OFST,
.ce_set_mask = A10_DIAGINT_TSERRA_MASK, .ce_set_mask = A10_DIAGINT_TSERRA_MASK,
.ue_set_mask = A10_DIAGINT_TDERRA_MASK, .ue_set_mask = A10_DIAGINT_TDERRA_MASK,
#endif
}; };
static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id) static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id)
...@@ -116,7 +112,6 @@ static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id) ...@@ -116,7 +112,6 @@ static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id)
return IRQ_NONE; return IRQ_NONE;
} }
#ifdef CONFIG_EDAC_DEBUG
static ssize_t altr_sdr_mc_err_inject_write(struct file *file, static ssize_t altr_sdr_mc_err_inject_write(struct file *file,
const char __user *data, const char __user *data,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
...@@ -191,14 +186,15 @@ static const struct file_operations altr_sdr_mc_debug_inject_fops = { ...@@ -191,14 +186,15 @@ static const struct file_operations altr_sdr_mc_debug_inject_fops = {
static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci) static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
{ {
if (mci->debugfs) if (!IS_ENABLED(CONFIG_EDAC_DEBUG))
debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci, return;
if (!mci->debugfs)
return;
edac_debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
&altr_sdr_mc_debug_inject_fops); &altr_sdr_mc_debug_inject_fops);
} }
#else
static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
{}
#endif
/* Get total memory size from Open Firmware DTB */ /* Get total memory size from Open Firmware DTB */
static unsigned long get_total_mem(void) static unsigned long get_total_mem(void)
......
...@@ -30,8 +30,7 @@ ...@@ -30,8 +30,7 @@
#define CV_CTLCFG_GEN_SB_ERR 0x2000 #define CV_CTLCFG_GEN_SB_ERR 0x2000
#define CV_CTLCFG_GEN_DB_ERR 0x4000 #define CV_CTLCFG_GEN_DB_ERR 0x4000
#define CV_CTLCFG_ECC_AUTO_EN (CV_CTLCFG_ECC_EN | \ #define CV_CTLCFG_ECC_AUTO_EN (CV_CTLCFG_ECC_EN)
CV_CTLCFG_ECC_CORR_EN)
/* SDRAM Controller Address Width Register */ /* SDRAM Controller Address Width Register */
#define CV_DRAMADDRW_OFST 0x2C #define CV_DRAMADDRW_OFST 0x2C
...@@ -181,13 +180,11 @@ struct altr_sdram_prv_data { ...@@ -181,13 +180,11 @@ struct altr_sdram_prv_data {
int ecc_irq_clr_mask; int ecc_irq_clr_mask;
int ecc_cnt_rst_offset; int ecc_cnt_rst_offset;
int ecc_cnt_rst_mask; int ecc_cnt_rst_mask;
#ifdef CONFIG_EDAC_DEBUG
struct edac_dev_sysfs_attribute *eccmgr_sysfs_attr; struct edac_dev_sysfs_attribute *eccmgr_sysfs_attr;
int ecc_enable_mask; int ecc_enable_mask;
int ce_set_mask; int ce_set_mask;
int ue_set_mask; int ue_set_mask;
int ce_ue_trgr_offset; int ce_ue_trgr_offset;
#endif
}; };
/* Altera SDRAM Memory Controller data */ /* Altera SDRAM Memory Controller data */
......
...@@ -173,7 +173,7 @@ static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct, ...@@ -173,7 +173,7 @@ static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
* scan the scrub rate mapping table for a close or matching bandwidth value to * scan the scrub rate mapping table for a close or matching bandwidth value to
* issue. If requested is too big, then use last maximum value found. * issue. If requested is too big, then use last maximum value found.
*/ */
static int __set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate) static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
{ {
u32 scrubval; u32 scrubval;
int i; int i;
...@@ -201,7 +201,14 @@ static int __set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate) ...@@ -201,7 +201,14 @@ static int __set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate)
scrubval = scrubrates[i].scrubval; scrubval = scrubrates[i].scrubval;
pci_write_bits32(ctl, SCRCTRL, scrubval, 0x001F); if (pvt->fam == 0x15 && pvt->model == 0x60) {
f15h_select_dct(pvt, 0);
pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
f15h_select_dct(pvt, 1);
pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
} else {
pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F);
}
if (scrubval) if (scrubval)
return scrubrates[i].bandwidth; return scrubrates[i].bandwidth;
...@@ -217,11 +224,15 @@ static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw) ...@@ -217,11 +224,15 @@ static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
if (pvt->fam == 0xf) if (pvt->fam == 0xf)
min_scrubrate = 0x0; min_scrubrate = 0x0;
if (pvt->fam == 0x15) {
/* Erratum #505 */ /* Erratum #505 */
if (pvt->fam == 0x15 && pvt->model < 0x10) if (pvt->model < 0x10)
f15h_select_dct(pvt, 0); f15h_select_dct(pvt, 0);
return __set_scrub_rate(pvt->F3, bw, min_scrubrate); if (pvt->model == 0x60)
min_scrubrate = 0x6;
}
return __set_scrub_rate(pvt, bw, min_scrubrate);
} }
static int get_scrub_rate(struct mem_ctl_info *mci) static int get_scrub_rate(struct mem_ctl_info *mci)
...@@ -230,10 +241,14 @@ static int get_scrub_rate(struct mem_ctl_info *mci) ...@@ -230,10 +241,14 @@ static int get_scrub_rate(struct mem_ctl_info *mci)
u32 scrubval = 0; u32 scrubval = 0;
int i, retval = -EINVAL; int i, retval = -EINVAL;
if (pvt->fam == 0x15) {
/* Erratum #505 */ /* Erratum #505 */
if (pvt->fam == 0x15 && pvt->model < 0x10) if (pvt->model < 0x10)
f15h_select_dct(pvt, 0); f15h_select_dct(pvt, 0);
if (pvt->model == 0x60)
amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
} else
amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
scrubval = scrubval & 0x001F; scrubval = scrubval & 0x001F;
......
...@@ -2,64 +2,10 @@ ...@@ -2,64 +2,10 @@
* AMD64 class Memory Controller kernel module * AMD64 class Memory Controller kernel module
* *
* Copyright (c) 2009 SoftwareBitMaker. * Copyright (c) 2009 SoftwareBitMaker.
* Copyright (c) 2009 Advanced Micro Devices, Inc. * Copyright (c) 2009-15 Advanced Micro Devices, Inc.
* *
* This file may be distributed under the terms of the * This file may be distributed under the terms of the
* GNU General Public License. * GNU General Public License.
*
* Originally Written by Thayne Harbaugh
*
* Changes by Douglas "norsk" Thompson <dougthompson@xmission.com>:
* - K8 CPU Revision D and greater support
*
* Changes by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>:
* - Module largely rewritten, with new (and hopefully correct)
* code for dealing with node and chip select interleaving,
* various code cleanup, and bug fixes
* - Added support for memory hoisting using DRAM hole address
* register
*
* Changes by Douglas "norsk" Thompson <dougthompson@xmission.com>:
* -K8 Rev (1207) revision support added, required Revision
* specific mini-driver code to support Rev F as well as
* prior revisions
*
* Changes by Douglas "norsk" Thompson <dougthompson@xmission.com>:
* -Family 10h revision support added. New PCI Device IDs,
* indicating new changes. Actual registers modified
* were slight, less than the Rev E to Rev F transition
* but changing the PCI Device ID was the proper thing to
* do, as it provides for almost automactic family
* detection. The mods to Rev F required more family
* information detection.
*
* Changes/Fixes by Borislav Petkov <bp@alien8.de>:
* - misc fixes and code cleanups
*
* This module is based on the following documents
* (available from http://www.amd.com/):
*
* Title: BIOS and Kernel Developer's Guide for AMD Athlon 64 and AMD
* Opteron Processors
* AMD publication #: 26094
*` Revision: 3.26
*
* Title: BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh
* Processors
* AMD publication #: 32559
* Revision: 3.00
* Issue Date: May 2006
*
* Title: BIOS and Kernel Developer's Guide (BKDG) For AMD Family 10h
* Processors
* AMD publication #: 31116
* Revision: 3.00
* Issue Date: September 07, 2007
*
* Sections in the first 2 documents are no longer in sync with each other.
* The Family 10h BKDG was totally re-written from scratch with a new
* presentation model.
* Therefore, comments that refer to a Document section might be off.
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -255,6 +201,8 @@ ...@@ -255,6 +201,8 @@
#define DCT_SEL_HI 0x114 #define DCT_SEL_HI 0x114
#define F15H_M60H_SCRCTRL 0x1C8
/* /*
* Function 3 - Misc Control * Function 3 - Misc Control
*/ */
......
#include "edac_module.h"
static struct dentry *edac_debugfs;
static ssize_t edac_fake_inject_write(struct file *file,
const char __user *data,
size_t count, loff_t *ppos)
{
struct device *dev = file->private_data;
struct mem_ctl_info *mci = to_mci(dev);
static enum hw_event_mc_err_type type;
u16 errcount = mci->fake_inject_count;
if (!errcount)
errcount = 1;
type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
: HW_EVENT_ERR_CORRECTED;
printk(KERN_DEBUG
"Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
errcount,
(type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
errcount > 1 ? "s" : "",
mci->fake_inject_layer[0],
mci->fake_inject_layer[1],
mci->fake_inject_layer[2]
);
edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
mci->fake_inject_layer[0],
mci->fake_inject_layer[1],
mci->fake_inject_layer[2],
"FAKE ERROR", "for EDAC testing only");
return count;
}
static const struct file_operations debug_fake_inject_fops = {
.open = simple_open,
.write = edac_fake_inject_write,
.llseek = generic_file_llseek,
};
int __init edac_debugfs_init(void)
{
edac_debugfs = debugfs_create_dir("edac", NULL);
if (IS_ERR(edac_debugfs)) {
edac_debugfs = NULL;
return -ENOMEM;
}
return 0;
}
void edac_debugfs_exit(void)
{
debugfs_remove(edac_debugfs);
}
int edac_create_debugfs_nodes(struct mem_ctl_info *mci)
{
struct dentry *d, *parent;
char name[80];
int i;
if (!edac_debugfs)
return -ENODEV;
d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
if (!d)
return -ENOMEM;
parent = d;
for (i = 0; i < mci->n_layers; i++) {
sprintf(name, "fake_inject_%s",
edac_layer_name[mci->layers[i].type]);
d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_layer[i]);
if (!d)
goto nomem;
}
d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_ue);
if (!d)
goto nomem;
d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_count);
if (!d)
goto nomem;
d = debugfs_create_file("fake_inject", S_IWUSR, parent,
&mci->dev,
&debug_fake_inject_fops);
if (!d)
goto nomem;
mci->debugfs = parent;
return 0;
nomem:
edac_debugfs_remove_recursive(mci->debugfs);
return -ENOMEM;
}
/* Create a toplevel dir under EDAC's debugfs hierarchy */
struct dentry *edac_debugfs_create_dir(const char *dirname)
{
if (!edac_debugfs)
return NULL;
return debugfs_create_dir(dirname, edac_debugfs);
}
EXPORT_SYMBOL_GPL(edac_debugfs_create_dir);
/* Create a toplevel dir under EDAC's debugfs hierarchy with parent @parent */
struct dentry *
edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent)
{
return debugfs_create_dir(dirname, parent);
}
EXPORT_SYMBOL_GPL(edac_debugfs_create_dir_at);
/*
* Create a file under EDAC's hierarchy or a sub-hierarchy:
*
* @name: file name
* @mode: file permissions
* @parent: parent dentry. If NULL, it becomes the toplevel EDAC dir
* @data: private data of caller
* @fops: file operations of this file
*/
struct dentry *
edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent,
void *data, const struct file_operations *fops)
{
if (!parent)
parent = edac_debugfs;
return debugfs_create_file(name, mode, parent, data, fops);
}
EXPORT_SYMBOL_GPL(edac_debugfs_create_file);
/* Wrapper for debugfs_create_x8() */
struct dentry *edac_debugfs_create_x8(const char *name, umode_t mode,
struct dentry *parent, u8 *value)
{
if (!parent)
parent = edac_debugfs;
return debugfs_create_x8(name, mode, parent, value);
}
EXPORT_SYMBOL_GPL(edac_debugfs_create_x8);
/* Wrapper for debugfs_create_x16() */
struct dentry *edac_debugfs_create_x16(const char *name, umode_t mode,
struct dentry *parent, u16 *value)
{
if (!parent)
parent = edac_debugfs;
return debugfs_create_x16(name, mode, parent, value);
}
EXPORT_SYMBOL_GPL(edac_debugfs_create_x16);
...@@ -94,6 +94,8 @@ do { \ ...@@ -94,6 +94,8 @@ do { \
#define edac_dev_name(dev) (dev)->dev_name #define edac_dev_name(dev) (dev)->dev_name
#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
/* /*
* The following are the structures to provide for a generic * The following are the structures to provide for a generic
* or abstract 'edac_device'. This set of structures and the * or abstract 'edac_device'. This set of structures and the
......
...@@ -1302,7 +1302,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, ...@@ -1302,7 +1302,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
grain_bits = fls_long(e->grain) + 1; grain_bits = fls_long(e->grain) + 1;
trace_mc_event(type, e->msg, e->label, e->error_count, trace_mc_event(type, e->msg, e->label, e->error_count,
mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer, mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer,
PAGES_TO_MiB(e->page_frame_number) | e->offset_in_page, (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page,
grain_bits, e->syndrome, e->other_detail); grain_bits, e->syndrome, e->other_detail);
edac_raw_mc_handle_error(type, mci, e); edac_raw_mc_handle_error(type, mci, e);
......
...@@ -229,7 +229,7 @@ static ssize_t channel_dimm_label_show(struct device *dev, ...@@ -229,7 +229,7 @@ static ssize_t channel_dimm_label_show(struct device *dev,
if (!rank->dimm->label[0]) if (!rank->dimm->label[0])
return 0; return 0;
return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", return snprintf(data, sizeof(rank->dimm->label) + 1, "%s\n",
rank->dimm->label); rank->dimm->label);
} }
...@@ -240,14 +240,21 @@ static ssize_t channel_dimm_label_store(struct device *dev, ...@@ -240,14 +240,21 @@ static ssize_t channel_dimm_label_store(struct device *dev,
struct csrow_info *csrow = to_csrow(dev); struct csrow_info *csrow = to_csrow(dev);
unsigned chan = to_channel(mattr); unsigned chan = to_channel(mattr);
struct rank_info *rank = csrow->channels[chan]; struct rank_info *rank = csrow->channels[chan];
size_t copy_count = count;
ssize_t max_size = 0; if (count == 0)
return -EINVAL;
if (data[count - 1] == '\0' || data[count - 1] == '\n')
copy_count -= 1;
if (copy_count == 0 || copy_count >= sizeof(rank->dimm->label))
return -EINVAL;
max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); strncpy(rank->dimm->label, data, copy_count);
strncpy(rank->dimm->label, data, max_size); rank->dimm->label[copy_count] = '\0';
rank->dimm->label[max_size] = '\0';
return max_size; return count;
} }
/* show function for dynamic chX_ce_count attribute */ /* show function for dynamic chX_ce_count attribute */
...@@ -485,7 +492,7 @@ static ssize_t dimmdev_label_show(struct device *dev, ...@@ -485,7 +492,7 @@ static ssize_t dimmdev_label_show(struct device *dev,
if (!dimm->label[0]) if (!dimm->label[0])
return 0; return 0;
return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label); return snprintf(data, sizeof(dimm->label) + 1, "%s\n", dimm->label);
} }
static ssize_t dimmdev_label_store(struct device *dev, static ssize_t dimmdev_label_store(struct device *dev,
...@@ -494,14 +501,21 @@ static ssize_t dimmdev_label_store(struct device *dev, ...@@ -494,14 +501,21 @@ static ssize_t dimmdev_label_store(struct device *dev,
size_t count) size_t count)
{ {
struct dimm_info *dimm = to_dimm(dev); struct dimm_info *dimm = to_dimm(dev);
size_t copy_count = count;
ssize_t max_size = 0; if (count == 0)
return -EINVAL;
max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); if (data[count - 1] == '\0' || data[count - 1] == '\n')
strncpy(dimm->label, data, max_size); copy_count -= 1;
dimm->label[max_size] = '\0';
return max_size; if (copy_count == 0 || copy_count >= sizeof(dimm->label))
return -EINVAL;
strncpy(dimm->label, data, copy_count);
dimm->label[copy_count] = '\0';
return count;
} }
static ssize_t dimmdev_size_show(struct device *dev, static ssize_t dimmdev_size_show(struct device *dev,
...@@ -785,47 +799,6 @@ static ssize_t mci_max_location_show(struct device *dev, ...@@ -785,47 +799,6 @@ static ssize_t mci_max_location_show(struct device *dev,
return p - data; return p - data;
} }
#ifdef CONFIG_EDAC_DEBUG
static ssize_t edac_fake_inject_write(struct file *file,
const char __user *data,
size_t count, loff_t *ppos)
{
struct device *dev = file->private_data;
struct mem_ctl_info *mci = to_mci(dev);
static enum hw_event_mc_err_type type;
u16 errcount = mci->fake_inject_count;
if (!errcount)
errcount = 1;
type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
: HW_EVENT_ERR_CORRECTED;
printk(KERN_DEBUG
"Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
errcount,
(type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
errcount > 1 ? "s" : "",
mci->fake_inject_layer[0],
mci->fake_inject_layer[1],
mci->fake_inject_layer[2]
);
edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
mci->fake_inject_layer[0],
mci->fake_inject_layer[1],
mci->fake_inject_layer[2],
"FAKE ERROR", "for EDAC testing only");
return count;
}
static const struct file_operations debug_fake_inject_fops = {
.open = simple_open,
.write = edac_fake_inject_write,
.llseek = generic_file_llseek,
};
#endif
/* default Control file */ /* default Control file */
static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
...@@ -896,71 +869,6 @@ static struct device_type mci_attr_type = { ...@@ -896,71 +869,6 @@ static struct device_type mci_attr_type = {
.release = mci_attr_release, .release = mci_attr_release,
}; };
#ifdef CONFIG_EDAC_DEBUG
static struct dentry *edac_debugfs;
int __init edac_debugfs_init(void)
{
edac_debugfs = debugfs_create_dir("edac", NULL);
if (IS_ERR(edac_debugfs)) {
edac_debugfs = NULL;
return -ENOMEM;
}
return 0;
}
void edac_debugfs_exit(void)
{
debugfs_remove(edac_debugfs);
}
static int edac_create_debug_nodes(struct mem_ctl_info *mci)
{
struct dentry *d, *parent;
char name[80];
int i;
if (!edac_debugfs)
return -ENODEV;
d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
if (!d)
return -ENOMEM;
parent = d;
for (i = 0; i < mci->n_layers; i++) {
sprintf(name, "fake_inject_%s",
edac_layer_name[mci->layers[i].type]);
d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_layer[i]);
if (!d)
goto nomem;
}
d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_ue);
if (!d)
goto nomem;
d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_count);
if (!d)
goto nomem;
d = debugfs_create_file("fake_inject", S_IWUSR, parent,
&mci->dev,
&debug_fake_inject_fops);
if (!d)
goto nomem;
mci->debugfs = parent;
return 0;
nomem:
debugfs_remove(mci->debugfs);
return -ENOMEM;
}
#endif
/* /*
* Create a new Memory Controller kobject instance, * Create a new Memory Controller kobject instance,
* mc<id> under the 'mc' directory * mc<id> under the 'mc' directory
...@@ -1039,9 +947,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, ...@@ -1039,9 +947,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
goto fail_unregister_dimm; goto fail_unregister_dimm;
#endif #endif
#ifdef CONFIG_EDAC_DEBUG edac_create_debugfs_nodes(mci);
edac_create_debug_nodes(mci);
#endif
return 0; return 0;
fail_unregister_dimm: fail_unregister_dimm:
...@@ -1070,7 +976,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) ...@@ -1070,7 +976,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
edac_dbg(0, "\n"); edac_dbg(0, "\n");
#ifdef CONFIG_EDAC_DEBUG #ifdef CONFIG_EDAC_DEBUG
debugfs_remove(mci->debugfs); edac_debugfs_remove_recursive(mci->debugfs);
#endif #endif
#ifdef CONFIG_EDAC_LEGACY_SYSFS #ifdef CONFIG_EDAC_LEGACY_SYSFS
edac_delete_csrow_objects(mci); edac_delete_csrow_objects(mci);
......
...@@ -60,15 +60,39 @@ extern void *edac_align_ptr(void **p, unsigned size, int n_elems); ...@@ -60,15 +60,39 @@ extern void *edac_align_ptr(void **p, unsigned size, int n_elems);
/* /*
* EDAC debugfs functions * EDAC debugfs functions
*/ */
#define edac_debugfs_remove_recursive debugfs_remove_recursive
#define edac_debugfs_remove debugfs_remove
#ifdef CONFIG_EDAC_DEBUG #ifdef CONFIG_EDAC_DEBUG
int edac_debugfs_init(void); int edac_debugfs_init(void);
void edac_debugfs_exit(void); void edac_debugfs_exit(void);
int edac_create_debugfs_nodes(struct mem_ctl_info *mci);
struct dentry *edac_debugfs_create_dir(const char *dirname);
struct dentry *
edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent);
struct dentry *
edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent,
void *data, const struct file_operations *fops);
struct dentry *
edac_debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, u8 *value);
struct dentry *
edac_debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value);
#else #else
static inline int edac_debugfs_init(void) static inline int edac_debugfs_init(void) { return -ENODEV; }
{ static inline void edac_debugfs_exit(void) { }
return -ENODEV; static inline int edac_create_debugfs_nodes(struct mem_ctl_info *mci) { return 0; }
} static inline struct dentry *edac_debugfs_create_dir(const char *dirname) { return NULL; }
static inline void edac_debugfs_exit(void) {} static inline struct dentry *
edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent) { return NULL; }
static inline struct dentry *
edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent,
void *data, const struct file_operations *fops) { return NULL; }
static inline struct dentry *
edac_debugfs_create_x8(const char *name, umode_t mode,
struct dentry *parent, u8 *value) { return NULL; }
static inline struct dentry *
edac_debugfs_create_x16(const char *name, umode_t mode,
struct dentry *parent, u16 *value) { return NULL; }
#endif #endif
/* /*
......
...@@ -66,26 +66,6 @@ struct ghes_edac_dimm_fill { ...@@ -66,26 +66,6 @@ struct ghes_edac_dimm_fill {
unsigned count; unsigned count;
}; };
char *memory_type[] = {
[MEM_EMPTY] = "EMPTY",
[MEM_RESERVED] = "RESERVED",
[MEM_UNKNOWN] = "UNKNOWN",
[MEM_FPM] = "FPM",
[MEM_EDO] = "EDO",
[MEM_BEDO] = "BEDO",
[MEM_SDR] = "SDR",
[MEM_RDR] = "RDR",
[MEM_DDR] = "DDR",
[MEM_RDDR] = "RDDR",
[MEM_RMBS] = "RMBS",
[MEM_DDR2] = "DDR2",
[MEM_FB_DDR2] = "FB_DDR2",
[MEM_RDDR2] = "RDDR2",
[MEM_XDR] = "XDR",
[MEM_DDR3] = "DDR3",
[MEM_RDDR3] = "RDDR3",
};
static void ghes_edac_count_dimms(const struct dmi_header *dh, void *arg) static void ghes_edac_count_dimms(const struct dmi_header *dh, void *arg)
{ {
int *num_dimm = arg; int *num_dimm = arg;
...@@ -173,7 +153,7 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg) ...@@ -173,7 +153,7 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
if (dimm->nr_pages) { if (dimm->nr_pages) {
edac_dbg(1, "DIMM%i: %s size = %d MB%s\n", edac_dbg(1, "DIMM%i: %s size = %d MB%s\n",
dimm_fill->count, memory_type[dimm->mtype], dimm_fill->count, edac_mem_types[dimm->mtype],
PAGES_TO_MiB(dimm->nr_pages), PAGES_TO_MiB(dimm->nr_pages),
(dimm->edac_mode != EDAC_NONE) ? "(ECC)" : ""); (dimm->edac_mode != EDAC_NONE) ? "(ECC)" : "");
edac_dbg(2, "\ttype %d, detail 0x%02x, width %d(total %d)\n", edac_dbg(2, "\ttype %d, detail 0x%02x, width %d(total %d)\n",
...@@ -417,7 +397,7 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev, ...@@ -417,7 +397,7 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
"APEI location: %s %s", e->location, e->other_detail); "APEI location: %s %s", e->location, e->other_detail);
trace_mc_event(type, e->msg, e->label, e->error_count, trace_mc_event(type, e->msg, e->label, e->error_count,
mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer, mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer,
PAGES_TO_MiB(e->page_frame_number) | 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 */ /* Report the error via EDAC API */
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include "edac_core.h" #include "edac_core.h"
#include "edac_module.h"
/* register addresses */ /* register addresses */
...@@ -966,24 +967,24 @@ static int i5100_setup_debugfs(struct mem_ctl_info *mci) ...@@ -966,24 +967,24 @@ static int i5100_setup_debugfs(struct mem_ctl_info *mci)
if (!i5100_debugfs) if (!i5100_debugfs)
return -ENODEV; return -ENODEV;
priv->debugfs = debugfs_create_dir(mci->bus->name, i5100_debugfs); priv->debugfs = edac_debugfs_create_dir_at(mci->bus->name, i5100_debugfs);
if (!priv->debugfs) if (!priv->debugfs)
return -ENOMEM; return -ENOMEM;
debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs, edac_debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs,
&priv->inject_channel); &priv->inject_channel);
debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs, edac_debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs,
&priv->inject_hlinesel); &priv->inject_hlinesel);
debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs, edac_debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs,
&priv->inject_deviceptr1); &priv->inject_deviceptr1);
debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs, edac_debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs,
&priv->inject_deviceptr2); &priv->inject_deviceptr2);
debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs, edac_debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs,
&priv->inject_eccmask1); &priv->inject_eccmask1);
debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs, edac_debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs,
&priv->inject_eccmask2); &priv->inject_eccmask2);
debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs, edac_debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs,
&mci->dev, &i5100_inject_enable_fops); &mci->dev, &i5100_inject_enable_fops);
return 0; return 0;
...@@ -1189,7 +1190,7 @@ static void i5100_remove_one(struct pci_dev *pdev) ...@@ -1189,7 +1190,7 @@ static void i5100_remove_one(struct pci_dev *pdev)
priv = mci->pvt_info; priv = mci->pvt_info;
debugfs_remove_recursive(priv->debugfs); edac_debugfs_remove_recursive(priv->debugfs);
priv->scrub_enable = 0; priv->scrub_enable = 0;
cancel_delayed_work_sync(&(priv->i5100_scrubbing)); cancel_delayed_work_sync(&(priv->i5100_scrubbing));
...@@ -1223,7 +1224,7 @@ static int __init i5100_init(void) ...@@ -1223,7 +1224,7 @@ static int __init i5100_init(void)
{ {
int pci_rc; int pci_rc;
i5100_debugfs = debugfs_create_dir("i5100_edac", NULL); i5100_debugfs = edac_debugfs_create_dir_at("i5100_edac", NULL);
pci_rc = pci_register_driver(&i5100_driver); pci_rc = pci_register_driver(&i5100_driver);
return (pci_rc < 0) ? pci_rc : 0; return (pci_rc < 0) ? pci_rc : 0;
...@@ -1231,7 +1232,7 @@ static int __init i5100_init(void) ...@@ -1231,7 +1232,7 @@ static int __init i5100_init(void)
static void __exit i5100_exit(void) static void __exit i5100_exit(void)
{ {
debugfs_remove(i5100_debugfs); edac_debugfs_remove(i5100_debugfs);
pci_unregister_driver(&i5100_driver); pci_unregister_driver(&i5100_driver);
} }
......
...@@ -199,6 +199,7 @@ static const struct of_device_id ppc4xx_edac_match[] = { ...@@ -199,6 +199,7 @@ static const struct of_device_id ppc4xx_edac_match[] = {
}, },
{ } { }
}; };
MODULE_DEVICE_TABLE(of, ppc4xx_edac_match);
static struct platform_driver ppc4xx_edac_driver = { static struct platform_driver ppc4xx_edac_driver = {
.probe = ppc4xx_edac_probe, .probe = ppc4xx_edac_probe,
......
...@@ -1688,6 +1688,7 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci, ...@@ -1688,6 +1688,7 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
{ {
struct sbridge_pvt *pvt = mci->pvt_info; struct sbridge_pvt *pvt = mci->pvt_info;
struct pci_dev *pdev; struct pci_dev *pdev;
u8 saw_chan_mask = 0;
int i; int i;
for (i = 0; i < sbridge_dev->n_devs; i++) { for (i = 0; i < sbridge_dev->n_devs; i++) {
...@@ -1721,6 +1722,7 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci, ...@@ -1721,6 +1722,7 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
{ {
int id = pdev->device - PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0; int id = pdev->device - PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0;
pvt->pci_tad[id] = pdev; pvt->pci_tad[id] = pdev;
saw_chan_mask |= 1 << id;
} }
break; break;
case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO: case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO:
...@@ -1741,10 +1743,8 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci, ...@@ -1741,10 +1743,8 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
!pvt-> pci_tad || !pvt->pci_ras || !pvt->pci_ta) !pvt-> pci_tad || !pvt->pci_ras || !pvt->pci_ta)
goto enodev; goto enodev;
for (i = 0; i < NUM_CHANNELS; i++) { if (saw_chan_mask != 0x0f)
if (!pvt->pci_tad[i])
goto enodev; goto enodev;
}
return 0; return 0;
enodev: enodev:
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include "edac_core.h" #include "edac_core.h"
#include "edac_module.h"
#define EDAC_MOD_STR "xgene_edac" #define EDAC_MOD_STR "xgene_edac"
...@@ -66,6 +67,8 @@ struct xgene_edac { ...@@ -66,6 +67,8 @@ struct xgene_edac {
struct list_head mcus; struct list_head mcus;
struct list_head pmds; struct list_head pmds;
struct list_head l3s;
struct list_head socs;
struct mutex mc_lock; struct mutex mc_lock;
int mc_active_mask; int mc_active_mask;
...@@ -172,12 +175,12 @@ static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci) ...@@ -172,12 +175,12 @@ static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
{ {
if (!IS_ENABLED(CONFIG_EDAC_DEBUG)) if (!IS_ENABLED(CONFIG_EDAC_DEBUG))
return; return;
#ifdef CONFIG_EDAC_DEBUG
if (!mci->debugfs) if (!mci->debugfs)
return; return;
debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
edac_debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
&xgene_edac_mc_debug_inject_fops); &xgene_edac_mc_debug_inject_fops);
#endif
} }
static void xgene_edac_mc_check(struct mem_ctl_info *mci) static void xgene_edac_mc_check(struct mem_ctl_info *mci)
...@@ -536,7 +539,8 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev, ...@@ -536,7 +539,8 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
pg_f = ctx->pmd_csr + cpu_idx * CPU_CSR_STRIDE + CPU_MEMERR_CPU_PAGE; pg_f = ctx->pmd_csr + cpu_idx * CPU_CSR_STRIDE + CPU_MEMERR_CPU_PAGE;
val = readl(pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET); val = readl(pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
if (val) { if (!val)
goto chk_lsu;
dev_err(edac_dev->dev, dev_err(edac_dev->dev,
"CPU%d L1 memory error ICF 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n", "CPU%d L1 memory error ICF 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val, ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
...@@ -544,8 +548,7 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev, ...@@ -544,8 +548,7 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
MEMERR_CPU_ICFESR_ERRINDEX_RD(val), MEMERR_CPU_ICFESR_ERRINDEX_RD(val),
MEMERR_CPU_ICFESR_ERRINFO_RD(val)); MEMERR_CPU_ICFESR_ERRINFO_RD(val));
if (val & MEMERR_CPU_ICFESR_CERR_MASK) if (val & MEMERR_CPU_ICFESR_CERR_MASK)
dev_err(edac_dev->dev, dev_err(edac_dev->dev, "One or more correctable error\n");
"One or more correctable error\n");
if (val & MEMERR_CPU_ICFESR_MULTCERR_MASK) if (val & MEMERR_CPU_ICFESR_MULTCERR_MASK)
dev_err(edac_dev->dev, "Multiple correctable error\n"); dev_err(edac_dev->dev, "Multiple correctable error\n");
switch (MEMERR_CPU_ICFESR_ERRTYPE_RD(val)) { switch (MEMERR_CPU_ICFESR_ERRTYPE_RD(val)) {
...@@ -572,12 +575,12 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev, ...@@ -572,12 +575,12 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
if (val & (MEMERR_CPU_ICFESR_CERR_MASK | if (val & (MEMERR_CPU_ICFESR_CERR_MASK |
MEMERR_CPU_ICFESR_MULTCERR_MASK)) MEMERR_CPU_ICFESR_MULTCERR_MASK))
edac_device_handle_ce(edac_dev, 0, 0, edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
edac_dev->ctl_name);
}
chk_lsu:
val = readl(pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET); val = readl(pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
if (val) { if (!val)
goto chk_mmu;
dev_err(edac_dev->dev, dev_err(edac_dev->dev,
"CPU%d memory error LSU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n", "CPU%d memory error LSU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val, ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
...@@ -585,8 +588,7 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev, ...@@ -585,8 +588,7 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
MEMERR_CPU_LSUESR_ERRINDEX_RD(val), MEMERR_CPU_LSUESR_ERRINDEX_RD(val),
MEMERR_CPU_LSUESR_ERRINFO_RD(val)); MEMERR_CPU_LSUESR_ERRINFO_RD(val));
if (val & MEMERR_CPU_LSUESR_CERR_MASK) if (val & MEMERR_CPU_LSUESR_CERR_MASK)
dev_err(edac_dev->dev, dev_err(edac_dev->dev, "One or more correctable error\n");
"One or more correctable error\n");
if (val & MEMERR_CPU_LSUESR_MULTCERR_MASK) if (val & MEMERR_CPU_LSUESR_MULTCERR_MASK)
dev_err(edac_dev->dev, "Multiple correctable error\n"); dev_err(edac_dev->dev, "Multiple correctable error\n");
switch (MEMERR_CPU_LSUESR_ERRTYPE_RD(val)) { switch (MEMERR_CPU_LSUESR_ERRTYPE_RD(val)) {
...@@ -617,23 +619,21 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev, ...@@ -617,23 +619,21 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
if (val & (MEMERR_CPU_LSUESR_CERR_MASK | if (val & (MEMERR_CPU_LSUESR_CERR_MASK |
MEMERR_CPU_LSUESR_MULTCERR_MASK)) MEMERR_CPU_LSUESR_MULTCERR_MASK))
edac_device_handle_ce(edac_dev, 0, 0, edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
edac_dev->ctl_name);
}
chk_mmu:
val = readl(pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET); val = readl(pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
if (val) { if (!val)
return;
dev_err(edac_dev->dev, dev_err(edac_dev->dev,
"CPU%d memory error MMU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X %s\n", "CPU%d memory error MMU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X %s\n",
ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val, ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
MEMERR_CPU_MMUESR_ERRWAY_RD(val), MEMERR_CPU_MMUESR_ERRWAY_RD(val),
MEMERR_CPU_MMUESR_ERRINDEX_RD(val), MEMERR_CPU_MMUESR_ERRINDEX_RD(val),
MEMERR_CPU_MMUESR_ERRINFO_RD(val), MEMERR_CPU_MMUESR_ERRINFO_RD(val),
val & MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK ? "LSU" : val & MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK ? "LSU" : "ICF");
"ICF");
if (val & MEMERR_CPU_MMUESR_CERR_MASK) if (val & MEMERR_CPU_MMUESR_CERR_MASK)
dev_err(edac_dev->dev, dev_err(edac_dev->dev, "One or more correctable error\n");
"One or more correctable error\n");
if (val & MEMERR_CPU_MMUESR_MULTCERR_MASK) if (val & MEMERR_CPU_MMUESR_MULTCERR_MASK)
dev_err(edac_dev->dev, "Multiple correctable error\n"); dev_err(edac_dev->dev, "Multiple correctable error\n");
switch (MEMERR_CPU_MMUESR_ERRTYPE_RD(val)) { switch (MEMERR_CPU_MMUESR_ERRTYPE_RD(val)) {
...@@ -647,8 +647,7 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev, ...@@ -647,8 +647,7 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
dev_err(edac_dev->dev, "Stage 1 UTB allocate error\n"); dev_err(edac_dev->dev, "Stage 1 UTB allocate error\n");
break; break;
case 3: case 3:
dev_err(edac_dev->dev, dev_err(edac_dev->dev, "TMO operation single bank error\n");
"TMO operation single bank error\n");
break; break;
case 4: case 4:
dev_err(edac_dev->dev, "Stage 2 UTB error\n"); dev_err(edac_dev->dev, "Stage 2 UTB error\n");
...@@ -660,8 +659,7 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev, ...@@ -660,8 +659,7 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
dev_err(edac_dev->dev, "Stage 2 UTB allocate error\n"); dev_err(edac_dev->dev, "Stage 2 UTB allocate error\n");
break; break;
case 7: case 7:
dev_err(edac_dev->dev, dev_err(edac_dev->dev, "TMO operation multiple bank error\n");
"TMO operation multiple bank error\n");
break; break;
} }
...@@ -669,7 +667,6 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev, ...@@ -669,7 +667,6 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
writel(val, pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET); writel(val, pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
}
} }
static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev) static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev)
...@@ -684,7 +681,8 @@ static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev) ...@@ -684,7 +681,8 @@ static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev)
/* Check L2 */ /* Check L2 */
pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE; pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
val = readl(pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET); val = readl(pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
if (val) { if (!val)
goto chk_l2c;
val_lo = readl(pg_e + MEMERR_L2C_L2EALR_PAGE_OFFSET); val_lo = readl(pg_e + MEMERR_L2C_L2EALR_PAGE_OFFSET);
val_hi = readl(pg_e + MEMERR_L2C_L2EAHR_PAGE_OFFSET); val_hi = readl(pg_e + MEMERR_L2C_L2EAHR_PAGE_OFFSET);
dev_err(edac_dev->dev, dev_err(edac_dev->dev,
...@@ -699,16 +697,13 @@ static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev) ...@@ -699,16 +697,13 @@ static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev)
MEMERR_L2C_L2ESR_ERRACTION_RD(val)); MEMERR_L2C_L2ESR_ERRACTION_RD(val));
if (val & MEMERR_L2C_L2ESR_ERR_MASK) if (val & MEMERR_L2C_L2ESR_ERR_MASK)
dev_err(edac_dev->dev, dev_err(edac_dev->dev, "One or more correctable error\n");
"One or more correctable error\n");
if (val & MEMERR_L2C_L2ESR_MULTICERR_MASK) if (val & MEMERR_L2C_L2ESR_MULTICERR_MASK)
dev_err(edac_dev->dev, "Multiple correctable error\n"); dev_err(edac_dev->dev, "Multiple correctable error\n");
if (val & MEMERR_L2C_L2ESR_UCERR_MASK) if (val & MEMERR_L2C_L2ESR_UCERR_MASK)
dev_err(edac_dev->dev, dev_err(edac_dev->dev, "One or more uncorrectable error\n");
"One or more uncorrectable error\n");
if (val & MEMERR_L2C_L2ESR_MULTUCERR_MASK) if (val & MEMERR_L2C_L2ESR_MULTUCERR_MASK)
dev_err(edac_dev->dev, dev_err(edac_dev->dev, "Multiple uncorrectable error\n");
"Multiple uncorrectable error\n");
switch (MEMERR_L2C_L2ESR_ERRTYPE_RD(val)) { switch (MEMERR_L2C_L2ESR_ERRTYPE_RD(val)) {
case 0: case 0:
...@@ -730,14 +725,12 @@ static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev) ...@@ -730,14 +725,12 @@ static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev)
if (val & (MEMERR_L2C_L2ESR_ERR_MASK | if (val & (MEMERR_L2C_L2ESR_ERR_MASK |
MEMERR_L2C_L2ESR_MULTICERR_MASK)) MEMERR_L2C_L2ESR_MULTICERR_MASK))
edac_device_handle_ce(edac_dev, 0, 0, edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
edac_dev->ctl_name);
if (val & (MEMERR_L2C_L2ESR_UCERR_MASK | if (val & (MEMERR_L2C_L2ESR_UCERR_MASK |
MEMERR_L2C_L2ESR_MULTUCERR_MASK)) MEMERR_L2C_L2ESR_MULTUCERR_MASK))
edac_device_handle_ue(edac_dev, 0, 0, edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
edac_dev->ctl_name);
}
chk_l2c:
/* Check if any memory request timed out on L2 cache */ /* Check if any memory request timed out on L2 cache */
pg_d = ctx->pmd_csr + CPU_L2C_PAGE; pg_d = ctx->pmd_csr + CPU_L2C_PAGE;
val = readl(pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET); val = readl(pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET);
...@@ -877,34 +870,24 @@ static const struct file_operations xgene_edac_pmd_debug_inject_fops[] = { ...@@ -877,34 +870,24 @@ static const struct file_operations xgene_edac_pmd_debug_inject_fops[] = {
{ } { }
}; };
static void xgene_edac_pmd_create_debugfs_nodes( static void
struct edac_device_ctl_info *edac_dev) xgene_edac_pmd_create_debugfs_nodes(struct edac_device_ctl_info *edac_dev)
{ {
struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info; struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
struct dentry *edac_debugfs; struct dentry *dbgfs_dir;
char name[30]; char name[10];
if (!IS_ENABLED(CONFIG_EDAC_DEBUG)) if (!IS_ENABLED(CONFIG_EDAC_DEBUG) || !ctx->edac->dfs)
return; return;
/* snprintf(name, sizeof(name), "PMD%d", ctx->pmd);
* Todo: Switch to common EDAC debug file system for edac device dbgfs_dir = edac_debugfs_create_dir_at(name, ctx->edac->dfs);
* when available. if (!dbgfs_dir)
*/
if (!ctx->edac->dfs) {
ctx->edac->dfs = debugfs_create_dir(edac_dev->dev->kobj.name,
NULL);
if (!ctx->edac->dfs)
return;
}
sprintf(name, "PMD%d", ctx->pmd);
edac_debugfs = debugfs_create_dir(name, ctx->edac->dfs);
if (!edac_debugfs)
return; return;
debugfs_create_file("l1_inject_ctrl", S_IWUSR, edac_debugfs, edac_dev, edac_debugfs_create_file("l1_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev,
&xgene_edac_pmd_debug_inject_fops[0]); &xgene_edac_pmd_debug_inject_fops[0]);
debugfs_create_file("l2_inject_ctrl", S_IWUSR, edac_debugfs, edac_dev, edac_debugfs_create_file("l2_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev,
&xgene_edac_pmd_debug_inject_fops[1]); &xgene_edac_pmd_debug_inject_fops[1]);
} }
...@@ -941,7 +924,7 @@ static int xgene_edac_pmd_add(struct xgene_edac *edac, struct device_node *np, ...@@ -941,7 +924,7 @@ static int xgene_edac_pmd_add(struct xgene_edac *edac, struct device_node *np,
goto err_group; goto err_group;
} }
sprintf(edac_name, "l2c%d", pmd); snprintf(edac_name, sizeof(edac_name), "l2c%d", pmd);
edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx), edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
edac_name, 1, "l2c", 1, 2, NULL, edac_name, 1, "l2c", 1, 2, NULL,
0, edac_device_alloc_index()); 0, edac_device_alloc_index());
...@@ -1016,10 +999,780 @@ static int xgene_edac_pmd_remove(struct xgene_edac_pmd_ctx *pmd) ...@@ -1016,10 +999,780 @@ static int xgene_edac_pmd_remove(struct xgene_edac_pmd_ctx *pmd)
return 0; return 0;
} }
/* L3 Error device */
#define L3C_ESR (0x0A * 4)
#define L3C_ESR_DATATAG_MASK BIT(9)
#define L3C_ESR_MULTIHIT_MASK BIT(8)
#define L3C_ESR_UCEVICT_MASK BIT(6)
#define L3C_ESR_MULTIUCERR_MASK BIT(5)
#define L3C_ESR_MULTICERR_MASK BIT(4)
#define L3C_ESR_UCERR_MASK BIT(3)
#define L3C_ESR_CERR_MASK BIT(2)
#define L3C_ESR_UCERRINTR_MASK BIT(1)
#define L3C_ESR_CERRINTR_MASK BIT(0)
#define L3C_ECR (0x0B * 4)
#define L3C_ECR_UCINTREN BIT(3)
#define L3C_ECR_CINTREN BIT(2)
#define L3C_UCERREN BIT(1)
#define L3C_CERREN BIT(0)
#define L3C_ELR (0x0C * 4)
#define L3C_ELR_ERRSYN(src) ((src & 0xFF800000) >> 23)
#define L3C_ELR_ERRWAY(src) ((src & 0x007E0000) >> 17)
#define L3C_ELR_AGENTID(src) ((src & 0x0001E000) >> 13)
#define L3C_ELR_ERRGRP(src) ((src & 0x00000F00) >> 8)
#define L3C_ELR_OPTYPE(src) ((src & 0x000000F0) >> 4)
#define L3C_ELR_PADDRHIGH(src) (src & 0x0000000F)
#define L3C_AELR (0x0D * 4)
#define L3C_BELR (0x0E * 4)
#define L3C_BELR_BANK(src) (src & 0x0000000F)
struct xgene_edac_dev_ctx {
struct list_head next;
struct device ddev;
char *name;
struct xgene_edac *edac;
struct edac_device_ctl_info *edac_dev;
int edac_idx;
void __iomem *dev_csr;
int version;
};
/*
* Version 1 of the L3 controller has broken single bit correctable logic for
* certain error syndromes. Log them as uncorrectable in that case.
*/
static bool xgene_edac_l3_promote_to_uc_err(u32 l3cesr, u32 l3celr)
{
if (l3cesr & L3C_ESR_DATATAG_MASK) {
switch (L3C_ELR_ERRSYN(l3celr)) {
case 0x13C:
case 0x0B4:
case 0x007:
case 0x00D:
case 0x00E:
case 0x019:
case 0x01A:
case 0x01C:
case 0x04E:
case 0x041:
return true;
}
} else if (L3C_ELR_ERRSYN(l3celr) == 9)
return true;
return false;
}
static void xgene_edac_l3_check(struct edac_device_ctl_info *edac_dev)
{
struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
u32 l3cesr;
u32 l3celr;
u32 l3caelr;
u32 l3cbelr;
l3cesr = readl(ctx->dev_csr + L3C_ESR);
if (!(l3cesr & (L3C_ESR_UCERR_MASK | L3C_ESR_CERR_MASK)))
return;
if (l3cesr & L3C_ESR_UCERR_MASK)
dev_err(edac_dev->dev, "L3C uncorrectable error\n");
if (l3cesr & L3C_ESR_CERR_MASK)
dev_warn(edac_dev->dev, "L3C correctable error\n");
l3celr = readl(ctx->dev_csr + L3C_ELR);
l3caelr = readl(ctx->dev_csr + L3C_AELR);
l3cbelr = readl(ctx->dev_csr + L3C_BELR);
if (l3cesr & L3C_ESR_MULTIHIT_MASK)
dev_err(edac_dev->dev, "L3C multiple hit error\n");
if (l3cesr & L3C_ESR_UCEVICT_MASK)
dev_err(edac_dev->dev,
"L3C dropped eviction of line with error\n");
if (l3cesr & L3C_ESR_MULTIUCERR_MASK)
dev_err(edac_dev->dev, "L3C multiple uncorrectable error\n");
if (l3cesr & L3C_ESR_DATATAG_MASK)
dev_err(edac_dev->dev,
"L3C data error syndrome 0x%X group 0x%X\n",
L3C_ELR_ERRSYN(l3celr), L3C_ELR_ERRGRP(l3celr));
else
dev_err(edac_dev->dev,
"L3C tag error syndrome 0x%X Way of Tag 0x%X Agent ID 0x%X Operation type 0x%X\n",
L3C_ELR_ERRSYN(l3celr), L3C_ELR_ERRWAY(l3celr),
L3C_ELR_AGENTID(l3celr), L3C_ELR_OPTYPE(l3celr));
/*
* NOTE: Address [41:38] in L3C_ELR_PADDRHIGH(l3celr).
* Address [37:6] in l3caelr. Lower 6 bits are zero.
*/
dev_err(edac_dev->dev, "L3C error address 0x%08X.%08X bank %d\n",
L3C_ELR_PADDRHIGH(l3celr) << 6 | (l3caelr >> 26),
(l3caelr & 0x3FFFFFFF) << 6, L3C_BELR_BANK(l3cbelr));
dev_err(edac_dev->dev,
"L3C error status register value 0x%X\n", l3cesr);
/* Clear L3C error interrupt */
writel(0, ctx->dev_csr + L3C_ESR);
if (ctx->version <= 1 &&
xgene_edac_l3_promote_to_uc_err(l3cesr, l3celr)) {
edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
return;
}
if (l3cesr & L3C_ESR_CERR_MASK)
edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
if (l3cesr & L3C_ESR_UCERR_MASK)
edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
}
static void xgene_edac_l3_hw_init(struct edac_device_ctl_info *edac_dev,
bool enable)
{
struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
u32 val;
val = readl(ctx->dev_csr + L3C_ECR);
val |= L3C_UCERREN | L3C_CERREN;
/* On disable, we just disable interrupt but keep error enabled */
if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
if (enable)
val |= L3C_ECR_UCINTREN | L3C_ECR_CINTREN;
else
val &= ~(L3C_ECR_UCINTREN | L3C_ECR_CINTREN);
}
writel(val, ctx->dev_csr + L3C_ECR);
if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
/* Enable/disable L3 error top level interrupt */
if (enable) {
xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
L3C_UNCORR_ERR_MASK);
xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK,
L3C_CORR_ERR_MASK);
} else {
xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
L3C_UNCORR_ERR_MASK);
xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK,
L3C_CORR_ERR_MASK);
}
}
}
static ssize_t xgene_edac_l3_inject_ctrl_write(struct file *file,
const char __user *data,
size_t count, loff_t *ppos)
{
struct edac_device_ctl_info *edac_dev = file->private_data;
struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
/* Generate all errors */
writel(0xFFFFFFFF, ctx->dev_csr + L3C_ESR);
return count;
}
static const struct file_operations xgene_edac_l3_debug_inject_fops = {
.open = simple_open,
.write = xgene_edac_l3_inject_ctrl_write,
.llseek = generic_file_llseek
};
static void
xgene_edac_l3_create_debugfs_nodes(struct edac_device_ctl_info *edac_dev)
{
struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
struct dentry *dbgfs_dir;
char name[10];
if (!IS_ENABLED(CONFIG_EDAC_DEBUG) || !ctx->edac->dfs)
return;
snprintf(name, sizeof(name), "l3c%d", ctx->edac_idx);
dbgfs_dir = edac_debugfs_create_dir_at(name, ctx->edac->dfs);
if (!dbgfs_dir)
return;
debugfs_create_file("l3_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev,
&xgene_edac_l3_debug_inject_fops);
}
static int xgene_edac_l3_add(struct xgene_edac *edac, struct device_node *np,
int version)
{
struct edac_device_ctl_info *edac_dev;
struct xgene_edac_dev_ctx *ctx;
struct resource res;
void __iomem *dev_csr;
int edac_idx;
int rc = 0;
if (!devres_open_group(edac->dev, xgene_edac_l3_add, GFP_KERNEL))
return -ENOMEM;
rc = of_address_to_resource(np, 0, &res);
if (rc < 0) {
dev_err(edac->dev, "no L3 resource address\n");
goto err_release_group;
}
dev_csr = devm_ioremap_resource(edac->dev, &res);
if (IS_ERR(dev_csr)) {
dev_err(edac->dev,
"devm_ioremap_resource failed for L3 resource address\n");
rc = PTR_ERR(dev_csr);
goto err_release_group;
}
edac_idx = edac_device_alloc_index();
edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
"l3c", 1, "l3c", 1, 0, NULL, 0,
edac_idx);
if (!edac_dev) {
rc = -ENOMEM;
goto err_release_group;
}
ctx = edac_dev->pvt_info;
ctx->dev_csr = dev_csr;
ctx->name = "xgene_l3_err";
ctx->edac_idx = edac_idx;
ctx->edac = edac;
ctx->edac_dev = edac_dev;
ctx->ddev = *edac->dev;
ctx->version = version;
edac_dev->dev = &ctx->ddev;
edac_dev->ctl_name = ctx->name;
edac_dev->dev_name = ctx->name;
edac_dev->mod_name = EDAC_MOD_STR;
if (edac_op_state == EDAC_OPSTATE_POLL)
edac_dev->edac_check = xgene_edac_l3_check;
xgene_edac_l3_create_debugfs_nodes(edac_dev);
rc = edac_device_add_device(edac_dev);
if (rc > 0) {
dev_err(edac->dev, "failed edac_device_add_device()\n");
rc = -ENOMEM;
goto err_ctl_free;
}
if (edac_op_state == EDAC_OPSTATE_INT)
edac_dev->op_state = OP_RUNNING_INTERRUPT;
list_add(&ctx->next, &edac->l3s);
xgene_edac_l3_hw_init(edac_dev, 1);
devres_remove_group(edac->dev, xgene_edac_l3_add);
dev_info(edac->dev, "X-Gene EDAC L3 registered\n");
return 0;
err_ctl_free:
edac_device_free_ctl_info(edac_dev);
err_release_group:
devres_release_group(edac->dev, xgene_edac_l3_add);
return rc;
}
static int xgene_edac_l3_remove(struct xgene_edac_dev_ctx *l3)
{
struct edac_device_ctl_info *edac_dev = l3->edac_dev;
xgene_edac_l3_hw_init(edac_dev, 0);
edac_device_del_device(l3->edac->dev);
edac_device_free_ctl_info(edac_dev);
return 0;
}
/* SoC error device */
#define IOBAXIS0TRANSERRINTSTS 0x0000
#define IOBAXIS0_M_ILLEGAL_ACCESS_MASK BIT(1)
#define IOBAXIS0_ILLEGAL_ACCESS_MASK BIT(0)
#define IOBAXIS0TRANSERRINTMSK 0x0004
#define IOBAXIS0TRANSERRREQINFOL 0x0008
#define IOBAXIS0TRANSERRREQINFOH 0x000c
#define REQTYPE_RD(src) (((src) & BIT(0)))
#define ERRADDRH_RD(src) (((src) & 0xffc00000) >> 22)
#define IOBAXIS1TRANSERRINTSTS 0x0010
#define IOBAXIS1TRANSERRINTMSK 0x0014
#define IOBAXIS1TRANSERRREQINFOL 0x0018
#define IOBAXIS1TRANSERRREQINFOH 0x001c
#define IOBPATRANSERRINTSTS 0x0020
#define IOBPA_M_REQIDRAM_CORRUPT_MASK BIT(7)
#define IOBPA_REQIDRAM_CORRUPT_MASK BIT(6)
#define IOBPA_M_TRANS_CORRUPT_MASK BIT(5)
#define IOBPA_TRANS_CORRUPT_MASK BIT(4)
#define IOBPA_M_WDATA_CORRUPT_MASK BIT(3)
#define IOBPA_WDATA_CORRUPT_MASK BIT(2)
#define IOBPA_M_RDATA_CORRUPT_MASK BIT(1)
#define IOBPA_RDATA_CORRUPT_MASK BIT(0)
#define IOBBATRANSERRINTSTS 0x0030
#define M_ILLEGAL_ACCESS_MASK BIT(15)
#define ILLEGAL_ACCESS_MASK BIT(14)
#define M_WIDRAM_CORRUPT_MASK BIT(13)
#define WIDRAM_CORRUPT_MASK BIT(12)
#define M_RIDRAM_CORRUPT_MASK BIT(11)
#define RIDRAM_CORRUPT_MASK BIT(10)
#define M_TRANS_CORRUPT_MASK BIT(9)
#define TRANS_CORRUPT_MASK BIT(8)
#define M_WDATA_CORRUPT_MASK BIT(7)
#define WDATA_CORRUPT_MASK BIT(6)
#define M_RBM_POISONED_REQ_MASK BIT(5)
#define RBM_POISONED_REQ_MASK BIT(4)
#define M_XGIC_POISONED_REQ_MASK BIT(3)
#define XGIC_POISONED_REQ_MASK BIT(2)
#define M_WRERR_RESP_MASK BIT(1)
#define WRERR_RESP_MASK BIT(0)
#define IOBBATRANSERRREQINFOL 0x0038
#define IOBBATRANSERRREQINFOH 0x003c
#define REQTYPE_F2_RD(src) ((src) & BIT(0))
#define ERRADDRH_F2_RD(src) (((src) & 0xffc00000) >> 22)
#define IOBBATRANSERRCSWREQID 0x0040
#define XGICTRANSERRINTSTS 0x0050
#define M_WR_ACCESS_ERR_MASK BIT(3)
#define WR_ACCESS_ERR_MASK BIT(2)
#define M_RD_ACCESS_ERR_MASK BIT(1)
#define RD_ACCESS_ERR_MASK BIT(0)
#define XGICTRANSERRINTMSK 0x0054
#define XGICTRANSERRREQINFO 0x0058
#define REQTYPE_MASK BIT(26)
#define ERRADDR_RD(src) ((src) & 0x03ffffff)
#define GLBL_ERR_STS 0x0800
#define MDED_ERR_MASK BIT(3)
#define DED_ERR_MASK BIT(2)
#define MSEC_ERR_MASK BIT(1)
#define SEC_ERR_MASK BIT(0)
#define GLBL_SEC_ERRL 0x0810
#define GLBL_SEC_ERRH 0x0818
#define GLBL_MSEC_ERRL 0x0820
#define GLBL_MSEC_ERRH 0x0828
#define GLBL_DED_ERRL 0x0830
#define GLBL_DED_ERRLMASK 0x0834
#define GLBL_DED_ERRH 0x0838
#define GLBL_DED_ERRHMASK 0x083c
#define GLBL_MDED_ERRL 0x0840
#define GLBL_MDED_ERRLMASK 0x0844
#define GLBL_MDED_ERRH 0x0848
#define GLBL_MDED_ERRHMASK 0x084c
static const char * const soc_mem_err_v1[] = {
"10GbE0",
"10GbE1",
"Security",
"SATA45",
"SATA23/ETH23",
"SATA01/ETH01",
"USB1",
"USB0",
"QML",
"QM0",
"QM1 (XGbE01)",
"PCIE4",
"PCIE3",
"PCIE2",
"PCIE1",
"PCIE0",
"CTX Manager",
"OCM",
"1GbE",
"CLE",
"AHBC",
"PktDMA",
"GFC",
"MSLIM",
"10GbE2",
"10GbE3",
"QM2 (XGbE23)",
"IOB",
"unknown",
"unknown",
"unknown",
"unknown",
};
static void xgene_edac_iob_gic_report(struct edac_device_ctl_info *edac_dev)
{
struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
u32 err_addr_lo;
u32 err_addr_hi;
u32 reg;
u32 info;
/* GIC transaction error interrupt */
reg = readl(ctx->dev_csr + XGICTRANSERRINTSTS);
if (!reg)
goto chk_iob_err;
dev_err(edac_dev->dev, "XGIC transaction error\n");
if (reg & RD_ACCESS_ERR_MASK)
dev_err(edac_dev->dev, "XGIC read size error\n");
if (reg & M_RD_ACCESS_ERR_MASK)
dev_err(edac_dev->dev, "Multiple XGIC read size error\n");
if (reg & WR_ACCESS_ERR_MASK)
dev_err(edac_dev->dev, "XGIC write size error\n");
if (reg & M_WR_ACCESS_ERR_MASK)
dev_err(edac_dev->dev, "Multiple XGIC write size error\n");
info = readl(ctx->dev_csr + XGICTRANSERRREQINFO);
dev_err(edac_dev->dev, "XGIC %s access @ 0x%08X (0x%08X)\n",
info & REQTYPE_MASK ? "read" : "write", ERRADDR_RD(info),
info);
writel(reg, ctx->dev_csr + XGICTRANSERRINTSTS);
chk_iob_err:
/* IOB memory error */
reg = readl(ctx->dev_csr + GLBL_ERR_STS);
if (!reg)
return;
if (reg & SEC_ERR_MASK) {
err_addr_lo = readl(ctx->dev_csr + GLBL_SEC_ERRL);
err_addr_hi = readl(ctx->dev_csr + GLBL_SEC_ERRH);
dev_err(edac_dev->dev,
"IOB single-bit correctable memory at 0x%08X.%08X error\n",
err_addr_lo, err_addr_hi);
writel(err_addr_lo, ctx->dev_csr + GLBL_SEC_ERRL);
writel(err_addr_hi, ctx->dev_csr + GLBL_SEC_ERRH);
}
if (reg & MSEC_ERR_MASK) {
err_addr_lo = readl(ctx->dev_csr + GLBL_MSEC_ERRL);
err_addr_hi = readl(ctx->dev_csr + GLBL_MSEC_ERRH);
dev_err(edac_dev->dev,
"IOB multiple single-bit correctable memory at 0x%08X.%08X error\n",
err_addr_lo, err_addr_hi);
writel(err_addr_lo, ctx->dev_csr + GLBL_MSEC_ERRL);
writel(err_addr_hi, ctx->dev_csr + GLBL_MSEC_ERRH);
}
if (reg & (SEC_ERR_MASK | MSEC_ERR_MASK))
edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
if (reg & DED_ERR_MASK) {
err_addr_lo = readl(ctx->dev_csr + GLBL_DED_ERRL);
err_addr_hi = readl(ctx->dev_csr + GLBL_DED_ERRH);
dev_err(edac_dev->dev,
"IOB double-bit uncorrectable memory at 0x%08X.%08X error\n",
err_addr_lo, err_addr_hi);
writel(err_addr_lo, ctx->dev_csr + GLBL_DED_ERRL);
writel(err_addr_hi, ctx->dev_csr + GLBL_DED_ERRH);
}
if (reg & MDED_ERR_MASK) {
err_addr_lo = readl(ctx->dev_csr + GLBL_MDED_ERRL);
err_addr_hi = readl(ctx->dev_csr + GLBL_MDED_ERRH);
dev_err(edac_dev->dev,
"Multiple IOB double-bit uncorrectable memory at 0x%08X.%08X error\n",
err_addr_lo, err_addr_hi);
writel(err_addr_lo, ctx->dev_csr + GLBL_MDED_ERRL);
writel(err_addr_hi, ctx->dev_csr + GLBL_MDED_ERRH);
}
if (reg & (DED_ERR_MASK | MDED_ERR_MASK))
edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
}
static void xgene_edac_rb_report(struct edac_device_ctl_info *edac_dev)
{
struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
u32 err_addr_lo;
u32 err_addr_hi;
u32 reg;
/* IOB Bridge agent transaction error interrupt */
reg = readl(ctx->dev_csr + IOBBATRANSERRINTSTS);
if (!reg)
return;
dev_err(edac_dev->dev, "IOB bridge agent (BA) transaction error\n");
if (reg & WRERR_RESP_MASK)
dev_err(edac_dev->dev, "IOB BA write response error\n");
if (reg & M_WRERR_RESP_MASK)
dev_err(edac_dev->dev,
"Multiple IOB BA write response error\n");
if (reg & XGIC_POISONED_REQ_MASK)
dev_err(edac_dev->dev, "IOB BA XGIC poisoned write error\n");
if (reg & M_XGIC_POISONED_REQ_MASK)
dev_err(edac_dev->dev,
"Multiple IOB BA XGIC poisoned write error\n");
if (reg & RBM_POISONED_REQ_MASK)
dev_err(edac_dev->dev, "IOB BA RBM poisoned write error\n");
if (reg & M_RBM_POISONED_REQ_MASK)
dev_err(edac_dev->dev,
"Multiple IOB BA RBM poisoned write error\n");
if (reg & WDATA_CORRUPT_MASK)
dev_err(edac_dev->dev, "IOB BA write error\n");
if (reg & M_WDATA_CORRUPT_MASK)
dev_err(edac_dev->dev, "Multiple IOB BA write error\n");
if (reg & TRANS_CORRUPT_MASK)
dev_err(edac_dev->dev, "IOB BA transaction error\n");
if (reg & M_TRANS_CORRUPT_MASK)
dev_err(edac_dev->dev, "Multiple IOB BA transaction error\n");
if (reg & RIDRAM_CORRUPT_MASK)
dev_err(edac_dev->dev,
"IOB BA RDIDRAM read transaction ID error\n");
if (reg & M_RIDRAM_CORRUPT_MASK)
dev_err(edac_dev->dev,
"Multiple IOB BA RDIDRAM read transaction ID error\n");
if (reg & WIDRAM_CORRUPT_MASK)
dev_err(edac_dev->dev,
"IOB BA RDIDRAM write transaction ID error\n");
if (reg & M_WIDRAM_CORRUPT_MASK)
dev_err(edac_dev->dev,
"Multiple IOB BA RDIDRAM write transaction ID error\n");
if (reg & ILLEGAL_ACCESS_MASK)
dev_err(edac_dev->dev,
"IOB BA XGIC/RB illegal access error\n");
if (reg & M_ILLEGAL_ACCESS_MASK)
dev_err(edac_dev->dev,
"Multiple IOB BA XGIC/RB illegal access error\n");
err_addr_lo = readl(ctx->dev_csr + IOBBATRANSERRREQINFOL);
err_addr_hi = readl(ctx->dev_csr + IOBBATRANSERRREQINFOH);
dev_err(edac_dev->dev, "IOB BA %s access at 0x%02X.%08X (0x%08X)\n",
REQTYPE_F2_RD(err_addr_hi) ? "read" : "write",
ERRADDRH_F2_RD(err_addr_hi), err_addr_lo, err_addr_hi);
if (reg & WRERR_RESP_MASK)
dev_err(edac_dev->dev, "IOB BA requestor ID 0x%08X\n",
readl(ctx->dev_csr + IOBBATRANSERRCSWREQID));
writel(reg, ctx->dev_csr + IOBBATRANSERRINTSTS);
}
static void xgene_edac_pa_report(struct edac_device_ctl_info *edac_dev)
{
struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
u32 err_addr_lo;
u32 err_addr_hi;
u32 reg;
/* IOB Processing agent transaction error interrupt */
reg = readl(ctx->dev_csr + IOBPATRANSERRINTSTS);
if (!reg)
goto chk_iob_axi0;
dev_err(edac_dev->dev, "IOB procesing agent (PA) transaction error\n");
if (reg & IOBPA_RDATA_CORRUPT_MASK)
dev_err(edac_dev->dev, "IOB PA read data RAM error\n");
if (reg & IOBPA_M_RDATA_CORRUPT_MASK)
dev_err(edac_dev->dev,
"Mutilple IOB PA read data RAM error\n");
if (reg & IOBPA_WDATA_CORRUPT_MASK)
dev_err(edac_dev->dev, "IOB PA write data RAM error\n");
if (reg & IOBPA_M_WDATA_CORRUPT_MASK)
dev_err(edac_dev->dev,
"Mutilple IOB PA write data RAM error\n");
if (reg & IOBPA_TRANS_CORRUPT_MASK)
dev_err(edac_dev->dev, "IOB PA transaction error\n");
if (reg & IOBPA_M_TRANS_CORRUPT_MASK)
dev_err(edac_dev->dev, "Mutilple IOB PA transaction error\n");
if (reg & IOBPA_REQIDRAM_CORRUPT_MASK)
dev_err(edac_dev->dev, "IOB PA transaction ID RAM error\n");
if (reg & IOBPA_M_REQIDRAM_CORRUPT_MASK)
dev_err(edac_dev->dev,
"Multiple IOB PA transaction ID RAM error\n");
writel(reg, ctx->dev_csr + IOBPATRANSERRINTSTS);
chk_iob_axi0:
/* IOB AXI0 Error */
reg = readl(ctx->dev_csr + IOBAXIS0TRANSERRINTSTS);
if (!reg)
goto chk_iob_axi1;
err_addr_lo = readl(ctx->dev_csr + IOBAXIS0TRANSERRREQINFOL);
err_addr_hi = readl(ctx->dev_csr + IOBAXIS0TRANSERRREQINFOH);
dev_err(edac_dev->dev,
"%sAXI slave 0 illegal %s access @ 0x%02X.%08X (0x%08X)\n",
reg & IOBAXIS0_M_ILLEGAL_ACCESS_MASK ? "Multiple " : "",
REQTYPE_RD(err_addr_hi) ? "read" : "write",
ERRADDRH_RD(err_addr_hi), err_addr_lo, err_addr_hi);
writel(reg, ctx->dev_csr + IOBAXIS0TRANSERRINTSTS);
chk_iob_axi1:
/* IOB AXI1 Error */
reg = readl(ctx->dev_csr + IOBAXIS1TRANSERRINTSTS);
if (!reg)
return;
err_addr_lo = readl(ctx->dev_csr + IOBAXIS1TRANSERRREQINFOL);
err_addr_hi = readl(ctx->dev_csr + IOBAXIS1TRANSERRREQINFOH);
dev_err(edac_dev->dev,
"%sAXI slave 1 illegal %s access @ 0x%02X.%08X (0x%08X)\n",
reg & IOBAXIS0_M_ILLEGAL_ACCESS_MASK ? "Multiple " : "",
REQTYPE_RD(err_addr_hi) ? "read" : "write",
ERRADDRH_RD(err_addr_hi), err_addr_lo, err_addr_hi);
writel(reg, ctx->dev_csr + IOBAXIS1TRANSERRINTSTS);
}
static void xgene_edac_soc_check(struct edac_device_ctl_info *edac_dev)
{
struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
const char * const *soc_mem_err = NULL;
u32 pcp_hp_stat;
u32 pcp_lp_stat;
u32 reg;
int i;
xgene_edac_pcp_rd(ctx->edac, PCPHPERRINTSTS, &pcp_hp_stat);
xgene_edac_pcp_rd(ctx->edac, PCPLPERRINTSTS, &pcp_lp_stat);
xgene_edac_pcp_rd(ctx->edac, MEMERRINTSTS, &reg);
if (!((pcp_hp_stat & (IOB_PA_ERR_MASK | IOB_BA_ERR_MASK |
IOB_XGIC_ERR_MASK | IOB_RB_ERR_MASK)) ||
(pcp_lp_stat & CSW_SWITCH_TRACE_ERR_MASK) || reg))
return;
if (pcp_hp_stat & IOB_XGIC_ERR_MASK)
xgene_edac_iob_gic_report(edac_dev);
if (pcp_hp_stat & (IOB_RB_ERR_MASK | IOB_BA_ERR_MASK))
xgene_edac_rb_report(edac_dev);
if (pcp_hp_stat & IOB_PA_ERR_MASK)
xgene_edac_pa_report(edac_dev);
if (pcp_lp_stat & CSW_SWITCH_TRACE_ERR_MASK) {
dev_info(edac_dev->dev,
"CSW switch trace correctable memory parity error\n");
edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
}
if (!reg)
return;
if (ctx->version == 1)
soc_mem_err = soc_mem_err_v1;
if (!soc_mem_err) {
dev_err(edac_dev->dev, "SoC memory parity error 0x%08X\n",
reg);
edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
return;
}
for (i = 0; i < 31; i++) {
if (reg & (1 << i)) {
dev_err(edac_dev->dev, "%s memory parity error\n",
soc_mem_err[i]);
edac_device_handle_ue(edac_dev, 0, 0,
edac_dev->ctl_name);
}
}
}
static void xgene_edac_soc_hw_init(struct edac_device_ctl_info *edac_dev,
bool enable)
{
struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
/* Enable SoC IP error interrupt */
if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
if (enable) {
xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
IOB_PA_ERR_MASK |
IOB_BA_ERR_MASK |
IOB_XGIC_ERR_MASK |
IOB_RB_ERR_MASK);
xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK,
CSW_SWITCH_TRACE_ERR_MASK);
} else {
xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
IOB_PA_ERR_MASK |
IOB_BA_ERR_MASK |
IOB_XGIC_ERR_MASK |
IOB_RB_ERR_MASK);
xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK,
CSW_SWITCH_TRACE_ERR_MASK);
}
writel(enable ? 0x0 : 0xFFFFFFFF,
ctx->dev_csr + IOBAXIS0TRANSERRINTMSK);
writel(enable ? 0x0 : 0xFFFFFFFF,
ctx->dev_csr + IOBAXIS1TRANSERRINTMSK);
writel(enable ? 0x0 : 0xFFFFFFFF,
ctx->dev_csr + XGICTRANSERRINTMSK);
xgene_edac_pcp_setbits(ctx->edac, MEMERRINTMSK,
enable ? 0x0 : 0xFFFFFFFF);
}
}
static int xgene_edac_soc_add(struct xgene_edac *edac, struct device_node *np,
int version)
{
struct edac_device_ctl_info *edac_dev;
struct xgene_edac_dev_ctx *ctx;
void __iomem *dev_csr;
struct resource res;
int edac_idx;
int rc;
if (!devres_open_group(edac->dev, xgene_edac_soc_add, GFP_KERNEL))
return -ENOMEM;
rc = of_address_to_resource(np, 0, &res);
if (rc < 0) {
dev_err(edac->dev, "no SoC resource address\n");
goto err_release_group;
}
dev_csr = devm_ioremap_resource(edac->dev, &res);
if (IS_ERR(dev_csr)) {
dev_err(edac->dev,
"devm_ioremap_resource failed for soc resource address\n");
rc = PTR_ERR(dev_csr);
goto err_release_group;
}
edac_idx = edac_device_alloc_index();
edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
"SOC", 1, "SOC", 1, 2, NULL, 0,
edac_idx);
if (!edac_dev) {
rc = -ENOMEM;
goto err_release_group;
}
ctx = edac_dev->pvt_info;
ctx->dev_csr = dev_csr;
ctx->name = "xgene_soc_err";
ctx->edac_idx = edac_idx;
ctx->edac = edac;
ctx->edac_dev = edac_dev;
ctx->ddev = *edac->dev;
ctx->version = version;
edac_dev->dev = &ctx->ddev;
edac_dev->ctl_name = ctx->name;
edac_dev->dev_name = ctx->name;
edac_dev->mod_name = EDAC_MOD_STR;
if (edac_op_state == EDAC_OPSTATE_POLL)
edac_dev->edac_check = xgene_edac_soc_check;
rc = edac_device_add_device(edac_dev);
if (rc > 0) {
dev_err(edac->dev, "failed edac_device_add_device()\n");
rc = -ENOMEM;
goto err_ctl_free;
}
if (edac_op_state == EDAC_OPSTATE_INT)
edac_dev->op_state = OP_RUNNING_INTERRUPT;
list_add(&ctx->next, &edac->socs);
xgene_edac_soc_hw_init(edac_dev, 1);
devres_remove_group(edac->dev, xgene_edac_soc_add);
dev_info(edac->dev, "X-Gene EDAC SoC registered\n");
return 0;
err_ctl_free:
edac_device_free_ctl_info(edac_dev);
err_release_group:
devres_release_group(edac->dev, xgene_edac_soc_add);
return rc;
}
static int xgene_edac_soc_remove(struct xgene_edac_dev_ctx *soc)
{
struct edac_device_ctl_info *edac_dev = soc->edac_dev;
xgene_edac_soc_hw_init(edac_dev, 0);
edac_device_del_device(soc->edac->dev);
edac_device_free_ctl_info(edac_dev);
return 0;
}
static irqreturn_t xgene_edac_isr(int irq, void *dev_id) static irqreturn_t xgene_edac_isr(int irq, void *dev_id)
{ {
struct xgene_edac *ctx = dev_id; struct xgene_edac *ctx = dev_id;
struct xgene_edac_pmd_ctx *pmd; struct xgene_edac_pmd_ctx *pmd;
struct xgene_edac_dev_ctx *node;
unsigned int pcp_hp_stat; unsigned int pcp_hp_stat;
unsigned int pcp_lp_stat; unsigned int pcp_lp_stat;
...@@ -1030,16 +1783,21 @@ static irqreturn_t xgene_edac_isr(int irq, void *dev_id) ...@@ -1030,16 +1783,21 @@ static irqreturn_t xgene_edac_isr(int irq, void *dev_id)
(MCU_CORR_ERR_MASK & pcp_lp_stat)) { (MCU_CORR_ERR_MASK & pcp_lp_stat)) {
struct xgene_edac_mc_ctx *mcu; struct xgene_edac_mc_ctx *mcu;
list_for_each_entry(mcu, &ctx->mcus, next) { list_for_each_entry(mcu, &ctx->mcus, next)
xgene_edac_mc_check(mcu->mci); xgene_edac_mc_check(mcu->mci);
} }
}
list_for_each_entry(pmd, &ctx->pmds, next) { list_for_each_entry(pmd, &ctx->pmds, next) {
if ((PMD0_MERR_MASK << pmd->pmd) & pcp_hp_stat) if ((PMD0_MERR_MASK << pmd->pmd) & pcp_hp_stat)
xgene_edac_pmd_check(pmd->edac_dev); xgene_edac_pmd_check(pmd->edac_dev);
} }
list_for_each_entry(node, &ctx->l3s, next)
xgene_edac_l3_check(node->edac_dev);
list_for_each_entry(node, &ctx->socs, next)
xgene_edac_soc_check(node->edac_dev);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1058,6 +1816,8 @@ static int xgene_edac_probe(struct platform_device *pdev) ...@@ -1058,6 +1816,8 @@ static int xgene_edac_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, edac); platform_set_drvdata(pdev, edac);
INIT_LIST_HEAD(&edac->mcus); INIT_LIST_HEAD(&edac->mcus);
INIT_LIST_HEAD(&edac->pmds); INIT_LIST_HEAD(&edac->pmds);
INIT_LIST_HEAD(&edac->l3s);
INIT_LIST_HEAD(&edac->socs);
spin_lock_init(&edac->lock); spin_lock_init(&edac->lock);
mutex_init(&edac->mc_lock); mutex_init(&edac->mc_lock);
...@@ -1122,6 +1882,8 @@ static int xgene_edac_probe(struct platform_device *pdev) ...@@ -1122,6 +1882,8 @@ static int xgene_edac_probe(struct platform_device *pdev)
} }
} }
edac->dfs = edac_debugfs_create_dir(pdev->dev.kobj.name);
for_each_child_of_node(pdev->dev.of_node, child) { for_each_child_of_node(pdev->dev.of_node, child) {
if (!of_device_is_available(child)) if (!of_device_is_available(child))
continue; continue;
...@@ -1131,6 +1893,14 @@ static int xgene_edac_probe(struct platform_device *pdev) ...@@ -1131,6 +1893,14 @@ static int xgene_edac_probe(struct platform_device *pdev)
xgene_edac_pmd_add(edac, child, 1); xgene_edac_pmd_add(edac, child, 1);
if (of_device_is_compatible(child, "apm,xgene-edac-pmd-v2")) if (of_device_is_compatible(child, "apm,xgene-edac-pmd-v2"))
xgene_edac_pmd_add(edac, child, 2); xgene_edac_pmd_add(edac, child, 2);
if (of_device_is_compatible(child, "apm,xgene-edac-l3"))
xgene_edac_l3_add(edac, child, 1);
if (of_device_is_compatible(child, "apm,xgene-edac-l3-v2"))
xgene_edac_l3_add(edac, child, 2);
if (of_device_is_compatible(child, "apm,xgene-edac-soc"))
xgene_edac_soc_add(edac, child, 0);
if (of_device_is_compatible(child, "apm,xgene-edac-soc-v1"))
xgene_edac_soc_add(edac, child, 1);
} }
return 0; return 0;
...@@ -1146,14 +1916,21 @@ static int xgene_edac_remove(struct platform_device *pdev) ...@@ -1146,14 +1916,21 @@ static int xgene_edac_remove(struct platform_device *pdev)
struct xgene_edac_mc_ctx *temp_mcu; struct xgene_edac_mc_ctx *temp_mcu;
struct xgene_edac_pmd_ctx *pmd; struct xgene_edac_pmd_ctx *pmd;
struct xgene_edac_pmd_ctx *temp_pmd; struct xgene_edac_pmd_ctx *temp_pmd;
struct xgene_edac_dev_ctx *node;
struct xgene_edac_dev_ctx *temp_node;
list_for_each_entry_safe(mcu, temp_mcu, &edac->mcus, next) { list_for_each_entry_safe(mcu, temp_mcu, &edac->mcus, next)
xgene_edac_mc_remove(mcu); xgene_edac_mc_remove(mcu);
}
list_for_each_entry_safe(pmd, temp_pmd, &edac->pmds, next) { list_for_each_entry_safe(pmd, temp_pmd, &edac->pmds, next)
xgene_edac_pmd_remove(pmd); xgene_edac_pmd_remove(pmd);
}
list_for_each_entry_safe(node, temp_node, &edac->l3s, next)
xgene_edac_l3_remove(node);
list_for_each_entry_safe(node, temp_node, &edac->socs, next)
xgene_edac_soc_remove(node);
return 0; return 0;
} }
......
...@@ -769,12 +769,10 @@ struct mem_ctl_info { ...@@ -769,12 +769,10 @@ struct mem_ctl_info {
/* the internal state of this controller instance */ /* the internal state of this controller instance */
int op_state; int op_state;
#ifdef CONFIG_EDAC_DEBUG
struct dentry *debugfs; struct dentry *debugfs;
u8 fake_inject_layer[EDAC_MAX_LAYERS]; u8 fake_inject_layer[EDAC_MAX_LAYERS];
u32 fake_inject_ue; u32 fake_inject_ue;
u16 fake_inject_count; u16 fake_inject_count;
#endif
}; };
/* /*
......
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