@@ -83,6 +83,23 @@
#define CDAT_SSLBIS_ENTRY_PORT_Y(entry, i) (((entry)[4 + (i) * 2] & 0xffff0000) >> 16)
#define CDAT_SSLBIS_ENTRY_LAT_OR_BW(entry, i) ((entry)[4 + (i) * 2 + 1] & 0x0000ffff)
+/**
+ * struct cxl_dsmas - host unmarshaled version of DSMAS data
+ *
+ * As defined in the Coherent Device Attribute Table (CDAT) specification this
+ * represents a single DSMAS entry in that table.
+ *
+ * @dpa_base: The lowest Device Physical Address associated with this DSMAD
+ * @length: Length in bytes of this DSMAD
+ * @non_volatile: If set, the memory region represents Non-Volatile memory
+ */
+struct cxl_dsmas {
+ u64 dpa_base;
+ u64 length;
+ /* Flags */
+ u8 non_volatile:1;
+};
+
/**
* struct cxl_cdat - CXL CDAT data
*
@@ -9,6 +9,8 @@
#include <linux/bitops.h>
#include <linux/io.h>
+#include "cdat.h"
+
/**
* DOC: cxl objects
*
@@ -343,6 +345,8 @@ struct cxl_nvdimm {
* @component_reg_phys: component register capability base address (optional)
* @dead: last ep has been removed, force port re-creation
* @depth: How deep this port is relative to the root. depth 0 is the root.
+ * @dsmas_ary: Array of DSMAS entries as parsed from the CDAT table
+ * @nr_dsmas: Number of entries in dsmas_ary
*/
struct cxl_port {
struct device dev;
@@ -354,6 +358,8 @@ struct cxl_port {
resource_size_t component_reg_phys;
bool dead;
unsigned int depth;
+ struct cxl_dsmas *dsmas_ary;
+ int nr_dsmas;
};
/**
@@ -73,6 +73,70 @@ static int set_decoders(struct device *dev, void *data)
return 0;
}
+static void parse_dsmas(struct cxl_port *port, struct cxl_dev_state *cxlds)
+{
+ struct device *dev = &port->dev;
+ struct cxl_dsmas *dsmas_ary = NULL;
+ u32 *data = cxlds->cdat.table;
+ int bytes_left = cxlds->cdat.length;
+ int nr_dsmas = 0;
+
+ if (!data) {
+ dev_info(dev, "No CDAT data available for DSMAS\n");
+ return;
+ }
+
+ /* Skip header */
+ data += CDAT_HEADER_LENGTH_DW;
+ bytes_left -= CDAT_HEADER_LENGTH_BYTES;
+
+ while (bytes_left > 0) {
+ u32 *cur_rec = data;
+ u8 type = FIELD_GET(CDAT_STRUCTURE_DW0_TYPE, cur_rec[0]);
+ u16 length = FIELD_GET(CDAT_STRUCTURE_DW0_LENGTH, cur_rec[0]);
+
+ if (type == CDAT_STRUCTURE_DW0_TYPE_DSMAS) {
+ struct cxl_dsmas *new_ary;
+ u8 flags;
+
+ new_ary = devm_krealloc(dev, dsmas_ary,
+ sizeof(*dsmas_ary) * (nr_dsmas + 1),
+ GFP_KERNEL);
+ if (!new_ary) {
+ dev_err(dev,
+ "Failed to allocate memory for DSMAS data (nr_dsmas %d)\n",
+ nr_dsmas);
+ return;
+ }
+ dsmas_ary = new_ary;
+
+ flags = FIELD_GET(CDAT_DSMAS_DW1_FLAGS, cur_rec[1]);
+
+ dsmas_ary[nr_dsmas].dpa_base = CDAT_DSMAS_DPA_OFFSET(cur_rec);
+ dsmas_ary[nr_dsmas].length = CDAT_DSMAS_DPA_LEN(cur_rec);
+ dsmas_ary[nr_dsmas].non_volatile = CDAT_DSMAS_NON_VOLATILE(flags);
+
+ dev_dbg(dev, "DSMAS %d: %llx:%llx %s\n",
+ nr_dsmas,
+ dsmas_ary[nr_dsmas].dpa_base,
+ dsmas_ary[nr_dsmas].dpa_base +
+ dsmas_ary[nr_dsmas].length,
+ (dsmas_ary[nr_dsmas].non_volatile ?
+ "Persistent" : "Volatile")
+ );
+
+ nr_dsmas++;
+ }
+
+ data += (length / sizeof(u32));
+ bytes_left -= length;
+ }
+
+ dev_dbg(dev, "Found %d DSMAS entries\n", nr_dsmas);
+ port->dsmas_ary = dsmas_ary;
+ port->nr_dsmas = nr_dsmas;
+}
+
static int cxl_port_probe(struct device *dev)
{
struct cxl_port *port = to_cxl_port(dev);
@@ -87,6 +151,7 @@ static int cxl_port_probe(struct device *dev)
rc = devm_add_action_or_reset(dev, schedule_detach, cxlmd);
if (rc)
return rc;
+ parse_dsmas(port, cxlmd->cxlds);
} else {
rc = devm_cxl_port_enumerate_dports(port);
if (rc < 0)