if (ctx->map.intlv_mode <= NOHASH_32CHAN) return 0;
if (ctx->map.intlv_mode >= MI3_HASH_8CHAN &&
ctx->map.intlv_mode <= MI3_HASH_32CHAN) return 0;
/* * Modes matching the ranges above are returned as-is. * * All other modes are "fixed up" by adding 20h to make a unique value.
*/
ctx->map.intlv_mode += 0x20;
return 0;
}
staticint get_intlv_mode(struct addr_ctx *ctx)
{ int ret;
switch (df_cfg.rev) { case DF2:
ret = df2_get_intlv_mode(ctx); break; case DF3:
ret = df3_get_intlv_mode(ctx); break; case DF3p5:
ret = df3p5_get_intlv_mode(ctx); break; case DF4:
ret = df4_get_intlv_mode(ctx); break; case DF4p5:
ret = df4p5_get_intlv_mode(ctx); break; default:
ret = -EINVAL;
}
switch (df_cfg.rev) { case DF2:
hi_addr_offset = FIELD_GET(DF2_HI_ADDR_OFFSET, reg_dram_offset); break; case DF3: case DF3p5:
hi_addr_offset = FIELD_GET(DF3_HI_ADDR_OFFSET, reg_dram_offset); break; case DF4: case DF4p5:
hi_addr_offset = FIELD_GET(DF4_HI_ADDR_OFFSET, reg_dram_offset); break; default:
hi_addr_offset = 0;
atl_debug_on_bad_df_rev();
}
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous)
shift = MI300_DRAM_LIMIT_LSB;
return hi_addr_offset << shift;
}
/* * Returns: 0 if offset is disabled. * 1 if offset is enabled. * -EINVAL on error.
*/ staticint get_dram_offset(struct addr_ctx *ctx, u64 *norm_offset)
{
u32 reg_dram_offset;
u8 map_num;
/* Should not be called for map 0. */ if (!ctx->map.num) {
atl_debug(ctx, "Trying to find DRAM offset for map 0"); return -EINVAL;
}
/* * DramOffset registers don't exist for map 0, so the base register * actually refers to map 1. * Adjust the map_num for the register offsets.
*/
map_num = ctx->map.num - 1;
/* * On MI300 systems, the Coherent Station Fabric ID is derived * later. And it does not depend on the register value.
*/ if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous) return 0;
for (ctx->map.num = 1; ctx->map.num < df_cfg.num_coh_st_maps; ctx->map.num++) {
ret = get_dram_offset(ctx, norm_offset); if (ret < 0) return ret;
/* Continue search if this map's offset is not enabled. */ if (!ret) continue;
/* Enabled offsets should never be 0. */ if (*norm_offset == 0) {
atl_debug(ctx, "Enabled map %u offset is 0", ctx->map.num); return -EINVAL;
}
/* Offsets should always increase from one map to the next. */ if (*norm_offset <= last_offset) {
atl_debug(ctx, "Map %u offset (0x%016llx) <= previous (0x%016llx)",
ctx->map.num, *norm_offset, last_offset); return -EINVAL;
}
/* Match if this map's offset is less than the current calculated address. */ if (ctx->ret_addr >= *norm_offset) break;
last_offset = *norm_offset;
}
/* * Finished search without finding a match. * Reset to map 0 and no offset.
*/ if (ctx->map.num >= df_cfg.num_coh_st_maps) {
ctx->map.num = 0;
*norm_offset = 0;
}
if (find_normalized_offset(ctx, &norm_offset)) return -EINVAL;
if (get_dram_addr_map(ctx)) return -EINVAL;
if (!valid_map(ctx)) return -EINVAL;
ctx->ret_addr -= norm_offset;
return 0;
}
static u8 get_num_intlv_chan(struct addr_ctx *ctx)
{ switch (ctx->map.intlv_mode) { case NONE: return 1; case NOHASH_2CHAN: case DF2_2CHAN_HASH: case DF3_COD4_2CHAN_HASH: case DF4_NPS4_2CHAN_HASH: case DF4p5_NPS4_2CHAN_1K_HASH: case DF4p5_NPS4_2CHAN_2K_HASH: return 2; case DF4_NPS4_3CHAN_HASH: case DF4p5_NPS4_3CHAN_1K_HASH: case DF4p5_NPS4_3CHAN_2K_HASH: return 3; case NOHASH_4CHAN: case DF3_COD2_4CHAN_HASH: case DF4_NPS2_4CHAN_HASH: case DF4p5_NPS2_4CHAN_1K_HASH: case DF4p5_NPS2_4CHAN_2K_HASH: return 4; case DF4_NPS2_5CHAN_HASH: case DF4p5_NPS2_5CHAN_1K_HASH: case DF4p5_NPS2_5CHAN_2K_HASH: return 5; case DF3_6CHAN: case DF4_NPS2_6CHAN_HASH: case DF4p5_NPS2_6CHAN_1K_HASH: case DF4p5_NPS2_6CHAN_2K_HASH: return 6; case NOHASH_8CHAN: case DF3_COD1_8CHAN_HASH: case DF4_NPS1_8CHAN_HASH: case MI3_HASH_8CHAN: case DF4p5_NPS1_8CHAN_1K_HASH: case DF4p5_NPS1_8CHAN_2K_HASH: return 8; case DF4_NPS1_10CHAN_HASH: case DF4p5_NPS1_10CHAN_1K_HASH: case DF4p5_NPS1_10CHAN_2K_HASH: return 10; case DF4_NPS1_12CHAN_HASH: case DF4p5_NPS1_12CHAN_1K_HASH: case DF4p5_NPS1_12CHAN_2K_HASH: return 12; case NOHASH_16CHAN: case MI3_HASH_16CHAN: case DF4p5_NPS1_16CHAN_1K_HASH: case DF4p5_NPS1_16CHAN_2K_HASH: return 16; case DF4p5_NPS0_24CHAN_1K_HASH: case DF4p5_NPS0_24CHAN_2K_HASH: return 24; case NOHASH_32CHAN: case MI3_HASH_32CHAN: return 32; default:
atl_debug_on_bad_intlv_mode(ctx); return 0;
}
}
/* * Get the number of bits needed to cover this many channels. * order_base_2() rounds up automatically.
*/
ctx->map.total_intlv_bits = order_base_2(ctx->map.total_intlv_chan);
}
switch (df_cfg.rev) { case DF2:
addr_sel = FIELD_GET(DF2_INTLV_ADDR_SEL, ctx->map.base); break; case DF3: case DF3p5:
addr_sel = FIELD_GET(DF3_INTLV_ADDR_SEL, ctx->map.base); break; case DF4: case DF4p5:
addr_sel = FIELD_GET(DF4_INTLV_ADDR_SEL, ctx->map.intlv); break; default:
atl_debug_on_bad_df_rev(); break;
}
/* Add '8' to get the 'interleave bit position'. */ return addr_sel + 8;
}
static u8 get_num_intlv_dies(struct addr_ctx *ctx)
{
u8 dies = 0;
switch (df_cfg.rev) { case DF2:
dies = FIELD_GET(DF2_INTLV_NUM_DIES, ctx->map.limit); break; case DF3:
dies = FIELD_GET(DF3_INTLV_NUM_DIES, ctx->map.base); break; case DF3p5:
dies = FIELD_GET(DF3p5_INTLV_NUM_DIES, ctx->map.base); break; case DF4: case DF4p5:
dies = FIELD_GET(DF4_INTLV_NUM_DIES, ctx->map.intlv); break; default:
atl_debug_on_bad_df_rev(); break;
}
/* Register value is log2, e.g. 0 -> 1 die, 1 -> 2 dies, etc. */ return 1 << dies;
}
/* * Verify the interleave bits are correct in the different interleaving * settings. * * If @num_intlv_dies and/or @num_intlv_sockets are 1, it means the * respective interleaving is disabled.
*/ staticinlinebool map_bits_valid(struct addr_ctx *ctx, u8 bit1, u8 bit2,
u8 num_intlv_dies, u8 num_intlv_sockets)
{ if (!(ctx->map.intlv_bit_pos == bit1 || ctx->map.intlv_bit_pos == bit2)) {
pr_debug("Invalid interleave bit: %u", ctx->map.intlv_bit_pos); returnfalse;
}
if (ctx->map.num_intlv_dies > num_intlv_dies) {
pr_debug("Invalid number of interleave dies: %u", ctx->map.num_intlv_dies); returnfalse;
}
if (ctx->map.num_intlv_sockets > num_intlv_sockets) {
pr_debug("Invalid number of interleave sockets: %u", ctx->map.num_intlv_sockets); returnfalse;
}
returntrue;
}
staticint validate_address_map(struct addr_ctx *ctx)
{ switch (ctx->map.intlv_mode) { case DF2_2CHAN_HASH: case DF3_COD4_2CHAN_HASH: case DF3_COD2_4CHAN_HASH: case DF3_COD1_8CHAN_HASH: if (!map_bits_valid(ctx, 8, 9, 1, 1)) goto err; break;
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_1K_HASH: case DF4p5_NPS2_4CHAN_2K_HASH: case DF4p5_NPS1_8CHAN_1K_HASH: case DF4p5_NPS1_8CHAN_2K_HASH: case DF4p5_NPS1_16CHAN_1K_HASH: case DF4p5_NPS1_16CHAN_2K_HASH: if (!map_bits_valid(ctx, 8, 8, 1, 2)) goto err; break;
case DF4p5_NPS4_3CHAN_1K_HASH: case DF4p5_NPS4_3CHAN_2K_HASH: case DF4p5_NPS2_5CHAN_1K_HASH: case DF4p5_NPS2_5CHAN_2K_HASH: case DF4p5_NPS2_6CHAN_1K_HASH: case DF4p5_NPS2_6CHAN_2K_HASH: case DF4p5_NPS1_10CHAN_1K_HASH: case DF4p5_NPS1_10CHAN_2K_HASH: case DF4p5_NPS1_12CHAN_1K_HASH: case DF4p5_NPS1_12CHAN_2K_HASH: if (ctx->map.num_intlv_sockets != 1 || !map_bits_valid(ctx, 8, 0, 1, 1)) goto err; break;
case DF4p5_NPS0_24CHAN_1K_HASH: case DF4p5_NPS0_24CHAN_2K_HASH: if (ctx->map.num_intlv_sockets < 2 || !map_bits_valid(ctx, 8, 0, 1, 2)) goto err; break;
case MI3_HASH_8CHAN: case MI3_HASH_16CHAN: case MI3_HASH_32CHAN: if (!map_bits_valid(ctx, 8, 8, 4, 1)) goto err; break;
/* Nothing to do for modes that don't need special validation checks. */ default: break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.