aboutsummaryrefslogtreecommitdiff
path: root/drivers/edac
diff options
context:
space:
mode:
authorQiuxu Zhuo2020-11-05 15:49:34 +0800
committerTony Luck2020-11-19 12:52:47 -0800
commit2223d8c781a0c1a8cf26b1d8f13aff84557ecbfc (patch)
tree1f56140d993959cd1a85512a6a4d6d1f950a450a /drivers/edac
parent10590a9d4f23e0a519730d79d39331df60ad2079 (diff)
EDAC/igen6: Add debugfs interface for Intel client SoC EDAC driver
Add debugfs support to fake memory correctable errors to test the error reporting path and the error address decoding logic in the igen6_edac driver. Please note that the fake errors are also reported to EDAC core and then the CE counter in EDAC sysfs is also increased. Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'drivers/edac')
-rw-r--r--drivers/edac/igen6_edac.c59
1 files changed, 59 insertions, 0 deletions
diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c
index 318b9b67080f..6c0039e1171f 100644
--- a/drivers/edac/igen6_edac.c
+++ b/drivers/edac/igen6_edac.c
@@ -612,6 +612,10 @@ static int igen6_get_dimm_config(struct mem_ctl_info *mci)
}
#ifdef CONFIG_EDAC_DEBUG
+/* Top of upper usable DRAM */
+static u64 igen6_touud;
+#define TOUUD_OFFSET 0xa8
+
static void igen6_reg_dump(struct igen6_imc *imc)
{
int i;
@@ -632,10 +636,54 @@ static void igen6_reg_dump(struct igen6_imc *imc)
readl(imc->window + MAD_DIMM_CH0_OFFSET + i * 4));
}
edac_dbg(2, "TOLUD : 0x%x", igen6_tolud);
+ edac_dbg(2, "TOUUD : 0x%llx", igen6_touud);
edac_dbg(2, "TOM : 0x%llx", igen6_tom);
}
+
+static struct dentry *igen6_test;
+
+static int debugfs_u64_set(void *data, u64 val)
+{
+ u64 ecclog;
+
+ if ((val >= igen6_tolud && val < _4GB) || val >= igen6_touud) {
+ edac_dbg(0, "Address 0x%llx out of range\n", val);
+ return 0;
+ }
+
+ pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val);
+
+ val >>= ECC_ERROR_LOG_ADDR_SHIFT;
+ ecclog = (val << ECC_ERROR_LOG_ADDR_SHIFT) | ECC_ERROR_LOG_CE;
+
+ if (!ecclog_gen_pool_add(0, ecclog))
+ irq_work_queue(&ecclog_irq_work);
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
+
+static void igen6_debug_setup(void)
+{
+ igen6_test = edac_debugfs_create_dir("igen6_test");
+ if (!igen6_test)
+ return;
+
+ if (!edac_debugfs_create_file("addr", 0200, igen6_test,
+ NULL, &fops_u64_wo)) {
+ debugfs_remove(igen6_test);
+ igen6_test = NULL;
+ }
+}
+
+static void igen6_debug_teardown(void)
+{
+ debugfs_remove_recursive(igen6_test);
+}
#else
static void igen6_reg_dump(struct igen6_imc *imc) {}
+static void igen6_debug_setup(void) {}
+static void igen6_debug_teardown(void) {}
#endif
static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar)
@@ -691,6 +739,15 @@ static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar)
*mchbar = MCHBAR_BASE(u.v);
+#ifdef CONFIG_EDAC_DEBUG
+ if (pci_read_config_dword(pdev, TOUUD_OFFSET, &u.v_lo))
+ edac_dbg(2, "Failed to read lower TOUUD\n");
+ else if (pci_read_config_dword(pdev, TOUUD_OFFSET + 4, &u.v_hi))
+ edac_dbg(2, "Failed to read upper TOUUD\n");
+ else
+ igen6_touud = u.v & GENMASK_ULL(38, 20);
+#endif
+
return 0;
fail:
return -ENODEV;
@@ -849,6 +906,7 @@ static int igen6_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto fail4;
}
+ igen6_debug_setup();
return 0;
fail4:
unregister_nmi_handler(NMI_SERR, IGEN6_NMI_NAME);
@@ -865,6 +923,7 @@ static void igen6_remove(struct pci_dev *pdev)
{
edac_dbg(2, "\n");
+ igen6_debug_teardown();
errcmd_enable_error_reporting(false);
unregister_nmi_handler(NMI_SERR, IGEN6_NMI_NAME);
irq_work_sync(&ecclog_irq_work);