Linux CXL
 help / color / mirror / Atom feed
From: Ben Cheatham <Benjamin.Cheatham@amd.com>
To: <linux-cxl@vger.kernel.org>, <dave@stgolabs.net>,
	<jic23@kernel.org>, <dave.jiang@intel.com>,
	<alison.schofield@intel.com>
Cc: <linux-iommu@vger.kernel.org>, <benjamin.cheatham@amd.com>,
	<terry.bowman@amd.com>, <robert.richter@amd.com>
Subject: [PATCH 09/15] cxl/core: Add Cache ID verification
Date: Wed, 23 Sep 2026 12:33:36 -0500	[thread overview]
Message-ID: <20260923173342.5584-10-Benjamin.Cheatham@amd.com> (raw)
In-Reply-To: <20260923173342.5584-1-Benjamin.Cheatham@amd.com>

Check if system firmware has pre-programmed a CXL.cache device's cache
id as part of cxl_cache::probe(). A cache id is required when multiple
CXL.cache devices are present under a host bridge, so fail probe if the
id has not been programmed and another device is present.

Also add @hdmd to struct cxl_dev_state for endpoint drivers to indicate
whether their device is using HDM-D flows. This is required for
correctly validating cache id programming.

Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
---
 drivers/cxl/cache.c         |  94 ++++++++++++++++++++-
 drivers/cxl/core/cache.c    | 158 ++++++++++++++++++++++++++++++++++++
 drivers/cxl/core/cachedev.c |   1 +
 drivers/cxl/core/port.c     |   1 +
 drivers/cxl/cxl.h           |  16 ++++
 drivers/cxl/cxlcache.h      |   6 ++
 include/cxl/cxl.h           |   4 +
 7 files changed, 278 insertions(+), 2 deletions(-)

diff --git a/drivers/cxl/cache.c b/drivers/cxl/cache.c
index dfefd301696a..6c098010149a 100644
--- a/drivers/cxl/cache.c
+++ b/drivers/cxl/cache.c
@@ -84,6 +84,90 @@ static int cxl_endpoint_cache_enabled(struct cxl_port *endpoint)
 	return 0;
 }
 
