[PATCH 11/15] NTB: hw: epf: Parse control-layout version and DMA locator

Koichiro Den posted 15 patches 3 weeks, 4 days ago
[PATCH 11/15] NTB: hw: epf: Parse control-layout version and DMA locator
Posted by Koichiro Den 3 weeks, 4 days ago
pci-epf-vntb can now expose either the historical control layout or a
versioned extension that carries per-MW offset/size tuples and an
optional DMA locator.

Teach ntb_hw_epf to parse the control-layout version first, keep
accepting the legacy format, and use the explicit MW size information
when version 1 is present. Also parse the DMA locator and cache its BAR,
offset, size, ABI, and channel count for the follow-up enumeration step.

Finally, reserve the tail of the MSI/MSI-X vector allocation for the
exported DMA child so ntb_hw_epf only requests the link and doorbell
vectors it owns.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
 drivers/ntb/hw/epf/ntb_hw_epf.c | 112 +++++++++++++++++++++++++++++---
 1 file changed, 104 insertions(+), 8 deletions(-)

diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c
index d420699ff7d6..6b427577b1bd 100644
--- a/drivers/ntb/hw/epf/ntb_hw_epf.c
+++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
@@ -31,7 +31,14 @@
 #define NTB_EPF_LINK_STATUS	0x0A
 #define LINK_STATUS_UP		BIT(0)
 
-#define NTB_EPF_TOPOLOGY	0x0C
+/*
+ * 0x0C was historically NTB_EPF_TOPOLOGY, but neither ntb_hw_epf nor
+ * pci-epf-{v,}ntb ever consumed it. Reuse it as a control-layout version
+ * selector while keeping 0 as the legacy format.
+ */
+#define NTB_EPF_CTRL_VERSION	0x0C
+#define NTB_EPF_CTRL_VERSION_LEGACY	0
+#define NTB_EPF_CTRL_VERSION_V1	1
 #define NTB_EPF_LOWER_ADDR	0x10
 #define NTB_EPF_UPPER_ADDR	0x14
 #define NTB_EPF_LOWER_SIZE	0x18
@@ -39,6 +46,13 @@
 #define NTB_EPF_MW_COUNT	0x20
 #define NTB_EPF_MW1_OFFSET	0x24
 #define NTB_EPF_SPAD_OFFSET	0x28
+#define NTB_EPF_MW_OFFSET(n)	(0x134 + (n) * 4)
+#define NTB_EPF_MW_SIZE(n)	(0x144 + (n) * 4)
+#define NTB_EPF_DMA_ABI		0x154
+#define NTB_EPF_DMA_BAR		0x158
+#define NTB_EPF_DMA_OFFSET	0x15C
+#define NTB_EPF_DMA_SIZE	0x160
+#define NTB_EPF_DMA_NUM_CHANS	0x164
 #define NTB_EPF_SPAD_COUNT	0x2C
 #define NTB_EPF_DB_ENTRY_SIZE	0x30
 #define NTB_EPF_DB_DATA(n)	(0x34 + (n) * 4)
@@ -101,6 +115,15 @@ struct ntb_epf_dev {
 	unsigned int mw_count;
 	unsigned int spad_count;
 	unsigned int db_count;
+	u32 ctrl_version;
+	u32 dma_abi;
+	u32 dma_offset;
+	u32 dma_size;
+	u32 dma_num_chans;
+	u32 dma_irq_base;
+	u32 dma_irq_count;
+	enum pci_barno dma_bar;
+	bool dma_aux_avail;
 
 	void __iomem *ctrl_reg;
 	void __iomem *db_reg;
@@ -375,6 +398,21 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
 		argument &= ~MSIX_ENABLE;
 	}
 
+	if (irq >= msi_min + ndev->dma_irq_count) {
+		ndev->dma_aux_avail = true;
+
+		/*
+		 * Reserve the tail of the vector space for the exported DMA
+		 * child.  ntb_hw_epf only requests the prefix used for link and
+		 * doorbell events.
+		 */
+		ndev->dma_irq_base = irq - ndev->dma_irq_count;
+		irq = ndev->dma_irq_base;
+	} else {
+		ndev->dma_aux_avail = false;
+		irq = min(NTB_EPF_MAX_DB_COUNT + 1, irq);
+	}
+
 	for (i = 0; i < irq; i++) {
 		ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr,
 				  0, "ntb_epf", ndev);
