Commit 20e190b1 authored by Qiuxu Zhuo's avatar Qiuxu Zhuo Committed by Tony Luck
Browse files

EDAC/igen6: Skip absent memory controllers



Some BIOS versions may fuse off certain memory controllers and set the
registers of these absent memory controllers to ~0. The current igen6_edac
mistakenly enumerates these absent memory controllers and registers them
with the EDAC core.

Skip the absent memory controllers to avoid mistakenly enumerating them.

Signed-off-by: default avatarQiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: default avatarTony Luck <tony.luck@intel.com>
Link: https://lore.kernel.org/r/20250408132455.489046-2-qiuxu.zhuo@intel.com
parent 8ffd015d
Loading
Loading
Loading
Loading
+62 −16
Original line number Diff line number Diff line
@@ -127,6 +127,7 @@

static const struct res_config {
	bool machine_check;
	/* The number of present memory controllers. */
	int num_imc;
	u32 imc_base;
	u32 cmf_base;
@@ -1201,23 +1202,21 @@ static void igen6_check(struct mem_ctl_info *mci)
		irq_work_queue(&ecclog_irq_work);
}

static int igen6_register_mci(int mc, u64 mchbar, struct pci_dev *pdev)
/* Check whether the memory controller is absent. */
static bool igen6_imc_absent(void __iomem *window)
{
	return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
}

static int igen6_register_mci(int mc, void __iomem *window, struct pci_dev *pdev)
{
	struct edac_mc_layer layers[2];
	struct mem_ctl_info *mci;
	struct igen6_imc *imc;
	void __iomem *window;
	int rc;

	edac_dbg(2, "\n");

	mchbar += mc * MCHBAR_SIZE;
	window = ioremap(mchbar, MCHBAR_SIZE);
	if (!window) {
		igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", mchbar);
		return -ENODEV;
	}

	layers[0].type = EDAC_MC_LAYER_CHANNEL;
	layers[0].size = NUM_CHANNELS;
	layers[0].is_virt_csrow = false;
@@ -1283,7 +1282,6 @@ static int igen6_register_mci(int mc, u64 mchbar, struct pci_dev *pdev)
fail2:
	edac_mc_free(mci);
fail:
	iounmap(window);
	return rc;
}

@@ -1309,6 +1307,56 @@ static void igen6_unregister_mcis(void)
	}
}

static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
{
	void __iomem *window;
	int lmc, pmc, rc;
	u64 base;

	for (lmc = 0, pmc = 0; pmc < NUM_IMC; pmc++) {
		base   = mchbar + pmc * MCHBAR_SIZE;
		window = ioremap(base, MCHBAR_SIZE);
		if (!window) {
			igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx for mc%d\n", base, pmc);
			rc = -ENOMEM;
			goto out_unregister_mcis;
		}

		if (igen6_imc_absent(window)) {
			iounmap(window);
			edac_dbg(2, "Skip absent mc%d\n", pmc);
			continue;
		}

		rc = igen6_register_mci(lmc, window, pdev);
		if (rc)
			goto out_iounmap;

		/* Done, if all present MCs are detected and registered. */
		if (++lmc >= res_cfg->num_imc)
			break;
	}

	if (!lmc) {
		igen6_printk(KERN_ERR, "No mc found.\n");
		return -ENODEV;
	}

	if (lmc < res_cfg->num_imc)
		igen6_printk(KERN_WARNING, "Expected %d mcs, but only %d detected.",
			     res_cfg->num_imc, lmc);

	return 0;

out_iounmap:
	iounmap(window);

out_unregister_mcis:
	igen6_unregister_mcis();

	return rc;
}

static int igen6_mem_slice_setup(u64 mchbar)
{
	struct igen6_imc *imc = &igen6_pvt->imc[0];
@@ -1405,7 +1453,7 @@ static void opstate_set(const struct res_config *cfg, const struct pci_device_id
static int igen6_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	u64 mchbar;
	int i, rc;
	int rc;

	edac_dbg(2, "\n");

@@ -1421,11 +1469,9 @@ static int igen6_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

	opstate_set(res_cfg, ent);

	for (i = 0; i < res_cfg->num_imc; i++) {
		rc = igen6_register_mci(i, mchbar, pdev);
	rc = igen6_register_mcis(pdev, mchbar);
	if (rc)
			goto fail2;
	}
		goto fail;

	if (res_cfg->num_imc > 1) {
		rc = igen6_mem_slice_setup(mchbar);