xref: /linux/drivers/cxl/core/atl.c (revision e812928be2ee1c2744adf20ed04e0ce1e2fc5c13)
1af74daf9SRobert Richter // SPDX-License-Identifier: GPL-2.0-only
2af74daf9SRobert Richter /*
3af74daf9SRobert Richter  * Copyright (C) 2025 Advanced Micro Devices, Inc.
4af74daf9SRobert Richter  */
5af74daf9SRobert Richter 
6af74daf9SRobert Richter #include <linux/prmt.h>
7af74daf9SRobert Richter #include <linux/pci.h>
8af74daf9SRobert Richter #include <linux/acpi.h>
9af74daf9SRobert Richter 
10af74daf9SRobert Richter #include <cxlmem.h>
11af74daf9SRobert Richter #include "core.h"
12af74daf9SRobert Richter 
13af74daf9SRobert Richter /*
14af74daf9SRobert Richter  * PRM Address Translation - CXL DPA to System Physical Address
15af74daf9SRobert Richter  *
16af74daf9SRobert Richter  * Reference:
17af74daf9SRobert Richter  *
18af74daf9SRobert Richter  * AMD Family 1Ah Models 00h–0Fh and Models 10h–1Fh
19af74daf9SRobert Richter  * ACPI v6.5 Porting Guide, Publication # 58088
20af74daf9SRobert Richter  */
21af74daf9SRobert Richter 
22af74daf9SRobert Richter static const guid_t prm_cxl_dpa_spa_guid =
23af74daf9SRobert Richter 	GUID_INIT(0xee41b397, 0x25d4, 0x452c, 0xad, 0x54, 0x48, 0xc6, 0xe3,
24af74daf9SRobert Richter 		  0x48, 0x0b, 0x94);
25af74daf9SRobert Richter 
26af74daf9SRobert Richter struct prm_cxl_dpa_spa_data {
27af74daf9SRobert Richter 	u64 dpa;
28af74daf9SRobert Richter 	u8 reserved;
29af74daf9SRobert Richter 	u8 devfn;
30af74daf9SRobert Richter 	u8 bus;
31af74daf9SRobert Richter 	u8 segment;
32af74daf9SRobert Richter 	u64 *spa;
33af74daf9SRobert Richter } __packed;
34af74daf9SRobert Richter 
35af74daf9SRobert Richter static u64 prm_cxl_dpa_spa(struct pci_dev *pci_dev, u64 dpa)
36af74daf9SRobert Richter {
37af74daf9SRobert Richter 	struct prm_cxl_dpa_spa_data data;
38af74daf9SRobert Richter 	u64 spa;
39af74daf9SRobert Richter 	int rc;
40af74daf9SRobert Richter 
41af74daf9SRobert Richter 	data = (struct prm_cxl_dpa_spa_data) {
42af74daf9SRobert Richter 		.dpa     = dpa,
43af74daf9SRobert Richter 		.devfn   = pci_dev->devfn,
44af74daf9SRobert Richter 		.bus     = pci_dev->bus->number,
45af74daf9SRobert Richter 		.segment = pci_domain_nr(pci_dev->bus),
46af74daf9SRobert Richter 		.spa     = &spa,
47af74daf9SRobert Richter 	};
48af74daf9SRobert Richter 
49af74daf9SRobert Richter 	rc = acpi_call_prm_handler(prm_cxl_dpa_spa_guid, &data);
50af74daf9SRobert Richter 	if (rc) {
51af74daf9SRobert Richter 		pci_dbg(pci_dev, "failed to get SPA for %#llx: %d\n", dpa, rc);
52af74daf9SRobert Richter 		return ULLONG_MAX;
53af74daf9SRobert Richter 	}
54af74daf9SRobert Richter 
55af74daf9SRobert Richter 	pci_dbg(pci_dev, "PRM address translation: DPA -> SPA: %#llx -> %#llx\n", dpa, spa);
56af74daf9SRobert Richter 
57af74daf9SRobert Richter 	return spa;
58af74daf9SRobert Richter }
59af74daf9SRobert Richter 
60af74daf9SRobert Richter static int cxl_prm_setup_root(struct cxl_root *cxl_root, void *data)
61af74daf9SRobert Richter {
62af74daf9SRobert Richter 	struct cxl_region_context *ctx = data;
63af74daf9SRobert Richter 	struct cxl_endpoint_decoder *cxled = ctx->cxled;
64af74daf9SRobert Richter 	struct cxl_decoder *cxld = &cxled->cxld;
65af74daf9SRobert Richter 	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
66af74daf9SRobert Richter 	struct range hpa_range = ctx->hpa_range;
67af74daf9SRobert Richter 	struct pci_dev *pci_dev;
68af74daf9SRobert Richter 	u64 spa_len, len;
69af74daf9SRobert Richter 	u64 addr, base_spa, base;
70af74daf9SRobert Richter 	int ways, gran;
71af74daf9SRobert Richter 
72af74daf9SRobert Richter 	/*
73af74daf9SRobert Richter 	 * When Normalized Addressing is enabled, the endpoint maintains a 1:1
74af74daf9SRobert Richter 	 * mapping between HPA and DPA. If disabled, skip address translation
75af74daf9SRobert Richter 	 * and perform only a range check.
76af74daf9SRobert Richter 	 */
77af74daf9SRobert Richter 	if (hpa_range.start != cxled->dpa_res->start)
78af74daf9SRobert Richter 		return 0;
79af74daf9SRobert Richter 
80af74daf9SRobert Richter 	/*
81af74daf9SRobert Richter 	 * Endpoints are programmed passthrough in Normalized Addressing mode.
82af74daf9SRobert Richter 	 */
83af74daf9SRobert Richter 	if (ctx->interleave_ways != 1) {
84af74daf9SRobert Richter 		dev_dbg(&cxld->dev, "unexpected interleaving config: ways: %d granularity: %d\n",
85af74daf9SRobert Richter 			ctx->interleave_ways, ctx->interleave_granularity);
86af74daf9SRobert Richter 		return -ENXIO;
87af74daf9SRobert Richter 	}
88af74daf9SRobert Richter 
89af74daf9SRobert Richter 	if (!cxlmd || !dev_is_pci(cxlmd->dev.parent)) {
90af74daf9SRobert Richter 		dev_dbg(&cxld->dev, "No endpoint found: %s, range %#llx-%#llx\n",
91af74daf9SRobert Richter 			dev_name(cxld->dev.parent), hpa_range.start,
92af74daf9SRobert Richter 			hpa_range.end);
93af74daf9SRobert Richter 		return -ENXIO;
94af74daf9SRobert Richter 	}
95af74daf9SRobert Richter 
96af74daf9SRobert Richter 	pci_dev = to_pci_dev(cxlmd->dev.parent);
97af74daf9SRobert Richter 
98af74daf9SRobert Richter 	/* Translate HPA range to SPA. */
99af74daf9SRobert Richter 	base = hpa_range.start;
100af74daf9SRobert Richter 	hpa_range.start = prm_cxl_dpa_spa(pci_dev, hpa_range.start);
101af74daf9SRobert Richter 	hpa_range.end = prm_cxl_dpa_spa(pci_dev, hpa_range.end);
102af74daf9SRobert Richter 	base_spa = hpa_range.start;
103af74daf9SRobert Richter 
104af74daf9SRobert Richter 	if (hpa_range.start == ULLONG_MAX || hpa_range.end == ULLONG_MAX) {
105af74daf9SRobert Richter 		dev_dbg(cxld->dev.parent,
106af74daf9SRobert Richter 			"CXL address translation: Failed to translate HPA range: %#llx-%#llx:%#llx-%#llx(%s)\n",
107af74daf9SRobert Richter 			hpa_range.start, hpa_range.end, ctx->hpa_range.start,
108af74daf9SRobert Richter 			ctx->hpa_range.end, dev_name(&cxld->dev));
109af74daf9SRobert Richter 		return -ENXIO;
110af74daf9SRobert Richter 	}
111af74daf9SRobert Richter 
112af74daf9SRobert Richter 	/*
113af74daf9SRobert Richter 	 * Since translated addresses include the interleaving offsets, align
114af74daf9SRobert Richter 	 * the range to 256 MB.
115af74daf9SRobert Richter 	 */
116af74daf9SRobert Richter 	hpa_range.start = ALIGN_DOWN(hpa_range.start, SZ_256M);
117af74daf9SRobert Richter 	hpa_range.end = ALIGN(hpa_range.end, SZ_256M) - 1;
118af74daf9SRobert Richter 
119af74daf9SRobert Richter 	len = range_len(&ctx->hpa_range);
120af74daf9SRobert Richter 	spa_len = range_len(&hpa_range);
121af74daf9SRobert Richter 	if (!len || !spa_len || spa_len % len) {
122af74daf9SRobert Richter 		dev_dbg(cxld->dev.parent,
123af74daf9SRobert Richter 			"CXL address translation: HPA range not contiguous: %#llx-%#llx:%#llx-%#llx(%s)\n",
124af74daf9SRobert Richter 			hpa_range.start, hpa_range.end, ctx->hpa_range.start,
125af74daf9SRobert Richter 			ctx->hpa_range.end, dev_name(&cxld->dev));
126af74daf9SRobert Richter 		return -ENXIO;
127af74daf9SRobert Richter 	}
128af74daf9SRobert Richter 
129af74daf9SRobert Richter 	ways = spa_len / len;
130af74daf9SRobert Richter 	gran = SZ_256;
131af74daf9SRobert Richter 
132af74daf9SRobert Richter 	/*
133af74daf9SRobert Richter 	 * Determine interleave granularity
134af74daf9SRobert Richter 	 *
135af74daf9SRobert Richter 	 * Note: The position of the chunk from one interleaving block to the
136af74daf9SRobert Richter 	 * next may vary and thus cannot be considered constant. Address offsets
137af74daf9SRobert Richter 	 * larger than the interleaving block size cannot be used to calculate
138af74daf9SRobert Richter 	 * the granularity.
139af74daf9SRobert Richter 	 */
140af74daf9SRobert Richter 	if (ways > 1) {
141af74daf9SRobert Richter 		while (gran <= SZ_16M) {
142af74daf9SRobert Richter 			addr = prm_cxl_dpa_spa(pci_dev, base + gran);
143af74daf9SRobert Richter 			if (addr != base_spa + gran)
144af74daf9SRobert Richter 				break;
145af74daf9SRobert Richter 			gran <<= 1;
146af74daf9SRobert Richter 		}
147af74daf9SRobert Richter 	}
148af74daf9SRobert Richter 
149af74daf9SRobert Richter 	if (gran > SZ_16M) {
150af74daf9SRobert Richter 		dev_dbg(cxld->dev.parent,
151af74daf9SRobert Richter 			"CXL address translation: Cannot determine granularity: %#llx-%#llx:%#llx-%#llx(%s)\n",
152af74daf9SRobert Richter 			hpa_range.start, hpa_range.end, ctx->hpa_range.start,
153af74daf9SRobert Richter 			ctx->hpa_range.end, dev_name(&cxld->dev));
154af74daf9SRobert Richter 		return -ENXIO;
155af74daf9SRobert Richter 	}
156af74daf9SRobert Richter 
157a2e79489SRobert Richter 	/*
158a2e79489SRobert Richter 	 * The current kernel implementation does not support endpoint
159a2e79489SRobert Richter 	 * setup with Normalized Addressing. It only translates an
160a2e79489SRobert Richter 	 * endpoint's DPA to the SPA range of the host bridge.
161a2e79489SRobert Richter 	 * Therefore, the endpoint address range cannot be determined,
162a2e79489SRobert Richter 	 * making a non-auto setup impossible. If a decoder requires
163a2e79489SRobert Richter 	 * address translation, reprogramming should be disabled and
164a2e79489SRobert Richter 	 * the decoder locked.
165a2e79489SRobert Richter 	 *
166a2e79489SRobert Richter 	 * The BIOS, however, provides all the necessary address
167a2e79489SRobert Richter 	 * translation data, which the kernel can use to reconfigure
168a2e79489SRobert Richter 	 * endpoint decoders with normalized addresses. Locking the
169a2e79489SRobert Richter 	 * decoders in the BIOS would prevent a capable kernel (or
170a2e79489SRobert Richter 	 * other operating systems) from shutting down auto-generated
171a2e79489SRobert Richter 	 * regions and managing resources dynamically.
172*208f4324SRobert Richter 	 *
173*208f4324SRobert Richter 	 * Indicate that Normalized Addressing is enabled.
174a2e79489SRobert Richter 	 */
175a2e79489SRobert Richter 	cxld->flags |= CXL_DECODER_F_LOCK;
176*208f4324SRobert Richter 	cxld->flags |= CXL_DECODER_F_NORMALIZED_ADDRESSING;
177a2e79489SRobert Richter 
178af74daf9SRobert Richter 	ctx->hpa_range = hpa_range;
179af74daf9SRobert Richter 	ctx->interleave_ways = ways;
180af74daf9SRobert Richter 	ctx->interleave_granularity = gran;
181af74daf9SRobert Richter 
182af74daf9SRobert Richter 	dev_dbg(&cxld->dev,
183af74daf9SRobert Richter 		"address mapping found for %s (hpa -> spa): %#llx+%#llx -> %#llx+%#llx ways:%d granularity:%d\n",
184af74daf9SRobert Richter 		dev_name(cxlmd->dev.parent), base, len, hpa_range.start,
185af74daf9SRobert Richter 		spa_len, ways, gran);
186af74daf9SRobert Richter 
187af74daf9SRobert Richter 	return 0;
188af74daf9SRobert Richter }
189af74daf9SRobert Richter 
190af74daf9SRobert Richter void cxl_setup_prm_address_translation(struct cxl_root *cxl_root)
191af74daf9SRobert Richter {
192af74daf9SRobert Richter 	struct device *host = cxl_root->port.uport_dev;
193af74daf9SRobert Richter 	u64 spa;
194af74daf9SRobert Richter 	struct prm_cxl_dpa_spa_data data = { .spa = &spa };
195af74daf9SRobert Richter 	int rc;
196af74daf9SRobert Richter 
197af74daf9SRobert Richter 	/*
198af74daf9SRobert Richter 	 * Applies only to PCIe Host Bridges which are children of the CXL Root
199af74daf9SRobert Richter 	 * Device (HID=“ACPI0017”). Check this and drop cxl_test instances.
200af74daf9SRobert Richter 	 */
201af74daf9SRobert Richter 	if (!acpi_match_device(host->driver->acpi_match_table, host))
202af74daf9SRobert Richter 		return;
203af74daf9SRobert Richter 
204af74daf9SRobert Richter 	/* Check kernel (-EOPNOTSUPP) and firmware support (-ENODEV) */
205af74daf9SRobert Richter 	rc = acpi_call_prm_handler(prm_cxl_dpa_spa_guid, &data);
206af74daf9SRobert Richter 	if (rc == -EOPNOTSUPP || rc == -ENODEV)
207af74daf9SRobert Richter 		return;
208af74daf9SRobert Richter 
209af74daf9SRobert Richter 	cxl_root->ops.translation_setup_root = cxl_prm_setup_root;
210af74daf9SRobert Richter }
211af74daf9SRobert Richter EXPORT_SYMBOL_NS_GPL(cxl_setup_prm_address_translation, "CXL");
212