Commit 2223d8c7 authored by Qiuxu Zhuo's avatar Qiuxu Zhuo Committed by Tony Luck
Browse files

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: default avatarQiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: default avatarTony Luck <tony.luck@intel.com>
parent 10590a9d
Loading
Loading
Loading
Loading
+59 −0
Original line number Diff line number Diff line
@@ -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);