diff options
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/igen6_edac.c | 59 |
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); |