EDAC/{skx_common,imh}: Add EDAC driver for Intel Diamond Rapids servers

Intel Diamond Rapids CPUs include Integrated Memory and I/O Hubs (IMH).
The memory controllers within the IMHs provide memory stacks to the
processor. Create a new driver for this IMH-based memory controllers
rather than applying additional patches to the existing i10nm_edac.c
for the following reasons:

1) The memory controllers are not presented as PCI devices; instead,
   the detection and all their registers have been transitioned to
   MMIO-based memory spaces.

2) Validation processes are costly. Modifications to i10nm_edac would
   require extensive validation checks against multiple platforms,
   including Ice Lake, Sapphire Rapids, Emerald Rapids, Granite Rapids,
   Sierra Forest, and Grand Ridge.

3) Future Intel CPUs will likely only need patches on top of this new
   EDAC driver. Validation can be limited to Diamond Rapids servers
   and future Intel CPU generations.

[Tony: Fix kerneldoc for struct local_reg]
[randconfig: Added dependencies on NFIT and DMI]

Tested-by: Yi Lai <yi1.lai@intel.com>
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20251119134132.2389472-5-qiuxu.zhuo@intel.com
This commit is contained in:
Qiuxu Zhuo
2025-11-21 10:19:43 -08:00
committed by Tony Luck
parent d4839582bc
commit 9fc67b1170
5 changed files with 648 additions and 26 deletions
+12
View File
@@ -283,6 +283,18 @@ config EDAC_I10NM
system has non-volatile DIMMs you should also manually
select CONFIG_ACPI_NFIT.
config EDAC_IMH
tristate "Intel Integrated Memory/IO Hub MC"
depends on X86_64 && X86_MCE_INTEL && ACPI
depends on ACPI_NFIT || !ACPI_NFIT # if ACPI_NFIT=m, EDAC_IMH can't be y
select DMI
select ACPI_ADXL
help
Support for error detection and correction the Intel
Integrated Memory/IO Hub Memory Controller. This MC IP is
first used on the Diamond Rapids servers but may appear on
others in the future.
config EDAC_PND2
tristate "Intel Pondicherry2"
depends on PCI && X86_64 && X86_MCE_INTEL
+3
View File
@@ -65,6 +65,9 @@ obj-$(CONFIG_EDAC_SKX) += skx_edac.o skx_edac_common.o
i10nm_edac-y := i10nm_base.o
obj-$(CONFIG_EDAC_I10NM) += i10nm_edac.o skx_edac_common.o
imh_edac-y := imh_base.o
obj-$(CONFIG_EDAC_IMH) += imh_edac.o skx_edac_common.o
obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o
obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -124,7 +124,7 @@ void skx_adxl_put(void)
}
EXPORT_SYMBOL_GPL(skx_adxl_put);
static void skx_init_mc_mapping(struct skx_dev *d)
void skx_init_mc_mapping(struct skx_dev *d)
{
/*
* By default, the BIOS presents all memory controllers within each
@@ -135,6 +135,7 @@ static void skx_init_mc_mapping(struct skx_dev *d)
for (int i = 0; i < d->num_imc; i++)
d->imc[i].mc_mapping = i;
}
EXPORT_SYMBOL_GPL(skx_init_mc_mapping);
void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc)
{
@@ -823,6 +824,9 @@ void skx_remove(void)
if (d->imc[i].mbase)
iounmap(d->imc[i].mbase);
if (d->imc[i].dev)
put_device(d->imc[i].dev);
for (j = 0; j < d->imc[i].num_channels; j++) {
if (d->imc[i].chan[j].cdev)
pci_dev_put(d->imc[i].chan[j].cdev);
@@ -846,7 +850,7 @@ EXPORT_SYMBOL_GPL(skx_remove);
/*
* Debug feature.
* Exercise the address decode logic by writing an address to
* /sys/kernel/debug/edac/{skx,i10nm}_test/addr.
* /sys/kernel/debug/edac/{skx,i10nm,imh}_test/addr.
*/
static struct dentry *skx_test;
+65 -24
View File
@@ -121,20 +121,33 @@ struct reg_rrl {
* memory controllers on the die.
*/
struct skx_dev {
struct list_head list;
/* {skx,i10nm}_edac */
u8 bus[4];
int seg;
struct pci_dev *sad_all;
struct pci_dev *util_all;
struct pci_dev *uracu; /* for i10nm CPU */
struct pci_dev *pcu_cr3; /* for HBM memory detection */
struct pci_dev *uracu;
struct pci_dev *pcu_cr3;
u32 mcroute;
/* imh_edac */
/* System-view MMIO base physical addresses. */
u64 mmio_base_h_north;
u64 mmio_base_h_south;
int pkg;
int num_imc;
struct list_head list;
struct skx_imc {
/* i10nm_edac */
struct pci_dev *mdev;
/* imh_edac */
struct device *dev;
struct mem_ctl_info *mci;
struct pci_dev *mdev; /* for i10nm CPU */
void __iomem *mbase; /* for i10nm CPU */
int chan_mmio_sz; /* for i10nm CPU */
void __iomem *mbase;
int chan_mmio_sz;
int num_channels; /* channels per memory controller */
int num_dimms; /* dimms per channel */
bool hbm_mc;
@@ -178,7 +191,8 @@ enum type {
SKX,
I10NM,
SPR,
GNR
GNR,
DMR,
};
enum {
@@ -237,10 +251,6 @@ struct pci_bdf {
struct res_config {
enum type type;
/* Configuration agent device ID */
unsigned int decs_did;
/* Default bus number configuration register offset */
int busno_cfg_offset;
/* DDR memory controllers per socket */
int ddr_imc_num;
/* DDR channels per DDR memory controller */
@@ -258,23 +268,53 @@ struct res_config {
/* Per HBM channel memory-mapped I/O size */
int hbm_chan_mmio_sz;
bool support_ddr5;
/* SAD device BDF */
struct pci_bdf sad_all_bdf;
/* PCU device BDF */
struct pci_bdf pcu_cr3_bdf;
/* UTIL device BDF */
struct pci_bdf util_all_bdf;
/* URACU device BDF */
struct pci_bdf uracu_bdf;
/* DDR mdev device BDF */
struct pci_bdf ddr_mdev_bdf;
/* HBM mdev device BDF */
struct pci_bdf hbm_mdev_bdf;
int sad_all_offset;
/* RRL register sets per DDR channel */
struct reg_rrl *reg_rrl_ddr;
/* RRL register sets per HBM channel */
struct reg_rrl *reg_rrl_hbm[2];
union {
/* {skx,i10nm}_edac */
struct {
/* Configuration agent device ID */
unsigned int decs_did;
/* Default bus number configuration register offset */
int busno_cfg_offset;
struct pci_bdf sad_all_bdf;
struct pci_bdf pcu_cr3_bdf;
struct pci_bdf util_all_bdf;
struct pci_bdf uracu_bdf;
struct pci_bdf ddr_mdev_bdf;
struct pci_bdf hbm_mdev_bdf;
int sad_all_offset;
};
/* imh_edac */
struct {
/* MMIO base physical address in local package view */
u64 mmio_base_l_north;
u64 mmio_base_l_south;
u64 ddr_imc_base;
u64 ddr_reg_mcmtr_offset;
u8 ddr_reg_mcmtr_width;
u64 ddr_reg_dimmmtr_offset;
u8 ddr_reg_dimmmtr_width;
u64 ubox_base;
u32 ubox_size;
u32 ubox_reg_mmio_base_offset;
u8 ubox_reg_mmio_base_width;
u32 ubox_reg_socket_id_offset;
u8 ubox_reg_socket_id_width;
u64 pcu_base;
u32 pcu_size;
u32 pcu_reg_capid3_offset;
u8 pcu_reg_capid3_width;
u64 sca_base;
u32 sca_size;
u32 sca_reg_tolm_offset;
u8 sca_reg_tolm_width;
u32 sca_reg_tohm_offset;
u8 sca_reg_tohm_width;
};
};
};
typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci,
@@ -287,6 +327,7 @@ void skx_adxl_put(void);
void skx_set_decode(skx_decode_f decode, skx_show_retry_log_f show_retry_log);
void skx_set_mem_cfg(bool mem_cfg_2lm);
void skx_set_res_cfg(struct res_config *cfg);
void skx_init_mc_mapping(struct skx_dev *d);
void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc);
int skx_get_src_id(struct skx_dev *d, int off, u8 *id);