diff options
Diffstat (limited to 'drivers/edac/sb_edac.c')
-rw-r--r-- | drivers/edac/sb_edac.c | 63 |
1 files changed, 47 insertions, 16 deletions
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 687d0f23b9cc..dc0591654011 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -300,6 +300,12 @@ enum domain { SOCK, }; +enum mirroring_mode { + NON_MIRRORING, + ADDR_RANGE_MIRRORING, + FULL_MIRRORING, +}; + struct sbridge_pvt; struct sbridge_info { enum type type; @@ -377,8 +383,9 @@ struct sbridge_pvt { struct sbridge_channel channel[NUM_CHANNELS]; /* Memory type detection */ - bool is_mirrored, is_lockstep, is_close_pg; + bool is_cur_addr_mirrored, is_lockstep, is_close_pg; bool is_chan_hash; + enum mirroring_mode mirror_mode; /* Memory description */ u64 tolm, tohm; @@ -1648,10 +1655,6 @@ static int get_dimm_config(struct mem_ctl_info *mci) enum edac_type mode; u32 reg; - if (pvt->info.type == HASWELL || pvt->info.type == BROADWELL) { - pci_read_config_dword(pvt->pci_ha, HASWELL_HASYSDEFEATURE2, ®); - pvt->is_chan_hash = GET_BITFIELD(reg, 21, 21); - } pvt->sbridge_dev->node_id = pvt->info.get_node_id(pvt); edac_dbg(0, "mc#%d: Node ID: %d, source ID: %d\n", pvt->sbridge_dev->mc, @@ -1663,22 +1666,45 @@ static int get_dimm_config(struct mem_ctl_info *mci) */ if (pvt->info.type == KNIGHTS_LANDING) { mode = EDAC_S4ECD4ED; - pvt->is_mirrored = false; + pvt->mirror_mode = NON_MIRRORING; + pvt->is_cur_addr_mirrored = false; if (knl_get_dimm_capacity(pvt, knl_mc_sizes) != 0) return -1; - pci_read_config_dword(pvt->pci_ta, KNL_MCMTR, &pvt->info.mcmtr); + if (pci_read_config_dword(pvt->pci_ta, KNL_MCMTR, &pvt->info.mcmtr)) { + edac_dbg(0, "Failed to read KNL_MCMTR register\n"); + return -ENODEV; + } } else { - pci_read_config_dword(pvt->pci_ras, RASENABLES, ®); + if (pvt->info.type == HASWELL || pvt->info.type == BROADWELL) { + if (pci_read_config_dword(pvt->pci_ha, HASWELL_HASYSDEFEATURE2, ®)) { + edac_dbg(0, "Failed to read HASWELL_HASYSDEFEATURE2 register\n"); + return -ENODEV; + } + pvt->is_chan_hash = GET_BITFIELD(reg, 21, 21); + if (GET_BITFIELD(reg, 28, 28)) { + pvt->mirror_mode = ADDR_RANGE_MIRRORING; + edac_dbg(0, "Address range partial memory mirroring is enabled\n"); + goto next; + } + } + if (pci_read_config_dword(pvt->pci_ras, RASENABLES, ®)) { + edac_dbg(0, "Failed to read RASENABLES register\n"); + return -ENODEV; + } if (IS_MIRROR_ENABLED(reg)) { - edac_dbg(0, "Memory mirror is enabled\n"); - pvt->is_mirrored = true; + pvt->mirror_mode = FULL_MIRRORING; + edac_dbg(0, "Full memory mirroring is enabled\n"); } else { - edac_dbg(0, "Memory mirror is disabled\n"); - pvt->is_mirrored = false; + pvt->mirror_mode = NON_MIRRORING; + edac_dbg(0, "Memory mirroring is disabled\n"); } - pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr); +next: + if (pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr)) { + edac_dbg(0, "Failed to read MCMTR register\n"); + return -ENODEV; + } if (IS_LOCKSTEP_ENABLED(pvt->info.mcmtr)) { edac_dbg(0, "Lockstep is enabled\n"); mode = EDAC_S8ECD8ED; @@ -2092,7 +2118,8 @@ static int get_memory_error_data(struct mem_ctl_info *mci, pci_read_config_dword(pvt->pci_tad[base_ch], tad_ch_nilv_offset[n_tads], &tad_offset); - if (pvt->is_mirrored) { + if (pvt->mirror_mode == FULL_MIRRORING || + (pvt->mirror_mode == ADDR_RANGE_MIRRORING && n_tads == 0)) { *channel_mask |= 1 << ((base_ch + 2) % 4); switch(ch_way) { case 2: @@ -2103,8 +2130,12 @@ static int get_memory_error_data(struct mem_ctl_info *mci, sprintf(msg, "Invalid mirror set. Can't decode addr"); return -EINVAL; } - } else + + pvt->is_cur_addr_mirrored = true; + } else { sck_xch = (1 << sck_way) * ch_way; + pvt->is_cur_addr_mirrored = false; + } if (pvt->is_lockstep) *channel_mask |= 1 << ((base_ch + 1) % 4); @@ -2967,7 +2998,7 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci, * EDAC core should be handling the channel mask, in order to point * to the group of dimm's where the error may be happening. */ - if (!pvt->is_lockstep && !pvt->is_mirrored && !pvt->is_close_pg) + if (!pvt->is_lockstep && !pvt->is_cur_addr_mirrored && !pvt->is_close_pg) channel = first_channel; snprintf(msg, sizeof(msg), |