[PATCH v9 7/7] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
Koichiro Den <[email protected]> Thu, 19 Feb 2026 17:13:18 +0900
| Newsgroups | dev.linux.lists.ntb,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- Changes since v8: - Add MMIO address alignment check - Drop 'eDMA' word from the subject drivers/pci/endpoint/pci-ep-msi.c | 99 ++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c index 50badffa9d72..f287fbf684ca 100644 --- a/drivers/pci/endpoint/pci-ep-msi.c +++ b/drivers/pci/endpoint/pci-ep-msi.c @@ -6,6 +6,8 @@ * Author: Frank Li <[email protected]> */ +#include <linux/align.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/export.h> #include <linux/interrupt.h> @@ -36,6 +38,86 @@ 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; + + addr = doorbell->phys_addr; + if (!IS_ALIGNED(addr, sizeof(u32))) + return -EINVAL; + + msg = kcalloc(num_db, sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + /* + * 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] = (struct pci_epf_doorbell_msg) { + .msg.address_lo = (u32)addr, + .msg.address_hi = (u32)(addr >> 32), + .msg.data = doorbell->u.db_mmio.data, + .virq = doorbell->u.db_mmio.irq, + .irq_flags = IRQF_SHARED, + .type = PCI_EPF_DOORBELL_EMBEDDED, + .bar = doorbell->bar, + .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; @@ -109,8 +191,21 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) if (!ret) return 0; - dev_err(dev, "Failed to allocate doorbell: %d\n", ret); - return ret; + /* + * 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_err(dev, "Failed to allocate doorbell: %d\n", ret); + return ret; + } + + dev_info(dev, "Using embedded (DMA) doorbell fallback\n"); + return 0; } EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell); -- 2.51.0