162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright(c) 2021 Intel Corporation. All rights reserved. */ 362306a36Sopenharmony_ci#include <linux/platform_device.h> 462306a36Sopenharmony_ci#include <linux/module.h> 562306a36Sopenharmony_ci#include <linux/device.h> 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/acpi.h> 862306a36Sopenharmony_ci#include <linux/pci.h> 962306a36Sopenharmony_ci#include <asm/div64.h> 1062306a36Sopenharmony_ci#include "cxlpci.h" 1162306a36Sopenharmony_ci#include "cxl.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define CXL_RCRB_SIZE SZ_8K 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct cxl_cxims_data { 1662306a36Sopenharmony_ci int nr_maps; 1762306a36Sopenharmony_ci u64 xormaps[] __counted_by(nr_maps); 1862306a36Sopenharmony_ci}; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Find a targets entry (n) in the host bridge interleave list. 2262306a36Sopenharmony_ci * CXL Specification 3.0 Table 9-22 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_cistatic int cxl_xor_calc_n(u64 hpa, struct cxl_cxims_data *cximsd, int iw, 2562306a36Sopenharmony_ci int ig) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci int i = 0, n = 0; 2862306a36Sopenharmony_ci u8 eiw; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci /* IW: 2,4,6,8,12,16 begin building 'n' using xormaps */ 3162306a36Sopenharmony_ci if (iw != 3) { 3262306a36Sopenharmony_ci for (i = 0; i < cximsd->nr_maps; i++) 3362306a36Sopenharmony_ci n |= (hweight64(hpa & cximsd->xormaps[i]) & 1) << i; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci /* IW: 3,6,12 add a modulo calculation to 'n' */ 3662306a36Sopenharmony_ci if (!is_power_of_2(iw)) { 3762306a36Sopenharmony_ci if (ways_to_eiw(iw, &eiw)) 3862306a36Sopenharmony_ci return -1; 3962306a36Sopenharmony_ci hpa &= GENMASK_ULL(51, eiw + ig); 4062306a36Sopenharmony_ci n |= do_div(hpa, 3) << i; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci return n; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic struct cxl_dport *cxl_hb_xor(struct cxl_root_decoder *cxlrd, int pos) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct cxl_cxims_data *cximsd = cxlrd->platform_data; 4862306a36Sopenharmony_ci struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd; 4962306a36Sopenharmony_ci struct cxl_decoder *cxld = &cxlsd->cxld; 5062306a36Sopenharmony_ci int ig = cxld->interleave_granularity; 5162306a36Sopenharmony_ci int iw = cxld->interleave_ways; 5262306a36Sopenharmony_ci int n = 0; 5362306a36Sopenharmony_ci u64 hpa; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (dev_WARN_ONCE(&cxld->dev, 5662306a36Sopenharmony_ci cxld->interleave_ways != cxlsd->nr_targets, 5762306a36Sopenharmony_ci "misconfigured root decoder\n")) 5862306a36Sopenharmony_ci return NULL; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci hpa = cxlrd->res->start + pos * ig; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* Entry (n) is 0 for no interleave (iw == 1) */ 6362306a36Sopenharmony_ci if (iw != 1) 6462306a36Sopenharmony_ci n = cxl_xor_calc_n(hpa, cximsd, iw, ig); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (n < 0) 6762306a36Sopenharmony_ci return NULL; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return cxlrd->cxlsd.target[n]; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct cxl_cxims_context { 7362306a36Sopenharmony_ci struct device *dev; 7462306a36Sopenharmony_ci struct cxl_root_decoder *cxlrd; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int cxl_parse_cxims(union acpi_subtable_headers *header, void *arg, 7862306a36Sopenharmony_ci const unsigned long end) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct acpi_cedt_cxims *cxims = (struct acpi_cedt_cxims *)header; 8162306a36Sopenharmony_ci struct cxl_cxims_context *ctx = arg; 8262306a36Sopenharmony_ci struct cxl_root_decoder *cxlrd = ctx->cxlrd; 8362306a36Sopenharmony_ci struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 8462306a36Sopenharmony_ci struct device *dev = ctx->dev; 8562306a36Sopenharmony_ci struct cxl_cxims_data *cximsd; 8662306a36Sopenharmony_ci unsigned int hbig, nr_maps; 8762306a36Sopenharmony_ci int rc; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci rc = eig_to_granularity(cxims->hbig, &hbig); 9062306a36Sopenharmony_ci if (rc) 9162306a36Sopenharmony_ci return rc; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Does this CXIMS entry apply to the given CXL Window? */ 9462306a36Sopenharmony_ci if (hbig != cxld->interleave_granularity) 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* IW 1,3 do not use xormaps and skip this parsing entirely */ 9862306a36Sopenharmony_ci if (is_power_of_2(cxld->interleave_ways)) 9962306a36Sopenharmony_ci /* 2, 4, 8, 16 way */ 10062306a36Sopenharmony_ci nr_maps = ilog2(cxld->interleave_ways); 10162306a36Sopenharmony_ci else 10262306a36Sopenharmony_ci /* 6, 12 way */ 10362306a36Sopenharmony_ci nr_maps = ilog2(cxld->interleave_ways / 3); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (cxims->nr_xormaps < nr_maps) { 10662306a36Sopenharmony_ci dev_dbg(dev, "CXIMS nr_xormaps[%d] expected[%d]\n", 10762306a36Sopenharmony_ci cxims->nr_xormaps, nr_maps); 10862306a36Sopenharmony_ci return -ENXIO; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci cximsd = devm_kzalloc(dev, struct_size(cximsd, xormaps, nr_maps), 11262306a36Sopenharmony_ci GFP_KERNEL); 11362306a36Sopenharmony_ci if (!cximsd) 11462306a36Sopenharmony_ci return -ENOMEM; 11562306a36Sopenharmony_ci cximsd->nr_maps = nr_maps; 11662306a36Sopenharmony_ci memcpy(cximsd->xormaps, cxims->xormap_list, 11762306a36Sopenharmony_ci nr_maps * sizeof(*cximsd->xormaps)); 11862306a36Sopenharmony_ci cxlrd->platform_data = cximsd; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic unsigned long cfmws_to_decoder_flags(int restrictions) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci unsigned long flags = CXL_DECODER_F_ENABLE; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE2) 12862306a36Sopenharmony_ci flags |= CXL_DECODER_F_TYPE2; 12962306a36Sopenharmony_ci if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE3) 13062306a36Sopenharmony_ci flags |= CXL_DECODER_F_TYPE3; 13162306a36Sopenharmony_ci if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE) 13262306a36Sopenharmony_ci flags |= CXL_DECODER_F_RAM; 13362306a36Sopenharmony_ci if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_PMEM) 13462306a36Sopenharmony_ci flags |= CXL_DECODER_F_PMEM; 13562306a36Sopenharmony_ci if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED) 13662306a36Sopenharmony_ci flags |= CXL_DECODER_F_LOCK; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return flags; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int cxl_acpi_cfmws_verify(struct device *dev, 14262306a36Sopenharmony_ci struct acpi_cedt_cfmws *cfmws) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int rc, expected_len; 14562306a36Sopenharmony_ci unsigned int ways; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO && 14862306a36Sopenharmony_ci cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_XOR) { 14962306a36Sopenharmony_ci dev_err(dev, "CFMWS Unknown Interleave Arithmetic: %d\n", 15062306a36Sopenharmony_ci cfmws->interleave_arithmetic); 15162306a36Sopenharmony_ci return -EINVAL; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!IS_ALIGNED(cfmws->base_hpa, SZ_256M)) { 15562306a36Sopenharmony_ci dev_err(dev, "CFMWS Base HPA not 256MB aligned\n"); 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!IS_ALIGNED(cfmws->window_size, SZ_256M)) { 16062306a36Sopenharmony_ci dev_err(dev, "CFMWS Window Size not 256MB aligned\n"); 16162306a36Sopenharmony_ci return -EINVAL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci rc = eiw_to_ways(cfmws->interleave_ways, &ways); 16562306a36Sopenharmony_ci if (rc) { 16662306a36Sopenharmony_ci dev_err(dev, "CFMWS Interleave Ways (%d) invalid\n", 16762306a36Sopenharmony_ci cfmws->interleave_ways); 16862306a36Sopenharmony_ci return -EINVAL; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci expected_len = struct_size(cfmws, interleave_targets, ways); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (cfmws->header.length < expected_len) { 17462306a36Sopenharmony_ci dev_err(dev, "CFMWS length %d less than expected %d\n", 17562306a36Sopenharmony_ci cfmws->header.length, expected_len); 17662306a36Sopenharmony_ci return -EINVAL; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (cfmws->header.length > expected_len) 18062306a36Sopenharmony_ci dev_dbg(dev, "CFMWS length %d greater than expected %d\n", 18162306a36Sopenharmony_ci cfmws->header.length, expected_len); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci * Note, @dev must be the first member, see 'struct cxl_chbs_context' 18862306a36Sopenharmony_ci * and mock_acpi_table_parse_cedt() 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_cistruct cxl_cfmws_context { 19162306a36Sopenharmony_ci struct device *dev; 19262306a36Sopenharmony_ci struct cxl_port *root_port; 19362306a36Sopenharmony_ci struct resource *cxl_res; 19462306a36Sopenharmony_ci int id; 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, 19862306a36Sopenharmony_ci struct cxl_cfmws_context *ctx) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int target_map[CXL_DECODER_MAX_INTERLEAVE]; 20162306a36Sopenharmony_ci struct cxl_port *root_port = ctx->root_port; 20262306a36Sopenharmony_ci struct resource *cxl_res = ctx->cxl_res; 20362306a36Sopenharmony_ci struct cxl_cxims_context cxims_ctx; 20462306a36Sopenharmony_ci struct cxl_root_decoder *cxlrd; 20562306a36Sopenharmony_ci struct device *dev = ctx->dev; 20662306a36Sopenharmony_ci cxl_calc_hb_fn cxl_calc_hb; 20762306a36Sopenharmony_ci struct cxl_decoder *cxld; 20862306a36Sopenharmony_ci unsigned int ways, i, ig; 20962306a36Sopenharmony_ci struct resource *res; 21062306a36Sopenharmony_ci int rc; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci rc = cxl_acpi_cfmws_verify(dev, cfmws); 21362306a36Sopenharmony_ci if (rc) { 21462306a36Sopenharmony_ci dev_err(dev, "CFMWS range %#llx-%#llx not registered\n", 21562306a36Sopenharmony_ci cfmws->base_hpa, 21662306a36Sopenharmony_ci cfmws->base_hpa + cfmws->window_size - 1); 21762306a36Sopenharmony_ci return rc; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci rc = eiw_to_ways(cfmws->interleave_ways, &ways); 22162306a36Sopenharmony_ci if (rc) 22262306a36Sopenharmony_ci return rc; 22362306a36Sopenharmony_ci rc = eig_to_granularity(cfmws->granularity, &ig); 22462306a36Sopenharmony_ci if (rc) 22562306a36Sopenharmony_ci return rc; 22662306a36Sopenharmony_ci for (i = 0; i < ways; i++) 22762306a36Sopenharmony_ci target_map[i] = cfmws->interleave_targets[i]; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci res = kzalloc(sizeof(*res), GFP_KERNEL); 23062306a36Sopenharmony_ci if (!res) 23162306a36Sopenharmony_ci return -ENOMEM; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci res->name = kasprintf(GFP_KERNEL, "CXL Window %d", ctx->id++); 23462306a36Sopenharmony_ci if (!res->name) 23562306a36Sopenharmony_ci goto err_name; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci res->start = cfmws->base_hpa; 23862306a36Sopenharmony_ci res->end = cfmws->base_hpa + cfmws->window_size - 1; 23962306a36Sopenharmony_ci res->flags = IORESOURCE_MEM; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* add to the local resource tracking to establish a sort order */ 24262306a36Sopenharmony_ci rc = insert_resource(cxl_res, res); 24362306a36Sopenharmony_ci if (rc) 24462306a36Sopenharmony_ci goto err_insert; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) 24762306a36Sopenharmony_ci cxl_calc_hb = cxl_hb_modulo; 24862306a36Sopenharmony_ci else 24962306a36Sopenharmony_ci cxl_calc_hb = cxl_hb_xor; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci cxlrd = cxl_root_decoder_alloc(root_port, ways, cxl_calc_hb); 25262306a36Sopenharmony_ci if (IS_ERR(cxlrd)) 25362306a36Sopenharmony_ci return PTR_ERR(cxlrd); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci cxld = &cxlrd->cxlsd.cxld; 25662306a36Sopenharmony_ci cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions); 25762306a36Sopenharmony_ci cxld->target_type = CXL_DECODER_HOSTONLYMEM; 25862306a36Sopenharmony_ci cxld->hpa_range = (struct range) { 25962306a36Sopenharmony_ci .start = res->start, 26062306a36Sopenharmony_ci .end = res->end, 26162306a36Sopenharmony_ci }; 26262306a36Sopenharmony_ci cxld->interleave_ways = ways; 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * Minimize the x1 granularity to advertise support for any 26562306a36Sopenharmony_ci * valid region granularity 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci if (ways == 1) 26862306a36Sopenharmony_ci ig = CXL_DECODER_MIN_GRANULARITY; 26962306a36Sopenharmony_ci cxld->interleave_granularity = ig; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_XOR) { 27262306a36Sopenharmony_ci if (ways != 1 && ways != 3) { 27362306a36Sopenharmony_ci cxims_ctx = (struct cxl_cxims_context) { 27462306a36Sopenharmony_ci .dev = dev, 27562306a36Sopenharmony_ci .cxlrd = cxlrd, 27662306a36Sopenharmony_ci }; 27762306a36Sopenharmony_ci rc = acpi_table_parse_cedt(ACPI_CEDT_TYPE_CXIMS, 27862306a36Sopenharmony_ci cxl_parse_cxims, &cxims_ctx); 27962306a36Sopenharmony_ci if (rc < 0) 28062306a36Sopenharmony_ci goto err_xormap; 28162306a36Sopenharmony_ci if (!cxlrd->platform_data) { 28262306a36Sopenharmony_ci dev_err(dev, "No CXIMS for HBIG %u\n", ig); 28362306a36Sopenharmony_ci rc = -EINVAL; 28462306a36Sopenharmony_ci goto err_xormap; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci rc = cxl_decoder_add(cxld, target_map); 28962306a36Sopenharmony_cierr_xormap: 29062306a36Sopenharmony_ci if (rc) 29162306a36Sopenharmony_ci put_device(&cxld->dev); 29262306a36Sopenharmony_ci else 29362306a36Sopenharmony_ci rc = cxl_decoder_autoremove(dev, cxld); 29462306a36Sopenharmony_ci return rc; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cierr_insert: 29762306a36Sopenharmony_ci kfree(res->name); 29862306a36Sopenharmony_cierr_name: 29962306a36Sopenharmony_ci kfree(res); 30062306a36Sopenharmony_ci return -ENOMEM; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, 30462306a36Sopenharmony_ci const unsigned long end) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct acpi_cedt_cfmws *cfmws = (struct acpi_cedt_cfmws *)header; 30762306a36Sopenharmony_ci struct cxl_cfmws_context *ctx = arg; 30862306a36Sopenharmony_ci struct device *dev = ctx->dev; 30962306a36Sopenharmony_ci int rc; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci rc = __cxl_parse_cfmws(cfmws, ctx); 31262306a36Sopenharmony_ci if (rc) 31362306a36Sopenharmony_ci dev_err(dev, 31462306a36Sopenharmony_ci "Failed to add decode range: [%#llx - %#llx] (%d)\n", 31562306a36Sopenharmony_ci cfmws->base_hpa, 31662306a36Sopenharmony_ci cfmws->base_hpa + cfmws->window_size - 1, rc); 31762306a36Sopenharmony_ci else 31862306a36Sopenharmony_ci dev_dbg(dev, "decode range: node: %d range [%#llx - %#llx]\n", 31962306a36Sopenharmony_ci phys_to_target_node(cfmws->base_hpa), cfmws->base_hpa, 32062306a36Sopenharmony_ci cfmws->base_hpa + cfmws->window_size - 1); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* never fail cxl_acpi load for a single window failure */ 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci__mock struct acpi_device *to_cxl_host_bridge(struct device *host, 32762306a36Sopenharmony_ci struct device *dev) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct acpi_device *adev = to_acpi_device(dev); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (!acpi_pci_find_root(adev->handle)) 33262306a36Sopenharmony_ci return NULL; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0) 33562306a36Sopenharmony_ci return adev; 33662306a36Sopenharmony_ci return NULL; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* Note, @dev is used by mock_acpi_table_parse_cedt() */ 34062306a36Sopenharmony_cistruct cxl_chbs_context { 34162306a36Sopenharmony_ci struct device *dev; 34262306a36Sopenharmony_ci unsigned long long uid; 34362306a36Sopenharmony_ci resource_size_t base; 34462306a36Sopenharmony_ci u32 cxl_version; 34562306a36Sopenharmony_ci}; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int cxl_get_chbs_iter(union acpi_subtable_headers *header, void *arg, 34862306a36Sopenharmony_ci const unsigned long end) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct cxl_chbs_context *ctx = arg; 35162306a36Sopenharmony_ci struct acpi_cedt_chbs *chbs; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (ctx->base != CXL_RESOURCE_NONE) 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci chbs = (struct acpi_cedt_chbs *) header; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (ctx->uid != chbs->uid) 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci ctx->cxl_version = chbs->cxl_version; 36262306a36Sopenharmony_ci if (!chbs->base) 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11 && 36662306a36Sopenharmony_ci chbs->length != CXL_RCRB_SIZE) 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ctx->base = chbs->base; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int cxl_get_chbs(struct device *dev, struct acpi_device *hb, 37562306a36Sopenharmony_ci struct cxl_chbs_context *ctx) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci unsigned long long uid; 37862306a36Sopenharmony_ci int rc; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid); 38162306a36Sopenharmony_ci if (rc != AE_OK) { 38262306a36Sopenharmony_ci dev_err(dev, "unable to retrieve _UID\n"); 38362306a36Sopenharmony_ci return -ENOENT; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci dev_dbg(dev, "UID found: %lld\n", uid); 38762306a36Sopenharmony_ci *ctx = (struct cxl_chbs_context) { 38862306a36Sopenharmony_ci .dev = dev, 38962306a36Sopenharmony_ci .uid = uid, 39062306a36Sopenharmony_ci .base = CXL_RESOURCE_NONE, 39162306a36Sopenharmony_ci .cxl_version = UINT_MAX, 39262306a36Sopenharmony_ci }; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs_iter, ctx); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int add_host_bridge_dport(struct device *match, void *arg) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci acpi_status rc; 40262306a36Sopenharmony_ci struct device *bridge; 40362306a36Sopenharmony_ci struct cxl_dport *dport; 40462306a36Sopenharmony_ci struct cxl_chbs_context ctx; 40562306a36Sopenharmony_ci struct acpi_pci_root *pci_root; 40662306a36Sopenharmony_ci struct cxl_port *root_port = arg; 40762306a36Sopenharmony_ci struct device *host = root_port->dev.parent; 40862306a36Sopenharmony_ci struct acpi_device *hb = to_cxl_host_bridge(host, match); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (!hb) 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci rc = cxl_get_chbs(match, hb, &ctx); 41462306a36Sopenharmony_ci if (rc) 41562306a36Sopenharmony_ci return rc; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (ctx.cxl_version == UINT_MAX) { 41862306a36Sopenharmony_ci dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n", 41962306a36Sopenharmony_ci ctx.uid); 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (ctx.base == CXL_RESOURCE_NONE) { 42462306a36Sopenharmony_ci dev_warn(match, "CHBS invalid for Host Bridge (UID %lld)\n", 42562306a36Sopenharmony_ci ctx.uid); 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci pci_root = acpi_pci_find_root(hb->handle); 43062306a36Sopenharmony_ci bridge = pci_root->bus->bridge; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* 43362306a36Sopenharmony_ci * In RCH mode, bind the component regs base to the dport. In 43462306a36Sopenharmony_ci * VH mode it will be bound to the CXL host bridge's port 43562306a36Sopenharmony_ci * object later in add_host_bridge_uport(). 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) { 43862306a36Sopenharmony_ci dev_dbg(match, "RCRB found for UID %lld: %pa\n", ctx.uid, 43962306a36Sopenharmony_ci &ctx.base); 44062306a36Sopenharmony_ci dport = devm_cxl_add_rch_dport(root_port, bridge, ctx.uid, 44162306a36Sopenharmony_ci ctx.base); 44262306a36Sopenharmony_ci } else { 44362306a36Sopenharmony_ci dport = devm_cxl_add_dport(root_port, bridge, ctx.uid, 44462306a36Sopenharmony_ci CXL_RESOURCE_NONE); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (IS_ERR(dport)) 44862306a36Sopenharmony_ci return PTR_ERR(dport); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * A host bridge is a dport to a CFMWS decode and it is a uport to the 45562306a36Sopenharmony_ci * dport (PCIe Root Ports) in the host bridge. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_cistatic int add_host_bridge_uport(struct device *match, void *arg) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct cxl_port *root_port = arg; 46062306a36Sopenharmony_ci struct device *host = root_port->dev.parent; 46162306a36Sopenharmony_ci struct acpi_device *hb = to_cxl_host_bridge(host, match); 46262306a36Sopenharmony_ci struct acpi_pci_root *pci_root; 46362306a36Sopenharmony_ci struct cxl_dport *dport; 46462306a36Sopenharmony_ci struct cxl_port *port; 46562306a36Sopenharmony_ci struct device *bridge; 46662306a36Sopenharmony_ci struct cxl_chbs_context ctx; 46762306a36Sopenharmony_ci resource_size_t component_reg_phys; 46862306a36Sopenharmony_ci int rc; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (!hb) 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci pci_root = acpi_pci_find_root(hb->handle); 47462306a36Sopenharmony_ci bridge = pci_root->bus->bridge; 47562306a36Sopenharmony_ci dport = cxl_find_dport_by_dev(root_port, bridge); 47662306a36Sopenharmony_ci if (!dport) { 47762306a36Sopenharmony_ci dev_dbg(host, "host bridge expected and not found\n"); 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (dport->rch) { 48262306a36Sopenharmony_ci dev_info(bridge, "host supports CXL (restricted)\n"); 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci rc = cxl_get_chbs(match, hb, &ctx); 48762306a36Sopenharmony_ci if (rc) 48862306a36Sopenharmony_ci return rc; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) { 49162306a36Sopenharmony_ci dev_warn(bridge, 49262306a36Sopenharmony_ci "CXL CHBS version mismatch, skip port registration\n"); 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci component_reg_phys = ctx.base; 49762306a36Sopenharmony_ci if (component_reg_phys != CXL_RESOURCE_NONE) 49862306a36Sopenharmony_ci dev_dbg(match, "CHBCR found for UID %lld: %pa\n", 49962306a36Sopenharmony_ci ctx.uid, &component_reg_phys); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus); 50262306a36Sopenharmony_ci if (rc) 50362306a36Sopenharmony_ci return rc; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci port = devm_cxl_add_port(host, bridge, component_reg_phys, dport); 50662306a36Sopenharmony_ci if (IS_ERR(port)) 50762306a36Sopenharmony_ci return PTR_ERR(port); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci dev_info(bridge, "host supports CXL\n"); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int add_root_nvdimm_bridge(struct device *match, void *data) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct cxl_decoder *cxld; 51762306a36Sopenharmony_ci struct cxl_port *root_port = data; 51862306a36Sopenharmony_ci struct cxl_nvdimm_bridge *cxl_nvb; 51962306a36Sopenharmony_ci struct device *host = root_port->dev.parent; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!is_root_decoder(match)) 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci cxld = to_cxl_decoder(match); 52562306a36Sopenharmony_ci if (!(cxld->flags & CXL_DECODER_F_PMEM)) 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci cxl_nvb = devm_cxl_add_nvdimm_bridge(host, root_port); 52962306a36Sopenharmony_ci if (IS_ERR(cxl_nvb)) { 53062306a36Sopenharmony_ci dev_dbg(host, "failed to register pmem\n"); 53162306a36Sopenharmony_ci return PTR_ERR(cxl_nvb); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci dev_dbg(host, "%s: add: %s\n", dev_name(&root_port->dev), 53462306a36Sopenharmony_ci dev_name(&cxl_nvb->dev)); 53562306a36Sopenharmony_ci return 1; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic struct lock_class_key cxl_root_key; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic void cxl_acpi_lock_reset_class(void *dev) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci device_lock_reset_class(dev); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic void del_cxl_resource(struct resource *res) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci kfree(res->name); 54862306a36Sopenharmony_ci kfree(res); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic void cxl_set_public_resource(struct resource *priv, struct resource *pub) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci priv->desc = (unsigned long) pub; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic struct resource *cxl_get_public_resource(struct resource *priv) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci return (struct resource *) priv->desc; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic void remove_cxl_resources(void *data) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct resource *res, *next, *cxl = data; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci for (res = cxl->child; res; res = next) { 56662306a36Sopenharmony_ci struct resource *victim = cxl_get_public_resource(res); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci next = res->sibling; 56962306a36Sopenharmony_ci remove_resource(res); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (victim) { 57262306a36Sopenharmony_ci remove_resource(victim); 57362306a36Sopenharmony_ci kfree(victim); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci del_cxl_resource(res); 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci/** 58162306a36Sopenharmony_ci * add_cxl_resources() - reflect CXL fixed memory windows in iomem_resource 58262306a36Sopenharmony_ci * @cxl_res: A standalone resource tree where each CXL window is a sibling 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * Walk each CXL window in @cxl_res and add it to iomem_resource potentially 58562306a36Sopenharmony_ci * expanding its boundaries to ensure that any conflicting resources become 58662306a36Sopenharmony_ci * children. If a window is expanded it may then conflict with a another window 58762306a36Sopenharmony_ci * entry and require the window to be truncated or trimmed. Consider this 58862306a36Sopenharmony_ci * situation: 58962306a36Sopenharmony_ci * 59062306a36Sopenharmony_ci * |-- "CXL Window 0" --||----- "CXL Window 1" -----| 59162306a36Sopenharmony_ci * |--------------- "System RAM" -------------| 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci * ...where platform firmware has established as System RAM resource across 2 59462306a36Sopenharmony_ci * windows, but has left some portion of window 1 for dynamic CXL region 59562306a36Sopenharmony_ci * provisioning. In this case "Window 0" will span the entirety of the "System 59662306a36Sopenharmony_ci * RAM" span, and "CXL Window 1" is truncated to the remaining tail past the end 59762306a36Sopenharmony_ci * of that "System RAM" resource. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_cistatic int add_cxl_resources(struct resource *cxl_res) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct resource *res, *new, *next; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci for (res = cxl_res->child; res; res = next) { 60462306a36Sopenharmony_ci new = kzalloc(sizeof(*new), GFP_KERNEL); 60562306a36Sopenharmony_ci if (!new) 60662306a36Sopenharmony_ci return -ENOMEM; 60762306a36Sopenharmony_ci new->name = res->name; 60862306a36Sopenharmony_ci new->start = res->start; 60962306a36Sopenharmony_ci new->end = res->end; 61062306a36Sopenharmony_ci new->flags = IORESOURCE_MEM; 61162306a36Sopenharmony_ci new->desc = IORES_DESC_CXL; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * Record the public resource in the private cxl_res tree for 61562306a36Sopenharmony_ci * later removal. 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci cxl_set_public_resource(res, new); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci insert_resource_expand_to_fit(&iomem_resource, new); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci next = res->sibling; 62262306a36Sopenharmony_ci while (next && resource_overlaps(new, next)) { 62362306a36Sopenharmony_ci if (resource_contains(new, next)) { 62462306a36Sopenharmony_ci struct resource *_next = next->sibling; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci remove_resource(next); 62762306a36Sopenharmony_ci del_cxl_resource(next); 62862306a36Sopenharmony_ci next = _next; 62962306a36Sopenharmony_ci } else 63062306a36Sopenharmony_ci next->start = new->end + 1; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int pair_cxl_resource(struct device *dev, void *data) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct resource *cxl_res = data; 63962306a36Sopenharmony_ci struct resource *p; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (!is_root_decoder(dev)) 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci for (p = cxl_res->child; p; p = p->sibling) { 64562306a36Sopenharmony_ci struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); 64662306a36Sopenharmony_ci struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 64762306a36Sopenharmony_ci struct resource res = { 64862306a36Sopenharmony_ci .start = cxld->hpa_range.start, 64962306a36Sopenharmony_ci .end = cxld->hpa_range.end, 65062306a36Sopenharmony_ci .flags = IORESOURCE_MEM, 65162306a36Sopenharmony_ci }; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (resource_contains(p, &res)) { 65462306a36Sopenharmony_ci cxlrd->res = cxl_get_public_resource(p); 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return 0; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int cxl_acpi_probe(struct platform_device *pdev) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci int rc; 66562306a36Sopenharmony_ci struct resource *cxl_res; 66662306a36Sopenharmony_ci struct cxl_port *root_port; 66762306a36Sopenharmony_ci struct device *host = &pdev->dev; 66862306a36Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(host); 66962306a36Sopenharmony_ci struct cxl_cfmws_context ctx; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci device_lock_set_class(&pdev->dev, &cxl_root_key); 67262306a36Sopenharmony_ci rc = devm_add_action_or_reset(&pdev->dev, cxl_acpi_lock_reset_class, 67362306a36Sopenharmony_ci &pdev->dev); 67462306a36Sopenharmony_ci if (rc) 67562306a36Sopenharmony_ci return rc; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci cxl_res = devm_kzalloc(host, sizeof(*cxl_res), GFP_KERNEL); 67862306a36Sopenharmony_ci if (!cxl_res) 67962306a36Sopenharmony_ci return -ENOMEM; 68062306a36Sopenharmony_ci cxl_res->name = "CXL mem"; 68162306a36Sopenharmony_ci cxl_res->start = 0; 68262306a36Sopenharmony_ci cxl_res->end = -1; 68362306a36Sopenharmony_ci cxl_res->flags = IORESOURCE_MEM; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL); 68662306a36Sopenharmony_ci if (IS_ERR(root_port)) 68762306a36Sopenharmony_ci return PTR_ERR(root_port); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci rc = bus_for_each_dev(adev->dev.bus, NULL, root_port, 69062306a36Sopenharmony_ci add_host_bridge_dport); 69162306a36Sopenharmony_ci if (rc < 0) 69262306a36Sopenharmony_ci return rc; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci rc = devm_add_action_or_reset(host, remove_cxl_resources, cxl_res); 69562306a36Sopenharmony_ci if (rc) 69662306a36Sopenharmony_ci return rc; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci ctx = (struct cxl_cfmws_context) { 69962306a36Sopenharmony_ci .dev = host, 70062306a36Sopenharmony_ci .root_port = root_port, 70162306a36Sopenharmony_ci .cxl_res = cxl_res, 70262306a36Sopenharmony_ci }; 70362306a36Sopenharmony_ci rc = acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx); 70462306a36Sopenharmony_ci if (rc < 0) 70562306a36Sopenharmony_ci return -ENXIO; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci rc = add_cxl_resources(cxl_res); 70862306a36Sopenharmony_ci if (rc) 70962306a36Sopenharmony_ci return rc; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * Populate the root decoders with their related iomem resource, 71362306a36Sopenharmony_ci * if present 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci device_for_each_child(&root_port->dev, cxl_res, pair_cxl_resource); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* 71862306a36Sopenharmony_ci * Root level scanned with host-bridge as dports, now scan host-bridges 71962306a36Sopenharmony_ci * for their role as CXL uports to their CXL-capable PCIe Root Ports. 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci rc = bus_for_each_dev(adev->dev.bus, NULL, root_port, 72262306a36Sopenharmony_ci add_host_bridge_uport); 72362306a36Sopenharmony_ci if (rc < 0) 72462306a36Sopenharmony_ci return rc; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_CXL_PMEM)) 72762306a36Sopenharmony_ci rc = device_for_each_child(&root_port->dev, root_port, 72862306a36Sopenharmony_ci add_root_nvdimm_bridge); 72962306a36Sopenharmony_ci if (rc < 0) 73062306a36Sopenharmony_ci return rc; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* In case PCI is scanned before ACPI re-trigger memdev attach */ 73362306a36Sopenharmony_ci cxl_bus_rescan(); 73462306a36Sopenharmony_ci return 0; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic const struct acpi_device_id cxl_acpi_ids[] = { 73862306a36Sopenharmony_ci { "ACPI0017" }, 73962306a36Sopenharmony_ci { }, 74062306a36Sopenharmony_ci}; 74162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, cxl_acpi_ids); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic const struct platform_device_id cxl_test_ids[] = { 74462306a36Sopenharmony_ci { "cxl_acpi" }, 74562306a36Sopenharmony_ci { }, 74662306a36Sopenharmony_ci}; 74762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, cxl_test_ids); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic struct platform_driver cxl_acpi_driver = { 75062306a36Sopenharmony_ci .probe = cxl_acpi_probe, 75162306a36Sopenharmony_ci .driver = { 75262306a36Sopenharmony_ci .name = KBUILD_MODNAME, 75362306a36Sopenharmony_ci .acpi_match_table = cxl_acpi_ids, 75462306a36Sopenharmony_ci }, 75562306a36Sopenharmony_ci .id_table = cxl_test_ids, 75662306a36Sopenharmony_ci}; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int __init cxl_acpi_init(void) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci return platform_driver_register(&cxl_acpi_driver); 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic void __exit cxl_acpi_exit(void) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci platform_driver_unregister(&cxl_acpi_driver); 76662306a36Sopenharmony_ci cxl_bus_drain(); 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/* load before dax_hmem sees 'Soft Reserved' CXL ranges */ 77062306a36Sopenharmony_cisubsys_initcall(cxl_acpi_init); 77162306a36Sopenharmony_cimodule_exit(cxl_acpi_exit); 77262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 77362306a36Sopenharmony_ciMODULE_IMPORT_NS(CXL); 77462306a36Sopenharmony_ciMODULE_IMPORT_NS(ACPI); 775