162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright(c) 2022 Intel Corporation. All rights reserved. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <cxl.h> 562306a36Sopenharmony_ci#include "core.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 862306a36Sopenharmony_ci#include "trace.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistatic bool cxl_is_hpa_in_range(u64 hpa, struct cxl_region *cxlr, int pos) 1162306a36Sopenharmony_ci{ 1262306a36Sopenharmony_ci struct cxl_region_params *p = &cxlr->params; 1362306a36Sopenharmony_ci int gran = p->interleave_granularity; 1462306a36Sopenharmony_ci int ways = p->interleave_ways; 1562306a36Sopenharmony_ci u64 offset; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci /* Is the hpa within this region at all */ 1862306a36Sopenharmony_ci if (hpa < p->res->start || hpa > p->res->end) { 1962306a36Sopenharmony_ci dev_dbg(&cxlr->dev, 2062306a36Sopenharmony_ci "Addr trans fail: hpa 0x%llx not in region\n", hpa); 2162306a36Sopenharmony_ci return false; 2262306a36Sopenharmony_ci } 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci /* Is the hpa in an expected chunk for its pos(-ition) */ 2562306a36Sopenharmony_ci offset = hpa - p->res->start; 2662306a36Sopenharmony_ci offset = do_div(offset, gran * ways); 2762306a36Sopenharmony_ci if ((offset >= pos * gran) && (offset < (pos + 1) * gran)) 2862306a36Sopenharmony_ci return true; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci dev_dbg(&cxlr->dev, 3162306a36Sopenharmony_ci "Addr trans fail: hpa 0x%llx not in expected chunk\n", hpa); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci return false; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic u64 cxl_dpa_to_hpa(u64 dpa, struct cxl_region *cxlr, 3762306a36Sopenharmony_ci struct cxl_endpoint_decoder *cxled) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa; 4062306a36Sopenharmony_ci struct cxl_region_params *p = &cxlr->params; 4162306a36Sopenharmony_ci int pos = cxled->pos; 4262306a36Sopenharmony_ci u16 eig = 0; 4362306a36Sopenharmony_ci u8 eiw = 0; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ways_to_eiw(p->interleave_ways, &eiw); 4662306a36Sopenharmony_ci granularity_to_eig(p->interleave_granularity, &eig); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * The device position in the region interleave set was removed 5062306a36Sopenharmony_ci * from the offset at HPA->DPA translation. To reconstruct the 5162306a36Sopenharmony_ci * HPA, place the 'pos' in the offset. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * The placement of 'pos' in the HPA is determined by interleave 5462306a36Sopenharmony_ci * ways and granularity and is defined in the CXL Spec 3.0 Section 5562306a36Sopenharmony_ci * 8.2.4.19.13 Implementation Note: Device Decode Logic 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* Remove the dpa base */ 5962306a36Sopenharmony_ci dpa_offset = dpa - cxl_dpa_resource_start(cxled); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci mask_upper = GENMASK_ULL(51, eig + 8); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (eiw < 8) { 6462306a36Sopenharmony_ci hpa_offset = (dpa_offset & mask_upper) << eiw; 6562306a36Sopenharmony_ci hpa_offset |= pos << (eig + 8); 6662306a36Sopenharmony_ci } else { 6762306a36Sopenharmony_ci bits_upper = (dpa_offset & mask_upper) >> (eig + 8); 6862306a36Sopenharmony_ci bits_upper = bits_upper * 3; 6962306a36Sopenharmony_ci hpa_offset = ((bits_upper << (eiw - 8)) + pos) << (eig + 8); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* The lower bits remain unchanged */ 7362306a36Sopenharmony_ci hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* Apply the hpa_offset to the region base address */ 7662306a36Sopenharmony_ci hpa = hpa_offset + p->res->start; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!cxl_is_hpa_in_range(hpa, cxlr, cxled->pos)) 7962306a36Sopenharmony_ci return ULLONG_MAX; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return hpa; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciu64 cxl_trace_hpa(struct cxl_region *cxlr, struct cxl_memdev *cxlmd, 8562306a36Sopenharmony_ci u64 dpa) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct cxl_region_params *p = &cxlr->params; 8862306a36Sopenharmony_ci struct cxl_endpoint_decoder *cxled = NULL; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci for (int i = 0; i < p->nr_targets; i++) { 9162306a36Sopenharmony_ci cxled = p->targets[i]; 9262306a36Sopenharmony_ci if (cxlmd == cxled_to_memdev(cxled)) 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci if (!cxled || cxlmd != cxled_to_memdev(cxled)) 9662306a36Sopenharmony_ci return ULLONG_MAX; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return cxl_dpa_to_hpa(dpa, cxlr, cxled); 9962306a36Sopenharmony_ci} 100