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