Re: [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
Frank Li <[email protected]>
| Newsgroups | dev.linux.lists.ntb,org.kernel.vger.dmaengine,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kernel.vger.linux-pci |
|---|---|
| Message-ID | <aYoCnEmXYWA0f6TH@lizhi-Precision-Tower-5810> |
On Mon, Feb 09, 2026 at 09:53:16PM +0900, Koichiro Den wrote: > pci_epf_alloc_doorbell() currently allocates MSI-backed doorbells using > the MSI domain returned by of_msi_map_get_device_domain(..., > DOMAIN_BUS_PLATFORM_MSI). On platforms where such an MSI irq domain is > not available, EPF drivers cannot provide doorbells to the RC. Even if > it's available and MSI device domain successfully created, the write > into the message address via BAR inbound mapping might not work for > platform-specific reasons (e.g. write into GITS_TRANSLATOR via iATU IB > mapping does not reach ITS at least on R-Car Gen4 Spider). > > Add an "embedded (DMA) doorbell" fallback path that uses EPC-provided > auxiliary resources to build doorbell address/data pairs backed by a > platform device MMIO region (e.g. dw-edma) and a shared platform IRQ. > > To let EPF drivers request interrupts correctly, extend struct > pci_epf_doorbell_msg with the doorbell type and required IRQ request > flags. Update pci-epf-test to handle shared IRQ constraints by using a > trivial primary handler to wake the threaded handler, and update > pci-epf-vntb to use the required irq_flags. > > Signed-off-by: Koichiro Den <[email protected]> > --- > drivers/pci/endpoint/functions/pci-epf-test.c | 29 +++- > drivers/pci/endpoint/functions/pci-epf-vntb.c | 3 +- > drivers/pci/endpoint/pci-ep-msi.c | 129 ++++++++++++++++-- > include/linux/pci-epf.h | 17 ++- > 4 files changed, 160 insertions(+), 18 deletions(-) > > diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c > index 23034f548c90..2f3b2e6a3e29 100644 > --- a/drivers/pci/endpoint/functions/pci-epf-test.c > +++ b/drivers/pci/endpoint/functions/pci-epf-test.c > @@ -711,6 +711,26 @@ static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data) > return IRQ_HANDLED; > } > > +/* > + * Embedded doorbell fallback uses a platform IRQ which is already owned by a > + * platform driver (e.g. dw-edma) and therefore must be requested IRQF_SHARED. > + * We cannot add IRQF_ONESHOT here because shared IRQ handlers must agree on > + * IRQF_ONESHOT. > + * > + * request_threaded_irq() with handler == NULL would be rejected for !ONESHOT > + * because the default primary handler only wakes the thread and does not > + * mask/ack the interrupt, which can livelock on level-triggered IRQs. > + * > + * In the embedded doorbell fallback, the IRQ owner is responsible for > + * acknowledging/deasserting the interrupt source in hardirq context before the > + * IRQ line is unmasked. Therefore this driver only needs a trivial primary > + * handler to wake the threaded handler. > + */ > +static irqreturn_t pci_epf_test_doorbell_primary(int irq, void *data) > +{ > + return IRQ_WAKE_THREAD; > +} > + > static void pci_epf_test_doorbell_cleanup(struct pci_epf_test *epf_test) > { > struct pci_epf_test_reg *reg = epf_test->reg[epf_test->test_reg_bar]; > @@ -731,6 +751,7 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, > u32 status = le32_to_cpu(reg->status); > struct pci_epf *epf = epf_test->epf; > struct pci_epc *epc = epf->epc; > + unsigned long irq_flags; > struct msi_msg *msg; > enum pci_barno bar; > size_t offset; > @@ -745,10 +766,14 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, > if (bar < BAR_0) > goto err_doorbell_cleanup; > > + irq_flags = epf->db_msg[0].irq_flags; > + if (!(irq_flags & IRQF_SHARED)) > + irq_flags |= IRQF_ONESHOT; > epf_test->db_irq_requested = false; > > - ret = request_threaded_irq(epf->db_msg[0].virq, NULL, > - pci_epf_test_doorbell_handler, IRQF_ONESHOT, > + ret = request_threaded_irq(epf->db_msg[0].virq, > + pci_epf_test_doorbell_primary, > + pci_epf_test_doorbell_handler, irq_flags, > "pci-ep-test-doorbell", epf_test); > if (ret) { > dev_err(&epf->dev, > diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c > index 3ecc5059f92b..d2fd1e194088 100644 > --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c > +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c > @@ -535,7 +535,8 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, > > for (i = 0; i < ntb->db_count; i++) { > ret = request_irq(epf->db_msg[i].virq, epf_ntb_doorbell_handler, > - 0, "pci_epf_vntb_db", ntb); > + epf->db_msg[i].irq_flags, "pci_epf_vntb_db", > + ntb); > > if (ret) { > dev_err(&epf->dev, > diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c > index ad8a81d6ad77..0e93d4789abd 100644 > --- a/drivers/pci/endpoint/pci-ep-msi.c > +++ b/drivers/pci/endpoint/pci-ep-msi.c > @@ -8,6 +8,7 @@ > > #include <linux/device.h> > #include <linux/export.h> > +#include <linux/interrupt.h> > #include <linux/irqdomain.h> > #include <linux/module.h> > #include <linux/msi.h> > @@ -35,23 +36,84 @@ static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) > pci_epc_put(epc); > } > > -int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) > +static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db) > { > struct pci_epc *epc = epf->epc; > - struct device *dev = &epf->dev; > - struct irq_domain *domain; > - void *msg; > - int ret; > - int i; > + const struct pci_epc_aux_resource *dma_ctrl = NULL; > + struct pci_epf_doorbell_msg *msg; > + int count, ret, i, db; > > - /* TODO: Multi-EPF support */ > - if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { > - dev_err(dev, "MSI doorbell doesn't support multiple EPF\n"); > - return -EINVAL; > + 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_DMA_CTRL_MMIO) { I suggest use PCI_EPC_EMBEDED_DOORBELL_MMIO directly because some vendor have really doorbell register space. below addr is that phys_addr + res[i].u.dma_chan_desc.db_offset. So vendor can easily change to their doorbell register space in future. Frank > + dma_ctrl = &res[i]; > + break; > + } > + } > + if (!dma_ctrl) > + return -ENODEV; > + > + msg = kcalloc(num_db, sizeof(*msg), GFP_KERNEL); > + if (!msg) > + return -ENOMEM; > + > + for (i = 0, db = 0; i < count && db < num_db; i++) { > + u64 addr; > + > + if (res[i].type != PCI_EPC_AUX_DMA_CHAN_DESC) > + continue; > + > + if (res[i].u.dma_chan_desc.db_offset >= dma_ctrl->size) > + continue; > + > + addr = (u64)dma_ctrl->phys_addr + res[i].u.dma_chan_desc.db_offset; > + > + msg[db].msg.address_lo = (u32)addr; > + msg[db].msg.address_hi = (u32)(addr >> 32); > + msg[db].msg.data = 0; > + msg[db].virq = res[i].u.dma_chan_desc.irq; > + msg[db].irq_flags = IRQF_SHARED; > + msg[db].type = PCI_EPF_DOORBELL_EMBEDDED; > + db++; > } > > - if (epf->db_msg) > - return -EBUSY; > + if (db != num_db) { > + kfree(msg); > + return -ENOSPC; > + } > + > + 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) > +{ suggest create patch, which only add helper function pci_epf_alloc_doorbell_msi(). Then add pci_epf_alloc_doorbell_embedded. Frank > + struct pci_epf_doorbell_msg *msg; > + struct device *dev = &epf->dev; > + struct pci_epc *epc = epf->epc; > + struct irq_domain *domain; > + int ret, i; > > domain = of_msi_map_get_device_domain(epc->dev.parent, 0, > DOMAIN_BUS_PLATFORM_MSI); > @@ -74,6 +136,11 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) > if (!msg) > return -ENOMEM; > > + for (i = 0; i < num_db; i++) { > + msg[i].irq_flags = 0; > + msg[i].type = PCI_EPF_DOORBELL_MSI; > + } > + > epf->num_db = num_db; > epf->db_msg = msg; > > @@ -90,13 +157,49 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) > for (i = 0; i < num_db; i++) > epf->db_msg[i].virq = msi_get_virq(epc->dev.parent, i); > > + return 0; > +} > + > +int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) > +{ > + struct pci_epc *epc = epf->epc; > + struct device *dev = &epf->dev; > + int ret; > + > + /* TODO: Multi-EPF support */ > + if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { > + dev_err(dev, "Doorbell doesn't support multiple EPF\n"); > + return -EINVAL; > + } > + > + if (epf->db_msg) > + return -EBUSY; > + > + ret = pci_epf_alloc_doorbell_msi(epf, num_db); > + if (!ret) > + return 0; > + > + 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; > } > EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell); > > void pci_epf_free_doorbell(struct pci_epf *epf) > { > - platform_device_msi_free_irqs_all(epf->epc->dev.parent); > + if (!epf->db_msg) > + return; > + > + if (epf->db_msg[0].type == PCI_EPF_DOORBELL_MSI) > + platform_device_msi_free_irqs_all(epf->epc->dev.parent); > > kfree(epf->db_msg); > epf->db_msg = NULL; > diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h > index 7737a7c03260..e6625198f401 100644 > --- a/include/linux/pci-epf.h > +++ b/include/linux/pci-epf.h > @@ -152,14 +152,27 @@ struct pci_epf_bar { > struct pci_epf_bar_submap *submap; > }; > > +enum pci_epf_doorbell_type { > + PCI_EPF_DOORBELL_MSI = 0, > + PCI_EPF_DOORBELL_EMBEDDED, > +}; > + > /** > * struct pci_epf_doorbell_msg - represents doorbell message > - * @msg: MSI message > - * @virq: IRQ number of this doorbell MSI message > + * @msg: Doorbell address/data pair to be mapped into BAR space. > + * For MSI-backed doorbells this is the MSI message, while for > + * "embedded" doorbells this represents an MMIO write that asserts > + * an interrupt on the EP side. > + * @virq: IRQ number of this doorbell message > + * @irq_flags: Required flags for request_irq()/request_threaded_irq(). > + * Callers may OR-in additional flags (e.g. IRQF_ONESHOT). > + * @type: Doorbell type. > */ > struct pci_epf_doorbell_msg { > struct msi_msg msg; > int virq; > + unsigned long irq_flags; > + enum pci_epf_doorbell_type type; > }; > > /** > -- > 2.51.0 >