@@ -504,21 +542,32 @@ 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 bar_sz, mw_size;
+	u32 offset;
 	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;
 
+	bar_sz = pci_resource_len(ndev->ntb.pdev, bar);
+
+	if (ndev->ctrl_version >= NTB_EPF_CTRL_VERSION_V1) {
+		offset = readl(ndev->ctrl_reg + NTB_EPF_MW_OFFSET(idx));
+		mw_size = readl(ndev->ctrl_reg + NTB_EPF_MW_SIZE(idx));
+	} else {
+		offset = idx == 0 ? readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET) : 0;
+		mw_size = bar_sz - offset;
+	}
+
+	if (!mw_size || offset + mw_size > bar_sz)
+		return -EINVAL;
+
 	if (base)
 		*base = pci_resource_start(ndev->ntb.pdev, bar) + offset;
 
 	if (size)
-		*size = pci_resource_len(ndev->ntb.pdev, bar) - offset;
+		*size = mw_size;
 
 	return 0;
 }
@@ -610,14 +659,61 @@ static inline void ntb_epf_init_struct(struct ntb_epf_dev *ndev,
 	ndev->ntb.ops = &ntb_epf_ops;
 }
 
+static int ntb_epf_parse_ctrl_version(struct ntb_epf_dev *ndev)
+{
+	struct device *dev = ndev->dev;
+	u32 ver;
+
+	ver = readl(ndev->ctrl_reg + NTB_EPF_CTRL_VERSION);
+	switch (ver) {
+	case NTB_EPF_CTRL_VERSION_LEGACY:
+	case NTB_EPF_CTRL_VERSION_V1:
+		ndev->ctrl_version = ver;
+		return 0;
+	default:
+		dev_err(dev, "Unsupported NTB EPF control version %u\n", ver);
+		return -EINVAL;
+	}
+}
+
+static void ntb_epf_parse_dma_locator(struct ntb_epf_dev *ndev)
+{
+	if (ndev->ctrl_version < NTB_EPF_CTRL_VERSION_V1) {
+		ndev->dma_abi = 0;
+		ndev->dma_bar = NO_BAR;
+		ndev->dma_offset = 0;
+		ndev->dma_size = 0;
+		ndev->dma_irq_count = 0;
+		return;
+	}
+
+	ndev->dma_abi = readl(ndev->ctrl_reg + NTB_EPF_DMA_ABI);
+	ndev->dma_bar = readl(ndev->ctrl_reg + NTB_EPF_DMA_BAR);
+	ndev->dma_offset = readl(ndev->ctrl_reg + NTB_EPF_DMA_OFFSET);
+	ndev->dma_size = readl(ndev->ctrl_reg + NTB_EPF_DMA_SIZE);
+	ndev->dma_num_chans = readl(ndev->ctrl_reg + NTB_EPF_DMA_NUM_CHANS);
+	if (ndev->dma_abi && !ndev->dma_num_chans)
+		ndev->dma_num_chans = 1;
+	ndev->dma_irq_count = ndev->dma_num_chans;
+}
+
 static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
 {
 	struct device *dev = ndev->dev;
 	int ret;
 
-	/* One Link interrupt and rest doorbell interrupt */
+	ret = ntb_epf_parse_ctrl_version(ndev);
+	if (ret)
+		return ret;
+
+	ntb_epf_parse_dma_locator(ndev);
+
+	/*
+	 * One Link interrupt and rest doorbell interrupt.
+	 * Remote DMA interrupt is best effort.
+	 */
 	ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
-			       NTB_EPF_MAX_DB_COUNT + 1);
+			       NTB_EPF_MAX_DB_COUNT + 1 + ndev->dma_irq_count);
 	if (ret) {
 		dev_err(dev, "Failed to init ISR\n");
 		return ret;
-- 
2.51.0