+static struct cxl_port *find_host_bridge(struct cxl_port *endpoint)
+{
+	struct cxl_port *parent = endpoint->parent_dport->port;
+	struct cxl_port *hb = endpoint;
+
+	if (is_cxl_root(parent))
+		return NULL;
+
+	while (!is_cxl_root(parent)) {
+		hb = parent;
+		parent = hb->parent_dport->port;
+	}
+
+	return hb;
+}
+
+static int get_num_cachedevs_present(struct cxl_port *host_bridge, u32 *num)
+{
+	u32 num_cachedevs = 0;
+	unsigned long index;
+	struct cxl_ep *ep;
+
+	xa_for_each(&host_bridge->endpoints, index, ep) {
+		if (is_cxl_cachedev(ep->ep))
+			num_cachedevs++;
+	}
+
+	*num = num_cachedevs;
+	return 0;
+}
+
+static void deprogram_cache_id(void *_cxlcd)
+{
+	struct cxl_cachedev *cxlcd = _cxlcd;
+	struct cxl_port *hb = find_host_bridge(cxlcd->endpoint);
+
+	if (cxlcd->cache_id == CXL_CACHE_ID_NO_ID)
+		return;
+
+	guard(device)(&hb->dev);
+	cxl_free_cache_id(cxlcd);
+}
+
+static int program_cache_id(struct cxl_cachedev *cxlcd)
+{
+	struct cxl_port *endpoint = cxlcd->endpoint;
+	struct cxl_port *hb = find_host_bridge(endpoint);
+	struct device *dev = &cxlcd->dev;
+	u32 num_cachedevs;
+	int rc;
+
+	if (!hb)
+		return -ENODEV;
+
+	/*
+	 * Cache ids are unique to the host bridge, so synchronize programming
+	 * around the bridge's device lock
+	 */
+
+	guard(device)(&hb->dev);
+	rc = get_num_cachedevs_present(hb, &num_cachedevs);
+	if (rc)
+		return rc;
+
+	if (!cxl_cache_id_supported(cxlcd))
+		return num_cachedevs > 1 ? -ENXIO : 0;
+
+	rc = cxl_cachedev_validate_cache_id(cxlcd);
+	if (rc && num_cachedevs > 1) {
+		dev_err(dev,
+			"Cache id not programmed with other CXL.cache devices present: %d\n",
+			rc);
+		return rc;
+	} else if (rc) {
+		cxlcd->cache_id = 0;
+	}
+
+	rc = cxl_allocate_cache_id(cxlcd);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
 /**
  * devm_cxl_add_cachedev - Add a CXL cache device
  * @cxlds: CXL device state to associate with the cachedev
@@ -142,10 +226,16 @@ static int cxl_cache_probe(struct device *dev)
 	}
 
 	rc = cxl_endpoint_cache_enabled(cxlcd->endpoint);
-	if (rc)
+	if (rc) {
 		dev_err(dev, "CXL.cache not enabled on parent port(s)");
+		return rc;
+	}
+
+	rc = program_cache_id(cxlcd);
+	if (rc)
+		return rc;
 
-	return rc;
+	return devm_add_action_or_reset(dev, deprogram_cache_id, cxlcd);
 }
 
 static struct cxl_driver cxl_cache_driver = {
diff --git a/drivers/cxl/core/cache.c b/drivers/cxl/core/cache.c
index 17df0e603b68..be570d3dc609 100644
--- a/drivers/cxl/core/cache.c
+++ b/drivers/cxl/core/cache.c
@@ -39,3 +39,161 @@ int cxl_dport_map_cache_id_dc(struct cxl_dport *dport)
 	return cxl_map_component_regs(map, &dport->regs.component,
 				      BIT(CXL_CM_CAP_CAP_ID_CACHE_ID_DC));
 }
+
+static int cache_decoder_committed(struct cxl_dport *dport)
+{
+	u32 cap, stat;
+
+	cap = readl(dport->regs.ciddc + CXL_CACHE_ID_DC_CAP_OFFSET);
+	if (!FIELD_GET(CXL_CACHE_ID_DC_CAP_COMMIT_REQ, cap))
+		return -ENXIO;
+
+	stat = readl(dport->regs.ciddc + CXL_CACHE_ID_DC_STATUS_OFFSET);
+	return FIELD_GET(CXL_CACHE_ID_DC_STATUS_COMMITTED, stat);
+}
+
+static int cache_decoder_get_id(struct cxl_dport *dport)
+{
+	u32 ctrl;
+
+	ctrl = readl(dport->regs.ciddc + CXL_CACHE_ID_DC_CTRL_OFFSET);
+	if (FIELD_GET(CXL_CACHE_ID_DC_CTRL_HDMD_PRESENT, ctrl))
+		 return FIELD_GET(CXL_CACHE_ID_DC_CTRL_HDMD_ID, ctrl);
+
+	return FIELD_GET(CXL_CACHE_ID_DC_CTRL_LOCAL_ID, ctrl);
+}
+
+static bool cache_decoder_valid(struct cxl_dport *dport, int id, bool endpoint)
+{
+	struct pci_dev *pdev = to_pci_dev(dport->dport_dev);
+	bool flit_256b;
+	u32 ctrl;
+
+	if (!dev_is_pci(dport->dport_dev))
+		return false;
+
+	flit_256b = cxl_pci_flit_256(pdev);
+	if (id && !flit_256b)
+		return false;
+
+	ctrl = readl(dport->regs.ciddc + CXL_CACHE_ID_DC_CTRL_OFFSET);
+	if (endpoint || !flit_256b) {
+		if (!FIELD_GET(CXL_CACHE_ID_DC_CTRL_ASGN_ID, ctrl))
+			return false;
+	} else if (!FIELD_GET(CXL_CACHE_ID_DC_CTRL_FWD_ID, ctrl)) {
+		return false;
+	}
+
+	return cache_decoder_get_id(dport) == id;
+}
+
+static int cache_idrt_committed(struct cxl_port *port)
+{
+	u32 cap, stat;
+
+	cap = readl(port->regs.cidrt + CXL_CACHE_ID_RT_CAP_OFFSET);
+	if (!FIELD_GET(CXL_CACHE_ID_RT_CAP_COMMIT_REQ, cap))
+		return true;
+
+	stat = readl(port->regs.cidrt + CXL_CACHE_ID_RT_STATUS_OFFSET);
+	return FIELD_GET(CXL_CACHE_ID_RT_STATUS_COMMITTED, stat);
+}
+
+static int cache_idrt_entry_valid(struct cxl_port *port, int id)
+{
+	u16 entry;
+	u32 cap;
+
+	cap = readl(port->regs.cidrt + CXL_CACHE_ID_RT_CAP_OFFSET);
+	if (id >= FIELD_GET(CXL_CACHE_ID_RT_CAP_TARGET_CNT, cap))
+		return false;
+
+	entry = readw(port->regs.cidrt + CXL_CACHE_ID_RT_TARGETN_OFFSET(id));
+	return FIELD_GET(CXL_CACHE_ID_RT_TARGETN_VALID, entry);
+}
+
+static struct ida *find_cache_id_ida(struct cxl_port *port)
+{
+	struct cxl_port *parent = parent_port_of(port);
+
+	while (parent) {
+		if (is_cxl_root(parent))
+			return &port->cache_ida;
+
+		port = parent;
+		parent = parent_port_of(port);
+	}
+
+	return NULL;
+}
+
+int cxl_allocate_cache_id(struct cxl_cachedev *cxlcd)
+{
+	struct ida *ida = find_cache_id_ida(cxlcd->endpoint);
+	int id;
+
+	if (!ida)
+		return -ENOSPC;
+
+	id = ida_alloc_range(ida, cxlcd->cache_id, cxlcd->cache_id, GFP_KERNEL);
+	if (id != cxlcd->cache_id)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_allocate_cache_id, "cxl_cache");
+
+void cxl_free_cache_id(struct cxl_cachedev *cxlcd)
+{
+	struct ida *ida = find_cache_id_ida(cxlcd->endpoint);
+
+	if (ida)
+		ida_free(ida, cxlcd->cache_id);
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_free_cache_id, "cxl_cache");
+
+bool cxl_cache_id_supported(struct cxl_cachedev *cxlcd)
+{
+	struct cxl_dport *dport = cxlcd->endpoint->parent_dport;
+	struct cxl_port *port = dport->port;
+
+	for (; !is_cxl_root(port);
+	     dport = port->parent_dport, port = dport->port) {
+		if (!port->regs.cidrt || !dport->regs.ciddc)
+			return false;
+	}
+
+	return true;
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_cache_id_supported, "cxl_cache");
+
+int cxl_cachedev_validate_cache_id(struct cxl_cachedev *cxlcd)
+{
+	struct cxl_dport *dport = cxlcd->endpoint->parent_dport;
+	struct cxl_port *port = dport->port;
+	int id = CXL_CACHE_ID_NO_ID;
+	bool endpoint = true;
+
+	cxlcd->cache_id = CXL_CACHE_ID_NO_ID;
+	id = cache_decoder_get_id(dport);
+
+	while (!is_cxl_root(port)) {
+		if (!cache_decoder_valid(dport, id, endpoint) ||
+		    !cache_decoder_committed(dport))
+			return -EINVAL;
+
+		endpoint = false;
+
+		if (!cache_idrt_entry_valid(port, id) ||
+		    !cache_idrt_committed(port))
+			return -EINVAL;
+
+		dport = port->parent_dport;
+		port = dport->port;
+	}
+
+	cxlcd->cache_id = id;
+	return 0;
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_cachedev_validate_cache_id, "cxl_cache");
+>>>>>>> conflict 1 of 1 ends
diff --git a/drivers/cxl/core/cachedev.c b/drivers/cxl/core/cachedev.c
index 2bd69133c631..38f0adf2b197 100644
--- a/drivers/cxl/core/cachedev.c
+++ b/drivers/cxl/core/cachedev.c
@@ -72,6 +72,7 @@ static struct cxl_cachedev *cxl_cachedev_alloc(struct cxl_dev_state *cxlds)
 	cxlcd->id = rc;
 	cxlcd->depth = -1;
 	cxlcd->endpoint = ERR_PTR(-ENODEV);
+	cxlcd->cache_id = CXL_CACHE_ID_NO_ID;
 
 	dev = &cxlcd->dev;
 	device_initialize(dev);
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 6da11d28bb5e..047801faa020 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -740,6 +740,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
 		dev->parent = uport_dev;
 
 	ida_init(&port->decoder_ida);
+	ida_init(&port->cache_ida);
 	port->hdm_end = -1;
 	port->commit_end = -1;
 	xa_init(&port->dports);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 9f0c1a39aaeb..ed6b56a269ee 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -229,9 +229,23 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw)
 /* CXL 4.0 8.2.4.28.1 CXL Cache ID Route Table Capability Structure */
 #define CXL_CACHE_ID_RT_CAP_OFFSET 0x0
 #define   CXL_CACHE_ID_RT_CAP_TARGET_CNT GENMASK(4, 0)
+#define   CXL_CACHE_ID_RT_CAP_COMMIT_REQ BIT(16)
+#define CXL_CACHE_ID_RT_STATUS_OFFSET 0x8
+#define   CXL_CACHE_ID_RT_STATUS_COMMITTED BIT(0)
 #define CXL_CACHE_ID_RT_TARGETN_OFFSET(n) (0x10 + (2 * (n)))
+#define   CXL_CACHE_ID_RT_TARGETN_VALID BIT(0)
 
 /* CXL 4.0 8.2.4.29.1 CXL Cache ID Decoder Capability Structure */
+#define CXL_CACHE_ID_DC_CAP_OFFSET 0x0
+#define   CXL_CACHE_ID_DC_CAP_COMMIT_REQ BIT(0)
+#define CXL_CACHE_ID_DC_CTRL_OFFSET 0x4
+#define   CXL_CACHE_ID_DC_CTRL_FWD_ID BIT(0)
+#define   CXL_CACHE_ID_DC_CTRL_ASGN_ID BIT(1)
+#define   CXL_CACHE_ID_DC_CTRL_HDMD_PRESENT BIT(2)
+#define   CXL_CACHE_ID_DC_CTRL_HDMD_ID GENMASK(11, 8)
+#define   CXL_CACHE_ID_DC_CTRL_LOCAL_ID GENMASK(19, 16)
+#define CXL_CACHE_ID_DC_STATUS_OFFSET 0x8
+#define   CXL_CACHE_ID_DC_STATUS_COMMITTED BIT(0)
 #define CXL_CACHE_ID_DC_CAPABILITY_LENGTH 0xC
 
 void cxl_probe_component_regs(struct device *dev, void __iomem *base,
@@ -578,6 +592,7 @@ struct cxl_dax_region {
  * @cdat_available: Should a CDAT attribute be available in sysfs
  * @pci_latency: Upstream latency in picoseconds
  * @component_reg_phys: Physical address of component register
+ * @cache_ida: ida used for cache id allocations on host bridges
  */
 struct cxl_port {
 	struct device dev;
@@ -603,6 +618,7 @@ struct cxl_port {
 	bool cdat_available;
 	long pci_latency;
 	resource_size_t component_reg_phys;
+	struct ida cache_ida;
 };
 
 struct cxl_root;
diff --git a/drivers/cxl/cxlcache.h b/drivers/cxl/cxlcache.h
index e9a4567b7f2c..1e4a1e61ad7b 100644
--- a/drivers/cxl/cxlcache.h
+++ b/drivers/cxl/cxlcache.h
@@ -11,6 +11,7 @@
  * @cxlds: device state backing this device
  * @detach_work: active cachedev lost a port in its ancestry
  * @endpoint: connection to the CXL port topology for this device
+ * @cache_id: id used for CXL cache routing capabilities
  * @id: id number of this cachedev instance
  * @depth: endpoint port depth in hierarchy
  */
@@ -19,6 +20,7 @@ struct cxl_cachedev {
 	struct cxl_dev_state *cxlds;
 	struct work_struct detach_work;
 	struct cxl_port *endpoint;
+	int cache_id;
 	int id;
 	int depth;
 };
@@ -39,4 +41,8 @@ struct cxl_cachedev *__devm_cxl_add_cachedev(struct cxl_dev_state *cxlds,
 int cxl_accel_read_cache_info(struct cxl_dev_state *cxlds);
 void cxl_clear_cache_enable(struct cxl_dev_state *cxlds);
 int devm_cxl_enable_cache(struct device *host, struct cxl_dev_state *cxlds);
+bool cxl_cache_id_supported(struct cxl_cachedev *cxlcd);
+int cxl_allocate_cache_id(struct cxl_cachedev *cxlcd);
+void cxl_free_cache_id(struct cxl_cachedev *cxlcd);
+int cxl_cachedev_validate_cache_id(struct cxl_cachedev *cxlcd);
 #endif
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 7a5ed64904a6..b99a9727e284 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -155,6 +155,8 @@ struct cxl_dpa_partition {
 
 #define CXL_NR_PARTITIONS_MAX 2
 
+#define CXL_CACHE_ID_NO_ID (-1)
+ 
 /**
  * struct cxl_cache_state - CXL cache device state for use by external drivers
  * @size: Size of device's cache
@@ -182,6 +184,7 @@ struct cxl_cache_state {
  * @cxlmd: The device representing the CXL.mem capabilities of @dev
  * @cxlcd: The device representing the CXL.cache capabilities of @dev
  * @cstate: CXL.cache information for @dev
+ * @hdmd: Whether the device is using HDM-D flows for CXL.cache
  * @reg_map: component and ras register mapping parameters
  * @regs: Parsed register blocks
  * @cxl_dvsec: Offset to the PCIe device DVSEC
@@ -201,6 +204,7 @@ struct cxl_dev_state {
 	struct cxl_memdev *cxlmd;
 	struct cxl_cachedev *cxlcd;
  	struct cxl_cache_state cstate;
+	bool hdmd;
 
 	/* private for Type2 drivers */
 	struct cxl_register_map reg_map;
-- 
2.53.0


  parent reply	other threads:[~2026-09-23 17:33 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-23 17:33 [PATCH 00/15] Add initial CXL.cache support Ben Cheatham
2026-09-23 17:33 ` [PATCH 01/15] cxl/core: Add CXL.cache device struct Ben Cheatham
2026-09-23 17:41   ` sashiko-bot
2026-09-23 17:33 ` [PATCH 02/15] cxl/cache: Add cxl_cache driver Ben Cheatham
2026-09-23 17:49   ` sashiko-bot
2026-09-23 17:33 ` [PATCH 03/15] cxl/core: Change cxl_ep_load() to use device pointer parameter Ben Cheatham
2026-09-23 17:33 ` [PATCH 04/15] cxl/core: Update devm_cxl_enumerate_ports() for cxl_cachedevs Ben Cheatham
2026-09-23 17:33 ` [PATCH 05/15] cxl/port: Split endpoint port probe on device type Ben Cheatham
2026-09-23 17:46   ` sashiko-bot
2026-09-23 17:33 ` [PATCH 06/15] cxl/core: Update devm_cxl_add_endpoint() for cxl_cachedevs Ben Cheatham
2026-09-23 17:51   ` sashiko-bot
2026-09-23 17:33 ` [PATCH 07/15] cxl/cache: Verify port hierarchy has CXL.cache enabled Ben Cheatham
2026-09-23 17:33 ` [PATCH 08/15] cxl/core, cache: Add Cache ID register probing and init Ben Cheatham
2026-09-23 17:46   ` sashiko-bot
2026-09-23 17:33 ` Ben Cheatham [this message]
2026-09-23 17:49   ` [PATCH 09/15] cxl/core: Add Cache ID verification sashiko-bot
2026-09-23 17:33 ` [PATCH 10/15] cxl/core: Add Cache ID allocation Ben Cheatham
2026-09-23 17:49   ` sashiko-bot
2026-09-23 17:33 ` [PATCH 11/15] cxl/core: Add support for HDM-D cache id programming Ben Cheatham
2026-09-23 17:51   ` sashiko-bot
2026-09-23 17:33 ` [PATCH 12/15] cxl/cache: Add snoop filter creation and set up Ben Cheatham
2026-09-23 17:50   ` sashiko-bot
2026-09-23 17:33 ` [PATCH 13/15] cxl/cache: Add snoop filter allocation Ben Cheatham
2026-09-23 17:58   ` sashiko-bot
2026-09-23 17:33 ` [PATCH 14/15] iommu, cxl: Configure IOMMU for CXL.cache Ben Cheatham
2026-09-23 17:57   ` sashiko-bot
2026-09-23 17:33 ` [PATCH 15/15] cxl/cache: Enable CXL.cache on successful probe Ben Cheatham
2026-09-23 17:35 ` [PATCH 00/15] Add initial CXL.cache support Cheatham, Benjamin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260923173342.5584-10-Benjamin.Cheatham@amd.com \
    --to=benjamin.cheatham@amd.com \
    --cc=alison.schofield@intel.com \
    --cc=dave.jiang@intel.com \
    --cc=dave@stgolabs.net \
    --cc=jic23@kernel.org \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-iommu@vger.kernel.org \
    --cc=robert.richter@amd.com \
    --cc=terry.bowman@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox