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