[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251023071916.901355-24-den@valinux.co.jp>
Date: Thu, 23 Oct 2025 16:19:14 +0900
From: Koichiro Den <den@...inux.co.jp>
To: ntb@...ts.linux.dev,
linux-pci@...r.kernel.org,
dmaengine@...r.kernel.org,
linux-kernel@...r.kernel.org
Cc: mani@...nel.org,
kwilczynski@...nel.org,
kishon@...nel.org,
bhelgaas@...gle.com,
corbet@....net,
vkoul@...nel.org,
jdmason@...zu.us,
dave.jiang@...el.com,
allenbh@...il.com,
Basavaraj.Natikar@....com,
Shyam-sundar.S-k@....com,
kurt.schwemmer@...rosemi.com,
logang@...tatee.com,
jingoohan1@...il.com,
lpieralisi@...nel.org,
robh@...nel.org,
jbrunet@...libre.com,
Frank.Li@....com,
fancer.lancer@...il.com,
arnd@...db.de,
pstanner@...hat.com,
elfring@...rs.sourceforge.net
Subject: [RFC PATCH 23/25] NTB: intr_dw_edma: Add DW eDMA emulated interrupt backend
Introduce a new NTB interrupt backend using DW eDMA's emulated interrupt
mechanism. Enables interrupt-based signaling from RC to EP where MSI is
impossible due to security restrictions on the platform.
Signed-off-by: Koichiro Den <den@...inux.co.jp>
---
drivers/ntb/Kconfig | 10 ++
drivers/ntb/Makefile | 1 +
drivers/ntb/intr_common.c | 8 +-
drivers/ntb/intr_dw_edma.c | 253 +++++++++++++++++++++++++++++++++++++
include/linux/ntb.h | 10 ++
5 files changed, 281 insertions(+), 1 deletion(-)
create mode 100644 drivers/ntb/intr_dw_edma.c
diff --git a/drivers/ntb/Kconfig b/drivers/ntb/Kconfig
index 2f22f44245b3..5b7e1563e639 100644
--- a/drivers/ntb/Kconfig
+++ b/drivers/ntb/Kconfig
@@ -29,6 +29,16 @@ config NTB_MSI
If unsure, say N.
+config NTB_DW_EDMA
+ bool "DW eDMA test-interrupt backend"
+ depends on PCI_ENDPOINT && PCIE_DW_EP && DW_EDMA
+ select NTB_INTR_COMMON
+ help
+ Use DW eDMA v0 test interrupt as a doorbell-like backend
+ for NTB transports when MSI is not available on EPF side.
+
+ If unsure, say N.
+
source "drivers/ntb/hw/Kconfig"
source "drivers/ntb/test/Kconfig"
diff --git a/drivers/ntb/Makefile b/drivers/ntb/Makefile
index feaa2a77cbf6..cae84d132b78 100644
--- a/drivers/ntb/Makefile
+++ b/drivers/ntb/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_NTB_TRANSPORT) += ntb_transport.o
ntb-y := core.o
ntb-$(CONFIG_NTB_INTR_COMMON) += intr_common.o
ntb-$(CONFIG_NTB_MSI) += msi.o
+ntb-$(CONFIG_NTB_DW_EDMA) += intr_dw_edma.o
diff --git a/drivers/ntb/intr_common.c b/drivers/ntb/intr_common.c
index e0e296fd3e3c..41b2752c6d03 100644
--- a/drivers/ntb/intr_common.c
+++ b/drivers/ntb/intr_common.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
-#include <linux/ntb.h>
#include <linux/module.h>
+#include <linux/ntb.h>
#include <linux/pci.h>
#include <linux/slab.h>
@@ -13,6 +13,12 @@ int ntb_intr_init(struct ntb_dev *ntb,
ntb->intr_backend = ntb_intr_msi_backend();
dev_info(&ntb->dev, "NTB interrupt MSI backend selected.\n");
}
+#endif
+#ifdef CONFIG_NTB_DW_EDMA
+ if (!ntb->intr_backend) {
+ ntb->intr_backend = ntb_intr_dw_edma_backend();
+ dev_info(&ntb->dev, "NTB interrupt DW eDMA backend selected.\n");
+ }
#endif
if (!ntb->intr_backend)
return -ENODEV;
diff --git a/drivers/ntb/intr_dw_edma.c b/drivers/ntb/intr_dw_edma.c
new file mode 100644
index 000000000000..0e408ecfaf61
--- /dev/null
+++ b/drivers/ntb/intr_dw_edma.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+
+#include <linux/dma/edma.h>
+#include <linux/ntb.h>
+#include <linux/pci-epc.h>
+
+struct ntb_intr_dw {
+ u64 base_addr;
+ u64 end_addr;
+
+ struct dw_edma *edma;
+ resource_size_t rd_status_off;
+ resource_size_t rd_clear_off;
+
+ u32 __iomem *peer_mws[];
+};
+
+struct ntb_intr_dw_ctx {
+ irq_handler_t handler;
+ void *dev;
+ struct dw_edma *edma;
+};
+
+static void dw_edma_selfirq_handler(struct dw_edma *dw, void *data)
+{
+ struct ntb_intr_dw_ctx *ctx = data;
+
+ ctx->handler(0, ctx->dev);
+}
+
+static int dw_edma_find_backend_for_ntb(struct ntb_dev *ntb, struct ntb_intr_dw *intr_dw)
+{
+ struct pci_epc *epc = NULL;
+
+ epc = ntb_get_pci_epc(ntb);
+ if (!epc)
+ return -ENODEV;
+ intr_dw->edma = dw_edma_find_by_child(&epc->dev);
+ if (!intr_dw->edma)
+ return -ENODEV;
+ dw_edma_selfirq_offsets(intr_dw->edma, &intr_dw->rd_status_off, &intr_dw->rd_clear_off);
+ return 0;
+}
+
+static int dw_intr_init(struct ntb_dev *ntb, void (*desc_changed)(void *ctx))
+{
+ struct ntb_intr_dw *intr_dw;
+ phys_addr_t mw_phys_addr;
+ resource_size_t mw_size;
+ int peer_widx;
+ int peers;
+ int ret;
+ int i;
+
+ peers = ntb_peer_port_count(ntb);
+ if (peers <= 0)
+ return -EINVAL;
+
+ intr_dw = devm_kzalloc(&ntb->dev, struct_size(intr_dw, peer_mws, peers),
+ GFP_KERNEL);
+ if (!intr_dw)
+ return -ENOMEM;
+
+ ret = dw_edma_find_backend_for_ntb(ntb, intr_dw);
+ if (ret) {
+ devm_kfree(&ntb->dev, intr_dw);
+ return ret;
+ }
+
+ for (i = 0; i < peers; i++) {
+ peer_widx = ntb_peer_mw_count(ntb) - 1 - i;
+
+ ret = ntb_peer_mw_get_addr(ntb, peer_widx, &mw_phys_addr,
+ &mw_size);
+ if (ret)
+ goto unroll;
+
+ intr_dw->peer_mws[i] = devm_ioremap(&ntb->dev, mw_phys_addr,
+ mw_size);
+ if (!intr_dw->peer_mws[i]) {
+ ret = -EFAULT;
+ goto unroll;
+ }
+ }
+
+ ntb->intr_priv = intr_dw;
+
+ return 0;
+
+unroll:
+ for (i = 0; i < peers; i++)
+ if (intr_dw->peer_mws[i])
+ devm_iounmap(&ntb->dev, intr_dw->peer_mws[i]);
+
+ devm_kfree(&ntb->dev, intr_dw);
+ return ret;
+}
+
+static int dw_intr_setup_mws(struct ntb_dev *ntb)
+{
+ struct ntb_intr_dw *dwc = ntb->intr_priv;
+ resource_size_t addr_align, size_align, offset;
+ resource_size_t mw_size = SZ_32K;
+ resource_size_t mw_min_size = mw_size;
+ u64 addr = dwc->rd_status_off;
+ int peer, peer_widx, ret;
+ int i;
+
+ for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
+ peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
+ if (peer_widx < 0)
+ return peer_widx;
+
+ ret = ntb_mw_get_align(ntb, peer, peer_widx, &addr_align,
+ NULL, NULL, NULL);
+ if (ret)
+ return ret;
+
+ addr &= ~(addr_align - 1);
+ }
+
+ for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
+ peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
+ if (peer_widx < 0) {
+ ret = peer_widx;
+ goto error_out;
+ }
+
+ ret = ntb_mw_get_align(ntb, peer, peer_widx, NULL,
+ &size_align, NULL, &offset);
+ if (ret)
+ goto error_out;
+
+ mw_size = round_up(mw_size, size_align);
+ if (mw_size < mw_min_size)
+ mw_min_size = mw_size;
+
+ ret = ntb_mw_set_trans(ntb, peer, peer_widx,
+ addr, mw_size, offset);
+ if (ret)
+ goto error_out;
+ }
+
+ dwc->base_addr = addr;
+ dwc->end_addr = addr + mw_min_size;
+
+ return 0;
+
+error_out:
+ for (i = 0; i < peer; i++) {
+ peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
+ if (peer_widx < 0)
+ continue;
+
+ ntb_mw_clear_trans(ntb, i, peer_widx);
+ }
+
+ return ret;
+}
+
+static void dw_intr_clear_mws(struct ntb_dev *ntb)
+{
+ int peer, peer_widx;
+
+ for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
+ peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
+ if (peer_widx < 0)
+ continue;
+
+ ntb_mw_clear_trans(ntb, peer, peer_widx);
+ }
+}
+
+static void dw_intr_release_irq(void *data)
+{
+ struct ntb_intr_dw_ctx *ctx = data;
+
+ dw_edma_unregister_selfirq(ctx->edma, dw_edma_selfirq_handler, ctx);
+ kfree(ctx);
+}
+
+static int dw_intr_request_irq(struct ntb_dev *ntb, irq_handler_t h,
+ const char *name, void *dev_id,
+ struct ntb_intr_desc *intr_desc)
+{
+ struct ntb_intr_dw *dwc = ntb->intr_priv;
+ struct dw_edma *edma = dwc->edma;
+ int ret;
+
+ if (intr_desc->ctx)
+ return 1;
+
+ struct ntb_intr_dw_ctx *ctx __free(kfree) = kzalloc(
+ sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->handler = h;
+ ctx->dev = dev_id;
+ ctx->edma = edma;
+
+ ret = dw_edma_register_selfirq(edma, dw_edma_selfirq_handler, ctx);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&ntb->dev, dw_intr_release_irq, ctx);
+ if (ret)
+ return ret;
+
+ intr_desc->addr_offset = dwc->rd_status_off - dwc->base_addr;
+ intr_desc->data = 0x0;
+ intr_desc->ctx = no_free_ptr(ctx);
+ return 1;
+}
+
+static void dw_intr_free_irq(struct ntb_dev *ntb, int irq, void *dev_id,
+ struct ntb_intr_desc *intr_desc)
+{
+ struct ntb_intr_dw *dwc = ntb->intr_priv;
+ struct dw_edma *edma = dwc->edma;
+ struct ntb_intr_dw_ctx *ctx;
+
+ ctx = intr_desc->ctx;
+ dw_edma_unregister_selfirq(edma, dw_edma_selfirq_handler, ctx);
+ devm_remove_action(&ntb->dev, dw_intr_release_irq, ctx);
+ kfree(ctx);
+}
+
+static int dw_intr_peer_trigger(struct ntb_dev *ntb, int peer, struct ntb_intr_desc *desc)
+{
+ struct ntb_intr_dw *intr_dw = ntb->intr_priv;
+ int idx;
+
+ idx = desc->addr_offset / sizeof(*intr_dw->peer_mws[peer]);
+
+ iowrite32(desc->data, &intr_dw->peer_mws[peer][idx]);
+
+ return 0;
+}
+
+static const struct ntb_intr_backend ntb_intr_backend_dw_edma = {
+ .name = "dw-edma-testirq",
+ .init = dw_intr_init,
+ .setup_mws = dw_intr_setup_mws,
+ .clear_mws = dw_intr_clear_mws,
+ .request_irq = dw_intr_request_irq,
+ .free_irq = dw_intr_free_irq,
+ .peer_trigger = dw_intr_peer_trigger,
+};
+
+const struct ntb_intr_backend *ntb_intr_dw_edma_backend(void)
+{
+ return &ntb_intr_backend_dw_edma;
+}
diff --git a/include/linux/ntb.h b/include/linux/ntb.h
index 1a88fe45471e..7daba67928e9 100644
--- a/include/linux/ntb.h
+++ b/include/linux/ntb.h
@@ -1664,6 +1664,7 @@ struct ntb_intr_desc {
u32 addr_offset;
u32 data;
u16 vector_offset;
+ void *ctx;
};
struct ntb_intr_backend {
@@ -1734,4 +1735,13 @@ static inline const struct ntb_intr_backend *ntb_intr_msi_backend(void)
}
#endif /* CONFIG_NTB_MSI */
+#ifdef CONFIG_NTB_DW_EDMA
+extern const struct ntb_intr_backend *ntb_intr_dw_edma_backend(void);
+#else
+static inline const struct ntb_intr_backend *ntb_intr_dw_edma_backend(void)
+{
+ return NULL;
+}
+#endif /* CONFIG_NTB_DW_EDMA */
+
#endif
--
2.48.1
Powered by blists - more mailing lists