2023-08-30 17:31:07 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
|
|
|
|
2023-10-24 12:59:35 +02:00
|
|
|
#include <cxl.h>
|
|
|
|
#include "core.h"
|
|
|
|
|
2023-08-30 17:31:07 +02:00
|
|
|
#define CREATE_TRACE_POINTS
|
|
|
|
#include "trace.h"
|
2023-10-24 12:59:35 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|