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