@@ -569,10 +569,10 @@ static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl)
*ctrl |= CXL_HDM_DECODER0_CTRL_COMMIT;
}
-static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl)
+static void cxld_set_coherence(struct cxl_decoder *cxld, u32 *ctrl)
{
u32p_replace_bits(ctrl,
- !!(cxld->target_type == CXL_DECODER_EXPANDER),
+ !!(cxld->coherence == CXL_DECODER_HOSTONLYCOH),
CXL_HDM_DECODER0_CTRL_HOSTONLY);
}
@@ -667,7 +667,7 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
/* common decoder settings */
ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
cxld_set_interleave(cxld, &ctrl);
- cxld_set_type(cxld, &ctrl);
+ cxld_set_coherence(cxld, &ctrl);
base = cxld->hpa_range.start;
size = range_len(&cxld->hpa_range);
@@ -846,10 +846,13 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
cxld->flags |= CXL_DECODER_F_ENABLE;
if (ctrl & CXL_HDM_DECODER0_CTRL_LOCK)
cxld->flags |= CXL_DECODER_F_LOCK;
- if (FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl))
+ if (FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl)) {
cxld->target_type = CXL_DECODER_EXPANDER;
- else
+ cxld->coherence = CXL_DECODER_HOSTONLYCOH;
+ } else {
cxld->target_type = CXL_DECODER_ACCEL;
+ cxld->coherence = CXL_DECODER_DEVCOH;
+ }
guard(rwsem_write)(&cxl_region_rwsem);
if (cxld->id != cxl_num_decoders_committed(port)) {
@@ -879,13 +882,18 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
cxld->target_type = CXL_DECODER_EXPANDER;
else
cxld->target_type = CXL_DECODER_ACCEL;
+ if (cxlds->coherence == CXL_DEVCOH_HOSTONLY)
+ cxld->coherence = CXL_DECODER_HOSTONLYCOH;
+ else
+ cxld->coherence = CXL_DECODER_DEVCOH;
} else {
- /* To be overridden by region type at commit time */
+ /* To be overridden by region type/coherence at commit time */
cxld->target_type = CXL_DECODER_EXPANDER;
+ cxld->coherence = CXL_DECODER_HOSTONLYCOH;
}
if (!FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl) &&
- cxld->target_type == CXL_DECODER_EXPANDER) {
+ cxld->coherence == CXL_DECODER_HOSTONLYCOH) {
ctrl |= CXL_HDM_DECODER0_CTRL_HOSTONLY;
writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(which));
}
@@ -1450,6 +1450,7 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev)
mds->cxlds.reg_map.host = dev;
mds->cxlds.reg_map.resource = CXL_RESOURCE_NONE;
mds->cxlds.type = CXL_DEVTYPE_CLASSMEM;
+ mds->cxlds.coherence = CXL_DEVCOH_HOSTONLY;
mds->ram_perf.qos_class = CXL_QOS_CLASS_INVALID;
mds->pmem_perf.qos_class = CXL_QOS_CLASS_INVALID;
@@ -1769,6 +1769,7 @@ static int cxl_decoder_init(struct cxl_port *port, struct cxl_decoder *cxld)
cxld->interleave_ways = 1;
cxld->interleave_granularity = PAGE_SIZE;
cxld->target_type = CXL_DECODER_EXPANDER;
+ cxld->coherence = CXL_DECODER_HOSTONLYCOH;
cxld->hpa_range = (struct range) {
.start = 0,
.end = -1,
@@ -1005,9 +1005,10 @@ static int cxl_rr_alloc_decoder(struct cxl_port *port, struct cxl_region *cxlr,
}
/*
- * Endpoints should already match the region type, but backstop that
- * assumption with an assertion. Switch-decoders change mapping-type
- * based on what is mapped when they are assigned to a region.
+ * Endpoints should already match the region type/coherence, but
+ * backstop that assumption with an assertion. Switch-decoders change
+ * mapping-type/coherence based on what is mapped when they are assigned
+ * to a region.
*/
dev_WARN_ONCE(&cxlr->dev,
port == cxled_to_port(cxled) &&
@@ -1016,6 +1017,13 @@ static int cxl_rr_alloc_decoder(struct cxl_port *port, struct cxl_region *cxlr,
dev_name(&cxled_to_memdev(cxled)->dev),
dev_name(&cxld->dev), cxld->target_type, cxlr->type);
cxld->target_type = cxlr->type;
+ dev_WARN_ONCE(&cxlr->dev,
+ port == cxled_to_port(cxled) &&
+ cxld->coherence != cxlr->coherence,
+ "%s:%s mismatch decoder coherence %d -> %d\n",
+ dev_name(&cxled_to_memdev(cxled)->dev),
+ dev_name(&cxld->dev), cxld->coherence, cxlr->coherence);
+ cxld->coherence = cxlr->coherence;
cxl_rr->decoder = cxld;
return 0;
}
@@ -1925,6 +1933,29 @@ static int cxl_region_attach(struct cxl_region *cxlr,
return -ENXIO;
}
+ /* Set the coherence of region to that of the first endpoint */
+ if (cxlr->coherence == CXL_DECODER_INVALIDCOH) {
+ unsigned long flags = cxlrd->cxlsd.cxld.flags;
+ enum cxl_decoder_coherence coherence = cxled->cxld.coherence;
+
+ cxlr->coherence = coherence;
+ if ((coherence == CXL_DECODER_HOSTONLYCOH &&
+ !(flags & CXL_DECODER_F_HOSTONLYCOH)) ||
+ (coherence == CXL_DECODER_DEVCOH &&
+ !(flags & CXL_DECODER_F_DEVCOH))) {
+ dev_dbg(&cxlr->dev,
+"%s:%s endpoint coherence: %d isn't supported by root decoder: %#lx\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
+ coherence, flags);
+ return -ENXIO;
+ }
+ } else if (cxled->cxld.coherence != cxlr->coherence) {
+ dev_dbg(&cxlr->dev, "%s:%s coherence mismatch: %d vs %d\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
+ cxled->cxld.coherence, cxlr->coherence);
+ return -ENXIO;
+ }
+
if (!cxled->dpa_res) {
dev_dbg(&cxlr->dev, "%s:%s: missing DPA allocation.\n",
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
@@ -328,6 +328,12 @@ enum cxl_decoder_type {
CXL_DECODER_EXPANDER = 3,
};
+enum cxl_decoder_coherence {
+ CXL_DECODER_INVALIDCOH,
+ CXL_DECODER_HOSTONLYCOH,
+ CXL_DECODER_DEVCOH,
+};
+
/*
* Current specification goes up to 8, double that seems a reasonable
* software max for the foreseeable future
@@ -344,6 +350,7 @@ enum cxl_decoder_type {
* @interleave_ways: number of cxl_dports in this decode
* @interleave_granularity: data stride per dport
* @target_type: accelerator vs expander (type2 vs type3) selector
+ * @coherence: host only vs device coherence selector
* @region: currently assigned region for this decoder
* @flags: memory type capabilities and locking
* @commit: device/decoder-type specific callback to commit settings to hw
@@ -356,6 +363,7 @@ struct cxl_decoder {
int interleave_ways;
int interleave_granularity;
enum cxl_decoder_type target_type;
+ enum cxl_decoder_coherence coherence;
struct cxl_region *region;
unsigned long flags;
int (*commit)(struct cxl_decoder *cxld);
@@ -517,6 +525,7 @@ struct cxl_region_params {
* @id: This region's id. Id is globally unique across all regions
* @mode: Endpoint decoder allocation / access mode
* @type: Endpoint decoder target type
+ * @coherence: Endpoint decoder coherence
* @cxl_nvb: nvdimm bridge for coordinating @cxlr_pmem setup / shutdown
* @cxlr_pmem: (for pmem regions) cached copy of the nvdimm bridge
* @flags: Region state flags
@@ -530,6 +539,7 @@ struct cxl_region {
int id;
enum cxl_decoder_mode mode;
enum cxl_decoder_type type;
+ enum cxl_decoder_coherence coherence;
struct cxl_nvdimm_bridge *cxl_nvb;
struct cxl_pmem_region *cxlr_pmem;
unsigned long flags;
@@ -394,6 +394,16 @@ enum cxl_devtype {
CXL_DEVTYPE_CLASSMEM,
};
+/*
+ * enum cxl_devcoherence - the coherence of the cxl device
+ * @CXL_DEVCOH_DEV - HDM-D (type 2) or HDM-DB (type 2/3)
+ * @CXL_DEVCOH_HOSTONLY - HDM-H (type 3)
+ */
+enum cxl_devcoherence {
+ CXL_DEVCOH_DEV,
+ CXL_DEVCOH_HOSTONLY,
+};
+
/**
* struct cxl_dpa_perf - DPA performance property entry
* @dpa_range: range for DPA address
@@ -427,6 +437,7 @@ struct cxl_dpa_perf {
* @ram_res: Active Volatile memory capacity configuration
* @serial: PCIe Device Serial Number
* @type: Generic Memory Class device or Vendor Specific Memory device
+ * @coherence: Device or Host only coherence
* @cxl_mbox: CXL mailbox context
*/
struct cxl_dev_state {
@@ -442,6 +453,7 @@ struct cxl_dev_state {
struct resource ram_res;
u64 serial;
enum cxl_devtype type;
+ enum cxl_devcoherence coherence;
struct cxl_mailbox cxl_mbox;
};