diff options
Diffstat (limited to 'drivers/cxl/core/trace.c')
| -rw-r--r-- | drivers/cxl/core/trace.c | 94 | 
1 files changed, 94 insertions, 0 deletions
diff --git a/drivers/cxl/core/trace.c b/drivers/cxl/core/trace.c index 29ae7ce81dc5..d0403dc3c8ab 100644 --- a/drivers/cxl/core/trace.c +++ b/drivers/cxl/core/trace.c @@ -1,5 +1,99 @@  // SPDX-License-Identifier: GPL-2.0-only  /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ +#include <cxl.h> +#include "core.h" +  #define CREATE_TRACE_POINTS  #include "trace.h" + +static bool cxl_is_hpa_in_range(u64 hpa, struct cxl_region *cxlr, int pos) +{ +	struct cxl_region_params *p = &cxlr->params; +	int gran = p->interleave_granularity; +	int ways = p->interleave_ways; +	u64 offset; + +	/* Is the hpa within this region at all */ +	if (hpa < p->res->start || hpa > p->res->end) { +		dev_dbg(&cxlr->dev, +			"Addr trans fail: hpa 0x%llx not in region\n", hpa); +		return false; +	} + +	/* Is the hpa in an expected chunk for its pos(-ition) */ +	offset = hpa - p->res->start; +	offset = do_div(offset, gran * ways); +	if ((offset >= pos * gran) && (offset < (pos + 1) * gran)) +		return true; + +	dev_dbg(&cxlr->dev, +		"Addr trans fail: hpa 0x%llx not in expected chunk\n", hpa); + +	return false; +} + +static u64 cxl_dpa_to_hpa(u64 dpa,  struct cxl_region *cxlr, +			  struct cxl_endpoint_decoder *cxled) +{ +	u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa; +	struct cxl_region_params *p = &cxlr->params; +	int pos = cxled->pos; +	u16 eig = 0; +	u8 eiw = 0; + +	ways_to_eiw(p->interleave_ways, &eiw); +	granularity_to_eig(p->interleave_granularity, &eig); + +	/* +	 * The device position in the region interleave set was removed +	 * from the offset at HPA->DPA translation. To reconstruct the +	 * HPA, place the 'pos' in the offset. +	 * +	 * The placement of 'pos' in the HPA is determined by interleave +	 * ways and granularity and is defined in the CXL Spec 3.0 Section +	 * 8.2.4.19.13 Implementation Note: Device Decode Logic +	 */ + +	/* Remove the dpa base */ +	dpa_offset = dpa - cxl_dpa_resource_start(cxled); + +	mask_upper = GENMASK_ULL(51, eig + 8); + +	if (eiw < 8) { +		hpa_offset = (dpa_offset & mask_upper) << eiw; +		hpa_offset |= pos << (eig + 8); +	} else { +		bits_upper = (dpa_offset & mask_upper) >> (eig + 8); +		bits_upper = bits_upper * 3; +		hpa_offset = ((bits_upper << (eiw - 8)) + pos) << (eig + 8); +	} + +	/* The lower bits remain unchanged */ +	hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0); + +	/* Apply the hpa_offset to the region base address */ +	hpa = hpa_offset + p->res->start; + +	if (!cxl_is_hpa_in_range(hpa, cxlr, cxled->pos)) +		return ULLONG_MAX; + +	return hpa; +} + +u64 cxl_trace_hpa(struct cxl_region *cxlr, struct cxl_memdev *cxlmd, +		  u64 dpa) +{ +	struct cxl_region_params *p = &cxlr->params; +	struct cxl_endpoint_decoder *cxled = NULL; + +	for (int i = 0; i <  p->nr_targets; i++) { +		cxled = p->targets[i]; +		if (cxlmd == cxled_to_memdev(cxled)) +			break; +	} +	if (!cxled || cxlmd != cxled_to_memdev(cxled)) +		return ULLONG_MAX; + +	return cxl_dpa_to_hpa(dpa, cxlr, cxled); +}  |