summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQiuxu Zhuo <qiuxu.zhuo@intel.com>2026-07-30 10:42:36 +0800
committerTony Luck <tony.luck@intel.com>2026-07-30 11:33:06 -0700
commite492449e39b7aee9bfcb1bda3181fa942d24cdd2 (patch)
treeb4df2dd2e1f18de4841f0394443a4623240825ef
parent8ac9136d79e960b8b8b9a41b9d4076e4afe6de5e (diff)
EDAC/igen6: Detect present memory controllers at runtime
The igen6_edac currently relies on res_config::num_imc to describe the number of memory controllers supported by each SoC. As a result, adding support for a new platform requires updating this configuration even though the hardware can be discovered at runtime. Instead, detect the number of present memory controllers at runtime and size the driver state accordingly. This eliminates the need to update res_config whenever a new SoC variant is added. Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com> Signed-off-by: Tony Luck <tony.luck@intel.com> Link: https://patch.msgid.link/20260730024238.4096623-9-qiuxu.zhuo@intel.com
-rw-r--r--drivers/edac/igen6_edac.c95
1 files changed, 63 insertions, 32 deletions
diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c
index 2960af172c57..d468c655348c 100644
--- a/drivers/edac/igen6_edac.c
+++ b/drivers/edac/igen6_edac.c
@@ -42,7 +42,8 @@
#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo))
-#define NUM_IMC 2 /* Max memory controllers */
+/* Probing upper bound, not a hardware capability limit. */
+#define MAX_IMC_TO_PROBE 8
#define NUM_CHANNELS 2 /* Max channels */
#define NUM_DIMMS 2 /* Max DIMMs per channel */
@@ -182,11 +183,11 @@ static struct res_config {
} *res_cfg;
static struct igen6_pvt {
- struct igen6_imc imc[NUM_IMC];
void __iomem *memss_pma_cr;
u64 ms_hash;
u64 ms_s_size;
int ms_l_map;
+ struct igen6_imc imc[];
} *igen6_pvt;
/* The top of low usable DRAM */
@@ -353,6 +354,46 @@ static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
return 0;
}
+/* Check whether the memory controller is absent. */
+static bool imc_absent(void __iomem *window)
+{
+ return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
+}
+
+/* Return MMIO base address of the memory controller if it's present, otherwise return NULL. */
+static void __iomem *map_imc_window(u64 mchbar, int pmc)
+{
+ void __iomem *window;
+
+ window = ioremap(mchbar + pmc * MCHBAR_SIZE, MCHBAR_SIZE);
+ if (!window)
+ return NULL;
+
+ if (imc_absent(window)) {
+ iounmap(window);
+ return NULL;
+ }
+
+ return window;
+}
+
+/* Return the number of present memory controllers. */
+static int get_imc_num(u64 mchbar)
+{
+ void __iomem *window;
+ int lmc, pmc;
+
+ for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
+ window = map_imc_window(mchbar, pmc);
+ if (window) {
+ iounmap(window);
+ lmc++;
+ }
+ }
+
+ return lmc;
+}
+
static bool ehl_ibecc_available(struct pci_dev *pdev)
{
u32 v;
@@ -1457,18 +1498,27 @@ static struct igen6_pvt *igen6_pvt_setup(struct pci_dev *pdev)
{
void __iomem *memss_pma_cr;
struct igen6_pvt *pvt;
+ int imc_num, rc;
u64 mchbar;
- int rc;
- pvt = kzalloc_obj(*igen6_pvt);
- if (!pvt)
+ rc = get_mchbar(pdev, &mchbar);
+ if (rc)
return NULL;
- rc = get_mchbar(pdev, &mchbar);
- if (rc) {
- kfree(pvt);
+ imc_num = get_imc_num(mchbar);
+ if (!imc_num) {
+ igen6_printk(KERN_ERR, "No mc found.\n");
return NULL;
}
+ edac_dbg(2, "%d mcs found.\n", imc_num);
+
+ /* Use the runtime detected IMC count. */
+ if (res_cfg->num_imc != imc_num)
+ res_cfg->num_imc = imc_num;
+
+ pvt = kzalloc_flex(*pvt, imc, imc_num);
+ if (!pvt)
+ return NULL;
memss_pma_cr = ioremap(mchbar, MCHBAR_SIZE * 2);
if (!memss_pma_cr) {
@@ -1553,12 +1603,6 @@ static void igen6_check(struct mem_ctl_info *mci)
irq_work_queue(&ecclog_irq_work);
}
-/* Check whether the memory controller is absent. */
-static bool igen6_imc_absent(void __iomem *window)
-{
- return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
-}
-
static void imc_release(struct device *dev)
{
/* Nothing to do, the 'imc' owns the 'dev' and will also release it. */
@@ -1670,26 +1714,15 @@ 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);
+ for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
+ window = map_imc_window(mchbar, pmc);
+ if (!window)
continue;
- }
rc = igen6_register_mci(lmc, window, pdev);
if (rc)
- goto out_iounmap;
+ goto err_unregister;
/* Done, if all present MCs are detected and registered. */
if (++lmc >= res_cfg->num_imc)
@@ -1709,10 +1742,8 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
return 0;
-out_iounmap:
+err_unregister:
iounmap(window);
-
-out_unregister_mcis:
igen6_unregister_mcis();
return rc;