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 | |
10 | static 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 | |
36 | static 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 | |
84 | u64 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 | |