#include "internal.h"
static u16 get_dst_fabric_id(struct addr_ctx *ctx)
{
switch (df_cfg.rev) {
case DF2: return FIELD_GET(DF2_DST_FABRIC_ID, ctx->map.limit);
case DF3: return FIELD_GET(DF3_DST_FABRIC_ID, ctx->map.limit);
case DF3p5: return FIELD_GET(DF3p5_DST_FABRIC_ID, ctx->map.limit);
case DF4: return FIELD_GET(DF4_DST_FABRIC_ID, ctx->map.ctl);
case DF4p5: return FIELD_GET(DF4p5_DST_FABRIC_ID, ctx->map.ctl);
default:
atl_debug_on_bad_df_rev();
return 0;
}
}
static u64 make_space_for_coh_st_id_at_intlv_bit(struct addr_ctx *ctx)
{
return expand_bits(ctx->map.intlv_bit_pos,
ctx->map.total_intlv_bits,
ctx->ret_addr);
}
static u64 make_space_for_coh_st_id_split_2_1(struct addr_ctx *ctx)
{
u64 denorm_addr = expand_bits(ctx->map.intlv_bit_pos, 1, ctx->ret_addr);
if (ctx->map.total_intlv_bits <= 1)
return denorm_addr;
return expand_bits(12, ctx->map.total_intlv_bits - 1, denorm_addr);
}
static u64 make_space_for_coh_st_id_mi300(struct addr_ctx *ctx)
{
u8 num_intlv_bits = ilog2(ctx->map.num_intlv_chan);
u64 denorm_addr;
if (ctx->map.intlv_bit_pos != 8) {
pr_debug("Invalid interleave bit: %u", ctx->map.intlv_bit_pos);
return ~0ULL;
}
denorm_addr = expand_bits(8, min(num_intlv_bits, 4), ctx->ret_addr);
denorm_addr = expand_bits(12, ilog2(ctx->map.num_intlv_dies), denorm_addr);
if (num_intlv_bits > 4)
denorm_addr = expand_bits(14, 1, denorm_addr);
return denorm_addr;
}
static u64 make_space_for_coh_st_id(struct addr_ctx *ctx)
{
switch (ctx->map.intlv_mode) {
case NOHASH_2CHAN:
case NOHASH_4CHAN:
case NOHASH_8CHAN:
case NOHASH_16CHAN:
case NOHASH_32CHAN:
case DF2_2CHAN_HASH:
return make_space_for_coh_st_id_at_intlv_bit(ctx);
case DF3_COD4_2CHAN_HASH:
case DF3_COD2_4CHAN_HASH:
case DF3_COD1_8CHAN_HASH:
case DF4_NPS4_2CHAN_HASH:
case DF4_NPS2_4CHAN_HASH:
case DF4_NPS1_8CHAN_HASH:
case DF4p5_NPS4_2CHAN_1K_HASH:
case DF4p5_NPS4_2CHAN_2K_HASH:
case DF4p5_NPS2_4CHAN_2K_HASH:
case DF4p5_NPS1_8CHAN_2K_HASH:
case DF4p5_NPS1_16CHAN_2K_HASH:
return make_space_for_coh_st_id_split_2_1(ctx);
case MI3_HASH_8CHAN:
case MI3_HASH_16CHAN:
case MI3_HASH_32CHAN:
return make_space_for_coh_st_id_mi300(ctx);
default:
atl_debug_on_bad_intlv_mode(ctx);
return ~0ULL;
}
}
static u16 get_coh_st_id_df2(struct addr_ctx *ctx)
{
u8 num_socket_intlv_bits = ilog2(ctx->map.num_intlv_sockets);
u8 num_die_intlv_bits = ilog2(ctx->map.num_intlv_dies);
u8 num_intlv_bits;
u16 coh_st_id, mask;
coh_st_id = ctx->coh_st_fabric_id - get_dst_fabric_id(ctx);
num_intlv_bits = order_base_2(ctx->map.num_intlv_chan);
mask = GENMASK(num_intlv_bits - 1, 0);
coh_st_id &= mask;
if (num_die_intlv_bits) {
u16 die_bits;
mask = GENMASK(num_die_intlv_bits - 1, 0);
die_bits = ctx->coh_st_fabric_id & df_cfg.die_id_mask;
die_bits >>= df_cfg.die_id_shift;
coh_st_id |= (die_bits & mask) << num_intlv_bits;
num_intlv_bits += num_die_intlv_bits;
}
if (num_socket_intlv_bits) {
u16 socket_bits;
mask = GENMASK(num_socket_intlv_bits - 1, 0);
socket_bits = ctx->coh_st_fabric_id & df_cfg.socket_id_mask;
socket_bits >>= df_cfg.socket_id_shift;
coh_st_id |= (socket_bits & mask) << num_intlv_bits;
}
return coh_st_id;
}
static u16 get_coh_st_id_df4(struct addr_ctx *ctx)
{
u8 num_intlv_bits = ilog2(ctx->map.num_intlv_chan);
u16 mask = df_cfg.component_id_mask;
u16 socket_bits;
u16 coh_st_id = ctx->coh_st_fabric_id & mask;
coh_st_id -= get_dst_fabric_id(ctx) & mask;
mask = GENMASK(num_intlv_bits - 1, 0);
coh_st_id &= mask;
if (ctx->map.num_intlv_sockets <= 1)
return coh_st_id;
num_intlv_bits = ilog2(ctx->map.num_intlv_sockets);
coh_st_id <<= num_intlv_bits;
mask = GENMASK(num_intlv_bits - 1, 0);
socket_bits = (ctx->coh_st_fabric_id & df_cfg.socket_id_mask) >> df_cfg.socket_id_shift;
coh_st_id |= socket_bits & mask;
return coh_st_id;
}
static u16 get_coh_st_id_mi300(struct addr_ctx *ctx)
{
u8 channel_bits, die_bits, stack_bit;
u16 die_id;
ctx->coh_st_fabric_id -= get_dst_fabric_id(ctx);
die_id = (ctx->coh_st_fabric_id & df_cfg.die_id_mask) >> df_cfg.die_id_shift;
channel_bits = FIELD_GET(GENMASK(3, 0), ctx->coh_st_fabric_id);
stack_bit = FIELD_GET(BIT(4), ctx->coh_st_fabric_id) << 6;
die_bits = die_id << 4;
return stack_bit | die_bits | channel_bits;
}
static u16 calculate_coh_st_id(struct addr_ctx *ctx)
{
switch (ctx->map.intlv_mode) {
case NOHASH_2CHAN:
case NOHASH_4CHAN:
case NOHASH_8CHAN:
case NOHASH_16CHAN:
case NOHASH_32CHAN:
case DF3_COD4_2CHAN_HASH:
case DF3_COD2_4CHAN_HASH:
case DF3_COD1_8CHAN_HASH:
case DF2_2CHAN_HASH:
return get_coh_st_id_df2(ctx);
case DF4_NPS4_2CHAN_HASH:
case DF4_NPS2_4CHAN_HASH:
case DF4_NPS1_8CHAN_HASH:
case DF4p5_NPS4_2CHAN_1K_HASH:
case DF4p5_NPS4_2CHAN_2K_HASH:
case DF4p5_NPS2_4CHAN_2K_HASH:
case DF4p5_NPS1_8CHAN_2K_HASH:
case DF4p5_NPS1_16CHAN_2K_HASH:
return get_coh_st_id_df4(ctx);
case MI3_HASH_8CHAN:
case MI3_HASH_16CHAN:
case MI3_HASH_32CHAN:
return get_coh_st_id_mi300(ctx);
case DF4p5_NPS2_4CHAN_1K_HASH:
case DF4p5_NPS1_8CHAN_1K_HASH:
case DF4p5_NPS1_16CHAN_1K_HASH:
return ctx->coh_st_fabric_id - get_dst_fabric_id(ctx);
default:
atl_debug_on_bad_intlv_mode(ctx);
return ~0;
}
}
static u64 insert_coh_st_id_at_intlv_bit(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id)
{
return denorm_addr | (coh_st_id << ctx->map.intlv_bit_pos);
}
static u64 insert_coh_st_id_split_2_1(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id)
{
denorm_addr |= (coh_st_id & BIT(0)) << ctx->map.intlv_bit_pos;
denorm_addr |= (coh_st_id & GENMASK(2, 1)) << 11;
return denorm_addr;
}
static u64 insert_coh_st_id_split_2_2(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id)
{
denorm_addr |= (coh_st_id & GENMASK(1, 0)) << 8;
denorm_addr |= (coh_st_id & GENMASK(3, 2)) << 10;
return denorm_addr;
}
static u64 insert_coh_st_id(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id)
{
switch (ctx->map.intlv_mode) {
case NOHASH_2CHAN:
case NOHASH_4CHAN:
case NOHASH_8CHAN:
case NOHASH_16CHAN:
case NOHASH_32CHAN:
case MI3_HASH_8CHAN:
case MI3_HASH_16CHAN:
case MI3_HASH_32CHAN:
case DF2_2CHAN_HASH:
return insert_coh_st_id_at_intlv_bit(ctx, denorm_addr, coh_st_id);
case DF3_COD4_2CHAN_HASH:
case DF3_COD2_4CHAN_HASH:
case DF3_COD1_8CHAN_HASH:
case DF4_NPS4_2CHAN_HASH:
case DF4_NPS2_4CHAN_HASH:
case DF4_NPS1_8CHAN_HASH:
case DF4p5_NPS4_2CHAN_1K_HASH:
case DF4p5_NPS4_2CHAN_2K_HASH:
case DF4p5_NPS2_4CHAN_2K_HASH:
case DF4p5_NPS1_8CHAN_2K_HASH:
case DF4p5_NPS1_16CHAN_2K_HASH:
return insert_coh_st_id_split_2_1(ctx, denorm_addr, coh_st_id);
case DF4p5_NPS2_4CHAN_1K_HASH:
case DF4p5_NPS1_8CHAN_1K_HASH:
case DF4p5_NPS1_16CHAN_1K_HASH:
return insert_coh_st_id_split_2_2(ctx, denorm_addr, coh_st_id);
default:
atl_debug_on_bad_intlv_mode(ctx);
return ~0ULL;
}
}
static const u16 phy_to_log_coh_st_map_mi300[] = {
12, 13, 14, 15,
8, 9, 10, 11,
4, 5, 6, 7,
0, 1, 2, 3,
28, 29, 30, 31,
24, 25, 26, 27,
20, 21, 22, 23,
16, 17, 18, 19,
};
static u16 get_logical_coh_st_fabric_id_mi300(struct addr_ctx *ctx)
{
if (ctx->inst_id >= ARRAY_SIZE(phy_to_log_coh_st_map_mi300)) {
atl_debug(ctx, "Instance ID out of range");
return ~0;
}
return phy_to_log_coh_st_map_mi300[ctx->inst_id] | (ctx->node_id << df_cfg.node_id_shift);
}
static u16 get_logical_coh_st_fabric_id(struct addr_ctx *ctx)
{
u16 component_id, log_fabric_id;
u16 phys_fabric_id = ctx->coh_st_fabric_id;
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous)
return get_logical_coh_st_fabric_id_mi300(ctx);
if (!FIELD_GET(DF4_REMAP_EN, ctx->map.ctl) &&
ctx->map.intlv_mode != DF3_6CHAN)
return phys_fabric_id;
component_id = phys_fabric_id & df_cfg.component_id_mask;
for (log_fabric_id = 0; log_fabric_id < MAX_COH_ST_CHANNELS; log_fabric_id++) {
if (ctx->map.remap_array[log_fabric_id] == component_id)
break;
}
if (log_fabric_id == MAX_COH_ST_CHANNELS)
atl_debug(ctx, "COH_ST remap entry not found for 0x%x",
log_fabric_id);
return (phys_fabric_id & df_cfg.node_id_mask) | log_fabric_id;
}
static u16 get_logical_coh_st_fabric_id_for_current_spa(struct addr_ctx *ctx,
struct df4p5_denorm_ctx *denorm_ctx)
{
bool hash_ctl_64k, hash_ctl_2M, hash_ctl_1G, hash_ctl_1T;
bool hash_pa8, hash_pa9, hash_pa12, hash_pa13;
u64 cs_id = 0;
hash_ctl_64k = FIELD_GET(DF4_HASH_CTL_64K, ctx->map.ctl);
hash_ctl_2M = FIELD_GET(DF4_HASH_CTL_2M, ctx->map.ctl);
hash_ctl_1G = FIELD_GET(DF4_HASH_CTL_1G, ctx->map.ctl);
hash_ctl_1T = FIELD_GET(DF4p5_HASH_CTL_1T, ctx->map.ctl);
hash_pa8 = FIELD_GET(BIT_ULL(8), denorm_ctx->current_spa);
hash_pa8 ^= FIELD_GET(BIT_ULL(14), denorm_ctx->current_spa);
hash_pa8 ^= FIELD_GET(BIT_ULL(16), denorm_ctx->current_spa) & hash_ctl_64k;
hash_pa8 ^= FIELD_GET(BIT_ULL(21), denorm_ctx->current_spa) & hash_ctl_2M;
hash_pa8 ^= FIELD_GET(BIT_ULL(30), denorm_ctx->current_spa) & hash_ctl_1G;
hash_pa8 ^= FIELD_GET(BIT_ULL(40), denorm_ctx->current_spa) & hash_ctl_1T;
hash_pa9 = FIELD_GET(BIT_ULL(9), denorm_ctx->current_spa);
hash_pa9 ^= FIELD_GET(BIT_ULL(17), denorm_ctx->current_spa) & hash_ctl_64k;
hash_pa9 ^= FIELD_GET(BIT_ULL(22), denorm_ctx->current_spa) & hash_ctl_2M;
hash_pa9 ^= FIELD_GET(BIT_ULL(31), denorm_ctx->current_spa) & hash_ctl_1G;
hash_pa9 ^= FIELD_GET(BIT_ULL(41), denorm_ctx->current_spa) & hash_ctl_1T;
hash_pa12 = FIELD_GET(BIT_ULL(12), denorm_ctx->current_spa);
hash_pa12 ^= FIELD_GET(BIT_ULL(18), denorm_ctx->current_spa) & hash_ctl_64k;
hash_pa12 ^= FIELD_GET(BIT_ULL(23), denorm_ctx->current_spa) & hash_ctl_2M;
hash_pa12 ^= FIELD_GET(BIT_ULL(32), denorm_ctx->current_spa) & hash_ctl_1G;
hash_pa12 ^= FIELD_GET(BIT_ULL(42), denorm_ctx->current_spa) & hash_ctl_1T;
hash_pa13 = FIELD_GET(BIT_ULL(13), denorm_ctx->current_spa);
hash_pa13 ^= FIELD_GET(BIT_ULL(19), denorm_ctx->current_spa) & hash_ctl_64k;
hash_pa13 ^= FIELD_GET(BIT_ULL(24), denorm_ctx->current_spa) & hash_ctl_2M;
hash_pa13 ^= FIELD_GET(BIT_ULL(33), denorm_ctx->current_spa) & hash_ctl_1G;
hash_pa13 ^= FIELD_GET(BIT_ULL(43), denorm_ctx->current_spa) & hash_ctl_1T;
switch (ctx->map.intlv_mode) {
case DF4p5_NPS0_24CHAN_1K_HASH:
cs_id = FIELD_GET(GENMASK_ULL(63, 13), denorm_ctx->current_spa) << 3;
cs_id %= denorm_ctx->mod_value;
cs_id <<= 2;
cs_id |= (hash_pa9 | (hash_pa12 << 1));
cs_id |= hash_pa8 << df_cfg.socket_id_shift;
break;
case DF4p5_NPS0_24CHAN_2K_HASH:
cs_id = FIELD_GET(GENMASK_ULL(63, 14), denorm_ctx->current_spa) << 4;
cs_id %= denorm_ctx->mod_value;
cs_id <<= 2;
cs_id |= (hash_pa12 | (hash_pa13 << 1));
cs_id |= hash_pa8 << df_cfg.socket_id_shift;
break;
case DF4p5_NPS1_12CHAN_1K_HASH:
cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
cs_id %= denorm_ctx->mod_value;
cs_id <<= 2;
cs_id |= (hash_pa8 | (hash_pa9 << 1));
break;
case DF4p5_NPS1_12CHAN_2K_HASH:
cs_id = FIELD_GET(GENMASK_ULL(63, 13), denorm_ctx->current_spa) << 3;
cs_id %= denorm_ctx->mod_value;
cs_id <<= 2;
cs_id |= (hash_pa8 | (hash_pa12 << 1));
break;
case DF4p5_NPS2_6CHAN_1K_HASH:
case DF4p5_NPS1_10CHAN_1K_HASH:
cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
cs_id |= (FIELD_GET(BIT_ULL(9), denorm_ctx->current_spa) << 1);
cs_id %= denorm_ctx->mod_value;
cs_id <<= 1;
cs_id |= hash_pa8;
break;
case DF4p5_NPS2_6CHAN_2K_HASH:
case DF4p5_NPS1_10CHAN_2K_HASH:
cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
cs_id %= denorm_ctx->mod_value;
cs_id <<= 1;
cs_id |= hash_pa8;
break;
case DF4p5_NPS4_3CHAN_1K_HASH:
case DF4p5_NPS2_5CHAN_1K_HASH:
cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
cs_id |= FIELD_GET(GENMASK_ULL(9, 8), denorm_ctx->current_spa);
cs_id %= denorm_ctx->mod_value;
break;
case DF4p5_NPS4_3CHAN_2K_HASH:
case DF4p5_NPS2_5CHAN_2K_HASH:
cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
cs_id |= FIELD_GET(BIT_ULL(8), denorm_ctx->current_spa) << 1;
cs_id %= denorm_ctx->mod_value;
break;
default:
atl_debug_on_bad_intlv_mode(ctx);
return 0;
}
if (cs_id > 0xffff) {
atl_debug(ctx, "Translation error: Resulting cs_id larger than u16\n");
return 0;
}
return cs_id;
}
static int denorm_addr_common(struct addr_ctx *ctx)
{
u64 denorm_addr;
u16 coh_st_id;
ctx->coh_st_fabric_id = get_logical_coh_st_fabric_id(ctx);
denorm_addr = make_space_for_coh_st_id(ctx);
coh_st_id = calculate_coh_st_id(ctx);
ctx->ret_addr = insert_coh_st_id(ctx, denorm_addr, coh_st_id);
return 0;
}
static int denorm_addr_df3_6chan(struct addr_ctx *ctx)
{
u16 coh_st_id = ctx->coh_st_fabric_id & df_cfg.component_id_mask;
u8 total_intlv_bits = ctx->map.total_intlv_bits;
u8 low_bit, intlv_bit = ctx->map.intlv_bit_pos;
u64 msb_intlv_bits, temp_addr_a, temp_addr_b;
u8 np2_bits = ctx->map.np2_bits;
if (ctx->map.intlv_mode != DF3_6CHAN)
return -EINVAL;
low_bit = 14 + np2_bits - total_intlv_bits;
msb_intlv_bits = ctx->ret_addr >> low_bit;
msb_intlv_bits &= 0x3;
if (msb_intlv_bits == 3) {
u8 addr_mod, phys_addr_msb, msb_coh_st_id;
temp_addr_b = GENMASK_ULL(low_bit - 1, intlv_bit) & ctx->ret_addr;
temp_addr_b >>= intlv_bit;
addr_mod = temp_addr_b % 3;
msb_coh_st_id = (coh_st_id >> 1) & 0x3;
phys_addr_msb = (intlv_bit + np2_bits + 1);
phys_addr_msb &= BIT(0);
phys_addr_msb++;
phys_addr_msb *= 3 - addr_mod + msb_coh_st_id;
phys_addr_msb %= 3;
temp_addr_b |= phys_addr_msb << (low_bit - total_intlv_bits - intlv_bit);
coh_st_id &= BIT(0);
coh_st_id |= GENMASK(2, 1);
} else {
temp_addr_b = GENMASK_ULL(63, intlv_bit) & ctx->ret_addr;
temp_addr_b >>= intlv_bit;
}
temp_addr_a = GENMASK_ULL(intlv_bit - 1, 0) & ctx->ret_addr;
temp_addr_b <<= intlv_bit + total_intlv_bits;
ctx->ret_addr = temp_addr_a | temp_addr_b;
ctx->ret_addr |= coh_st_id << intlv_bit;
return 0;
}
static int denorm_addr_df4_np2(struct addr_ctx *ctx)
{
bool hash_ctl_64k, hash_ctl_2M, hash_ctl_1G;
u16 group, group_offset, log_coh_st_offset;
unsigned int mod_value, shift_value;
u16 mask = df_cfg.component_id_mask;
u64 temp_addr_a, temp_addr_b;
bool hash_pa8, hashed_bit;
switch (ctx->map.intlv_mode) {
case DF4_NPS4_3CHAN_HASH:
mod_value = 3;
shift_value = 13;
break;
case DF4_NPS2_6CHAN_HASH:
mod_value = 3;
shift_value = 12;
break;
case DF4_NPS1_12CHAN_HASH:
mod_value = 3;
shift_value = 11;
break;
case DF4_NPS2_5CHAN_HASH:
mod_value = 5;
shift_value = 13;
break;
case DF4_NPS1_10CHAN_HASH:
mod_value = 5;
shift_value = 12;
break;
default:
atl_debug_on_bad_intlv_mode(ctx);
return -EINVAL;
}
if (ctx->map.num_intlv_sockets == 1) {
hash_pa8 = BIT_ULL(shift_value) & ctx->ret_addr;
temp_addr_a = remove_bits(shift_value, shift_value, ctx->ret_addr);
} else {
hash_pa8 = ctx->coh_st_fabric_id & df_cfg.socket_id_mask;
temp_addr_a = ctx->ret_addr;
}
temp_addr_a = expand_bits(8, 1, temp_addr_a);
if (ctx->map.intlv_mode == DF4_NPS2_6CHAN_HASH ||
ctx->map.intlv_mode == DF4_NPS1_10CHAN_HASH) {
temp_addr_a = expand_bits(13, 1, temp_addr_a);
} else if (ctx->map.intlv_mode == DF4_NPS1_12CHAN_HASH) {
temp_addr_a = expand_bits(12, 2, temp_addr_a);
}
temp_addr_a &= GENMASK_ULL(13, 0);
shift_value += 1 - ilog2(ctx->map.num_intlv_sockets);
temp_addr_b = GENMASK_ULL(63, shift_value) & ctx->ret_addr;
temp_addr_b >>= shift_value;
temp_addr_b *= mod_value;
log_coh_st_offset = (ctx->coh_st_fabric_id & mask) - (get_dst_fabric_id(ctx) & mask);
group = log_coh_st_offset / mod_value;
group_offset = log_coh_st_offset % mod_value;
if (hash_pa8) {
if (!group_offset)
group_offset = mod_value - 1;
else
group_offset--;
}
temp_addr_b += group_offset;
temp_addr_b <<= 14;
ctx->ret_addr = temp_addr_a | temp_addr_b;
hash_ctl_64k = FIELD_GET(DF4_HASH_CTL_64K, ctx->map.ctl);
hash_ctl_2M = FIELD_GET(DF4_HASH_CTL_2M, ctx->map.ctl);
hash_ctl_1G = FIELD_GET(DF4_HASH_CTL_1G, ctx->map.ctl);
hashed_bit = !!hash_pa8;
hashed_bit ^= FIELD_GET(BIT_ULL(14), ctx->ret_addr);
hashed_bit ^= FIELD_GET(BIT_ULL(16), ctx->ret_addr) & hash_ctl_64k;
hashed_bit ^= FIELD_GET(BIT_ULL(21), ctx->ret_addr) & hash_ctl_2M;
hashed_bit ^= FIELD_GET(BIT_ULL(30), ctx->ret_addr) & hash_ctl_1G;
ctx->ret_addr |= hashed_bit << 8;
if (ctx->map.intlv_mode == DF4_NPS4_3CHAN_HASH ||
ctx->map.intlv_mode == DF4_NPS2_5CHAN_HASH)
return 0;
if (ctx->map.intlv_mode == DF4_NPS1_12CHAN_HASH)
hashed_bit = !!(group & BIT(1));
else
hashed_bit = group & BIT(0);
hashed_bit ^= FIELD_GET(BIT_ULL(18), ctx->ret_addr) & hash_ctl_64k;
hashed_bit ^= FIELD_GET(BIT_ULL(23), ctx->ret_addr) & hash_ctl_2M;
hashed_bit ^= FIELD_GET(BIT_ULL(32), ctx->ret_addr) & hash_ctl_1G;
ctx->ret_addr |= hashed_bit << 13;
if (ctx->map.intlv_mode != DF4_NPS1_12CHAN_HASH)
return 0;
hashed_bit = group & BIT(0);
hashed_bit ^= FIELD_GET(BIT_ULL(17), ctx->ret_addr) & hash_ctl_64k;
hashed_bit ^= FIELD_GET(BIT_ULL(22), ctx->ret_addr) & hash_ctl_2M;
hashed_bit ^= FIELD_GET(BIT_ULL(31), ctx->ret_addr) & hash_ctl_1G;
ctx->ret_addr |= hashed_bit << 12;
return 0;
}
static u64 normalize_addr_df4p5_np2(struct addr_ctx *ctx, struct df4p5_denorm_ctx *denorm_ctx,
u64 addr)
{
u64 temp_addr_a = 0, temp_addr_b = 0;
switch (ctx->map.intlv_mode) {
case DF4p5_NPS0_24CHAN_1K_HASH:
case DF4p5_NPS1_12CHAN_1K_HASH:
case DF4p5_NPS2_6CHAN_1K_HASH:
case DF4p5_NPS4_3CHAN_1K_HASH:
case DF4p5_NPS1_10CHAN_1K_HASH:
case DF4p5_NPS2_5CHAN_1K_HASH:
temp_addr_a = FIELD_GET(GENMASK_ULL(11, 10), addr) << 8;
break;
case DF4p5_NPS0_24CHAN_2K_HASH:
case DF4p5_NPS1_12CHAN_2K_HASH:
case DF4p5_NPS2_6CHAN_2K_HASH:
case DF4p5_NPS4_3CHAN_2K_HASH:
case DF4p5_NPS1_10CHAN_2K_HASH:
case DF4p5_NPS2_5CHAN_2K_HASH:
temp_addr_a = FIELD_GET(GENMASK_ULL(11, 9), addr) << 8;
break;
default:
atl_debug_on_bad_intlv_mode(ctx);
return 0;
}
switch (ctx->map.intlv_mode) {
case DF4p5_NPS0_24CHAN_1K_HASH:
temp_addr_b = FIELD_GET(GENMASK_ULL(63, 13), addr) / denorm_ctx->mod_value;
temp_addr_b <<= 10;
break;
case DF4p5_NPS0_24CHAN_2K_HASH:
temp_addr_b = FIELD_GET(GENMASK_ULL(63, 14), addr) / denorm_ctx->mod_value;
temp_addr_b <<= 11;
break;
case DF4p5_NPS1_12CHAN_1K_HASH:
temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) / denorm_ctx->mod_value;
temp_addr_b <<= 10;
break;
case DF4p5_NPS1_12CHAN_2K_HASH:
temp_addr_b = FIELD_GET(GENMASK_ULL(63, 13), addr) / denorm_ctx->mod_value;
temp_addr_b <<= 11;
break;
case DF4p5_NPS2_6CHAN_1K_HASH:
case DF4p5_NPS1_10CHAN_1K_HASH:
temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) << 1;
temp_addr_b |= FIELD_GET(BIT_ULL(9), addr);
temp_addr_b /= denorm_ctx->mod_value;
temp_addr_b <<= 10;
break;
case DF4p5_NPS2_6CHAN_2K_HASH:
case DF4p5_NPS1_10CHAN_2K_HASH:
temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) / denorm_ctx->mod_value;
temp_addr_b <<= 11;
break;
case DF4p5_NPS4_3CHAN_1K_HASH:
case DF4p5_NPS2_5CHAN_1K_HASH:
temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) << 2;
temp_addr_b |= FIELD_GET(GENMASK_ULL(9, 8), addr);
temp_addr_b /= denorm_ctx->mod_value;
temp_addr_b <<= 10;
break;
case DF4p5_NPS4_3CHAN_2K_HASH:
case DF4p5_NPS2_5CHAN_2K_HASH:
temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) << 1;
temp_addr_b |= FIELD_GET(BIT_ULL(8), addr);
temp_addr_b /= denorm_ctx->mod_value;
temp_addr_b <<= 11;
break;
default:
atl_debug_on_bad_intlv_mode(ctx);
return 0;
}
return denorm_ctx->base_denorm_addr | temp_addr_a | temp_addr_b;
}
static void recalculate_hashed_bits_df4p5_np2(struct addr_ctx *ctx,
struct df4p5_denorm_ctx *denorm_ctx)
{
bool hash_ctl_64k, hash_ctl_2M, hash_ctl_1G, hash_ctl_1T, hashed_bit;
if (!denorm_ctx->rehash_vector)
return;
hash_ctl_64k = FIELD_GET(DF4_HASH_CTL_64K, ctx->map.ctl);
hash_ctl_2M = FIELD_GET(DF4_HASH_CTL_2M, ctx->map.ctl);
hash_ctl_1G = FIELD_GET(DF4_HASH_CTL_1G, ctx->map.ctl);
hash_ctl_1T = FIELD_GET(DF4p5_HASH_CTL_1T, ctx->map.ctl);
if (denorm_ctx->rehash_vector & BIT_ULL(8)) {
hashed_bit = FIELD_GET(BIT_ULL(8), denorm_ctx->current_spa);
hashed_bit ^= FIELD_GET(BIT_ULL(14), denorm_ctx->current_spa);
hashed_bit ^= FIELD_GET(BIT_ULL(16), denorm_ctx->current_spa) & hash_ctl_64k;
hashed_bit ^= FIELD_GET(BIT_ULL(21), denorm_ctx->current_spa) & hash_ctl_2M;
hashed_bit ^= FIELD_GET(BIT_ULL(30), denorm_ctx->current_spa) & hash_ctl_1G;
hashed_bit ^= FIELD_GET(BIT_ULL(40), denorm_ctx->current_spa) & hash_ctl_1T;
if (FIELD_GET(BIT_ULL(8), denorm_ctx->current_spa) != hashed_bit)
denorm_ctx->current_spa ^= BIT_ULL(8);
}
if (denorm_ctx->rehash_vector & BIT_ULL(9)) {
hashed_bit = FIELD_GET(BIT_ULL(9), denorm_ctx->current_spa);
hashed_bit ^= FIELD_GET(BIT_ULL(17), denorm_ctx->current_spa) & hash_ctl_64k;
hashed_bit ^= FIELD_GET(BIT_ULL(22), denorm_ctx->current_spa) & hash_ctl_2M;
hashed_bit ^= FIELD_GET(BIT_ULL(31), denorm_ctx->current_spa) & hash_ctl_1G;
hashed_bit ^= FIELD_GET(BIT_ULL(41), denorm_ctx->current_spa) & hash_ctl_1T;
if (FIELD_GET(BIT_ULL(9), denorm_ctx->current_spa) != hashed_bit)
denorm_ctx->current_spa ^= BIT_ULL(9);
}
if (denorm_ctx->rehash_vector & BIT_ULL(12)) {
hashed_bit = FIELD_GET(BIT_ULL(12), denorm_ctx->current_spa);
hashed_bit ^= FIELD_GET(BIT_ULL(18), denorm_ctx->current_spa) & hash_ctl_64k;
hashed_bit ^= FIELD_GET(BIT_ULL(23), denorm_ctx->current_spa) & hash_ctl_2M;
hashed_bit ^= FIELD_GET(BIT_ULL(32), denorm_ctx->current_spa) & hash_ctl_1G;
hashed_bit ^= FIELD_GET(BIT_ULL(42), denorm_ctx->current_spa) & hash_ctl_1T;
if (FIELD_GET(BIT_ULL(12), denorm_ctx->current_spa) != hashed_bit)
denorm_ctx->current_spa ^= BIT_ULL(12);
}
if (denorm_ctx->rehash_vector & BIT_ULL(13)) {
hashed_bit = FIELD_GET(BIT_ULL(13), denorm_ctx->current_spa);
hashed_bit ^= FIELD_GET(BIT_ULL(19), denorm_ctx->current_spa) & hash_ctl_64k;
hashed_bit ^= FIELD_GET(BIT_ULL(24), denorm_ctx->current_spa) & hash_ctl_2M;
hashed_bit ^= FIELD_GET(BIT_ULL(33), denorm_ctx->current_spa) & hash_ctl_1G;
hashed_bit ^= FIELD_GET(BIT_ULL(43), denorm_ctx->current_spa) & hash_ctl_1T;
if (FIELD_GET(BIT_ULL(13), denorm_ctx->current_spa) != hashed_bit)
denorm_ctx->current_spa ^= BIT_ULL(13);
}
}
static bool match_logical_coh_st_fabric_id(struct addr_ctx *ctx,
struct df4p5_denorm_ctx *denorm_ctx)
{
u16 id = get_logical_coh_st_fabric_id_for_current_spa(ctx, denorm_ctx);
atl_debug(ctx, "Checking calculated logical coherent station fabric id:\n");
atl_debug(ctx, " calculated fabric id = 0x%x\n", id);
atl_debug(ctx, " expected fabric id = 0x%x\n", denorm_ctx->coh_st_fabric_id);
return denorm_ctx->coh_st_fabric_id == id;
}
static bool match_norm_addr(struct addr_ctx *ctx, struct df4p5_denorm_ctx *denorm_ctx)
{
u64 addr = remove_base_and_hole(ctx, denorm_ctx->current_spa);
addr = normalize_addr_df4p5_np2(ctx, denorm_ctx, addr);
atl_debug(ctx, "Checking calculated normalized address:\n");
atl_debug(ctx, " calculated normalized addr = 0x%016llx\n", addr);
atl_debug(ctx, " expected normalized addr = 0x%016llx\n", ctx->ret_addr);
return addr == ctx->ret_addr;
}
static int check_permutations(struct addr_ctx *ctx, struct df4p5_denorm_ctx *denorm_ctx)
{
u64 test_perm, temp_addr, denorm_addr, num_perms;
unsigned int dropped_remainder;
denorm_ctx->div_addr *= denorm_ctx->mod_value;
num_perms = denorm_ctx->mod_value << denorm_ctx->perm_shift;
for (test_perm = 0; test_perm < num_perms; test_perm++) {
denorm_addr = denorm_ctx->base_denorm_addr;
dropped_remainder = test_perm >> denorm_ctx->perm_shift;
temp_addr = denorm_ctx->div_addr + dropped_remainder;
switch (ctx->map.intlv_mode) {
case DF4p5_NPS0_24CHAN_2K_HASH:
denorm_addr |= temp_addr << 14;
break;
case DF4p5_NPS0_24CHAN_1K_HASH:
case DF4p5_NPS1_12CHAN_2K_HASH:
denorm_addr |= temp_addr << 13;
break;
case DF4p5_NPS1_12CHAN_1K_HASH:
case DF4p5_NPS2_6CHAN_2K_HASH:
case DF4p5_NPS1_10CHAN_2K_HASH:
denorm_addr |= temp_addr << 12;
break;
case DF4p5_NPS2_6CHAN_1K_HASH:
case DF4p5_NPS1_10CHAN_1K_HASH:
denorm_addr |= FIELD_GET(BIT_ULL(0), temp_addr) << 9;
denorm_addr |= FIELD_GET(GENMASK_ULL(63, 1), temp_addr) << 12;
break;
case DF4p5_NPS4_3CHAN_1K_HASH:
case DF4p5_NPS2_5CHAN_1K_HASH:
denorm_addr |= FIELD_GET(GENMASK_ULL(1, 0), temp_addr) << 8;
denorm_addr |= FIELD_GET(GENMASK_ULL(63, 2), (temp_addr)) << 12;
break;
case DF4p5_NPS4_3CHAN_2K_HASH:
case DF4p5_NPS2_5CHAN_2K_HASH:
denorm_addr |= FIELD_GET(BIT_ULL(0), temp_addr) << 8;
denorm_addr |= FIELD_GET(GENMASK_ULL(63, 1), temp_addr) << 12;
break;
default:
atl_debug_on_bad_intlv_mode(ctx);
return -EINVAL;
}
switch (ctx->map.intlv_mode) {
case DF4p5_NPS0_24CHAN_1K_HASH:
denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
denorm_addr |= FIELD_GET(BIT_ULL(1), test_perm) << 9;
denorm_addr |= FIELD_GET(BIT_ULL(2), test_perm) << 12;
break;
case DF4p5_NPS0_24CHAN_2K_HASH:
denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
denorm_addr |= FIELD_GET(BIT_ULL(1), test_perm) << 12;
denorm_addr |= FIELD_GET(BIT_ULL(2), test_perm) << 13;
break;
case DF4p5_NPS1_12CHAN_2K_HASH:
denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
denorm_addr |= FIELD_GET(BIT_ULL(1), test_perm) << 12;
break;
case DF4p5_NPS1_12CHAN_1K_HASH:
case DF4p5_NPS4_3CHAN_1K_HASH:
case DF4p5_NPS2_5CHAN_1K_HASH:
denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
denorm_addr |= FIELD_GET(BIT_ULL(1), test_perm) << 9;
break;
case DF4p5_NPS2_6CHAN_1K_HASH:
case DF4p5_NPS2_6CHAN_2K_HASH:
case DF4p5_NPS4_3CHAN_2K_HASH:
case DF4p5_NPS1_10CHAN_1K_HASH:
case DF4p5_NPS1_10CHAN_2K_HASH:
case DF4p5_NPS2_5CHAN_2K_HASH:
denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
break;
default:
atl_debug_on_bad_intlv_mode(ctx);
return -EINVAL;
}
denorm_ctx->current_spa = add_base_and_hole(ctx, denorm_addr);
recalculate_hashed_bits_df4p5_np2(ctx, denorm_ctx);
atl_debug(ctx, "Checking potential system physical address 0x%016llx\n",
denorm_ctx->current_spa);
if (!match_logical_coh_st_fabric_id(ctx, denorm_ctx))
continue;
if (!match_norm_addr(ctx, denorm_ctx))
continue;
if (denorm_ctx->resolved_spa == INVALID_SPA ||
denorm_ctx->current_spa > denorm_ctx->resolved_spa)
denorm_ctx->resolved_spa = denorm_ctx->current_spa;
}
if (denorm_ctx->resolved_spa == INVALID_SPA) {
atl_debug(ctx, "Failed to find valid SPA for normalized address 0x%016llx\n",
ctx->ret_addr);
return -EINVAL;
}
ctx->ret_addr = remove_base_and_hole(ctx, denorm_ctx->resolved_spa);
return 0;
}
static int init_df4p5_denorm_ctx(struct addr_ctx *ctx, struct df4p5_denorm_ctx *denorm_ctx)
{
denorm_ctx->current_spa = INVALID_SPA;
denorm_ctx->resolved_spa = INVALID_SPA;
switch (ctx->map.intlv_mode) {
case DF4p5_NPS0_24CHAN_1K_HASH:
denorm_ctx->perm_shift = 3;
denorm_ctx->rehash_vector = BIT(8) | BIT(9) | BIT(12);
break;
case DF4p5_NPS0_24CHAN_2K_HASH:
denorm_ctx->perm_shift = 3;
denorm_ctx->rehash_vector = BIT(8) | BIT(12) | BIT(13);
break;
case DF4p5_NPS1_12CHAN_1K_HASH:
denorm_ctx->perm_shift = 2;
denorm_ctx->rehash_vector = BIT(8);
break;
case DF4p5_NPS1_12CHAN_2K_HASH:
denorm_ctx->perm_shift = 2;
denorm_ctx->rehash_vector = BIT(8) | BIT(12);
break;
case DF4p5_NPS2_6CHAN_1K_HASH:
case DF4p5_NPS2_6CHAN_2K_HASH:
case DF4p5_NPS1_10CHAN_1K_HASH:
case DF4p5_NPS1_10CHAN_2K_HASH:
denorm_ctx->perm_shift = 1;
denorm_ctx->rehash_vector = BIT(8);
break;
case DF4p5_NPS4_3CHAN_1K_HASH:
case DF4p5_NPS2_5CHAN_1K_HASH:
denorm_ctx->perm_shift = 2;
denorm_ctx->rehash_vector = 0;
break;
case DF4p5_NPS4_3CHAN_2K_HASH:
case DF4p5_NPS2_5CHAN_2K_HASH:
denorm_ctx->perm_shift = 1;
denorm_ctx->rehash_vector = 0;
break;
default:
atl_debug_on_bad_intlv_mode(ctx);
return -EINVAL;
}
denorm_ctx->base_denorm_addr = FIELD_GET(GENMASK_ULL(7, 0), ctx->ret_addr);
switch (ctx->map.intlv_mode) {
case DF4p5_NPS0_24CHAN_1K_HASH:
case DF4p5_NPS1_12CHAN_1K_HASH:
case DF4p5_NPS2_6CHAN_1K_HASH:
case DF4p5_NPS4_3CHAN_1K_HASH:
case DF4p5_NPS1_10CHAN_1K_HASH:
case DF4p5_NPS2_5CHAN_1K_HASH:
denorm_ctx->base_denorm_addr |= FIELD_GET(GENMASK_ULL(9, 8), ctx->ret_addr) << 10;
denorm_ctx->div_addr = FIELD_GET(GENMASK_ULL(63, 10), ctx->ret_addr);
break;
case DF4p5_NPS0_24CHAN_2K_HASH:
case DF4p5_NPS1_12CHAN_2K_HASH:
case DF4p5_NPS2_6CHAN_2K_HASH:
case DF4p5_NPS4_3CHAN_2K_HASH:
case DF4p5_NPS1_10CHAN_2K_HASH:
case DF4p5_NPS2_5CHAN_2K_HASH:
denorm_ctx->base_denorm_addr |= FIELD_GET(GENMASK_ULL(10, 8), ctx->ret_addr) << 9;
denorm_ctx->div_addr = FIELD_GET(GENMASK_ULL(63, 11), ctx->ret_addr);
break;
default:
atl_debug_on_bad_intlv_mode(ctx);
return -EINVAL;
}
if (ctx->map.num_intlv_chan % 3 == 0)
denorm_ctx->mod_value = 3;
else
denorm_ctx->mod_value = 5;
denorm_ctx->coh_st_fabric_id = get_logical_coh_st_fabric_id(ctx) - get_dst_fabric_id(ctx);
atl_debug(ctx, "Initialized df4p5_denorm_ctx:");
atl_debug(ctx, " mod_value = %d", denorm_ctx->mod_value);
atl_debug(ctx, " perm_shift = %d", denorm_ctx->perm_shift);
atl_debug(ctx, " rehash_vector = 0x%x", denorm_ctx->rehash_vector);
atl_debug(ctx, " base_denorm_addr = 0x%016llx", denorm_ctx->base_denorm_addr);
atl_debug(ctx, " div_addr = 0x%016llx", denorm_ctx->div_addr);
atl_debug(ctx, " coh_st_fabric_id = 0x%x", denorm_ctx->coh_st_fabric_id);
return 0;
}
static int denorm_addr_df4p5_np2(struct addr_ctx *ctx)
{
struct df4p5_denorm_ctx denorm_ctx;
int ret = 0;
memset(&denorm_ctx, 0, sizeof(denorm_ctx));
atl_debug(ctx, "Denormalizing DF 4.5 normalized address 0x%016llx", ctx->ret_addr);
ret = init_df4p5_denorm_ctx(ctx, &denorm_ctx);
if (ret)
return ret;
return check_permutations(ctx, &denorm_ctx);
}
int denormalize_address(struct addr_ctx *ctx)
{
switch (ctx->map.intlv_mode) {
case NONE:
return 0;
case DF4_NPS4_3CHAN_HASH:
case DF4_NPS2_6CHAN_HASH:
case DF4_NPS1_12CHAN_HASH:
case DF4_NPS2_5CHAN_HASH:
case DF4_NPS1_10CHAN_HASH:
return denorm_addr_df4_np2(ctx);
case DF4p5_NPS0_24CHAN_1K_HASH:
case DF4p5_NPS4_3CHAN_1K_HASH:
case DF4p5_NPS2_6CHAN_1K_HASH:
case DF4p5_NPS1_12CHAN_1K_HASH:
case DF4p5_NPS2_5CHAN_1K_HASH:
case DF4p5_NPS1_10CHAN_1K_HASH:
case DF4p5_NPS4_3CHAN_2K_HASH:
case DF4p5_NPS2_6CHAN_2K_HASH:
case DF4p5_NPS1_12CHAN_2K_HASH:
case DF4p5_NPS0_24CHAN_2K_HASH:
case DF4p5_NPS2_5CHAN_2K_HASH:
case DF4p5_NPS1_10CHAN_2K_HASH:
return denorm_addr_df4p5_np2(ctx);
case DF3_6CHAN:
return denorm_addr_df3_6chan(ctx);
default:
return denorm_addr_common(ctx);
}
}