1// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
3
4#include <cxl.h>
5#include "core.h"
6
7#define CREATE_TRACE_POINTS
8#include "trace.h"
9
10static bool cxl_is_hpa_in_range(u64 hpa, struct cxl_region *cxlr, int pos)
11{
12 struct cxl_region_params *p = &cxlr->params;
13 int gran = p->interleave_granularity;
14 int ways = p->interleave_ways;
15 u64 offset;
16
17 /* Is the hpa within this region at all */
18 if (hpa < p->res->start || hpa > p->res->end) {
19 dev_dbg(&cxlr->dev,
20 "Addr trans fail: hpa 0x%llx not in region\n", hpa);
21 return false;
22 }
23
24 /* Is the hpa in an expected chunk for its pos(-ition) */
25 offset = hpa - p->res->start;
26 offset = do_div(offset, gran * ways);
27 if ((offset >= pos * gran) && (offset < (pos + 1) * gran))
28 return true;
29
30 dev_dbg(&cxlr->dev,
31 "Addr trans fail: hpa 0x%llx not in expected chunk\n", hpa);
32
33 return false;
34}
35
36static u64 cxl_dpa_to_hpa(u64 dpa, struct cxl_region *cxlr,
37 struct cxl_endpoint_decoder *cxled)
38{
39 u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa;
40 struct cxl_region_params *p = &cxlr->params;
41 int pos = cxled->pos;
42 u16 eig = 0;
43 u8 eiw = 0;
44
45 ways_to_eiw(ways: p->interleave_ways, eiw: &eiw);
46 granularity_to_eig(granularity: p->interleave_granularity, eig: &eig);
47
48 /*
49 * The device position in the region interleave set was removed
50 * from the offset at HPA->DPA translation. To reconstruct the
51 * HPA, place the 'pos' in the offset.
52 *
53 * The placement of 'pos' in the HPA is determined by interleave
54 * ways and granularity and is defined in the CXL Spec 3.0 Section
55 * 8.2.4.19.13 Implementation Note: Device Decode Logic
56 */
57
58 /* Remove the dpa base */
59 dpa_offset = dpa - cxl_dpa_resource_start(cxled);
60
61 mask_upper = GENMASK_ULL(51, eig + 8);
62
63 if (eiw < 8) {
64 hpa_offset = (dpa_offset & mask_upper) << eiw;
65 hpa_offset |= pos << (eig + 8);
66 } else {
67 bits_upper = (dpa_offset & mask_upper) >> (eig + 8);
68 bits_upper = bits_upper * 3;
69 hpa_offset = ((bits_upper << (eiw - 8)) + pos) << (eig + 8);
70 }
71
72 /* The lower bits remain unchanged */
73 hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0);
74
75 /* Apply the hpa_offset to the region base address */
76 hpa = hpa_offset + p->res->start;
77
78 if (!cxl_is_hpa_in_range(hpa, cxlr, pos: cxled->pos))
79 return ULLONG_MAX;
80
81 return hpa;
82}
83
84u64 cxl_trace_hpa(struct cxl_region *cxlr, struct cxl_memdev *cxlmd,
85 u64 dpa)
86{
87 struct cxl_region_params *p = &cxlr->params;
88 struct cxl_endpoint_decoder *cxled = NULL;
89
90 for (int i = 0; i < p->nr_targets; i++) {
91 cxled = p->targets[i];
92 if (cxlmd == cxled_to_memdev(cxled))
93 break;
94 }
95 if (!cxled || cxlmd != cxled_to_memdev(cxled))
96 return ULLONG_MAX;
97
98 return cxl_dpa_to_hpa(dpa, cxlr, cxled);
99}
100

source code of linux/drivers/cxl/core/trace.c