x86/sb_edac: Add row column translation for Broadwell

The sb_edac driver lacks translation for DIMM internal address.

Add memory address translation for row/column/bank/bank_group
on Broadwell.

Signed-off-by: Youquan Song <youquan.song@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://lore.kernel.org/all/20220722233338.341567-1-tony.luck@intel.com
This commit is contained in:
Youquan Song 2022-07-22 16:33:38 -07:00 committed by Tony Luck
parent d5f5e49953
commit d389059685
1 changed files with 138 additions and 10 deletions

View File

@ -335,6 +335,12 @@ struct sbridge_info {
struct sbridge_channel {
u32 ranks;
u32 dimms;
struct dimm {
u32 rowbits;
u32 colbits;
u32 bank_xor_enable;
u32 amap_fine;
} dimm[MAX_DIMMS];
};
struct pci_id_descr {
@ -1603,7 +1609,7 @@ static int __populate_dimms(struct mem_ctl_info *mci,
banks = 8;
for (i = 0; i < channels; i++) {
u32 mtr;
u32 mtr, amap = 0;
int max_dimms_per_channel;
@ -1615,6 +1621,7 @@ static int __populate_dimms(struct mem_ctl_info *mci,
max_dimms_per_channel = ARRAY_SIZE(mtr_regs);
if (!pvt->pci_tad[i])
continue;
pci_read_config_dword(pvt->pci_tad[i], 0x8c, &amap);
}
for (j = 0; j < max_dimms_per_channel; j++) {
@ -1627,6 +1634,7 @@ static int __populate_dimms(struct mem_ctl_info *mci,
mtr_regs[j], &mtr);
}
edac_dbg(4, "Channel #%d MTR%d = %x\n", i, j, mtr);
if (IS_DIMM_PRESENT(mtr)) {
if (!IS_ECC_ENABLED(pvt->info.mcmtr)) {
sbridge_printk(KERN_ERR, "CPU SrcID #%d, Ha #%d, Channel #%d has DIMMs, but ECC is disabled\n",
@ -1661,6 +1669,11 @@ static int __populate_dimms(struct mem_ctl_info *mci,
dimm->dtype = pvt->info.get_width(pvt, mtr);
dimm->mtype = mtype;
dimm->edac_mode = mode;
pvt->channel[i].dimm[j].rowbits = order_base_2(rows);
pvt->channel[i].dimm[j].colbits = order_base_2(cols);
pvt->channel[i].dimm[j].bank_xor_enable =
GET_BITFIELD(pvt->info.mcmtr, 9, 9);
pvt->channel[i].dimm[j].amap_fine = GET_BITFIELD(amap, 0, 0);
snprintf(dimm->label, sizeof(dimm->label),
"CPU_SrcID#%u_Ha#%u_Chan#%u_DIMM#%u",
pvt->sbridge_dev->source_id, pvt->sbridge_dev->dom, i, j);
@ -1922,6 +1935,99 @@ static struct mem_ctl_info *get_mci_for_node_id(u8 node_id, u8 ha)
return NULL;
}
static u8 sb_close_row[] = {
15, 16, 17, 18, 20, 21, 22, 28, 10, 11, 12, 13, 29, 30, 31, 32, 33
};
static u8 sb_close_column[] = {
3, 4, 5, 14, 19, 23, 24, 25, 26, 27
};
static u8 sb_open_row[] = {
14, 15, 16, 20, 28, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33
};
static u8 sb_open_column[] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 12
};
static u8 sb_open_fine_column[] = {
3, 4, 5, 7, 8, 9, 10, 11, 12, 13
};
static int sb_bits(u64 addr, int nbits, u8 *bits)
{
int i, res = 0;
for (i = 0; i < nbits; i++)
res |= ((addr >> bits[i]) & 1) << i;
return res;
}
static int sb_bank_bits(u64 addr, int b0, int b1, int do_xor, int x0, int x1)
{
int ret = GET_BITFIELD(addr, b0, b0) | (GET_BITFIELD(addr, b1, b1) << 1);
if (do_xor)
ret ^= GET_BITFIELD(addr, x0, x0) | (GET_BITFIELD(addr, x1, x1) << 1);
return ret;
}
static bool sb_decode_ddr4(struct mem_ctl_info *mci, int ch, u8 rank,
u64 rank_addr, char *msg)
{
int dimmno = 0;
int row, col, bank_address, bank_group;
struct sbridge_pvt *pvt;
u32 bg0 = 0, rowbits = 0, colbits = 0;
u32 amap_fine = 0, bank_xor_enable = 0;
dimmno = (rank < 12) ? rank / 4 : 2;
pvt = mci->pvt_info;
amap_fine = pvt->channel[ch].dimm[dimmno].amap_fine;
bg0 = amap_fine ? 6 : 13;
rowbits = pvt->channel[ch].dimm[dimmno].rowbits;
colbits = pvt->channel[ch].dimm[dimmno].colbits;
bank_xor_enable = pvt->channel[ch].dimm[dimmno].bank_xor_enable;
if (pvt->is_lockstep) {
pr_warn_once("LockStep row/column decode is not supported yet!\n");
msg[0] = '\0';
return false;
}
if (pvt->is_close_pg) {
row = sb_bits(rank_addr, rowbits, sb_close_row);
col = sb_bits(rank_addr, colbits, sb_close_column);
col |= 0x400; /* C10 is autoprecharge, always set */
bank_address = sb_bank_bits(rank_addr, 8, 9, bank_xor_enable, 22, 28);
bank_group = sb_bank_bits(rank_addr, 6, 7, bank_xor_enable, 20, 21);
} else {
row = sb_bits(rank_addr, rowbits, sb_open_row);
if (amap_fine)
col = sb_bits(rank_addr, colbits, sb_open_fine_column);
else
col = sb_bits(rank_addr, colbits, sb_open_column);
bank_address = sb_bank_bits(rank_addr, 18, 19, bank_xor_enable, 22, 23);
bank_group = sb_bank_bits(rank_addr, bg0, 17, bank_xor_enable, 20, 21);
}
row &= (1u << rowbits) - 1;
sprintf(msg, "row:0x%x col:0x%x bank_addr:%d bank_group:%d",
row, col, bank_address, bank_group);
return true;
}
static bool sb_decode_ddr3(struct mem_ctl_info *mci, int ch, u8 rank,
u64 rank_addr, char *msg)
{
pr_warn_once("DDR3 row/column decode not support yet!\n");
msg[0] = '\0';
return false;
}
static int get_memory_error_data(struct mem_ctl_info *mci,
u64 addr,
u8 *socket, u8 *ha,
@ -1937,12 +2043,13 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
int interleave_mode, shiftup = 0;
unsigned int sad_interleave[MAX_INTERLEAVE];
u32 reg, dram_rule;
u8 ch_way, sck_way, pkg, sad_ha = 0;
u8 ch_way, sck_way, pkg, sad_ha = 0, rankid = 0;
u32 tad_offset;
u32 rir_way;
u32 mb, gb;
u64 ch_addr, offset, limit = 0, prv = 0;
u64 rank_addr;
enum mem_type mtype;
/*
* Step 0) Check if the address is at special memory ranges
@ -2226,6 +2333,28 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
pci_read_config_dword(pvt->pci_tad[base_ch], rir_offset[n_rir][idx], &reg);
*rank = RIR_RNK_TGT(pvt->info.type, reg);
if (pvt->info.type == BROADWELL) {
if (pvt->is_close_pg)
shiftup = 6;
else
shiftup = 13;
rank_addr = ch_addr >> shiftup;
rank_addr /= (1 << rir_way);
rank_addr <<= shiftup;
rank_addr |= ch_addr & GENMASK_ULL(shiftup - 1, 0);
rank_addr -= RIR_OFFSET(pvt->info.type, reg);
mtype = pvt->info.get_memory_type(pvt);
rankid = *rank;
if (mtype == MEM_DDR4 || mtype == MEM_RDDR4)
sb_decode_ddr4(mci, base_ch, rankid, rank_addr, msg);
else
sb_decode_ddr3(mci, base_ch, rankid, rank_addr, msg);
} else {
msg[0] = '\0';
}
edac_dbg(0, "RIR#%d: channel address 0x%08Lx < 0x%08Lx, RIR interleave %d, index %d\n",
n_rir,
ch_addr,
@ -2950,7 +3079,7 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
struct mem_ctl_info *new_mci;
struct sbridge_pvt *pvt = mci->pvt_info;
enum hw_event_mc_err_type tp_event;
char *optype, msg[256];
char *optype, msg[256], msg_full[512];
bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0);
bool overflow = GET_BITFIELD(m->status, 62, 62);
bool uncorrected_error = GET_BITFIELD(m->status, 61, 61);
@ -3089,18 +3218,17 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
*/
if (!pvt->is_lockstep && !pvt->is_cur_addr_mirrored && !pvt->is_close_pg)
channel = first_channel;
snprintf(msg, sizeof(msg),
"%s%s area:%s err_code:%04x:%04x socket:%d ha:%d channel_mask:%ld rank:%d",
snprintf(msg_full, sizeof(msg_full),
"%s%s area:%s err_code:%04x:%04x socket:%d ha:%d channel_mask:%ld rank:%d %s",
overflow ? " OVERFLOW" : "",
(uncorrected_error && recoverable) ? " recoverable" : "",
area_type,
mscod, errcode,
socket, ha,
channel_mask,
rank);
rank, msg);
edac_dbg(0, "%s\n", msg);
edac_dbg(0, "%s\n", msg_full);
/* FIXME: need support for channel mask */
@ -3111,7 +3239,7 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
edac_mc_handle_error(tp_event, mci, core_err_cnt,
m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
channel, dimm, -1,
optype, msg);
optype, msg_full);
return;
err_parsing:
edac_mc_handle_error(tp_event, mci, core_err_cnt, 0, 0, 0,