cxl: Add callback to parse the SSLBIS subtable from CDAT
authorDave Jiang <dave.jiang@intel.com>
Thu, 21 Dec 2023 22:03:26 +0000 (15:03 -0700)
committerDan Williams <dan.j.williams@intel.com>
Fri, 22 Dec 2023 22:33:28 +0000 (14:33 -0800)
Provide a callback to parse the Switched Scoped Latency and Bandwidth
Information Structure (SSLBIS) in the CDAT structures. The SSLBIS
contains the bandwidth and latency information that's tied to the
CXL switch that the data table has been read from. The extracted
values are stored to the cxl_dport correlated by the port_id
depending on the SSLBIS entry.

Coherent Device Attribute Table 1.03 2.1 Switched Scoped Latency
and Bandwidth Information Structure (DSLBIS)

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Link: https://lore.kernel.org/r/170319620635.2212653.5194389158785365150.stgit@djiang5-mobl3
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/cxl/core/cdat.c
drivers/cxl/cxl.h
drivers/cxl/port.c

index 97d8ef8848c66df2d968f2b0084808c4f8051a0c..b3ab47d250e16969571ddd4e4d4384489151ca21 100644 (file)
@@ -187,4 +187,102 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
 }
 EXPORT_SYMBOL_NS_GPL(cxl_endpoint_parse_cdat, CXL);
 
+static int cdat_sslbis_handler(union acpi_subtable_headers *header, void *arg,
+                              const unsigned long end)
+{
+       struct acpi_cdat_sslbis *sslbis;
+       int size = sizeof(header->cdat) + sizeof(*sslbis);
+       struct cxl_port *port = arg;
+       struct device *dev = &port->dev;
+       struct acpi_cdat_sslbe *entry;
+       int remain, entries, i;
+       u16 len;
+
+       len = le16_to_cpu((__force __le16)header->cdat.length);
+       remain = len - size;
+       if (!remain || remain % sizeof(*entry) ||
+           (unsigned long)header + len > end) {
+               dev_warn(dev, "Malformed SSLBIS table length: (%u)\n", len);
+               return -EINVAL;
+       }
+
+       /* Skip common header */
+       sslbis = (struct acpi_cdat_sslbis *)((unsigned long)header +
+                                            sizeof(header->cdat));
+
+       /* Unrecognized data type, we can skip */
+       if (sslbis->data_type > ACPI_HMAT_WRITE_BANDWIDTH)
+               return 0;
+
+       entries = remain / sizeof(*entry);
+       entry = (struct acpi_cdat_sslbe *)((unsigned long)header + sizeof(*sslbis));
+
+       for (i = 0; i < entries; i++) {
+               u16 x = le16_to_cpu((__force __le16)entry->portx_id);
+               u16 y = le16_to_cpu((__force __le16)entry->porty_id);
+               __le64 le_base;
+               __le16 le_val;
+               struct cxl_dport *dport;
+               unsigned long index;
+               u16 dsp_id;
+               u64 val;
+
+               switch (x) {
+               case ACPI_CDAT_SSLBIS_US_PORT:
+                       dsp_id = y;
+                       break;
+               case ACPI_CDAT_SSLBIS_ANY_PORT:
+                       switch (y) {
+                       case ACPI_CDAT_SSLBIS_US_PORT:
+                               dsp_id = x;
+                               break;
+                       case ACPI_CDAT_SSLBIS_ANY_PORT:
+                               dsp_id = ACPI_CDAT_SSLBIS_ANY_PORT;
+                               break;
+                       default:
+                               dsp_id = y;
+                               break;
+                       }
+                       break;
+               default:
+                       dsp_id = x;
+                       break;
+               }
+
+               le_base = (__force __le64)sslbis->entry_base_unit;
+               le_val = (__force __le16)entry->latency_or_bandwidth;
+
+               if (check_mul_overflow(le64_to_cpu(le_base),
+                                      le16_to_cpu(le_val), &val))
+                       dev_warn(dev, "SSLBIS value overflowed!\n");
+
+               xa_for_each(&port->dports, index, dport) {
+                       if (dsp_id == ACPI_CDAT_SSLBIS_ANY_PORT ||
+                           dsp_id == dport->port_id)
+                               cxl_access_coordinate_set(&dport->sw_coord,
+                                                         sslbis->data_type,
+                                                         val);
+               }
+
+               entry++;
+       }
+
+       return 0;
+}
+
+void cxl_switch_parse_cdat(struct cxl_port *port)
+{
+       int rc;
+
+       if (!port->cdat.table)
+               return;
+
+       rc = cdat_table_parse(ACPI_CDAT_TYPE_SSLBIS, cdat_sslbis_handler,
+                             port, port->cdat.table);
+       rc = cdat_table_parse_output(rc);
+       if (rc)
+               dev_dbg(&port->dev, "Failed to parse SSLBIS: %d\n", rc);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_switch_parse_cdat, CXL);
+
 MODULE_IMPORT_NS(CXL);
index be3b5eda875c6d2a57dc0a6df3403d8e04c88ac5..22f664b9f4c63ef34dbc9fc9ce0eac76ea7d88b5 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/bitfield.h>
 #include <linux/bitops.h>
 #include <linux/log2.h>
+#include <linux/node.h>
 #include <linux/io.h>
 
 /**
@@ -634,6 +635,7 @@ struct cxl_rcrb_info {
  * @rch: Indicate whether this dport was enumerated in RCH or VH mode
  * @port: reference to cxl_port that contains this downstream port
  * @regs: Dport parsed register blocks
+ * @sw_coord: access coordinates (performance) for switch from CDAT
  */
 struct cxl_dport {
        struct device *dport_dev;
@@ -643,6 +645,7 @@ struct cxl_dport {
        bool rch;
        struct cxl_port *port;
        struct cxl_regs regs;
+       struct access_coordinate sw_coord;
 };
 
 /**
@@ -840,6 +843,7 @@ static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
 #endif
 
 void cxl_endpoint_parse_cdat(struct cxl_port *port);
+void cxl_switch_parse_cdat(struct cxl_port *port);
 
 /*
  * Unit test builds overrides this to __weak, find the 'strong' version
index a889c4e6cb2721b1245a772cea1b8069eae9347a..da3c3a08bd626991c51df75981578d0adcb0f5f9 100644 (file)
@@ -69,6 +69,8 @@ static int cxl_switch_port_probe(struct cxl_port *port)
        if (rc < 0)
                return rc;
 
+       cxl_switch_parse_cdat(port);
+
        cxlhdm = devm_cxl_setup_hdm(port, NULL);
        if (!IS_ERR(cxlhdm))
                return devm_cxl_enumerate_decoders(cxlhdm, NULL);