775 lines · 18.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * AMD Address Translation Library * * map.c : Functions to read and decode DRAM address maps * * Copyright (c) 2023, Advanced Micro Devices, Inc. * All Rights Reserved. * * Author: Yazen Ghannam <Yazen.Ghannam@amd.com> */ #include "internal.h" static int df2_get_intlv_mode(struct addr_ctx *ctx){	ctx->map.intlv_mode = FIELD_GET(DF2_INTLV_NUM_CHAN, ctx->map.base); 	if (ctx->map.intlv_mode == 8)		ctx->map.intlv_mode = DF2_2CHAN_HASH; 	if (ctx->map.intlv_mode != NONE &&	    ctx->map.intlv_mode != NOHASH_2CHAN &&	    ctx->map.intlv_mode != DF2_2CHAN_HASH)		return -EINVAL; 	return 0;} static int df3_get_intlv_mode(struct addr_ctx *ctx){	ctx->map.intlv_mode = FIELD_GET(DF3_INTLV_NUM_CHAN, ctx->map.base);	return 0;} static int df3p5_get_intlv_mode(struct addr_ctx *ctx){	ctx->map.intlv_mode = FIELD_GET(DF3p5_INTLV_NUM_CHAN, ctx->map.base); 	if (ctx->map.intlv_mode == DF3_6CHAN)		return -EINVAL; 	return 0;} static int df4_get_intlv_mode(struct addr_ctx *ctx){	ctx->map.intlv_mode = FIELD_GET(DF4_INTLV_NUM_CHAN, ctx->map.intlv); 	if (ctx->map.intlv_mode == DF3_COD4_2CHAN_HASH ||	    ctx->map.intlv_mode == DF3_COD2_4CHAN_HASH ||	    ctx->map.intlv_mode == DF3_COD1_8CHAN_HASH ||	    ctx->map.intlv_mode == DF3_6CHAN)		return -EINVAL; 	return 0;} static int df4p5_get_intlv_mode(struct addr_ctx *ctx){	ctx->map.intlv_mode = FIELD_GET(DF4p5_INTLV_NUM_CHAN, ctx->map.intlv); 	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;} static int 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;	} 	if (ret)		atl_debug_on_bad_df_rev(); 	return ret;} static u64 get_hi_addr_offset(u32 reg_dram_offset){	u8 shift = DF_DRAM_BASE_LIMIT_LSB;	u64 hi_addr_offset; 	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. */static int 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; 	if (df_cfg.rev >= DF4) {		/* Read D18F7x140 (DramOffset) */		if (df_indirect_read_instance(ctx->node_id, 7, 0x140 + (4 * map_num),					      ctx->inst_id, &reg_dram_offset))			return -EINVAL; 	} else {		/* Read D18F0x1B4 (DramOffset) */		if (df_indirect_read_instance(ctx->node_id, 0, 0x1B4 + (4 * map_num),					      ctx->inst_id, &reg_dram_offset))			return -EINVAL;	} 	if (!FIELD_GET(DF_HI_ADDR_OFFSET_EN, reg_dram_offset))		return 0; 	*norm_offset = get_hi_addr_offset(reg_dram_offset); 	return 1;} static int df3_6ch_get_dram_addr_map(struct addr_ctx *ctx){	u16 dst_fabric_id = FIELD_GET(DF3_DST_FABRIC_ID, ctx->map.limit);	u8 i, j, shift = 4, mask = 0xF;	u32 reg, offset = 0x60;	u16 dst_node_id; 	/* Get Socket 1 register. */	if (dst_fabric_id & df_cfg.socket_id_mask)		offset = 0x68; 	/* Read D18F0x06{0,8} (DF::Skt0CsTargetRemap0)/(DF::Skt0CsTargetRemap1) */	if (df_indirect_read_broadcast(ctx->node_id, 0, offset, &reg))		return -EINVAL; 	/* Save 8 remap entries. */	for (i = 0, j = 0; i < 8; i++, j++)		ctx->map.remap_array[i] = (reg >> (j * shift)) & mask; 	dst_node_id = dst_fabric_id & df_cfg.node_id_mask;	dst_node_id >>= df_cfg.node_id_shift; 	/* Read D18F2x090 (DF::Np2ChannelConfig) */	if (df_indirect_read_broadcast(dst_node_id, 2, 0x90, &reg))		return -EINVAL; 	ctx->map.np2_bits = FIELD_GET(DF_LOG2_ADDR_64K_SPACE0, reg);	return 0;} static int df2_get_dram_addr_map(struct addr_ctx *ctx){	/* Read D18F0x110 (DramBaseAddress). */	if (df_indirect_read_instance(ctx->node_id, 0, 0x110 + (8 * ctx->map.num),				      ctx->inst_id, &ctx->map.base))		return -EINVAL; 	/* Read D18F0x114 (DramLimitAddress). */	if (df_indirect_read_instance(ctx->node_id, 0, 0x114 + (8 * ctx->map.num),				      ctx->inst_id, &ctx->map.limit))		return -EINVAL; 	return 0;} static int df3_get_dram_addr_map(struct addr_ctx *ctx){	if (df2_get_dram_addr_map(ctx))		return -EINVAL; 	/* Read D18F0x3F8 (DfGlobalCtl). */	if (df_indirect_read_instance(ctx->node_id, 0, 0x3F8,				      ctx->inst_id, &ctx->map.ctl))		return -EINVAL; 	return 0;} static int df4_get_dram_addr_map(struct addr_ctx *ctx){	u8 remap_sel, i, j, shift = 4, mask = 0xF;	u32 remap_reg; 	/* Read D18F7xE00 (DramBaseAddress). */	if (df_indirect_read_instance(ctx->node_id, 7, 0xE00 + (16 * ctx->map.num),				      ctx->inst_id, &ctx->map.base))		return -EINVAL; 	/* Read D18F7xE04 (DramLimitAddress). */	if (df_indirect_read_instance(ctx->node_id, 7, 0xE04 + (16 * ctx->map.num),				      ctx->inst_id, &ctx->map.limit))		return -EINVAL; 	/* Read D18F7xE08 (DramAddressCtl). */	if (df_indirect_read_instance(ctx->node_id, 7, 0xE08 + (16 * ctx->map.num),				      ctx->inst_id, &ctx->map.ctl))		return -EINVAL; 	/* Read D18F7xE0C (DramAddressIntlv). */	if (df_indirect_read_instance(ctx->node_id, 7, 0xE0C + (16 * ctx->map.num),				      ctx->inst_id, &ctx->map.intlv))		return -EINVAL; 	/* Check if Remap Enable bit is valid. */	if (!FIELD_GET(DF4_REMAP_EN, ctx->map.ctl))		return 0; 	/* Fill with bogus values, because '0' is a valid value. */	memset(&ctx->map.remap_array, 0xFF, sizeof(ctx->map.remap_array)); 	/* Get Remap registers. */	remap_sel = FIELD_GET(DF4_REMAP_SEL, ctx->map.ctl); 	/* Read D18F7x180 (CsTargetRemap0A). */	if (df_indirect_read_instance(ctx->node_id, 7, 0x180 + (8 * remap_sel),				      ctx->inst_id, &remap_reg))		return -EINVAL; 	/* Save first 8 remap entries. */	for (i = 0, j = 0; i < 8; i++, j++)		ctx->map.remap_array[i] = (remap_reg >> (j * shift)) & mask; 	/* Read D18F7x184 (CsTargetRemap0B). */	if (df_indirect_read_instance(ctx->node_id, 7, 0x184 + (8 * remap_sel),				      ctx->inst_id, &remap_reg))		return -EINVAL; 	/* Save next 8 remap entries. */	for (i = 8, j = 0; i < 16; i++, j++)		ctx->map.remap_array[i] = (remap_reg >> (j * shift)) & mask; 	return 0;} static int df4p5_get_dram_addr_map(struct addr_ctx *ctx){	u8 remap_sel, i, j, shift = 5, mask = 0x1F;	u32 remap_reg; 	/* Read D18F7x200 (DramBaseAddress). */	if (df_indirect_read_instance(ctx->node_id, 7, 0x200 + (16 * ctx->map.num),				      ctx->inst_id, &ctx->map.base))		return -EINVAL; 	/* Read D18F7x204 (DramLimitAddress). */	if (df_indirect_read_instance(ctx->node_id, 7, 0x204 + (16 * ctx->map.num),				      ctx->inst_id, &ctx->map.limit))		return -EINVAL; 	/* Read D18F7x208 (DramAddressCtl). */	if (df_indirect_read_instance(ctx->node_id, 7, 0x208 + (16 * ctx->map.num),				      ctx->inst_id, &ctx->map.ctl))		return -EINVAL; 	/* Read D18F7x20C (DramAddressIntlv). */	if (df_indirect_read_instance(ctx->node_id, 7, 0x20C + (16 * ctx->map.num),				      ctx->inst_id, &ctx->map.intlv))		return -EINVAL; 	/* Check if Remap Enable bit is valid. */	if (!FIELD_GET(DF4_REMAP_EN, ctx->map.ctl))		return 0; 	/* Fill with bogus values, because '0' is a valid value. */	memset(&ctx->map.remap_array, 0xFF, sizeof(ctx->map.remap_array)); 	/* Get Remap registers. */	remap_sel = FIELD_GET(DF4p5_REMAP_SEL, ctx->map.ctl); 	/* Read D18F7x180 (CsTargetRemap0A). */	if (df_indirect_read_instance(ctx->node_id, 7, 0x180 + (24 * remap_sel),				      ctx->inst_id, &remap_reg))		return -EINVAL; 	/* Save first 6 remap entries. */	for (i = 0, j = 0; i < 6; i++, j++)		ctx->map.remap_array[i] = (remap_reg >> (j * shift)) & mask; 	/* Read D18F7x184 (CsTargetRemap0B). */	if (df_indirect_read_instance(ctx->node_id, 7, 0x184 + (24 * remap_sel),				      ctx->inst_id, &remap_reg))		return -EINVAL; 	/* Save next 6 remap entries. */	for (i = 6, j = 0; i < 12; i++, j++)		ctx->map.remap_array[i] = (remap_reg >> (j * shift)) & mask; 	/* Read D18F7x188 (CsTargetRemap0C). */	if (df_indirect_read_instance(ctx->node_id, 7, 0x188 + (24 * remap_sel),				      ctx->inst_id, &remap_reg))		return -EINVAL; 	/* Save next 6 remap entries. */	for (i = 12, j = 0; i < 18; i++, j++)		ctx->map.remap_array[i] = (remap_reg >> (j * shift)) & mask; 	return 0;} static int get_dram_addr_map(struct addr_ctx *ctx){	switch (df_cfg.rev) {	case DF2:	return df2_get_dram_addr_map(ctx);	case DF3:	case DF3p5:	return df3_get_dram_addr_map(ctx);	case DF4:	return df4_get_dram_addr_map(ctx);	case DF4p5:	return df4p5_get_dram_addr_map(ctx);	default:			atl_debug_on_bad_df_rev();			return -EINVAL;	}} static int get_coh_st_fabric_id(struct addr_ctx *ctx){	u32 reg; 	/*	 * 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; 	/* Read D18F0x50 (FabricBlockInstanceInformation3). */	if (df_indirect_read_instance(ctx->node_id, 0, 0x50, ctx->inst_id, &reg))		return -EINVAL; 	if (df_cfg.rev < DF4p5)		ctx->coh_st_fabric_id = FIELD_GET(DF2_COH_ST_FABRIC_ID, reg);	else		ctx->coh_st_fabric_id = FIELD_GET(DF4p5_COH_ST_FABRIC_ID, reg); 	return 0;} static int find_normalized_offset(struct addr_ctx *ctx, u64 *norm_offset){	u64 last_offset = 0;	int ret; 	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;	} 	return 0;} static bool valid_map(struct addr_ctx *ctx){	if (df_cfg.rev >= DF4)		return FIELD_GET(DF_ADDR_RANGE_VAL, ctx->map.ctl);	else		return FIELD_GET(DF_ADDR_RANGE_VAL, ctx->map.base);} static int get_address_map_common(struct addr_ctx *ctx){	u64 norm_offset = 0; 	if (get_coh_st_fabric_id(ctx))		return -EINVAL; 	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:

Lines 1–500 of 775.