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;
&altr_sdr_mc_debug_inject_fops);
if (!mci->debugfs)
return;
edac_debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
&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;
/* Erratum #505 */ if (pvt->fam == 0x15) {
if (pvt->fam == 0x15 && pvt->model < 0x10) /* Erratum #505 */
f15h_select_dct(pvt, 0); if (pvt->model < 0x10)
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,11 +241,15 @@ static int get_scrub_rate(struct mem_ctl_info *mci) ...@@ -230,11 +241,15 @@ static int get_scrub_rate(struct mem_ctl_info *mci)
u32 scrubval = 0; u32 scrubval = 0;
int i, retval = -EINVAL; int i, retval = -EINVAL;
/* Erratum #505 */ if (pvt->fam == 0x15) {
if (pvt->fam == 0x15 && pvt->model < 0x10) /* Erratum #505 */
f15h_select_dct(pvt, 0); if (pvt->model < 0x10)
f15h_select_dct(pvt, 0);
amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); if (pvt->model == 0x60)
amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
} else
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,25 +967,25 @@ static int i5100_setup_debugfs(struct mem_ctl_info *mci) ...@@ -966,25 +967,25 @@ 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:
......
This diff is collapsed.
...@@ -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