xref: /linux/drivers/cxl/core/cdat.c (revision 3a07362fab1653d3aca31a9155c8cc776138fd02)
1ad6f04c0SDave Jiang // SPDX-License-Identifier: GPL-2.0-only
2ad6f04c0SDave Jiang /* Copyright(c) 2023 Intel Corporation. All rights reserved. */
3ad6f04c0SDave Jiang #include <linux/acpi.h>
4ad6f04c0SDave Jiang #include <linux/xarray.h>
5ad6f04c0SDave Jiang #include <linux/fw_table.h>
663cef81bSDave Jiang #include <linux/node.h>
763cef81bSDave Jiang #include <linux/overflow.h>
8ad6f04c0SDave Jiang #include "cxlpci.h"
986557b7eSDave Jiang #include "cxlmem.h"
10e16bf7e0SDan Williams #include "core.h"
11ad6f04c0SDave Jiang #include "cxl.h"
123d9f4a19SDave Jiang #include "core.h"
13ad6f04c0SDave Jiang 
14ad6f04c0SDave Jiang struct dsmas_entry {
15ad6f04c0SDave Jiang 	struct range dpa_range;
16ad6f04c0SDave Jiang 	u8 handle;
17*001c5d19SDave Jiang 	struct access_coordinate coord[ACCESS_COORDINATE_MAX];
187a4f148dSDave Jiang 
197a4f148dSDave Jiang 	int entries;
207a4f148dSDave Jiang 	int qos_class;
21ad6f04c0SDave Jiang };
22ad6f04c0SDave Jiang 
cdat_normalize(u16 entry,u64 base,u8 type)2351293c56SDave Jiang static u32 cdat_normalize(u16 entry, u64 base, u8 type)
2451293c56SDave Jiang {
2551293c56SDave Jiang 	u32 value;
2651293c56SDave Jiang 
2751293c56SDave Jiang 	/*
2851293c56SDave Jiang 	 * Check for invalid and overflow values
2951293c56SDave Jiang 	 */
3051293c56SDave Jiang 	if (entry == 0xffff || !entry)
3151293c56SDave Jiang 		return 0;
3251293c56SDave Jiang 	else if (base > (UINT_MAX / (entry)))
3351293c56SDave Jiang 		return 0;
3451293c56SDave Jiang 
3551293c56SDave Jiang 	/*
3651293c56SDave Jiang 	 * CDAT fields follow the format of HMAT fields. See table 5 Device
3751293c56SDave Jiang 	 * Scoped Latency and Bandwidth Information Structure in Coherent Device
3851293c56SDave Jiang 	 * Attribute Table (CDAT) Specification v1.01.
3951293c56SDave Jiang 	 */
4051293c56SDave Jiang 	value = entry * base;
4151293c56SDave Jiang 	switch (type) {
4251293c56SDave Jiang 	case ACPI_HMAT_ACCESS_LATENCY:
4351293c56SDave Jiang 	case ACPI_HMAT_READ_LATENCY:
4451293c56SDave Jiang 	case ACPI_HMAT_WRITE_LATENCY:
4551293c56SDave Jiang 		value = DIV_ROUND_UP(value, 1000);
4651293c56SDave Jiang 		break;
4751293c56SDave Jiang 	default:
4851293c56SDave Jiang 		break;
4951293c56SDave Jiang 	}
5051293c56SDave Jiang 	return value;
5151293c56SDave Jiang }
5251293c56SDave Jiang 
cdat_dsmas_handler(union acpi_subtable_headers * header,void * arg,const unsigned long end)53ad6f04c0SDave Jiang static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
54ad6f04c0SDave Jiang 			      const unsigned long end)
55ad6f04c0SDave Jiang {
56ad6f04c0SDave Jiang 	struct acpi_cdat_header *hdr = &header->cdat;
57ad6f04c0SDave Jiang 	struct acpi_cdat_dsmas *dsmas;
58ad6f04c0SDave Jiang 	int size = sizeof(*hdr) + sizeof(*dsmas);
59ad6f04c0SDave Jiang 	struct xarray *dsmas_xa = arg;
60ad6f04c0SDave Jiang 	struct dsmas_entry *dent;
61ad6f04c0SDave Jiang 	u16 len;
62ad6f04c0SDave Jiang 	int rc;
63ad6f04c0SDave Jiang 
64ad6f04c0SDave Jiang 	len = le16_to_cpu((__force __le16)hdr->length);
65ad6f04c0SDave Jiang 	if (len != size || (unsigned long)hdr + len > end) {
66ad6f04c0SDave Jiang 		pr_warn("Malformed DSMAS table length: (%u:%u)\n", size, len);
67ad6f04c0SDave Jiang 		return -EINVAL;
68ad6f04c0SDave Jiang 	}
69ad6f04c0SDave Jiang 
70ad6f04c0SDave Jiang 	/* Skip common header */
71ad6f04c0SDave Jiang 	dsmas = (struct acpi_cdat_dsmas *)(hdr + 1);
72ad6f04c0SDave Jiang 
73ad6f04c0SDave Jiang 	dent = kzalloc(sizeof(*dent), GFP_KERNEL);
74ad6f04c0SDave Jiang 	if (!dent)
75ad6f04c0SDave Jiang 		return -ENOMEM;
76ad6f04c0SDave Jiang 
77ad6f04c0SDave Jiang 	dent->handle = dsmas->dsmad_handle;
78ad6f04c0SDave Jiang 	dent->dpa_range.start = le64_to_cpu((__force __le64)dsmas->dpa_base_address);
79ad6f04c0SDave Jiang 	dent->dpa_range.end = le64_to_cpu((__force __le64)dsmas->dpa_base_address) +
80ad6f04c0SDave Jiang 			      le64_to_cpu((__force __le64)dsmas->dpa_length) - 1;
81ad6f04c0SDave Jiang 
82ad6f04c0SDave Jiang 	rc = xa_insert(dsmas_xa, dent->handle, dent, GFP_KERNEL);
83ad6f04c0SDave Jiang 	if (rc) {
84ad6f04c0SDave Jiang 		kfree(dent);
85ad6f04c0SDave Jiang 		return rc;
86ad6f04c0SDave Jiang 	}
87ad6f04c0SDave Jiang 
88ad6f04c0SDave Jiang 	return 0;
89ad6f04c0SDave Jiang }
90ad6f04c0SDave Jiang 
__cxl_access_coordinate_set(struct access_coordinate * coord,int access,unsigned int val)91*001c5d19SDave Jiang static void __cxl_access_coordinate_set(struct access_coordinate *coord,
9263cef81bSDave Jiang 					int access, unsigned int val)
9363cef81bSDave Jiang {
9463cef81bSDave Jiang 	switch (access) {
9563cef81bSDave Jiang 	case ACPI_HMAT_ACCESS_LATENCY:
9663cef81bSDave Jiang 		coord->read_latency = val;
9763cef81bSDave Jiang 		coord->write_latency = val;
9863cef81bSDave Jiang 		break;
9963cef81bSDave Jiang 	case ACPI_HMAT_READ_LATENCY:
10063cef81bSDave Jiang 		coord->read_latency = val;
10163cef81bSDave Jiang 		break;
10263cef81bSDave Jiang 	case ACPI_HMAT_WRITE_LATENCY:
10363cef81bSDave Jiang 		coord->write_latency = val;
10463cef81bSDave Jiang 		break;
10563cef81bSDave Jiang 	case ACPI_HMAT_ACCESS_BANDWIDTH:
10663cef81bSDave Jiang 		coord->read_bandwidth = val;
10763cef81bSDave Jiang 		coord->write_bandwidth = val;
10863cef81bSDave Jiang 		break;
10963cef81bSDave Jiang 	case ACPI_HMAT_READ_BANDWIDTH:
11063cef81bSDave Jiang 		coord->read_bandwidth = val;
11163cef81bSDave Jiang 		break;
11263cef81bSDave Jiang 	case ACPI_HMAT_WRITE_BANDWIDTH:
11363cef81bSDave Jiang 		coord->write_bandwidth = val;
11463cef81bSDave Jiang 		break;
11563cef81bSDave Jiang 	}
11663cef81bSDave Jiang }
11763cef81bSDave Jiang 
cxl_access_coordinate_set(struct access_coordinate * coord,int access,unsigned int val)118*001c5d19SDave Jiang static void cxl_access_coordinate_set(struct access_coordinate *coord,
119*001c5d19SDave Jiang 				      int access, unsigned int val)
120*001c5d19SDave Jiang {
121*001c5d19SDave Jiang 	for (int i = 0; i < ACCESS_COORDINATE_MAX; i++)
122*001c5d19SDave Jiang 		__cxl_access_coordinate_set(&coord[i], access, val);
123*001c5d19SDave Jiang }
124*001c5d19SDave Jiang 
cdat_dslbis_handler(union acpi_subtable_headers * header,void * arg,const unsigned long end)12563cef81bSDave Jiang static int cdat_dslbis_handler(union acpi_subtable_headers *header, void *arg,
12663cef81bSDave Jiang 			       const unsigned long end)
12763cef81bSDave Jiang {
12863cef81bSDave Jiang 	struct acpi_cdat_header *hdr = &header->cdat;
12963cef81bSDave Jiang 	struct acpi_cdat_dslbis *dslbis;
13063cef81bSDave Jiang 	int size = sizeof(*hdr) + sizeof(*dslbis);
13163cef81bSDave Jiang 	struct xarray *dsmas_xa = arg;
13263cef81bSDave Jiang 	struct dsmas_entry *dent;
13363cef81bSDave Jiang 	__le64 le_base;
13463cef81bSDave Jiang 	__le16 le_val;
13563cef81bSDave Jiang 	u64 val;
13663cef81bSDave Jiang 	u16 len;
13763cef81bSDave Jiang 
13863cef81bSDave Jiang 	len = le16_to_cpu((__force __le16)hdr->length);
13963cef81bSDave Jiang 	if (len != size || (unsigned long)hdr + len > end) {
14063cef81bSDave Jiang 		pr_warn("Malformed DSLBIS table length: (%u:%u)\n", size, len);
14163cef81bSDave Jiang 		return -EINVAL;
14263cef81bSDave Jiang 	}
14363cef81bSDave Jiang 
14463cef81bSDave Jiang 	/* Skip common header */
14563cef81bSDave Jiang 	dslbis = (struct acpi_cdat_dslbis *)(hdr + 1);
14663cef81bSDave Jiang 
14763cef81bSDave Jiang 	/* Skip unrecognized data type */
14863cef81bSDave Jiang 	if (dslbis->data_type > ACPI_HMAT_WRITE_BANDWIDTH)
14963cef81bSDave Jiang 		return 0;
15063cef81bSDave Jiang 
15163cef81bSDave Jiang 	/* Not a memory type, skip */
15263cef81bSDave Jiang 	if ((dslbis->flags & ACPI_HMAT_MEMORY_HIERARCHY) != ACPI_HMAT_MEMORY)
15363cef81bSDave Jiang 		return 0;
15463cef81bSDave Jiang 
15563cef81bSDave Jiang 	dent = xa_load(dsmas_xa, dslbis->handle);
15663cef81bSDave Jiang 	if (!dent) {
15763cef81bSDave Jiang 		pr_warn("No matching DSMAS entry for DSLBIS entry.\n");
15863cef81bSDave Jiang 		return 0;
15963cef81bSDave Jiang 	}
16063cef81bSDave Jiang 
16163cef81bSDave Jiang 	le_base = (__force __le64)dslbis->entry_base_unit;
16263cef81bSDave Jiang 	le_val = (__force __le16)dslbis->entry[0];
16351293c56SDave Jiang 	val = cdat_normalize(le16_to_cpu(le_val), le64_to_cpu(le_base),
16451293c56SDave Jiang 			     dslbis->data_type);
16563cef81bSDave Jiang 
166*001c5d19SDave Jiang 	cxl_access_coordinate_set(dent->coord, dslbis->data_type, val);
16763cef81bSDave Jiang 
16863cef81bSDave Jiang 	return 0;
16963cef81bSDave Jiang }
17063cef81bSDave Jiang 
cdat_table_parse_output(int rc)17163cef81bSDave Jiang static int cdat_table_parse_output(int rc)
17263cef81bSDave Jiang {
17363cef81bSDave Jiang 	if (rc < 0)
17463cef81bSDave Jiang 		return rc;
17563cef81bSDave Jiang 	if (rc == 0)
17663cef81bSDave Jiang 		return -ENOENT;
17763cef81bSDave Jiang 
17863cef81bSDave Jiang 	return 0;
17963cef81bSDave Jiang }
18063cef81bSDave Jiang 
cxl_cdat_endpoint_process(struct cxl_port * port,struct xarray * dsmas_xa)181ad6f04c0SDave Jiang static int cxl_cdat_endpoint_process(struct cxl_port *port,
182ad6f04c0SDave Jiang 				     struct xarray *dsmas_xa)
183ad6f04c0SDave Jiang {
18463cef81bSDave Jiang 	int rc;
18563cef81bSDave Jiang 
18663cef81bSDave Jiang 	rc = cdat_table_parse(ACPI_CDAT_TYPE_DSMAS, cdat_dsmas_handler,
187c6c3187dSRobert Richter 			      dsmas_xa, port->cdat.table, port->cdat.length);
18863cef81bSDave Jiang 	rc = cdat_table_parse_output(rc);
18963cef81bSDave Jiang 	if (rc)
19063cef81bSDave Jiang 		return rc;
19163cef81bSDave Jiang 
19263cef81bSDave Jiang 	rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler,
193c6c3187dSRobert Richter 			      dsmas_xa, port->cdat.table, port->cdat.length);
19463cef81bSDave Jiang 	return cdat_table_parse_output(rc);
195ad6f04c0SDave Jiang }
196ad6f04c0SDave Jiang 
cxl_port_perf_data_calculate(struct cxl_port * port,struct xarray * dsmas_xa)1977a4f148dSDave Jiang static int cxl_port_perf_data_calculate(struct cxl_port *port,
1987a4f148dSDave Jiang 					struct xarray *dsmas_xa)
1997a4f148dSDave Jiang {
200*001c5d19SDave Jiang 	struct access_coordinate ep_c[ACCESS_COORDINATE_MAX];
2017a4f148dSDave Jiang 	struct dsmas_entry *dent;
2027a4f148dSDave Jiang 	int valid_entries = 0;
2037a4f148dSDave Jiang 	unsigned long index;
2047a4f148dSDave Jiang 	int rc;
2057a4f148dSDave Jiang 
206*001c5d19SDave Jiang 	rc = cxl_endpoint_get_perf_coordinates(port, ep_c);
2077a4f148dSDave Jiang 	if (rc) {
208863027d4SDave Jiang 		dev_dbg(&port->dev, "Failed to retrieve ep perf coordinates.\n");
209863027d4SDave Jiang 		return rc;
210863027d4SDave Jiang 	}
211863027d4SDave Jiang 
21298e7ab33SDave Jiang 	struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port);
21398e7ab33SDave Jiang 
21498e7ab33SDave Jiang 	if (!cxl_root)
21598e7ab33SDave Jiang 		return -ENODEV;
21698e7ab33SDave Jiang 
2177a4f148dSDave Jiang 	if (!cxl_root->ops || !cxl_root->ops->qos_class)
2187a4f148dSDave Jiang 		return -EOPNOTSUPP;
2197a4f148dSDave Jiang 
2207a4f148dSDave Jiang 	xa_for_each(dsmas_xa, index, dent) {
2217a4f148dSDave Jiang 		int qos_class;
2227a4f148dSDave Jiang 
223*001c5d19SDave Jiang 		cxl_coordinates_combine(dent->coord, dent->coord, ep_c);
2247a4f148dSDave Jiang 		dent->entries = 1;
225*001c5d19SDave Jiang 		rc = cxl_root->ops->qos_class(cxl_root,
226*001c5d19SDave Jiang 					      &dent->coord[ACCESS_COORDINATE_CPU],
227*001c5d19SDave Jiang 					      1, &qos_class);
2287a4f148dSDave Jiang 		if (rc != 1)
2297a4f148dSDave Jiang 			continue;
2307a4f148dSDave Jiang 
2317a4f148dSDave Jiang 		valid_entries++;
2327a4f148dSDave Jiang 		dent->qos_class = qos_class;
2337a4f148dSDave Jiang 	}
2347a4f148dSDave Jiang 
2357a4f148dSDave Jiang 	if (!valid_entries)
2367a4f148dSDave Jiang 		return -ENOENT;
2377a4f148dSDave Jiang 
2387a4f148dSDave Jiang 	return 0;
2397a4f148dSDave Jiang }
2407a4f148dSDave Jiang 
update_perf_entry(struct device * dev,struct dsmas_entry * dent,struct cxl_dpa_perf * dpa_perf)24100413c15SDave Jiang static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
24200413c15SDave Jiang 			      struct cxl_dpa_perf *dpa_perf)
24386557b7eSDave Jiang {
244*001c5d19SDave Jiang 	for (int i = 0; i < ACCESS_COORDINATE_MAX; i++)
245*001c5d19SDave Jiang 		dpa_perf->coord[i] = dent->coord[i];
24686557b7eSDave Jiang 	dpa_perf->dpa_range = dent->dpa_range;
24786557b7eSDave Jiang 	dpa_perf->qos_class = dent->qos_class;
24886557b7eSDave Jiang 	dev_dbg(dev,
24986557b7eSDave Jiang 		"DSMAS: dpa: %#llx qos: %d read_bw: %d write_bw %d read_lat: %d write_lat: %d\n",
25086557b7eSDave Jiang 		dent->dpa_range.start, dpa_perf->qos_class,
251*001c5d19SDave Jiang 		dent->coord[ACCESS_COORDINATE_CPU].read_bandwidth,
252*001c5d19SDave Jiang 		dent->coord[ACCESS_COORDINATE_CPU].write_bandwidth,
253*001c5d19SDave Jiang 		dent->coord[ACCESS_COORDINATE_CPU].read_latency,
254*001c5d19SDave Jiang 		dent->coord[ACCESS_COORDINATE_CPU].write_latency);
25586557b7eSDave Jiang }
25686557b7eSDave Jiang 
cxl_memdev_set_qos_class(struct cxl_dev_state * cxlds,struct xarray * dsmas_xa)25786557b7eSDave Jiang static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
25886557b7eSDave Jiang 				     struct xarray *dsmas_xa)
25986557b7eSDave Jiang {
26086557b7eSDave Jiang 	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
26186557b7eSDave Jiang 	struct device *dev = cxlds->dev;
26286557b7eSDave Jiang 	struct range pmem_range = {
26386557b7eSDave Jiang 		.start = cxlds->pmem_res.start,
26486557b7eSDave Jiang 		.end = cxlds->pmem_res.end,
26586557b7eSDave Jiang 	};
26686557b7eSDave Jiang 	struct range ram_range = {
26786557b7eSDave Jiang 		.start = cxlds->ram_res.start,
26886557b7eSDave Jiang 		.end = cxlds->ram_res.end,
26986557b7eSDave Jiang 	};
27086557b7eSDave Jiang 	struct dsmas_entry *dent;
27186557b7eSDave Jiang 	unsigned long index;
27286557b7eSDave Jiang 
27386557b7eSDave Jiang 	xa_for_each(dsmas_xa, index, dent) {
27486557b7eSDave Jiang 		if (resource_size(&cxlds->ram_res) &&
27586557b7eSDave Jiang 		    range_contains(&ram_range, &dent->dpa_range))
27600413c15SDave Jiang 			update_perf_entry(dev, dent, &mds->ram_perf);
27786557b7eSDave Jiang 		else if (resource_size(&cxlds->pmem_res) &&
27886557b7eSDave Jiang 			 range_contains(&pmem_range, &dent->dpa_range))
27900413c15SDave Jiang 			update_perf_entry(dev, dent, &mds->pmem_perf);
28086557b7eSDave Jiang 		else
28186557b7eSDave Jiang 			dev_dbg(dev, "no partition for dsmas dpa: %#llx\n",
28286557b7eSDave Jiang 				dent->dpa_range.start);
28386557b7eSDave Jiang 	}
28486557b7eSDave Jiang }
28586557b7eSDave Jiang 
match_cxlrd_qos_class(struct device * dev,void * data)286185c1a48SDave Jiang static int match_cxlrd_qos_class(struct device *dev, void *data)
287185c1a48SDave Jiang {
288185c1a48SDave Jiang 	int dev_qos_class = *(int *)data;
289185c1a48SDave Jiang 	struct cxl_root_decoder *cxlrd;
290185c1a48SDave Jiang 
291185c1a48SDave Jiang 	if (!is_root_decoder(dev))
292185c1a48SDave Jiang 		return 0;
293185c1a48SDave Jiang 
294185c1a48SDave Jiang 	cxlrd = to_cxl_root_decoder(dev);
295185c1a48SDave Jiang 	if (cxlrd->qos_class == CXL_QOS_CLASS_INVALID)
296185c1a48SDave Jiang 		return 0;
297185c1a48SDave Jiang 
298185c1a48SDave Jiang 	if (cxlrd->qos_class == dev_qos_class)
299185c1a48SDave Jiang 		return 1;
300185c1a48SDave Jiang 
301185c1a48SDave Jiang 	return 0;
302185c1a48SDave Jiang }
303185c1a48SDave Jiang 
reset_dpa_perf(struct cxl_dpa_perf * dpa_perf)30400413c15SDave Jiang static void reset_dpa_perf(struct cxl_dpa_perf *dpa_perf)
305185c1a48SDave Jiang {
30600413c15SDave Jiang 	*dpa_perf = (struct cxl_dpa_perf) {
30700413c15SDave Jiang 		.qos_class = CXL_QOS_CLASS_INVALID,
30800413c15SDave Jiang 	};
309185c1a48SDave Jiang }
31000413c15SDave Jiang 
cxl_qos_match(struct cxl_port * root_port,struct cxl_dpa_perf * dpa_perf)31100413c15SDave Jiang static bool cxl_qos_match(struct cxl_port *root_port,
31200413c15SDave Jiang 			  struct cxl_dpa_perf *dpa_perf)
31300413c15SDave Jiang {
31400413c15SDave Jiang 	if (dpa_perf->qos_class == CXL_QOS_CLASS_INVALID)
31500413c15SDave Jiang 		return false;
31600413c15SDave Jiang 
31700413c15SDave Jiang 	if (!device_for_each_child(&root_port->dev, &dpa_perf->qos_class,
31800413c15SDave Jiang 				   match_cxlrd_qos_class))
31900413c15SDave Jiang 		return false;
32000413c15SDave Jiang 
32100413c15SDave Jiang 	return true;
322185c1a48SDave Jiang }
323185c1a48SDave Jiang 
match_cxlrd_hb(struct device * dev,void * data)324185c1a48SDave Jiang static int match_cxlrd_hb(struct device *dev, void *data)
325185c1a48SDave Jiang {
326185c1a48SDave Jiang 	struct device *host_bridge = data;
327185c1a48SDave Jiang 	struct cxl_switch_decoder *cxlsd;
328185c1a48SDave Jiang 	struct cxl_root_decoder *cxlrd;
329185c1a48SDave Jiang 
330185c1a48SDave Jiang 	if (!is_root_decoder(dev))
331185c1a48SDave Jiang 		return 0;
332185c1a48SDave Jiang 
333185c1a48SDave Jiang 	cxlrd = to_cxl_root_decoder(dev);
334185c1a48SDave Jiang 	cxlsd = &cxlrd->cxlsd;
335185c1a48SDave Jiang 
336e16bf7e0SDan Williams 	guard(rwsem_read)(&cxl_region_rwsem);
337185c1a48SDave Jiang 	for (int i = 0; i < cxlsd->nr_targets; i++) {
338185c1a48SDave Jiang 		if (host_bridge == cxlsd->target[i]->dport_dev)
339185c1a48SDave Jiang 			return 1;
340185c1a48SDave Jiang 	}
341185c1a48SDave Jiang 
342185c1a48SDave Jiang 	return 0;
343185c1a48SDave Jiang }
344185c1a48SDave Jiang 
cxl_qos_class_verify(struct cxl_memdev * cxlmd)345185c1a48SDave Jiang static int cxl_qos_class_verify(struct cxl_memdev *cxlmd)
346185c1a48SDave Jiang {
347185c1a48SDave Jiang 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
348185c1a48SDave Jiang 	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
34944cd71efSDave Jiang 	struct cxl_port *root_port;
350185c1a48SDave Jiang 	int rc;
351185c1a48SDave Jiang 
35244cd71efSDave Jiang 	struct cxl_root *cxl_root __free(put_cxl_root) =
35344cd71efSDave Jiang 		find_cxl_root(cxlmd->endpoint);
35444cd71efSDave Jiang 
35544cd71efSDave Jiang 	if (!cxl_root)
356185c1a48SDave Jiang 		return -ENODEV;
357185c1a48SDave Jiang 
35844cd71efSDave Jiang 	root_port = &cxl_root->port;
35944cd71efSDave Jiang 
360185c1a48SDave Jiang 	/* Check that the QTG IDs are all sane between end device and root decoders */
36100413c15SDave Jiang 	if (!cxl_qos_match(root_port, &mds->ram_perf))
36200413c15SDave Jiang 		reset_dpa_perf(&mds->ram_perf);
36300413c15SDave Jiang 	if (!cxl_qos_match(root_port, &mds->pmem_perf))
36400413c15SDave Jiang 		reset_dpa_perf(&mds->pmem_perf);
365185c1a48SDave Jiang 
366185c1a48SDave Jiang 	/* Check to make sure that the device's host bridge is under a root decoder */
367185c1a48SDave Jiang 	rc = device_for_each_child(&root_port->dev,
36810cb393dSDave Jiang 				   cxlmd->endpoint->host_bridge, match_cxlrd_hb);
369185c1a48SDave Jiang 	if (!rc) {
37000413c15SDave Jiang 		reset_dpa_perf(&mds->ram_perf);
37100413c15SDave Jiang 		reset_dpa_perf(&mds->pmem_perf);
372185c1a48SDave Jiang 	}
373185c1a48SDave Jiang 
374185c1a48SDave Jiang 	return rc;
375185c1a48SDave Jiang }
376185c1a48SDave Jiang 
discard_dsmas(struct xarray * xa)377ad6f04c0SDave Jiang static void discard_dsmas(struct xarray *xa)
378ad6f04c0SDave Jiang {
379ad6f04c0SDave Jiang 	unsigned long index;
380ad6f04c0SDave Jiang 	void *ent;
381ad6f04c0SDave Jiang 
382ad6f04c0SDave Jiang 	xa_for_each(xa, index, ent) {
383ad6f04c0SDave Jiang 		xa_erase(xa, index);
384ad6f04c0SDave Jiang 		kfree(ent);
385ad6f04c0SDave Jiang 	}
386ad6f04c0SDave Jiang 	xa_destroy(xa);
387ad6f04c0SDave Jiang }
DEFINE_FREE(dsmas,struct xarray *,if (_T)discard_dsmas (_T))388ad6f04c0SDave Jiang DEFINE_FREE(dsmas, struct xarray *, if (_T) discard_dsmas(_T))
389ad6f04c0SDave Jiang 
390ad6f04c0SDave Jiang void cxl_endpoint_parse_cdat(struct cxl_port *port)
391ad6f04c0SDave Jiang {
39286557b7eSDave Jiang 	struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
39386557b7eSDave Jiang 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
394ad6f04c0SDave Jiang 	struct xarray __dsmas_xa;
395ad6f04c0SDave Jiang 	struct xarray *dsmas_xa __free(dsmas) = &__dsmas_xa;
396ad6f04c0SDave Jiang 	int rc;
397ad6f04c0SDave Jiang 
398ad6f04c0SDave Jiang 	xa_init(&__dsmas_xa);
399ad6f04c0SDave Jiang 	if (!port->cdat.table)
400ad6f04c0SDave Jiang 		return;
401ad6f04c0SDave Jiang 
402ad6f04c0SDave Jiang 	rc = cxl_cdat_endpoint_process(port, dsmas_xa);
403ad6f04c0SDave Jiang 	if (rc < 0) {
404ad6f04c0SDave Jiang 		dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
405ad6f04c0SDave Jiang 		return;
406ad6f04c0SDave Jiang 	}
407ad6f04c0SDave Jiang 
4087a4f148dSDave Jiang 	rc = cxl_port_perf_data_calculate(port, dsmas_xa);
4097a4f148dSDave Jiang 	if (rc) {
4107a4f148dSDave Jiang 		dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
4117a4f148dSDave Jiang 		return;
4127a4f148dSDave Jiang 	}
4137a4f148dSDave Jiang 
41486557b7eSDave Jiang 	cxl_memdev_set_qos_class(cxlds, dsmas_xa);
415185c1a48SDave Jiang 	cxl_qos_class_verify(cxlmd);
416cc214417SDave Jiang 	cxl_memdev_update_perf(cxlmd);
417ad6f04c0SDave Jiang }
418ad6f04c0SDave Jiang EXPORT_SYMBOL_NS_GPL(cxl_endpoint_parse_cdat, CXL);
419ad6f04c0SDave Jiang 
cdat_sslbis_handler(union acpi_subtable_headers * header,void * arg,const unsigned long end)42080aa780dSDave Jiang static int cdat_sslbis_handler(union acpi_subtable_headers *header, void *arg,
42180aa780dSDave Jiang 			       const unsigned long end)
42280aa780dSDave Jiang {
42399b52aacSDave Jiang 	struct acpi_cdat_sslbis_table {
42499b52aacSDave Jiang 		struct acpi_cdat_header header;
42599b52aacSDave Jiang 		struct acpi_cdat_sslbis sslbis_header;
42699b52aacSDave Jiang 		struct acpi_cdat_sslbe entries[];
42799b52aacSDave Jiang 	} *tbl = (struct acpi_cdat_sslbis_table *)header;
42899b52aacSDave Jiang 	int size = sizeof(header->cdat) + sizeof(tbl->sslbis_header);
42980aa780dSDave Jiang 	struct acpi_cdat_sslbis *sslbis;
43080aa780dSDave Jiang 	struct cxl_port *port = arg;
43180aa780dSDave Jiang 	struct device *dev = &port->dev;
43280aa780dSDave Jiang 	int remain, entries, i;
43380aa780dSDave Jiang 	u16 len;
43480aa780dSDave Jiang 
43580aa780dSDave Jiang 	len = le16_to_cpu((__force __le16)header->cdat.length);
43680aa780dSDave Jiang 	remain = len - size;
43799b52aacSDave Jiang 	if (!remain || remain % sizeof(tbl->entries[0]) ||
43880aa780dSDave Jiang 	    (unsigned long)header + len > end) {
43980aa780dSDave Jiang 		dev_warn(dev, "Malformed SSLBIS table length: (%u)\n", len);
44080aa780dSDave Jiang 		return -EINVAL;
44180aa780dSDave Jiang 	}
44280aa780dSDave Jiang 
44399b52aacSDave Jiang 	sslbis = &tbl->sslbis_header;
44480aa780dSDave Jiang 	/* Unrecognized data type, we can skip */
44580aa780dSDave Jiang 	if (sslbis->data_type > ACPI_HMAT_WRITE_BANDWIDTH)
44680aa780dSDave Jiang 		return 0;
44780aa780dSDave Jiang 
44899b52aacSDave Jiang 	entries = remain / sizeof(tbl->entries[0]);
44999b52aacSDave Jiang 	if (struct_size(tbl, entries, entries) != len)
45099b52aacSDave Jiang 		return -EINVAL;
45180aa780dSDave Jiang 
45280aa780dSDave Jiang 	for (i = 0; i < entries; i++) {
45399b52aacSDave Jiang 		u16 x = le16_to_cpu((__force __le16)tbl->entries[i].portx_id);
45499b52aacSDave Jiang 		u16 y = le16_to_cpu((__force __le16)tbl->entries[i].porty_id);
45580aa780dSDave Jiang 		__le64 le_base;
45680aa780dSDave Jiang 		__le16 le_val;
45780aa780dSDave Jiang 		struct cxl_dport *dport;
45880aa780dSDave Jiang 		unsigned long index;
45980aa780dSDave Jiang 		u16 dsp_id;
46080aa780dSDave Jiang 		u64 val;
46180aa780dSDave Jiang 
46280aa780dSDave Jiang 		switch (x) {
46380aa780dSDave Jiang 		case ACPI_CDAT_SSLBIS_US_PORT:
46480aa780dSDave Jiang 			dsp_id = y;
46580aa780dSDave Jiang 			break;
46680aa780dSDave Jiang 		case ACPI_CDAT_SSLBIS_ANY_PORT:
46780aa780dSDave Jiang 			switch (y) {
46880aa780dSDave Jiang 			case ACPI_CDAT_SSLBIS_US_PORT:
46980aa780dSDave Jiang 				dsp_id = x;
47080aa780dSDave Jiang 				break;
47180aa780dSDave Jiang 			case ACPI_CDAT_SSLBIS_ANY_PORT:
47280aa780dSDave Jiang 				dsp_id = ACPI_CDAT_SSLBIS_ANY_PORT;
47380aa780dSDave Jiang 				break;
47480aa780dSDave Jiang 			default:
47580aa780dSDave Jiang 				dsp_id = y;
47680aa780dSDave Jiang 				break;
47780aa780dSDave Jiang 			}
47880aa780dSDave Jiang 			break;
47980aa780dSDave Jiang 		default:
48080aa780dSDave Jiang 			dsp_id = x;
48180aa780dSDave Jiang 			break;
48280aa780dSDave Jiang 		}
48380aa780dSDave Jiang 
48499b52aacSDave Jiang 		le_base = (__force __le64)tbl->sslbis_header.entry_base_unit;
48599b52aacSDave Jiang 		le_val = (__force __le16)tbl->entries[i].latency_or_bandwidth;
48651293c56SDave Jiang 		val = cdat_normalize(le16_to_cpu(le_val), le64_to_cpu(le_base),
48751293c56SDave Jiang 				     sslbis->data_type);
48880aa780dSDave Jiang 
48980aa780dSDave Jiang 		xa_for_each(&port->dports, index, dport) {
49080aa780dSDave Jiang 			if (dsp_id == ACPI_CDAT_SSLBIS_ANY_PORT ||
491*001c5d19SDave Jiang 			    dsp_id == dport->port_id) {
492*001c5d19SDave Jiang 				cxl_access_coordinate_set(dport->coord,
49380aa780dSDave Jiang 							  sslbis->data_type,
49480aa780dSDave Jiang 							  val);
49580aa780dSDave Jiang 			}
49680aa780dSDave Jiang 		}
497*001c5d19SDave Jiang 	}
49880aa780dSDave Jiang 
49980aa780dSDave Jiang 	return 0;
50080aa780dSDave Jiang }
50180aa780dSDave Jiang 
cxl_switch_parse_cdat(struct cxl_port * port)50280aa780dSDave Jiang void cxl_switch_parse_cdat(struct cxl_port *port)
50380aa780dSDave Jiang {
50480aa780dSDave Jiang 	int rc;
50580aa780dSDave Jiang 
50680aa780dSDave Jiang 	if (!port->cdat.table)
50780aa780dSDave Jiang 		return;
50880aa780dSDave Jiang 
50980aa780dSDave Jiang 	rc = cdat_table_parse(ACPI_CDAT_TYPE_SSLBIS, cdat_sslbis_handler,
510c6c3187dSRobert Richter 			      port, port->cdat.table, port->cdat.length);
51180aa780dSDave Jiang 	rc = cdat_table_parse_output(rc);
51280aa780dSDave Jiang 	if (rc)
51380aa780dSDave Jiang 		dev_dbg(&port->dev, "Failed to parse SSLBIS: %d\n", rc);
51480aa780dSDave Jiang }
51580aa780dSDave Jiang EXPORT_SYMBOL_NS_GPL(cxl_switch_parse_cdat, CXL);
51680aa780dSDave Jiang 
__cxl_coordinates_combine(struct access_coordinate * out,struct access_coordinate * c1,struct access_coordinate * c2)517*001c5d19SDave Jiang static void __cxl_coordinates_combine(struct access_coordinate *out,
518032f7b37SDave Jiang 				      struct access_coordinate *c1,
519032f7b37SDave Jiang 				      struct access_coordinate *c2)
520032f7b37SDave Jiang {
521032f7b37SDave Jiang 		if (c1->write_bandwidth && c2->write_bandwidth)
522032f7b37SDave Jiang 			out->write_bandwidth = min(c1->write_bandwidth,
523032f7b37SDave Jiang 						   c2->write_bandwidth);
524032f7b37SDave Jiang 		out->write_latency = c1->write_latency + c2->write_latency;
525032f7b37SDave Jiang 
526032f7b37SDave Jiang 		if (c1->read_bandwidth && c2->read_bandwidth)
527032f7b37SDave Jiang 			out->read_bandwidth = min(c1->read_bandwidth,
528032f7b37SDave Jiang 						  c2->read_bandwidth);
529032f7b37SDave Jiang 		out->read_latency = c1->read_latency + c2->read_latency;
530032f7b37SDave Jiang }
531032f7b37SDave Jiang 
532*001c5d19SDave Jiang /**
533*001c5d19SDave Jiang  * cxl_coordinates_combine - Combine the two input coordinates
534*001c5d19SDave Jiang  *
535*001c5d19SDave Jiang  * @out: Output coordinate of c1 and c2 combined
536*001c5d19SDave Jiang  * @c1: input coordinates
537*001c5d19SDave Jiang  * @c2: input coordinates
538*001c5d19SDave Jiang  */
cxl_coordinates_combine(struct access_coordinate * out,struct access_coordinate * c1,struct access_coordinate * c2)539*001c5d19SDave Jiang void cxl_coordinates_combine(struct access_coordinate *out,
540*001c5d19SDave Jiang 			     struct access_coordinate *c1,
541*001c5d19SDave Jiang 			     struct access_coordinate *c2)
542*001c5d19SDave Jiang {
543*001c5d19SDave Jiang 	for (int i = 0; i < ACCESS_COORDINATE_MAX; i++)
544*001c5d19SDave Jiang 		__cxl_coordinates_combine(&out[i], &c1[i], &c2[i]);
545*001c5d19SDave Jiang }
546*001c5d19SDave Jiang 
547ad6f04c0SDave Jiang MODULE_IMPORT_NS(CXL);
5483d9f4a19SDave Jiang 
cxl_region_perf_data_calculate(struct cxl_region * cxlr,struct cxl_endpoint_decoder * cxled)5493d9f4a19SDave Jiang void cxl_region_perf_data_calculate(struct cxl_region *cxlr,
5503d9f4a19SDave Jiang 				    struct cxl_endpoint_decoder *cxled)
5513d9f4a19SDave Jiang {
5523d9f4a19SDave Jiang 	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
5533d9f4a19SDave Jiang 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
5543d9f4a19SDave Jiang 	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
5553d9f4a19SDave Jiang 	struct range dpa = {
5563d9f4a19SDave Jiang 			.start = cxled->dpa_res->start,
5573d9f4a19SDave Jiang 			.end = cxled->dpa_res->end,
5583d9f4a19SDave Jiang 	};
5593d9f4a19SDave Jiang 	struct cxl_dpa_perf *perf;
5603d9f4a19SDave Jiang 
5613d9f4a19SDave Jiang 	switch (cxlr->mode) {
5623d9f4a19SDave Jiang 	case CXL_DECODER_RAM:
5633d9f4a19SDave Jiang 		perf = &mds->ram_perf;
5643d9f4a19SDave Jiang 		break;
5653d9f4a19SDave Jiang 	case CXL_DECODER_PMEM:
5663d9f4a19SDave Jiang 		perf = &mds->pmem_perf;
5673d9f4a19SDave Jiang 		break;
5683d9f4a19SDave Jiang 	default:
5693d9f4a19SDave Jiang 		return;
5703d9f4a19SDave Jiang 	}
5713d9f4a19SDave Jiang 
5723d9f4a19SDave Jiang 	lockdep_assert_held(&cxl_dpa_rwsem);
5733d9f4a19SDave Jiang 
5743d9f4a19SDave Jiang 	if (!range_contains(&perf->dpa_range, &dpa))
5753d9f4a19SDave Jiang 		return;
5763d9f4a19SDave Jiang 
5773d9f4a19SDave Jiang 	for (int i = 0; i < ACCESS_COORDINATE_MAX; i++) {
5783d9f4a19SDave Jiang 		/* Get total bandwidth and the worst latency for the cxl region */
5793d9f4a19SDave Jiang 		cxlr->coord[i].read_latency = max_t(unsigned int,
5803d9f4a19SDave Jiang 						    cxlr->coord[i].read_latency,
581*001c5d19SDave Jiang 						    perf->coord[i].read_latency);
5823d9f4a19SDave Jiang 		cxlr->coord[i].write_latency = max_t(unsigned int,
5833d9f4a19SDave Jiang 						     cxlr->coord[i].write_latency,
584*001c5d19SDave Jiang 						     perf->coord[i].write_latency);
585*001c5d19SDave Jiang 		cxlr->coord[i].read_bandwidth += perf->coord[i].read_bandwidth;
586*001c5d19SDave Jiang 		cxlr->coord[i].write_bandwidth += perf->coord[i].write_bandwidth;
5873d9f4a19SDave Jiang 	}
5883d9f4a19SDave Jiang }
589067353a4SDave Jiang 
cxl_update_hmat_access_coordinates(int nid,struct cxl_region * cxlr,enum access_coordinate_class access)590067353a4SDave Jiang int cxl_update_hmat_access_coordinates(int nid, struct cxl_region *cxlr,
591067353a4SDave Jiang 				       enum access_coordinate_class access)
592067353a4SDave Jiang {
593067353a4SDave Jiang 	return hmat_update_target_coordinates(nid, &cxlr->coord[access], access);
594067353a4SDave Jiang }
595debdce20SDave Jiang 
cxl_need_node_perf_attrs_update(int nid)596debdce20SDave Jiang bool cxl_need_node_perf_attrs_update(int nid)
597debdce20SDave Jiang {
598debdce20SDave Jiang 	return !acpi_node_backed_by_real_pxm(nid);
599debdce20SDave Jiang }
600