[PATCH 02/11] NTB: epf: Parse a versioned packed MW layout

Koichiro Den <[email protected]> Tue, 4 Aug 2026 03:04:29 +0900
Newsgroups dev.linux.lists.ntb,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci
Message-ID <[email protected]>
The legacy control layout cannot describe multiple logical MWs sharing
one BAR. Treat a zero version as legacy; version 1 describes all MWs as
one packed group using a BAR number and aggregate size. Derive the
equal-sized logical MWs from the existing MW count.

Signed-off-by: Koichiro Den <[email protected]>
---
 drivers/ntb/hw/epf/ntb_hw_epf.c | 102 ++++++++++++++++++++++++++++----
 1 file changed, 90 insertions(+), 12 deletions(-)

diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c
index c47607d4f8a7..46f37c2dea7b 100644
--- a/drivers/ntb/hw/epf/ntb_hw_epf.c
+++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
@@ -31,18 +31,22 @@
 #define NTB_EPF_LINK_STATUS	0x0A
 #define LINK_STATUS_UP		BIT(0)
 
-#define NTB_EPF_TOPOLOGY	0x0C
+#define NTB_EPF_CTRL_VERSION	0x0C
+#define NTB_EPF_CTRL_V1		1
 #define NTB_EPF_LOWER_ADDR	0x10
 #define NTB_EPF_UPPER_ADDR	0x14
 #define NTB_EPF_LOWER_SIZE	0x18
 #define NTB_EPF_UPPER_SIZE	0x1C
 #define NTB_EPF_MW_COUNT	0x20
-#define NTB_EPF_MW1_OFFSET	0x24
+#define NTB_EPF_MW1_OFFSET	0x24	/* v0 only */
 #define NTB_EPF_SPAD_OFFSET	0x28
 #define NTB_EPF_SPAD_COUNT	0x2C
 #define NTB_EPF_DB_ENTRY_SIZE	0x30
 #define NTB_EPF_DB_DATA(n)	(0x34 + (n) * 4)
 #define NTB_EPF_DB_OFFSET(n)	(0xB4 + (n) * 4)
