diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c index 9f653ace50e7..a07bbfd075d0 100644 --- a/drivers/edac/igen6_edac.c +++ b/drivers/edac/igen6_edac.c @@ -27,7 +27,7 @@ #include "edac_mc.h" #include "edac_module.h" -#define IGEN6_REVISION "v2.4" +#define IGEN6_REVISION "v2.5" #define EDAC_MOD_STR "igen6_edac" #define IGEN6_NMI_NAME "igen6_ibecc" @@ -75,7 +75,7 @@ #define IBECC_ACTIVATE_EN BIT(0) /* IBECC error log */ -#define ECC_ERROR_LOG_OFFSET (IBECC_BASE + 0x170) +#define ECC_ERROR_LOG_OFFSET (IBECC_BASE + res_cfg->ibecc_error_log_offset) #define ECC_ERROR_LOG_CE BIT_ULL(62) #define ECC_ERROR_LOG_UE BIT_ULL(63) #define ECC_ERROR_LOG_ADDR_SHIFT 5 @@ -89,27 +89,32 @@ #define MCHBAR_SIZE 0x10000 /* Parameters for the channel decode stage */ -#define MAD_INTER_CHANNEL_OFFSET 0x5000 +#define IMC_BASE (res_cfg->imc_base) +#define MAD_INTER_CHANNEL_OFFSET IMC_BASE #define MAD_INTER_CHANNEL_DDR_TYPE(v) GET_BITFIELD(v, 0, 2) #define MAD_INTER_CHANNEL_ECHM(v) GET_BITFIELD(v, 3, 3) #define MAD_INTER_CHANNEL_CH_L_MAP(v) GET_BITFIELD(v, 4, 4) #define MAD_INTER_CHANNEL_CH_S_SIZE(v) ((u64)GET_BITFIELD(v, 12, 19) << 29) /* Parameters for DRAM decode stage */ -#define MAD_INTRA_CH0_OFFSET 0x5004 +#define MAD_INTRA_CH0_OFFSET (IMC_BASE + 4) #define MAD_INTRA_CH_DIMM_L_MAP(v) GET_BITFIELD(v, 0, 0) /* DIMM characteristics */ -#define MAD_DIMM_CH0_OFFSET 0x500c +#define MAD_DIMM_CH0_OFFSET (IMC_BASE + 0xc) #define MAD_DIMM_CH_DIMM_L_SIZE(v) ((u64)GET_BITFIELD(v, 0, 6) << 29) #define MAD_DIMM_CH_DLW(v) GET_BITFIELD(v, 7, 8) #define MAD_DIMM_CH_DIMM_S_SIZE(v) ((u64)GET_BITFIELD(v, 16, 22) << 29) #define MAD_DIMM_CH_DSW(v) GET_BITFIELD(v, 24, 25) +/* Hash for memory controller selection */ +#define MAD_MC_HASH_OFFSET (IMC_BASE + 0x1b8) +#define MAC_MC_HASH_LSB(v) GET_BITFIELD(v, 1, 3) + /* Hash for channel selection */ -#define CHANNEL_HASH_OFFSET 0X5024 +#define CHANNEL_HASH_OFFSET (IMC_BASE + 0x24) /* Hash for enhanced channel selection */ -#define CHANNEL_EHASH_OFFSET 0X5028 +#define CHANNEL_EHASH_OFFSET (IMC_BASE + 0x28) #define CHANNEL_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6) #define CHANNEL_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26) #define CHANNEL_HASH_MODE(v) GET_BITFIELD(v, 28, 28) @@ -121,15 +126,17 @@ static struct res_config { bool machine_check; int num_imc; + u32 imc_base; u32 cmf_base; u32 cmf_size; u32 ms_hash_offset; u32 ibecc_base; + u32 ibecc_error_log_offset; bool (*ibecc_available)(struct pci_dev *pdev); /* Convert error address logged in IBECC to system physical address */ u64 (*err_addr_to_sys_addr)(u64 eaddr, int mc); /* Convert error address logged in IBECC to integrated memory controller address */ - u64 (*err_addr_to_imc_addr)(u64 eaddr); + u64 (*err_addr_to_imc_addr)(u64 eaddr, int mc); } *res_cfg; struct igen6_imc { @@ -209,6 +216,12 @@ static struct work_struct ecclog_work; /* Compute die IDs for Tiger Lake with IBECC */ #define DID_TGL_SKU 0x9a14 +/* Compute die IDs for Alder Lake with IBECC */ +#define DID_ADL_SKU1 0x4601 +#define DID_ADL_SKU2 0x4602 +#define DID_ADL_SKU3 0x4621 +#define DID_ADL_SKU4 0x4641 + static bool ehl_ibecc_available(struct pci_dev *pdev) { u32 v; @@ -224,7 +237,7 @@ static u64 ehl_err_addr_to_sys_addr(u64 eaddr, int mc) return eaddr; } -static u64 ehl_err_addr_to_imc_addr(u64 eaddr) +static u64 ehl_err_addr_to_imc_addr(u64 eaddr, int mc) { if (eaddr < igen6_tolud) return eaddr; @@ -315,22 +328,51 @@ static u64 tgl_err_addr_to_sys_addr(u64 eaddr, int mc) return mem_addr_to_sys_addr(maddr); } -static u64 tgl_err_addr_to_imc_addr(u64 eaddr) +static u64 tgl_err_addr_to_imc_addr(u64 eaddr, int mc) { return eaddr; } +static u64 adl_err_addr_to_sys_addr(u64 eaddr, int mc) +{ + return mem_addr_to_sys_addr(eaddr); +} + +static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc) +{ + u64 imc_addr, ms_s_size = igen6_pvt->ms_s_size; + struct igen6_imc *imc = &igen6_pvt->imc[mc]; + int intlv_bit; + u32 mc_hash; + + if (eaddr >= 2 * ms_s_size) + return eaddr - ms_s_size; + + mc_hash = readl(imc->window + MAD_MC_HASH_OFFSET); + + intlv_bit = MAC_MC_HASH_LSB(mc_hash) + 6; + + imc_addr = GET_BITFIELD(eaddr, intlv_bit + 1, 63) << intlv_bit | + GET_BITFIELD(eaddr, 0, intlv_bit - 1); + + return imc_addr; +} + static struct res_config ehl_cfg = { .num_imc = 1, + .imc_base = 0x5000, .ibecc_base = 0xdc00, .ibecc_available = ehl_ibecc_available, + .ibecc_error_log_offset = 0x170, .err_addr_to_sys_addr = ehl_err_addr_to_sys_addr, .err_addr_to_imc_addr = ehl_err_addr_to_imc_addr, }; static struct res_config icl_cfg = { .num_imc = 1, + .imc_base = 0x5000, .ibecc_base = 0xd800, + .ibecc_error_log_offset = 0x170, .ibecc_available = icl_ibecc_available, .err_addr_to_sys_addr = ehl_err_addr_to_sys_addr, .err_addr_to_imc_addr = ehl_err_addr_to_imc_addr, @@ -339,15 +381,28 @@ static struct res_config icl_cfg = { static struct res_config tgl_cfg = { .machine_check = true, .num_imc = 2, + .imc_base = 0x5000, .cmf_base = 0x11000, .cmf_size = 0x800, .ms_hash_offset = 0xac, .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, .ibecc_available = tgl_ibecc_available, .err_addr_to_sys_addr = tgl_err_addr_to_sys_addr, .err_addr_to_imc_addr = tgl_err_addr_to_imc_addr, }; +static struct res_config adl_cfg = { + .machine_check = true, + .num_imc = 2, + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x68, + .ibecc_available = tgl_ibecc_available, + .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, + .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, +}; + static const struct pci_device_id igen6_pci_tbl[] = { { PCI_VDEVICE(INTEL, DID_EHL_SKU5), (kernel_ulong_t)&ehl_cfg }, { PCI_VDEVICE(INTEL, DID_EHL_SKU6), (kernel_ulong_t)&ehl_cfg }, @@ -365,6 +420,10 @@ static const struct pci_device_id igen6_pci_tbl[] = { { PCI_VDEVICE(INTEL, DID_ICL_SKU11), (kernel_ulong_t)&icl_cfg }, { PCI_VDEVICE(INTEL, DID_ICL_SKU12), (kernel_ulong_t)&icl_cfg }, { PCI_VDEVICE(INTEL, DID_TGL_SKU), (kernel_ulong_t)&tgl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_SKU1), (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_SKU2), (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_SKU3), (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_SKU4), (kernel_ulong_t)&adl_cfg }, { }, }; MODULE_DEVICE_TABLE(pci, igen6_pci_tbl); @@ -624,7 +683,7 @@ static void ecclog_work_cb(struct work_struct *work) ECC_ERROR_LOG_ADDR_SHIFT; res.mc = node->mc; res.sys_addr = res_cfg->err_addr_to_sys_addr(eaddr, res.mc); - res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr); + res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr, res.mc); mci = igen6_pvt->imc[res.mc].mci; @@ -1071,6 +1130,9 @@ static int igen6_mem_slice_setup(u64 mchbar) edac_dbg(0, "ms_s_size: %llu MiB, ms_l_map %d\n", ms_s_size >> 20, ms_l_map); + if (!size) + return 0; + cmf = ioremap(base, size); if (!cmf) { igen6_printk(KERN_ERR, "Failed to ioremap cmf 0x%llx\n", base);