#include "internal.h"
static const u16 umc_coh_st_map[32] = {
0x393, 0x293, 0x193, 0x093,
0x392, 0x292, 0x192, 0x092,
0x391, 0x291, 0x191, 0x091,
0x390, 0x290, 0x190, 0x090,
0x793, 0x693, 0x593, 0x493,
0x792, 0x692, 0x592, 0x492,
0x791, 0x691, 0x591, 0x491,
0x790, 0x690, 0x590, 0x490,
};
#define UMC_ID_MI300 GENMASK(23, 12)
static u8 get_coh_st_inst_id_mi300(struct atl_err *err)
{
u16 umc_id = FIELD_GET(UMC_ID_MI300, err->ipid);
u8 i;
for (i = 0; i < ARRAY_SIZE(umc_coh_st_map); i++) {
if (umc_id == umc_coh_st_map[i])
break;
}
WARN_ON_ONCE(i >= ARRAY_SIZE(umc_coh_st_map));
return i;
}
struct xor_bits {
bool xor_enable;
u16 col_xor;
u32 row_xor;
};
#define NUM_BANK_BITS 4
#define NUM_COL_BITS 5
#define NUM_SID_BITS 2
static struct {
struct xor_bits bank[NUM_BANK_BITS];
struct xor_bits pc;
u8 bank_xor;
} addr_hash;
static struct {
u8 bank[NUM_BANK_BITS];
u8 col[NUM_COL_BITS];
u8 sid[NUM_SID_BITS];
u8 num_row_lo;
u8 num_row_hi;
u8 row_lo;
u8 row_hi;
u8 pc;
} bit_shifts;
#define MI300_UMC_CH_BASE 0x90000
#define MI300_ADDR_CFG (MI300_UMC_CH_BASE + 0x30)
#define MI300_ADDR_SEL (MI300_UMC_CH_BASE + 0x40)
#define MI300_COL_SEL_LO (MI300_UMC_CH_BASE + 0x50)
#define MI300_ADDR_SEL_2 (MI300_UMC_CH_BASE + 0xA4)
#define MI300_ADDR_HASH_BANK0 (MI300_UMC_CH_BASE + 0xC8)
#define MI300_ADDR_HASH_PC (MI300_UMC_CH_BASE + 0xE0)
#define MI300_ADDR_HASH_PC2 (MI300_UMC_CH_BASE + 0xE4)
#define ADDR_HASH_XOR_EN BIT(0)
#define ADDR_HASH_COL_XOR GENMASK(13, 1)
#define ADDR_HASH_ROW_XOR GENMASK(31, 14)
#define ADDR_HASH_BANK_XOR GENMASK(5, 0)
#define ADDR_CFG_NUM_ROW_LO GENMASK(11, 8)
#define ADDR_CFG_NUM_ROW_HI GENMASK(15, 12)
#define ADDR_SEL_BANK0 GENMASK(3, 0)
#define ADDR_SEL_BANK1 GENMASK(7, 4)
#define ADDR_SEL_BANK2 GENMASK(11, 8)
#define ADDR_SEL_BANK3 GENMASK(15, 12)
#define ADDR_SEL_BANK4 GENMASK(20, 16)
#define ADDR_SEL_ROW_LO GENMASK(27, 24)
#define ADDR_SEL_ROW_HI GENMASK(31, 28)
#define COL_SEL_LO_COL0 GENMASK(3, 0)
#define COL_SEL_LO_COL1 GENMASK(7, 4)
#define COL_SEL_LO_COL2 GENMASK(11, 8)
#define COL_SEL_LO_COL3 GENMASK(15, 12)
#define COL_SEL_LO_COL4 GENMASK(19, 16)
#define ADDR_SEL_2_BANK5 GENMASK(4, 0)
#define ADDR_SEL_2_CHAN GENMASK(15, 12)
int get_umc_info_mi300(void)
{
u32 temp;
int ret;
u8 i;
for (i = 0; i < NUM_BANK_BITS; i++) {
ret = amd_smn_read(0, MI300_ADDR_HASH_BANK0 + (i * 4), &temp);
if (ret)
return ret;
addr_hash.bank[i].xor_enable = FIELD_GET(ADDR_HASH_XOR_EN, temp);
addr_hash.bank[i].col_xor = FIELD_GET(ADDR_HASH_COL_XOR, temp);
addr_hash.bank[i].row_xor = FIELD_GET(ADDR_HASH_ROW_XOR, temp);
}
ret = amd_smn_read(0, MI300_ADDR_HASH_PC, &temp);
if (ret)
return ret;
addr_hash.pc.xor_enable = FIELD_GET(ADDR_HASH_XOR_EN, temp);
addr_hash.pc.col_xor = FIELD_GET(ADDR_HASH_COL_XOR, temp);
addr_hash.pc.row_xor = FIELD_GET(ADDR_HASH_ROW_XOR, temp);
ret = amd_smn_read(0, MI300_ADDR_HASH_PC2, &temp);
if (ret)
return ret;
addr_hash.bank_xor = FIELD_GET(ADDR_HASH_BANK_XOR, temp);
ret = amd_smn_read(0, MI300_ADDR_CFG, &temp);
if (ret)
return ret;
bit_shifts.num_row_hi = FIELD_GET(ADDR_CFG_NUM_ROW_HI, temp);
bit_shifts.num_row_lo = 10 + FIELD_GET(ADDR_CFG_NUM_ROW_LO, temp);
ret = amd_smn_read(0, MI300_ADDR_SEL, &temp);
if (ret)
return ret;
bit_shifts.bank[0] = 5 + FIELD_GET(ADDR_SEL_BANK0, temp);
bit_shifts.bank[1] = 5 + FIELD_GET(ADDR_SEL_BANK1, temp);
bit_shifts.bank[2] = 5 + FIELD_GET(ADDR_SEL_BANK2, temp);
bit_shifts.bank[3] = 5 + FIELD_GET(ADDR_SEL_BANK3, temp);
bit_shifts.sid[0] = 5 + FIELD_GET(ADDR_SEL_BANK4, temp);
bit_shifts.row_lo = 12 + FIELD_GET(ADDR_SEL_ROW_LO, temp);
bit_shifts.row_hi = 24 + FIELD_GET(ADDR_SEL_ROW_HI, temp);
ret = amd_smn_read(0, MI300_COL_SEL_LO, &temp);
if (ret)
return ret;
bit_shifts.col[0] = 2 + FIELD_GET(COL_SEL_LO_COL0, temp);
bit_shifts.col[1] = 2 + FIELD_GET(COL_SEL_LO_COL1, temp);
bit_shifts.col[2] = 2 + FIELD_GET(COL_SEL_LO_COL2, temp);
bit_shifts.col[3] = 2 + FIELD_GET(COL_SEL_LO_COL3, temp);
bit_shifts.col[4] = 2 + FIELD_GET(COL_SEL_LO_COL4, temp);
ret = amd_smn_read(0, MI300_ADDR_SEL_2, &temp);
if (ret)
return ret;
bit_shifts.sid[1] = 5 + FIELD_GET(ADDR_SEL_2_BANK5, temp);
bit_shifts.pc = 5 + FIELD_GET(ADDR_SEL_2_CHAN, temp);
return 0;
}
#define MI300_UMC_MCA_BANK GENMASK(9, 6)
#define MI300_UMC_MCA_ROW GENMASK(24, 10)
#define MI300_UMC_MCA_PC BIT(25)
#define MI300_UMC_MCA_SID GENMASK(27, 26)
static unsigned long convert_dram_to_norm_addr_mi300(unsigned long addr)
{
u16 i, col, row, bank, pc, sid;
u32 temp;
col = FIELD_GET(MI300_UMC_MCA_COL, addr);
bank = FIELD_GET(MI300_UMC_MCA_BANK, addr);
row = FIELD_GET(MI300_UMC_MCA_ROW, addr);
pc = FIELD_GET(MI300_UMC_MCA_PC, addr);
sid = FIELD_GET(MI300_UMC_MCA_SID, addr);
for (i = 0; i < NUM_BANK_BITS; i++) {
if (!addr_hash.bank[i].xor_enable)
continue;
temp = hweight16(col & addr_hash.bank[i].col_xor) & 1;
temp ^= hweight16(row & addr_hash.bank[i].row_xor) & 1;
bank ^= temp << i;
}
if (addr_hash.pc.xor_enable) {
temp = hweight16(col & addr_hash.pc.col_xor) & 1;
temp ^= hweight16(row & addr_hash.pc.row_xor) & 1;
temp ^= hweight16((bank | sid << NUM_BANK_BITS) & addr_hash.bank_xor) & 1;
pc ^= temp;
}
addr = 0;
for (i = 0; i < NUM_COL_BITS; i++) {
temp = (col >> i) & 0x1;
addr |= temp << bit_shifts.col[i];
}
for (i = 0; i < NUM_BANK_BITS; i++) {
temp = (bank >> i) & 0x1;
addr |= temp << bit_shifts.bank[i];
}
for (i = 0; i < bit_shifts.num_row_lo; i++) {
temp = (row >> i) & 0x1;
addr |= temp << (i + bit_shifts.row_lo);
}
for (i = 0; i < bit_shifts.num_row_hi; i++) {
temp = (row >> (i + bit_shifts.num_row_lo)) & 0x1;
addr |= temp << (i + bit_shifts.row_hi);
}
addr |= pc << bit_shifts.pc;
for (i = 0; i < NUM_SID_BITS; i++) {
temp = (sid >> i) & 0x1;
addr |= temp << bit_shifts.sid[i];
}
pr_debug("Addr=0x%016lx", addr);
pr_debug("Bank=%u Row=%u Column=%u PC=%u SID=%u", bank, row, col, pc, sid);
return addr;
}
#define MI300_NUM_COL BIT(HWEIGHT(MI300_UMC_MCA_COL))
static void _retire_row_mi300(struct atl_err *a_err)
{
unsigned long addr;
struct page *p;
u8 col;
for (col = 0; col < MI300_NUM_COL; col++) {
a_err->addr &= ~MI300_UMC_MCA_COL;
a_err->addr |= FIELD_PREP(MI300_UMC_MCA_COL, col);
addr = amd_convert_umc_mca_addr_to_sys_addr(a_err);
if (IS_ERR_VALUE(addr))
continue;
addr = PHYS_PFN(addr);
p = pfn_to_online_page(addr);
if (!p)
continue;
if (PageHWPoison(p))
continue;
memory_failure(addr, 0);
}
}
static void retire_row_mi300(struct atl_err *a_err)
{
_retire_row_mi300(a_err);
a_err->addr ^= MI300_UMC_MCA_ROW13;
_retire_row_mi300(a_err);
}
void amd_retire_dram_row(struct atl_err *a_err)
{
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous)
return retire_row_mi300(a_err);
}
EXPORT_SYMBOL_GPL(amd_retire_dram_row);
static unsigned long get_addr(unsigned long addr)
{
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous)
return convert_dram_to_norm_addr_mi300(addr);
return addr;
}
#define MCA_IPID_INST_ID_HI GENMASK_ULL(47, 44)
static u8 get_die_id(struct atl_err *err)
{
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous) {
u8 node_id = FIELD_GET(MCA_IPID_INST_ID_HI, err->ipid);
return node_id >> 2;
}
return topology_amd_node_id(err->cpu) % topology_amd_nodes_per_pkg();
}
#define UMC_CHANNEL_NUM GENMASK(31, 20)
static u8 get_coh_st_inst_id(struct atl_err *err)
{
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous)
return get_coh_st_inst_id_mi300(err);
return FIELD_GET(UMC_CHANNEL_NUM, err->ipid);
}
unsigned long convert_umc_mca_addr_to_sys_addr(struct atl_err *err)
{
u8 socket_id = topology_physical_package_id(err->cpu);
u8 coh_st_inst_id = get_coh_st_inst_id(err);
unsigned long addr = get_addr(err->addr);
u8 die_id = get_die_id(err);
unsigned long ret_addr;
pr_debug("socket_id=0x%x die_id=0x%x coh_st_inst_id=0x%x addr=0x%016lx",
socket_id, die_id, coh_st_inst_id, addr);
ret_addr = prm_umc_norm_to_sys_addr(socket_id, err->ipid, addr);
if (!IS_ERR_VALUE(ret_addr) || df_cfg.flags.prm_only)
return ret_addr;
return norm_to_sys_addr(socket_id, die_id, coh_st_inst_id, addr);
}