[PATCH v7 9/9] PCI: endpoint: pci-ep-msi: Add embedded eDMA doorbell fallback
Koichiro Den
den at valinux.co.jp
Sun Feb 15 08:38:47 PST 2026
Some endpoint platforms cannot use platform MSI / GIC ITS to implement
EP-side doorbells. In those cases, EPF drivers cannot provide an
interrupt-driven doorbell and often fall back to polling.
Add an "embedded" doorbell backend that uses a controller-integrated
doorbell target (e.g. DesignWare integrated eDMA interrupt-emulation
doorbell).
The backend locates the doorbell register and a corresponding Linux IRQ
via the EPC aux-resource API. If the doorbell register is already
exposed via a fixed BAR mapping, provide BAR+offset. Otherwise provide
the physical address so EPF drivers can map it into BAR space.
When MSI doorbell allocation fails with -ENODEV,
pci_epf_alloc_doorbell() falls back to this embedded backend.
Signed-off-by: Koichiro Den <den at valinux.co.jp>
---
drivers/pci/endpoint/pci-ep-msi.c | 90 +++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c
index a42f69ad24ad..6e1524c2d891 100644
--- a/drivers/pci/endpoint/pci-ep-msi.c
+++ b/drivers/pci/endpoint/pci-ep-msi.c
@@ -6,6 +6,7 @@
* Author: Frank Li <Frank.Li at nxp.com>
*/
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/interrupt.h>
@@ -36,6 +37,82 @@ static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
pci_epc_put(epc);
}
+static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db)
+{
+ const struct pci_epc_aux_resource *doorbell = NULL;
+ struct pci_epf_doorbell_msg *msg;
+ struct pci_epc *epc = epf->epc;
+ struct device *dev = &epf->dev;
+ int count, ret, i;
+ u64 addr;
+
+ count = pci_epc_get_aux_resources(epc, epf->func_no, epf->vfunc_no,
+ NULL, 0);
+ if (count == -EOPNOTSUPP || count == 0)
+ return -ENODEV;
+ if (count < 0)
+ return count;
+
+ struct pci_epc_aux_resource *res __free(kfree) =
+ kcalloc(count, sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+
+ ret = pci_epc_get_aux_resources(epc, epf->func_no, epf->vfunc_no,
+ res, count);
+ if (ret == -EOPNOTSUPP || ret == 0)
+ return -ENODEV;
+ if (ret < 0)
+ return ret;
+
+ count = ret;
+
+ for (i = 0; i < count; i++) {
+ if (res[i].type == PCI_EPC_AUX_DOORBELL_MMIO) {
+ if (doorbell) {
+ dev_warn(dev,
+ "Duplicate DOORBELL_MMIO resource found\n");
+ continue;
+ }
+ doorbell = &res[i];
+ }
+ }
+ if (!doorbell)
+ return -ENODEV;
+
+ msg = kcalloc(num_db, sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ addr = doorbell->phys_addr;
+
+ /*
+ * Embedded doorbell backends (e.g. DesignWare eDMA interrupt emulation)
+ * typically provide a single IRQ and do not offer per-doorbell
+ * distinguishable address/data pairs. The EPC aux resource therefore
+ * exposes one DOORBELL_MMIO entry (u.db_mmio.irq).
+ *
+ * Still, pci_epf_alloc_doorbell() allows requesting multiple doorbells.
+ * For such backends we replicate the same address/data for each entry
+ * and mark the IRQ as shared (IRQF_SHARED). Consumers must treat them
+ * as equivalent "kick" doorbells.
+ */
+ for (i = 0; i < num_db; i++) {
+ msg[i].msg.address_lo = (u32)addr;
+ msg[i].msg.address_hi = (u32)(addr >> 32);
+ msg[i].msg.data = 0;
+ msg[i].virq = doorbell->u.db_mmio.irq;
+ msg[i].irq_flags = IRQF_SHARED;
+ msg[i].type = PCI_EPF_DOORBELL_EMBEDDED;
+ msg[i].bar = doorbell->bar;
+ msg[i].offset = (doorbell->bar == NO_BAR) ? 0 : doorbell->bar_offset;
+ }
+
+ epf->num_db = num_db;
+ epf->db_msg = msg;
+ return 0;
+}
+
static int pci_epf_alloc_doorbell_msi(struct pci_epf *epf, u16 num_db)
{
struct pci_epf_doorbell_msg *msg;
@@ -110,6 +187,19 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
if (!ret)
return 0;
+ /*
+ * Fall back to embedded doorbell only when platform MSI is unavailable
+ * for this EPC.
+ */
+ if (ret != -ENODEV)
+ return ret;
+
+ ret = pci_epf_alloc_doorbell_embedded(epf, num_db);
+ if (!ret) {
+ dev_info(dev, "Using embedded (DMA) doorbell fallback\n");
+ return 0;
+ }
+
dev_err(dev, "Failed to allocate doorbell: %d\n", ret);
return ret;
}
--
2.51.0
More information about the Linux-rockchip
mailing list