+/* v1 only */
+#define NTB_EPF_MW_BAR		0x134
+#define NTB_EPF_MW_GROUP_SIZE	0x138
 
 /*
  * Legacy doorbell slot layout when paired with pci-epf-*ntb:
@@ -88,7 +92,8 @@ enum epf_irq_slot {
 	EPF_IRQ_DB_START,
 };
 
-#define NTB_EPF_MAX_MW_COUNT	(NTB_BAR_NUM - BAR_MW1)
+#define NTB_EPF_LEGACY_MAX_MW_COUNT	(NTB_BAR_NUM - BAR_MW1)
+#define NTB_EPF_MAX_MW_COUNT		16
 
 struct ntb_epf_dev;
 
@@ -105,9 +110,12 @@ struct ntb_epf_dev {
 
 	const enum pci_barno *barno_map;
 
+	u32 ctrl_version;
 	unsigned int mw_count;
 	unsigned int spad_count;
 	unsigned int db_count;
+	u32 mw_bar;
+	u32 mw_size;
 
 	void __iomem *ctrl_reg;
 	void __iomem *db_reg;
@@ -171,6 +179,9 @@ static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx)
 		return -EINVAL;
 	}
 
+	if (ndev->ctrl_version >= NTB_EPF_CTRL_V1)
+		return ndev->mw_bar;
+
 	return ndev->barno_map[BAR_MW1 + idx];
 }
 
@@ -211,8 +222,37 @@ static int ntb_epf_mw_get_align(struct ntb_dev *ntb, int pidx, int idx,
 	if (size_align)
 		*size_align = 1;
 
-	if (size_max)
-		*size_max = pci_resource_len(ndev->ntb.pdev, bar);
+	if (size_max) {
+		if (ndev->ctrl_version >= NTB_EPF_CTRL_V1)
+			*size_max = ndev->mw_size;
+		else
+			*size_max = pci_resource_len(ndev->ntb.pdev, bar);
+	}
+
+	return 0;
+}
+
+static int ntb_epf_mw_get_trans_group(struct ntb_dev *ntb, int pidx, int idx,
+				      int *gidx, int *gcount)
+{
+	struct ntb_epf_dev *ndev = ntb_ndev(ntb);
+	struct device *dev = ndev->dev;
+
+	if (pidx != NTB_DEF_PEER_IDX) {
+		dev_err(dev, "Unsupported Peer ID %d\n", pidx);
+		return -EINVAL;
+	}
+
+	if (idx < 0 || idx >= ndev->mw_count) {
+		dev_err(dev, "Unsupported Memory Window index %d\n", idx);
+		return -EINVAL;
+	}
+
+	if (gidx)
+		*gidx = ndev->ctrl_version >= NTB_EPF_CTRL_V1 ? 0 : idx;
+	if (gcount)
+		*gcount = ndev->ctrl_version >= NTB_EPF_CTRL_V1 ?
+			  ndev->mw_count : 1;
 
 	return 0;
 }
@@ -520,21 +560,27 @@ static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
 				    phys_addr_t *base, resource_size_t *size)
 {
 	struct ntb_epf_dev *ndev = ntb_ndev(ntb);
-	u32 offset = 0;
+	resource_size_t offset = 0;
 	int bar;
 
-	if (idx == 0)
-		offset = readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET);
-
 	bar = ntb_epf_mw_to_bar(ndev, idx);
 	if (bar < 0)
 		return bar;
 
+	if (ndev->ctrl_version >= NTB_EPF_CTRL_V1)
+		offset = (resource_size_t)idx * ndev->mw_size;
+	else if (idx == 0)
+		offset = readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET);
+
 	if (base)
 		*base = pci_resource_start(ndev->ntb.pdev, bar) + offset;
 
-	if (size)
-		*size = pci_resource_len(ndev->ntb.pdev, bar) - offset;
+	if (size) {
+		if (ndev->ctrl_version >= NTB_EPF_CTRL_V1)
+			*size = ndev->mw_size;
+		else
+			*size = pci_resource_len(ndev->ntb.pdev, bar) - offset;
+	}
 
 	return 0;
 }
@@ -601,6 +647,7 @@ static const struct ntb_dev_ops ntb_epf_ops = {
 	.db_vector_count	= ntb_epf_db_vector_count,
 	.db_vector_mask		= ntb_epf_db_vector_mask,
 	.db_set_mask		= ntb_epf_db_set_mask,
+	.mw_get_trans_group	= ntb_epf_mw_get_trans_group,
 	.mw_set_trans		= ntb_epf_mw_set_trans,
 	.mw_clear_trans		= ntb_epf_mw_clear_trans,
 	.peer_mw_get_addr	= ntb_epf_peer_mw_get_addr,
@@ -629,14 +676,45 @@ static inline void ntb_epf_init_struct(struct ntb_epf_dev *ndev,
 static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
 {
 	struct device *dev = ndev->dev;
+	resource_size_t bar_len;
+	u32 group_size;
 	int ret;
 
+	ndev->ctrl_version = readl(ndev->ctrl_reg + NTB_EPF_CTRL_VERSION);
 	ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT);
-	if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
+
+	if (ndev->ctrl_version && ndev->ctrl_version != NTB_EPF_CTRL_V1) {
+		dev_err(dev, "Unsupported control region version: %u\n",
+			ndev->ctrl_version);
+		return -EINVAL;
+	}
+
+	if ((!ndev->ctrl_version &&
+	     ndev->mw_count > NTB_EPF_LEGACY_MAX_MW_COUNT) ||
+	    ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
 		dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
 		return -EINVAL;
 	}
 
+	if (ndev->ctrl_version >= NTB_EPF_CTRL_V1) {
+		ndev->mw_bar = readl(ndev->ctrl_reg + NTB_EPF_MW_BAR);
+		group_size = readl(ndev->ctrl_reg + NTB_EPF_MW_GROUP_SIZE);
+
+		if (ndev->mw_count < 2 || ndev->mw_bar > BAR_5 ||
+		    !group_size || group_size % ndev->mw_count) {
+			dev_err(dev, "Invalid packed MW layout\n");
+			return -EINVAL;
+		}
+
+		ndev->mw_size = group_size / ndev->mw_count;
+		bar_len = pci_resource_len(ndev->ntb.pdev, ndev->mw_bar);
+		if (!IS_ALIGNED(ndev->mw_size, SZ_4K) || group_size != bar_len) {
+			dev_err(dev, "Packed MW layout does not match BAR%u\n",
+				ndev->mw_bar);
+			return -EINVAL;
+		}
+	}
+
 	/* One Link interrupt and rest doorbell interrupt */
 	ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
 			       NTB_EPF_MAX_DB_COUNT + 1);
-- 
2.51.0