xref: /linux/drivers/cxl/core/cdat.c (revision debdce20c4f28b7e5aa48512e7abf270a00e9051)
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;
1763cef81bSDave Jiang 	struct access_coordinate coord;
187a4f148dSDave Jiang 
197a4f148dSDave Jiang 	int entries;
207a4f148dSDave Jiang 	int qos_class;
21ad6f04c0SDave Jiang };
22ad6f04c0SDave Jiang 
23ad6f04c0SDave Jiang static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
24ad6f04c0SDave Jiang 			      const unsigned long end)
25ad6f04c0SDave Jiang {
26ad6f04c0SDave Jiang 	struct acpi_cdat_header *hdr = &header->cdat;
27ad6f04c0SDave Jiang 	struct acpi_cdat_dsmas *dsmas;
28ad6f04c0SDave Jiang 	int size = sizeof(*hdr) + sizeof(*dsmas);
29ad6f04c0SDave Jiang 	struct xarray *dsmas_xa = arg;
30ad6f04c0SDave Jiang 	struct dsmas_entry *dent;
31ad6f04c0SDave Jiang 	u16 len;
32ad6f04c0SDave Jiang 	int rc;
33ad6f04c0SDave Jiang 
34ad6f04c0SDave Jiang 	len = le16_to_cpu((__force __le16)hdr->length);
35ad6f04c0SDave Jiang 	if (len != size || (unsigned long)hdr + len > end) {
36ad6f04c0SDave Jiang 		pr_warn("Malformed DSMAS table length: (%u:%u)\n", size, len);
37ad6f04c0SDave Jiang 		return -EINVAL;
38ad6f04c0SDave Jiang 	}
39ad6f04c0SDave Jiang 
40ad6f04c0SDave Jiang 	/* Skip common header */
41ad6f04c0SDave Jiang 	dsmas = (struct acpi_cdat_dsmas *)(hdr + 1);
42ad6f04c0SDave Jiang 
43ad6f04c0SDave Jiang 	dent = kzalloc(sizeof(*dent), GFP_KERNEL);
44ad6f04c0SDave Jiang 	if (!dent)
45ad6f04c0SDave Jiang 		return -ENOMEM;
46ad6f04c0SDave Jiang 
47ad6f04c0SDave Jiang 	dent->handle = dsmas->dsmad_handle;
48ad6f04c0SDave Jiang 	dent->dpa_range.start = le64_to_cpu((__force __le64)dsmas->dpa_base_address);
49ad6f04c0SDave Jiang 	dent->dpa_range.end = le64_to_cpu((__force __le64)dsmas->dpa_base_address) +
50ad6f04c0SDave Jiang 			      le64_to_cpu((__force __le64)dsmas->dpa_length) - 1;
51ad6f04c0SDave Jiang 
52ad6f04c0SDave Jiang 	rc = xa_insert(dsmas_xa, dent->handle, dent, GFP_KERNEL);
53ad6f04c0SDave Jiang 	if (rc) {
54ad6f04c0SDave Jiang 		kfree(dent);
55ad6f04c0SDave Jiang 		return rc;
56ad6f04c0SDave Jiang 	}
57ad6f04c0SDave Jiang 
58ad6f04c0SDave Jiang 	return 0;
59ad6f04c0SDave Jiang }
60ad6f04c0SDave Jiang 
6163cef81bSDave Jiang static void cxl_access_coordinate_set(struct access_coordinate *coord,
6263cef81bSDave Jiang 				      int access, unsigned int val)
6363cef81bSDave Jiang {
6463cef81bSDave Jiang 	switch (access) {
6563cef81bSDave Jiang 	case ACPI_HMAT_ACCESS_LATENCY:
6663cef81bSDave Jiang 		coord->read_latency = val;
6763cef81bSDave Jiang 		coord->write_latency = val;
6863cef81bSDave Jiang 		break;
6963cef81bSDave Jiang 	case ACPI_HMAT_READ_LATENCY:
7063cef81bSDave Jiang 		coord->read_latency = val;
7163cef81bSDave Jiang 		break;
7263cef81bSDave Jiang 	case ACPI_HMAT_WRITE_LATENCY:
7363cef81bSDave Jiang 		coord->write_latency = val;
7463cef81bSDave Jiang 		break;
7563cef81bSDave Jiang 	case ACPI_HMAT_ACCESS_BANDWIDTH:
7663cef81bSDave Jiang 		coord->read_bandwidth = val;
7763cef81bSDave Jiang 		coord->write_bandwidth = val;
7863cef81bSDave Jiang 		break;
7963cef81bSDave Jiang 	case ACPI_HMAT_READ_BANDWIDTH:
8063cef81bSDave Jiang 		coord->read_bandwidth = val;
8163cef81bSDave Jiang 		break;
8263cef81bSDave Jiang 	case ACPI_HMAT_WRITE_BANDWIDTH:
8363cef81bSDave Jiang 		coord->write_bandwidth = val;
8463cef81bSDave Jiang 		break;
8563cef81bSDave Jiang 	}
8663cef81bSDave Jiang }
8763cef81bSDave Jiang 
8863cef81bSDave Jiang static int cdat_dslbis_handler(union acpi_subtable_headers *header, void *arg,
8963cef81bSDave Jiang 			       const unsigned long end)
9063cef81bSDave Jiang {
9163cef81bSDave Jiang 	struct acpi_cdat_header *hdr = &header->cdat;
9263cef81bSDave Jiang 	struct acpi_cdat_dslbis *dslbis;
9363cef81bSDave Jiang 	int size = sizeof(*hdr) + sizeof(*dslbis);
9463cef81bSDave Jiang 	struct xarray *dsmas_xa = arg;
9563cef81bSDave Jiang 	struct dsmas_entry *dent;
9663cef81bSDave Jiang 	__le64 le_base;
9763cef81bSDave Jiang 	__le16 le_val;
9863cef81bSDave Jiang 	u64 val;
9963cef81bSDave Jiang 	u16 len;
10063cef81bSDave Jiang 	int rc;
10163cef81bSDave Jiang 
10263cef81bSDave Jiang 	len = le16_to_cpu((__force __le16)hdr->length);
10363cef81bSDave Jiang 	if (len != size || (unsigned long)hdr + len > end) {
10463cef81bSDave Jiang 		pr_warn("Malformed DSLBIS table length: (%u:%u)\n", size, len);
10563cef81bSDave Jiang 		return -EINVAL;
10663cef81bSDave Jiang 	}
10763cef81bSDave Jiang 
10863cef81bSDave Jiang 	/* Skip common header */
10963cef81bSDave Jiang 	dslbis = (struct acpi_cdat_dslbis *)(hdr + 1);
11063cef81bSDave Jiang 
11163cef81bSDave Jiang 	/* Skip unrecognized data type */
11263cef81bSDave Jiang 	if (dslbis->data_type > ACPI_HMAT_WRITE_BANDWIDTH)
11363cef81bSDave Jiang 		return 0;
11463cef81bSDave Jiang 
11563cef81bSDave Jiang 	/* Not a memory type, skip */
11663cef81bSDave Jiang 	if ((dslbis->flags & ACPI_HMAT_MEMORY_HIERARCHY) != ACPI_HMAT_MEMORY)
11763cef81bSDave Jiang 		return 0;
11863cef81bSDave Jiang 
11963cef81bSDave Jiang 	dent = xa_load(dsmas_xa, dslbis->handle);
12063cef81bSDave Jiang 	if (!dent) {
12163cef81bSDave Jiang 		pr_warn("No matching DSMAS entry for DSLBIS entry.\n");
12263cef81bSDave Jiang 		return 0;
12363cef81bSDave Jiang 	}
12463cef81bSDave Jiang 
12563cef81bSDave Jiang 	le_base = (__force __le64)dslbis->entry_base_unit;
12663cef81bSDave Jiang 	le_val = (__force __le16)dslbis->entry[0];
12763cef81bSDave Jiang 	rc = check_mul_overflow(le64_to_cpu(le_base),
12863cef81bSDave Jiang 				le16_to_cpu(le_val), &val);
12963cef81bSDave Jiang 	if (rc)
13063cef81bSDave Jiang 		pr_warn("DSLBIS value overflowed.\n");
13163cef81bSDave Jiang 
13263cef81bSDave Jiang 	cxl_access_coordinate_set(&dent->coord, dslbis->data_type, val);
13363cef81bSDave Jiang 
13463cef81bSDave Jiang 	return 0;
13563cef81bSDave Jiang }
13663cef81bSDave Jiang 
13763cef81bSDave Jiang static int cdat_table_parse_output(int rc)
13863cef81bSDave Jiang {
13963cef81bSDave Jiang 	if (rc < 0)
14063cef81bSDave Jiang 		return rc;
14163cef81bSDave Jiang 	if (rc == 0)
14263cef81bSDave Jiang 		return -ENOENT;
14363cef81bSDave Jiang 
14463cef81bSDave Jiang 	return 0;
14563cef81bSDave Jiang }
14663cef81bSDave Jiang 
147ad6f04c0SDave Jiang static int cxl_cdat_endpoint_process(struct cxl_port *port,
148ad6f04c0SDave Jiang 				     struct xarray *dsmas_xa)
149ad6f04c0SDave Jiang {
15063cef81bSDave Jiang 	int rc;
15163cef81bSDave Jiang 
15263cef81bSDave Jiang 	rc = cdat_table_parse(ACPI_CDAT_TYPE_DSMAS, cdat_dsmas_handler,
153ad6f04c0SDave Jiang 			      dsmas_xa, port->cdat.table);
15463cef81bSDave Jiang 	rc = cdat_table_parse_output(rc);
15563cef81bSDave Jiang 	if (rc)
15663cef81bSDave Jiang 		return rc;
15763cef81bSDave Jiang 
15863cef81bSDave Jiang 	rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler,
15963cef81bSDave Jiang 			      dsmas_xa, port->cdat.table);
16063cef81bSDave Jiang 	return cdat_table_parse_output(rc);
161ad6f04c0SDave Jiang }
162ad6f04c0SDave Jiang 
1637a4f148dSDave Jiang static int cxl_port_perf_data_calculate(struct cxl_port *port,
1647a4f148dSDave Jiang 					struct xarray *dsmas_xa)
1657a4f148dSDave Jiang {
166863027d4SDave Jiang 	struct access_coordinate ep_c;
167863027d4SDave Jiang 	struct access_coordinate coord[ACCESS_COORDINATE_MAX];
1687a4f148dSDave Jiang 	struct dsmas_entry *dent;
1697a4f148dSDave Jiang 	int valid_entries = 0;
1707a4f148dSDave Jiang 	unsigned long index;
1717a4f148dSDave Jiang 	int rc;
1727a4f148dSDave Jiang 
173863027d4SDave Jiang 	rc = cxl_endpoint_get_perf_coordinates(port, &ep_c);
1747a4f148dSDave Jiang 	if (rc) {
175863027d4SDave Jiang 		dev_dbg(&port->dev, "Failed to retrieve ep perf coordinates.\n");
176863027d4SDave Jiang 		return rc;
177863027d4SDave Jiang 	}
178863027d4SDave Jiang 
179863027d4SDave Jiang 	rc = cxl_hb_get_perf_coordinates(port, coord);
180863027d4SDave Jiang 	if (rc)  {
181863027d4SDave Jiang 		dev_dbg(&port->dev, "Failed to retrieve hb perf coordinates.\n");
1827a4f148dSDave Jiang 		return rc;
1837a4f148dSDave Jiang 	}
1847a4f148dSDave Jiang 
18598e7ab33SDave Jiang 	struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port);
18698e7ab33SDave Jiang 
18798e7ab33SDave Jiang 	if (!cxl_root)
18898e7ab33SDave Jiang 		return -ENODEV;
18998e7ab33SDave Jiang 
1907a4f148dSDave Jiang 	if (!cxl_root->ops || !cxl_root->ops->qos_class)
1917a4f148dSDave Jiang 		return -EOPNOTSUPP;
1927a4f148dSDave Jiang 
1937a4f148dSDave Jiang 	xa_for_each(dsmas_xa, index, dent) {
1947a4f148dSDave Jiang 		int qos_class;
1957a4f148dSDave Jiang 
196863027d4SDave Jiang 		cxl_coordinates_combine(&dent->coord, &dent->coord, &ep_c);
197863027d4SDave Jiang 		/*
198863027d4SDave Jiang 		 * Keeping the host bridge coordinates separate from the dsmas
199863027d4SDave Jiang 		 * coordinates in order to allow calculation of access class
200863027d4SDave Jiang 		 * 0 and 1 for region later.
201863027d4SDave Jiang 		 */
2026ef83c4eSDave Jiang 		cxl_coordinates_combine(&coord[ACCESS_COORDINATE_CPU],
2036ef83c4eSDave Jiang 					&coord[ACCESS_COORDINATE_CPU],
204863027d4SDave Jiang 					&dent->coord);
2057a4f148dSDave Jiang 		dent->entries = 1;
206863027d4SDave Jiang 		rc = cxl_root->ops->qos_class(cxl_root,
2076ef83c4eSDave Jiang 					      &coord[ACCESS_COORDINATE_CPU],
208863027d4SDave Jiang 					      1, &qos_class);
2097a4f148dSDave Jiang 		if (rc != 1)
2107a4f148dSDave Jiang 			continue;
2117a4f148dSDave Jiang 
2127a4f148dSDave Jiang 		valid_entries++;
2137a4f148dSDave Jiang 		dent->qos_class = qos_class;
2147a4f148dSDave Jiang 	}
2157a4f148dSDave Jiang 
2167a4f148dSDave Jiang 	if (!valid_entries)
2177a4f148dSDave Jiang 		return -ENOENT;
2187a4f148dSDave Jiang 
2197a4f148dSDave Jiang 	return 0;
2207a4f148dSDave Jiang }
2217a4f148dSDave Jiang 
22200413c15SDave Jiang static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
22300413c15SDave Jiang 			      struct cxl_dpa_perf *dpa_perf)
22486557b7eSDave Jiang {
22586557b7eSDave Jiang 	dpa_perf->dpa_range = dent->dpa_range;
22686557b7eSDave Jiang 	dpa_perf->coord = dent->coord;
22786557b7eSDave Jiang 	dpa_perf->qos_class = dent->qos_class;
22886557b7eSDave Jiang 	dev_dbg(dev,
22986557b7eSDave Jiang 		"DSMAS: dpa: %#llx qos: %d read_bw: %d write_bw %d read_lat: %d write_lat: %d\n",
23086557b7eSDave Jiang 		dent->dpa_range.start, dpa_perf->qos_class,
23186557b7eSDave Jiang 		dent->coord.read_bandwidth, dent->coord.write_bandwidth,
23286557b7eSDave Jiang 		dent->coord.read_latency, dent->coord.write_latency);
23386557b7eSDave Jiang }
23486557b7eSDave Jiang 
23586557b7eSDave Jiang static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
23686557b7eSDave Jiang 				     struct xarray *dsmas_xa)
23786557b7eSDave Jiang {
23886557b7eSDave Jiang 	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
23986557b7eSDave Jiang 	struct device *dev = cxlds->dev;
24086557b7eSDave Jiang 	struct range pmem_range = {
24186557b7eSDave Jiang 		.start = cxlds->pmem_res.start,
24286557b7eSDave Jiang 		.end = cxlds->pmem_res.end,
24386557b7eSDave Jiang 	};
24486557b7eSDave Jiang 	struct range ram_range = {
24586557b7eSDave Jiang 		.start = cxlds->ram_res.start,
24686557b7eSDave Jiang 		.end = cxlds->ram_res.end,
24786557b7eSDave Jiang 	};
24886557b7eSDave Jiang 	struct dsmas_entry *dent;
24986557b7eSDave Jiang 	unsigned long index;
25086557b7eSDave Jiang 
25186557b7eSDave Jiang 	xa_for_each(dsmas_xa, index, dent) {
25286557b7eSDave Jiang 		if (resource_size(&cxlds->ram_res) &&
25386557b7eSDave Jiang 		    range_contains(&ram_range, &dent->dpa_range))
25400413c15SDave Jiang 			update_perf_entry(dev, dent, &mds->ram_perf);
25586557b7eSDave Jiang 		else if (resource_size(&cxlds->pmem_res) &&
25686557b7eSDave Jiang 			 range_contains(&pmem_range, &dent->dpa_range))
25700413c15SDave Jiang 			update_perf_entry(dev, dent, &mds->pmem_perf);
25886557b7eSDave Jiang 		else
25986557b7eSDave Jiang 			dev_dbg(dev, "no partition for dsmas dpa: %#llx\n",
26086557b7eSDave Jiang 				dent->dpa_range.start);
26186557b7eSDave Jiang 	}
26286557b7eSDave Jiang }
26386557b7eSDave Jiang 
264185c1a48SDave Jiang static int match_cxlrd_qos_class(struct device *dev, void *data)
265185c1a48SDave Jiang {
266185c1a48SDave Jiang 	int dev_qos_class = *(int *)data;
267185c1a48SDave Jiang 	struct cxl_root_decoder *cxlrd;
268185c1a48SDave Jiang 
269185c1a48SDave Jiang 	if (!is_root_decoder(dev))
270185c1a48SDave Jiang 		return 0;
271185c1a48SDave Jiang 
272185c1a48SDave Jiang 	cxlrd = to_cxl_root_decoder(dev);
273185c1a48SDave Jiang 	if (cxlrd->qos_class == CXL_QOS_CLASS_INVALID)
274185c1a48SDave Jiang 		return 0;
275185c1a48SDave Jiang 
276185c1a48SDave Jiang 	if (cxlrd->qos_class == dev_qos_class)
277185c1a48SDave Jiang 		return 1;
278185c1a48SDave Jiang 
279185c1a48SDave Jiang 	return 0;
280185c1a48SDave Jiang }
281185c1a48SDave Jiang 
28200413c15SDave Jiang static void reset_dpa_perf(struct cxl_dpa_perf *dpa_perf)
283185c1a48SDave Jiang {
28400413c15SDave Jiang 	*dpa_perf = (struct cxl_dpa_perf) {
28500413c15SDave Jiang 		.qos_class = CXL_QOS_CLASS_INVALID,
28600413c15SDave Jiang 	};
287185c1a48SDave Jiang }
28800413c15SDave Jiang 
28900413c15SDave Jiang static bool cxl_qos_match(struct cxl_port *root_port,
29000413c15SDave Jiang 			  struct cxl_dpa_perf *dpa_perf)
29100413c15SDave Jiang {
29200413c15SDave Jiang 	if (dpa_perf->qos_class == CXL_QOS_CLASS_INVALID)
29300413c15SDave Jiang 		return false;
29400413c15SDave Jiang 
29500413c15SDave Jiang 	if (!device_for_each_child(&root_port->dev, &dpa_perf->qos_class,
29600413c15SDave Jiang 				   match_cxlrd_qos_class))
29700413c15SDave Jiang 		return false;
29800413c15SDave Jiang 
29900413c15SDave Jiang 	return true;
300185c1a48SDave Jiang }
301185c1a48SDave Jiang 
302185c1a48SDave Jiang static int match_cxlrd_hb(struct device *dev, void *data)
303185c1a48SDave Jiang {
304185c1a48SDave Jiang 	struct device *host_bridge = data;
305185c1a48SDave Jiang 	struct cxl_switch_decoder *cxlsd;
306185c1a48SDave Jiang 	struct cxl_root_decoder *cxlrd;
307185c1a48SDave Jiang 
308185c1a48SDave Jiang 	if (!is_root_decoder(dev))
309185c1a48SDave Jiang 		return 0;
310185c1a48SDave Jiang 
311185c1a48SDave Jiang 	cxlrd = to_cxl_root_decoder(dev);
312185c1a48SDave Jiang 	cxlsd = &cxlrd->cxlsd;
313185c1a48SDave Jiang 
314e16bf7e0SDan Williams 	guard(rwsem_read)(&cxl_region_rwsem);
315185c1a48SDave Jiang 	for (int i = 0; i < cxlsd->nr_targets; i++) {
316185c1a48SDave Jiang 		if (host_bridge == cxlsd->target[i]->dport_dev)
317185c1a48SDave Jiang 			return 1;
318185c1a48SDave Jiang 	}
319185c1a48SDave Jiang 
320185c1a48SDave Jiang 	return 0;
321185c1a48SDave Jiang }
322185c1a48SDave Jiang 
323185c1a48SDave Jiang static int cxl_qos_class_verify(struct cxl_memdev *cxlmd)
324185c1a48SDave Jiang {
325185c1a48SDave Jiang 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
326185c1a48SDave Jiang 	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
32744cd71efSDave Jiang 	struct cxl_port *root_port;
328185c1a48SDave Jiang 	int rc;
329185c1a48SDave Jiang 
33044cd71efSDave Jiang 	struct cxl_root *cxl_root __free(put_cxl_root) =
33144cd71efSDave Jiang 		find_cxl_root(cxlmd->endpoint);
33244cd71efSDave Jiang 
33344cd71efSDave Jiang 	if (!cxl_root)
334185c1a48SDave Jiang 		return -ENODEV;
335185c1a48SDave Jiang 
33644cd71efSDave Jiang 	root_port = &cxl_root->port;
33744cd71efSDave Jiang 
338185c1a48SDave Jiang 	/* Check that the QTG IDs are all sane between end device and root decoders */
33900413c15SDave Jiang 	if (!cxl_qos_match(root_port, &mds->ram_perf))
34000413c15SDave Jiang 		reset_dpa_perf(&mds->ram_perf);
34100413c15SDave Jiang 	if (!cxl_qos_match(root_port, &mds->pmem_perf))
34200413c15SDave Jiang 		reset_dpa_perf(&mds->pmem_perf);
343185c1a48SDave Jiang 
344185c1a48SDave Jiang 	/* Check to make sure that the device's host bridge is under a root decoder */
345185c1a48SDave Jiang 	rc = device_for_each_child(&root_port->dev,
34610cb393dSDave Jiang 				   cxlmd->endpoint->host_bridge, match_cxlrd_hb);
347185c1a48SDave Jiang 	if (!rc) {
34800413c15SDave Jiang 		reset_dpa_perf(&mds->ram_perf);
34900413c15SDave Jiang 		reset_dpa_perf(&mds->pmem_perf);
350185c1a48SDave Jiang 	}
351185c1a48SDave Jiang 
352185c1a48SDave Jiang 	return rc;
353185c1a48SDave Jiang }
354185c1a48SDave Jiang 
355ad6f04c0SDave Jiang static void discard_dsmas(struct xarray *xa)
356ad6f04c0SDave Jiang {
357ad6f04c0SDave Jiang 	unsigned long index;
358ad6f04c0SDave Jiang 	void *ent;
359ad6f04c0SDave Jiang 
360ad6f04c0SDave Jiang 	xa_for_each(xa, index, ent) {
361ad6f04c0SDave Jiang 		xa_erase(xa, index);
362ad6f04c0SDave Jiang 		kfree(ent);
363ad6f04c0SDave Jiang 	}
364ad6f04c0SDave Jiang 	xa_destroy(xa);
365ad6f04c0SDave Jiang }
366ad6f04c0SDave Jiang DEFINE_FREE(dsmas, struct xarray *, if (_T) discard_dsmas(_T))
367ad6f04c0SDave Jiang 
368ad6f04c0SDave Jiang void cxl_endpoint_parse_cdat(struct cxl_port *port)
369ad6f04c0SDave Jiang {
37086557b7eSDave Jiang 	struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
37186557b7eSDave Jiang 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
372ad6f04c0SDave Jiang 	struct xarray __dsmas_xa;
373ad6f04c0SDave Jiang 	struct xarray *dsmas_xa __free(dsmas) = &__dsmas_xa;
374ad6f04c0SDave Jiang 	int rc;
375ad6f04c0SDave Jiang 
376ad6f04c0SDave Jiang 	xa_init(&__dsmas_xa);
377ad6f04c0SDave Jiang 	if (!port->cdat.table)
378ad6f04c0SDave Jiang 		return;
379ad6f04c0SDave Jiang 
380ad6f04c0SDave Jiang 	rc = cxl_cdat_endpoint_process(port, dsmas_xa);
381ad6f04c0SDave Jiang 	if (rc < 0) {
382ad6f04c0SDave Jiang 		dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
383ad6f04c0SDave Jiang 		return;
384ad6f04c0SDave Jiang 	}
385ad6f04c0SDave Jiang 
3867a4f148dSDave Jiang 	rc = cxl_port_perf_data_calculate(port, dsmas_xa);
3877a4f148dSDave Jiang 	if (rc) {
3887a4f148dSDave Jiang 		dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
3897a4f148dSDave Jiang 		return;
3907a4f148dSDave Jiang 	}
3917a4f148dSDave Jiang 
39286557b7eSDave Jiang 	cxl_memdev_set_qos_class(cxlds, dsmas_xa);
393185c1a48SDave Jiang 	cxl_qos_class_verify(cxlmd);
394cc214417SDave Jiang 	cxl_memdev_update_perf(cxlmd);
395ad6f04c0SDave Jiang }
396ad6f04c0SDave Jiang EXPORT_SYMBOL_NS_GPL(cxl_endpoint_parse_cdat, CXL);
397ad6f04c0SDave Jiang 
39880aa780dSDave Jiang static int cdat_sslbis_handler(union acpi_subtable_headers *header, void *arg,
39980aa780dSDave Jiang 			       const unsigned long end)
40080aa780dSDave Jiang {
40180aa780dSDave Jiang 	struct acpi_cdat_sslbis *sslbis;
40280aa780dSDave Jiang 	int size = sizeof(header->cdat) + sizeof(*sslbis);
40380aa780dSDave Jiang 	struct cxl_port *port = arg;
40480aa780dSDave Jiang 	struct device *dev = &port->dev;
40580aa780dSDave Jiang 	struct acpi_cdat_sslbe *entry;
40680aa780dSDave Jiang 	int remain, entries, i;
40780aa780dSDave Jiang 	u16 len;
40880aa780dSDave Jiang 
40980aa780dSDave Jiang 	len = le16_to_cpu((__force __le16)header->cdat.length);
41080aa780dSDave Jiang 	remain = len - size;
41180aa780dSDave Jiang 	if (!remain || remain % sizeof(*entry) ||
41280aa780dSDave Jiang 	    (unsigned long)header + len > end) {
41380aa780dSDave Jiang 		dev_warn(dev, "Malformed SSLBIS table length: (%u)\n", len);
41480aa780dSDave Jiang 		return -EINVAL;
41580aa780dSDave Jiang 	}
41680aa780dSDave Jiang 
41780aa780dSDave Jiang 	/* Skip common header */
41880aa780dSDave Jiang 	sslbis = (struct acpi_cdat_sslbis *)((unsigned long)header +
41980aa780dSDave Jiang 					     sizeof(header->cdat));
42080aa780dSDave Jiang 
42180aa780dSDave Jiang 	/* Unrecognized data type, we can skip */
42280aa780dSDave Jiang 	if (sslbis->data_type > ACPI_HMAT_WRITE_BANDWIDTH)
42380aa780dSDave Jiang 		return 0;
42480aa780dSDave Jiang 
42580aa780dSDave Jiang 	entries = remain / sizeof(*entry);
42680aa780dSDave Jiang 	entry = (struct acpi_cdat_sslbe *)((unsigned long)header + sizeof(*sslbis));
42780aa780dSDave Jiang 
42880aa780dSDave Jiang 	for (i = 0; i < entries; i++) {
42980aa780dSDave Jiang 		u16 x = le16_to_cpu((__force __le16)entry->portx_id);
43080aa780dSDave Jiang 		u16 y = le16_to_cpu((__force __le16)entry->porty_id);
43180aa780dSDave Jiang 		__le64 le_base;
43280aa780dSDave Jiang 		__le16 le_val;
43380aa780dSDave Jiang 		struct cxl_dport *dport;
43480aa780dSDave Jiang 		unsigned long index;
43580aa780dSDave Jiang 		u16 dsp_id;
43680aa780dSDave Jiang 		u64 val;
43780aa780dSDave Jiang 
43880aa780dSDave Jiang 		switch (x) {
43980aa780dSDave Jiang 		case ACPI_CDAT_SSLBIS_US_PORT:
44080aa780dSDave Jiang 			dsp_id = y;
44180aa780dSDave Jiang 			break;
44280aa780dSDave Jiang 		case ACPI_CDAT_SSLBIS_ANY_PORT:
44380aa780dSDave Jiang 			switch (y) {
44480aa780dSDave Jiang 			case ACPI_CDAT_SSLBIS_US_PORT:
44580aa780dSDave Jiang 				dsp_id = x;
44680aa780dSDave Jiang 				break;
44780aa780dSDave Jiang 			case ACPI_CDAT_SSLBIS_ANY_PORT:
44880aa780dSDave Jiang 				dsp_id = ACPI_CDAT_SSLBIS_ANY_PORT;
44980aa780dSDave Jiang 				break;
45080aa780dSDave Jiang 			default:
45180aa780dSDave Jiang 				dsp_id = y;
45280aa780dSDave Jiang 				break;
45380aa780dSDave Jiang 			}
45480aa780dSDave Jiang 			break;
45580aa780dSDave Jiang 		default:
45680aa780dSDave Jiang 			dsp_id = x;
45780aa780dSDave Jiang 			break;
45880aa780dSDave Jiang 		}
45980aa780dSDave Jiang 
46080aa780dSDave Jiang 		le_base = (__force __le64)sslbis->entry_base_unit;
46180aa780dSDave Jiang 		le_val = (__force __le16)entry->latency_or_bandwidth;
46280aa780dSDave Jiang 
46380aa780dSDave Jiang 		if (check_mul_overflow(le64_to_cpu(le_base),
46480aa780dSDave Jiang 				       le16_to_cpu(le_val), &val))
46580aa780dSDave Jiang 			dev_warn(dev, "SSLBIS value overflowed!\n");
46680aa780dSDave Jiang 
46780aa780dSDave Jiang 		xa_for_each(&port->dports, index, dport) {
46880aa780dSDave Jiang 			if (dsp_id == ACPI_CDAT_SSLBIS_ANY_PORT ||
46980aa780dSDave Jiang 			    dsp_id == dport->port_id)
47080aa780dSDave Jiang 				cxl_access_coordinate_set(&dport->sw_coord,
47180aa780dSDave Jiang 							  sslbis->data_type,
47280aa780dSDave Jiang 							  val);
47380aa780dSDave Jiang 		}
47480aa780dSDave Jiang 
47580aa780dSDave Jiang 		entry++;
47680aa780dSDave Jiang 	}
47780aa780dSDave Jiang 
47880aa780dSDave Jiang 	return 0;
47980aa780dSDave Jiang }
48080aa780dSDave Jiang 
48180aa780dSDave Jiang void cxl_switch_parse_cdat(struct cxl_port *port)
48280aa780dSDave Jiang {
48380aa780dSDave Jiang 	int rc;
48480aa780dSDave Jiang 
48580aa780dSDave Jiang 	if (!port->cdat.table)
48680aa780dSDave Jiang 		return;
48780aa780dSDave Jiang 
48880aa780dSDave Jiang 	rc = cdat_table_parse(ACPI_CDAT_TYPE_SSLBIS, cdat_sslbis_handler,
48980aa780dSDave Jiang 			      port, port->cdat.table);
49080aa780dSDave Jiang 	rc = cdat_table_parse_output(rc);
49180aa780dSDave Jiang 	if (rc)
49280aa780dSDave Jiang 		dev_dbg(&port->dev, "Failed to parse SSLBIS: %d\n", rc);
49380aa780dSDave Jiang }
49480aa780dSDave Jiang EXPORT_SYMBOL_NS_GPL(cxl_switch_parse_cdat, CXL);
49580aa780dSDave Jiang 
496032f7b37SDave Jiang /**
497032f7b37SDave Jiang  * cxl_coordinates_combine - Combine the two input coordinates
498032f7b37SDave Jiang  *
499032f7b37SDave Jiang  * @out: Output coordinate of c1 and c2 combined
500032f7b37SDave Jiang  * @c1: input coordinates
501032f7b37SDave Jiang  * @c2: input coordinates
502032f7b37SDave Jiang  */
503032f7b37SDave Jiang void cxl_coordinates_combine(struct access_coordinate *out,
504032f7b37SDave Jiang 			     struct access_coordinate *c1,
505032f7b37SDave Jiang 			     struct access_coordinate *c2)
506032f7b37SDave Jiang {
507032f7b37SDave Jiang 		if (c1->write_bandwidth && c2->write_bandwidth)
508032f7b37SDave Jiang 			out->write_bandwidth = min(c1->write_bandwidth,
509032f7b37SDave Jiang 						   c2->write_bandwidth);
510032f7b37SDave Jiang 		out->write_latency = c1->write_latency + c2->write_latency;
511032f7b37SDave Jiang 
512032f7b37SDave Jiang 		if (c1->read_bandwidth && c2->read_bandwidth)
513032f7b37SDave Jiang 			out->read_bandwidth = min(c1->read_bandwidth,
514032f7b37SDave Jiang 						  c2->read_bandwidth);
515032f7b37SDave Jiang 		out->read_latency = c1->read_latency + c2->read_latency;
516032f7b37SDave Jiang }
517032f7b37SDave Jiang 
518ad6f04c0SDave Jiang MODULE_IMPORT_NS(CXL);
5193d9f4a19SDave Jiang 
5203d9f4a19SDave Jiang void cxl_region_perf_data_calculate(struct cxl_region *cxlr,
5213d9f4a19SDave Jiang 				    struct cxl_endpoint_decoder *cxled)
5223d9f4a19SDave Jiang {
5233d9f4a19SDave Jiang 	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
5243d9f4a19SDave Jiang 	struct cxl_port *port = cxlmd->endpoint;
5253d9f4a19SDave Jiang 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
5263d9f4a19SDave Jiang 	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
5273d9f4a19SDave Jiang 	struct access_coordinate hb_coord[ACCESS_COORDINATE_MAX];
5283d9f4a19SDave Jiang 	struct access_coordinate coord;
5293d9f4a19SDave Jiang 	struct range dpa = {
5303d9f4a19SDave Jiang 			.start = cxled->dpa_res->start,
5313d9f4a19SDave Jiang 			.end = cxled->dpa_res->end,
5323d9f4a19SDave Jiang 	};
5333d9f4a19SDave Jiang 	struct cxl_dpa_perf *perf;
5343d9f4a19SDave Jiang 	int rc;
5353d9f4a19SDave Jiang 
5363d9f4a19SDave Jiang 	switch (cxlr->mode) {
5373d9f4a19SDave Jiang 	case CXL_DECODER_RAM:
5383d9f4a19SDave Jiang 		perf = &mds->ram_perf;
5393d9f4a19SDave Jiang 		break;
5403d9f4a19SDave Jiang 	case CXL_DECODER_PMEM:
5413d9f4a19SDave Jiang 		perf = &mds->pmem_perf;
5423d9f4a19SDave Jiang 		break;
5433d9f4a19SDave Jiang 	default:
5443d9f4a19SDave Jiang 		return;
5453d9f4a19SDave Jiang 	}
5463d9f4a19SDave Jiang 
5473d9f4a19SDave Jiang 	lockdep_assert_held(&cxl_dpa_rwsem);
5483d9f4a19SDave Jiang 
5493d9f4a19SDave Jiang 	if (!range_contains(&perf->dpa_range, &dpa))
5503d9f4a19SDave Jiang 		return;
5513d9f4a19SDave Jiang 
5523d9f4a19SDave Jiang 	rc = cxl_hb_get_perf_coordinates(port, hb_coord);
5533d9f4a19SDave Jiang 	if (rc)  {
5543d9f4a19SDave Jiang 		dev_dbg(&port->dev, "Failed to retrieve hb perf coordinates.\n");
5553d9f4a19SDave Jiang 		return;
5563d9f4a19SDave Jiang 	}
5573d9f4a19SDave Jiang 
5583d9f4a19SDave Jiang 	for (int i = 0; i < ACCESS_COORDINATE_MAX; i++) {
5593d9f4a19SDave Jiang 		/* Pickup the host bridge coords */
5603d9f4a19SDave Jiang 		cxl_coordinates_combine(&coord, &hb_coord[i], &perf->coord);
5613d9f4a19SDave Jiang 
5623d9f4a19SDave Jiang 		/* Get total bandwidth and the worst latency for the cxl region */
5633d9f4a19SDave Jiang 		cxlr->coord[i].read_latency = max_t(unsigned int,
5643d9f4a19SDave Jiang 						    cxlr->coord[i].read_latency,
5653d9f4a19SDave Jiang 						    coord.read_latency);
5663d9f4a19SDave Jiang 		cxlr->coord[i].write_latency = max_t(unsigned int,
5673d9f4a19SDave Jiang 						     cxlr->coord[i].write_latency,
5683d9f4a19SDave Jiang 						     coord.write_latency);
5693d9f4a19SDave Jiang 		cxlr->coord[i].read_bandwidth += coord.read_bandwidth;
5703d9f4a19SDave Jiang 		cxlr->coord[i].write_bandwidth += coord.write_bandwidth;
5713d9f4a19SDave Jiang 
5723d9f4a19SDave Jiang 		/*
5733d9f4a19SDave Jiang 		 * Convert latency to nanosec from picosec to be consistent
5743d9f4a19SDave Jiang 		 * with the resulting latency coordinates computed by the
5753d9f4a19SDave Jiang 		 * HMAT_REPORTING code.
5763d9f4a19SDave Jiang 		 */
5773d9f4a19SDave Jiang 		cxlr->coord[i].read_latency =
5783d9f4a19SDave Jiang 			DIV_ROUND_UP(cxlr->coord[i].read_latency, 1000);
5793d9f4a19SDave Jiang 		cxlr->coord[i].write_latency =
5803d9f4a19SDave Jiang 			DIV_ROUND_UP(cxlr->coord[i].write_latency, 1000);
5813d9f4a19SDave Jiang 	}
5823d9f4a19SDave Jiang }
583067353a4SDave Jiang 
584067353a4SDave Jiang int cxl_update_hmat_access_coordinates(int nid, struct cxl_region *cxlr,
585067353a4SDave Jiang 				       enum access_coordinate_class access)
586067353a4SDave Jiang {
587067353a4SDave Jiang 	return hmat_update_target_coordinates(nid, &cxlr->coord[access], access);
588067353a4SDave Jiang }
589*debdce20SDave Jiang 
590*debdce20SDave Jiang bool cxl_need_node_perf_attrs_update(int nid)
591*debdce20SDave Jiang {
592*debdce20SDave Jiang 	return !acpi_node_backed_by_real_pxm(nid);
593*debdce20SDave Jiang }
594