* [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
@ 2026-02-09 12:53 Koichiro Den
2026-02-09 12:53 ` [PATCH v6 1/8] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler Koichiro Den
` (8 more replies)
0 siblings, 9 replies; 33+ messages in thread
From: Koichiro Den @ 2026-02-09 12:53 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, cassel, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas, kishon, jdmason, allenbh
Cc: dmaengine, linux-pci, ntb, linux-kselftest, linux-kernel
Hi,
Some DesignWare PCIe endpoint platforms integrate a DesignWare eDMA
instance alongside the PCIe controller. In remote eDMA use cases, the host
needs access to the eDMA register block and the per-channel linked-list
(LL) regions via PCIe BARs, while the endpoint may still boot with a
standard EP configuration (and may also use dw-edma locally).
This series focuses on using DesignWare eDMA emulated interrupt doorbell as
a pci-ep-msi fallback, in a generalized manner without exporting any
DesignWare eDMA-specific API.
* dmaengine:
1. Add explicit deassert handling for eDMA interrupt emulation in the
IRQ handler so level-triggered/shared IRQ lines don't remain stuck.
=> Patch 01/08
2. Cache per-channel IRQ number and an interrupt-emulation doorbell
register offset, so integrated-controller drivers can expose these
to EPF users via the auxiliary resource metadata.
=> Patch 02/08
* pci/endpoint:
1. Add a generic auxiliary resource enumeration API
(pci_epc_get_aux_resources()) for EPF drivers to discover
controller-owned resources that can be mapped into BAR space (e.g.
an integrated DMA MMIO window and per-channel LL regions metadata).
=> Patch 03/08 - 05/08
2. Add an "embedded (DMA) doorbell" fallback to pci_epf_alloc_doorbell()
(used when platform MSI doorbells are unavailable/unusable), and
update in-tree users (pci-epf-test, pci-epf-vntb) to request IRQs
correctly (shared IRQ constraints, required flags).
=> Patch 06/08 - 08/08
Note: As discussed in the v4 thread, v4 Patch 01/09 (dw-edma per-channel
interrupt routing control via dma_slave_config.peripheral_config) is
dropped from this series for now, so the series contains only what's needed
by the current, concrete consumer.
This series evolved out of:
https://lore.kernel.org/linux-pci/20260118135440.1958279-1-den@valinux.co.jp/
Kernel base
===========
Patches 1-8 cleanly apply to pci.git 'controller/dwc':
Commit 43d324eeb08c ("PCI: dwc: Fix missing iATU setup when ECAM is enabled")
Tested on
=========
I tested the embedded (DMA) doorbell fallback path (via pci-epf-test) on
R-Car Spider boards:
$ ./pci_endpoint_test -t DOORBELL_TEST
TAP version 13
1..1
# Starting 1 tests from 1 test cases.
# RUN pcie_ep_doorbell.DOORBELL_TEST ...
# OK pcie_ep_doorbell.DOORBELL_TEST
ok 1 pcie_ep_doorbell.DOORBELL_TEST
# PASSED: 1 / 1 tests passed.
# Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
with the following message observed on the EP side:
[ 80.464653] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
(Note: for the test to pass on R-Car Spider, one of the following was required:
- echo 1048576 > functions/pci_epf_test/func1/pci_epf_test.0/bar2_size
- apply https://lore.kernel.org/all/20251023072217.901888-1-den@valinux.co.jp/)
Changelog
=========
* v5->v6 changes:
- Fix a double-free in v5 Patch 8/8 caused by mixing __free(kfree) with
an explicit kfree(). This is a functional bug (detectable by KASAN),
hence the respin solely for this fix. Sorry for the noise. No other
changes.
* v4->v5 changes:
- Change the series subject now that the series has evolved into a
consumer-driven set focused on the embedded doorbell fallback and its
in-tree users (epf-test and epf-vntb).
- Drop [PATCH v4 01/09] (dw-edma per-channel interrupt routing control)
from this series for now, so the series focuses on what's needed by the
current consumer (i.e. the doorbell fallback implementation).
- Replace the v4 embedded-doorbell "test variant + host/kselftest
plumbing" with a generic embedded-doorbell fallback in
pci_epf_alloc_doorbell(), including exposing required IRQ request flags
to EPF drivers.
- Two preparatory fix patches (Patch 6/8 and 7/8) to clean up error
handling and state management ahead of Patch 8/8.
- Rename *_get_remote_resource() to *_get_aux_resources() and adjust
relevant variable namings and kernel docs. Discussion may continue.
- Rework dw-edma per-channel metadata exposure to cache the needed info
in dw_edma_chip (IRQ number + emulation doorbell offset) and consume it
from the DesignWare EPC auxiliary resource provider without calling back
to dw-edma.
* v3->v4 changes:
- Drop dma_slave_caps.hw_id and the dmaengine selfirq callback
registration API. Instead, add a dw-edma specific dw_edma_chan_info()
helper and extend the EPC remote resource metadata accordingly.
- Add explicit acking for eDMA interrupt emulation and adjust the
dw-edma IRQ path for embedded-doorbell usage.
- Replace the previous EPC API smoke test with an embedded doorbell
test variant (pci-epf-test + pci_endpoint_test/selftests).
- Rebase onto pci.git controller/dwc commit 43d324eeb08c.
* v2->v3 changes:
- Replace DWC-specific helpers with a generic EPC remote resource query API.
- Add pci-epf-test smoke test and host/kselftest support for the new API.
- Drop the dw-edma-specific notify-only channel and polling approach
([PATCH v2 4/7] and [PATCH v2 5/7]), and rework notification handling
around a generic dmaengine_(un)register_selfirq() API implemented
by dw-edma.
* v1->v2 changes:
- Combine the two previously posted series into a single set (per Frank's
suggestion). Order dmaengine/dw-edma patches first so hw_id support
lands before the PCI LL-region helper, which assumes
dma_slave_caps.hw_id availability.
v5: https://lore.kernel.org/all/20260209062952.2049053-1-den@valinux.co.jp/
v4: https://lore.kernel.org/all/20260206172646.1556847-1-den@valinux.co.jp/
v3: https://lore.kernel.org/all/20260204145440.950609-1-den@valinux.co.jp/
v2: https://lore.kernel.org/all/20260127033420.3460579-1-den@valinux.co.jp/
v1: https://lore.kernel.org/dmaengine/20260126073652.3293564-1-den@valinux.co.jp/
+
https://lore.kernel.org/linux-pci/20260126071550.3233631-1-den@valinux.co.jp/
Thanks for reviewing,
Koichiro Den (8):
dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler
dmaengine: dw-edma: Cache per-channel IRQ and emulation doorbell
offset
PCI: endpoint: Add auxiliary resource query API
PCI: dwc: Record integrated eDMA register window
PCI: dwc: ep: Report integrated eDMA resources via EPC aux-resource
API
PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested
PCI: endpoint: pci-ep-msi: Fix error unwind and prevent double alloc
PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
drivers/dma/dw-edma/dw-edma-core.c | 57 +++++++-
drivers/dma/dw-edma/dw-edma-core.h | 19 +++
drivers/dma/dw-edma/dw-edma-v0-core.c | 22 +++
drivers/dma/dw-edma/dw-hdma-v0-core.c | 8 ++
.../pci/controller/dwc/pcie-designware-ep.c | 78 +++++++++++
drivers/pci/controller/dwc/pcie-designware.c | 4 +
drivers/pci/controller/dwc/pcie-designware.h | 2 +
drivers/pci/endpoint/functions/pci-epf-test.c | 38 ++++-
drivers/pci/endpoint/functions/pci-epf-vntb.c | 3 +-
drivers/pci/endpoint/pci-ep-msi.c | 130 ++++++++++++++++--
drivers/pci/endpoint/pci-epc-core.c | 41 ++++++
include/linux/dma/edma.h | 17 +++
include/linux/pci-epc.h | 46 +++++++
include/linux/pci-epf.h | 17 ++-
14 files changed, 460 insertions(+), 22 deletions(-)
--
2.51.0
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v6 1/8] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
@ 2026-02-09 12:53 ` Koichiro Den
2026-02-09 16:05 ` Frank Li
2026-02-09 12:53 ` [PATCH v6 2/8] dmaengine: dw-edma: Cache per-channel IRQ and emulation doorbell offset Koichiro Den
` (7 subsequent siblings)
8 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-09 12:53 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, cassel, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas, kishon, jdmason, allenbh
Cc: dmaengine, linux-pci, ntb, linux-kselftest, linux-kernel
Some DesignWare eDMA instances support "interrupt emulation", where a
software write can assert the IRQ line without setting the normal
DONE/ABORT status bits.
With a shared IRQ handler the driver cannot reliably distinguish an
emulated interrupt from a real one by only looking at DONE/ABORT status
bits. Leaving the emulated IRQ asserted may leave a level-triggered IRQ
line permanently asserted.
Add a core callback, .ack_emulated_irq(), to perform the core-specific
deassert sequence and call it from the read/write/common IRQ handlers.
Note that previously a direct software write could assert the emulated
IRQ without DMA activity, leading to the interrupt never getting
deasserted. This patch resolves it.
For v0, a zero write to INT_CLEAR deasserts the emulated IRQ and is a
no-op for real interrupts. HDMA is not tested or verified and is
therefore unsupported for now.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/dma/dw-edma/dw-edma-core.c | 48 ++++++++++++++++++++++++---
drivers/dma/dw-edma/dw-edma-core.h | 10 ++++++
drivers/dma/dw-edma/dw-edma-v0-core.c | 11 ++++++
3 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 8e5f7defa6b6..fe131abf1ca3 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -663,7 +663,24 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
chan->status = EDMA_ST_IDLE;
}
-static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
+static inline irqreturn_t dw_edma_interrupt_emulated(void *data)
+{
+ struct dw_edma_irq *dw_irq = data;
+ struct dw_edma *dw = dw_irq->dw;
+
+ /*
+ * Interrupt emulation may assert the IRQ line without updating the
+ * normal DONE/ABORT status bits. With a shared IRQ handler we
+ * cannot reliably detect such events by status registers alone, so
+ * always perform the core-specific deassert sequence.
+ */
+ if (dw_edma_core_ack_emulated_irq(dw))
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+static inline irqreturn_t dw_edma_interrupt_write_inner(int irq, void *data)
{
struct dw_edma_irq *dw_irq = data;
@@ -672,7 +689,7 @@ static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
dw_edma_abort_interrupt);
}
-static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
+static inline irqreturn_t dw_edma_interrupt_read_inner(int irq, void *data)
{
struct dw_edma_irq *dw_irq = data;
@@ -681,12 +698,33 @@ static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
dw_edma_abort_interrupt);
}
-static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
+static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
+{
+ irqreturn_t ret = IRQ_NONE;
+
+ ret |= dw_edma_interrupt_write_inner(irq, data);
+ ret |= dw_edma_interrupt_emulated(data);
+
+ return ret;
+}
+
+static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
+{
+ irqreturn_t ret = IRQ_NONE;
+
+ ret |= dw_edma_interrupt_read_inner(irq, data);
+ ret |= dw_edma_interrupt_emulated(data);
+
+ return ret;
+}
+
+static inline irqreturn_t dw_edma_interrupt_common(int irq, void *data)
{
irqreturn_t ret = IRQ_NONE;
- ret |= dw_edma_interrupt_write(irq, data);
- ret |= dw_edma_interrupt_read(irq, data);
+ ret |= dw_edma_interrupt_write_inner(irq, data);
+ ret |= dw_edma_interrupt_read_inner(irq, data);
+ ret |= dw_edma_interrupt_emulated(data);
return ret;
}
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 71894b9e0b15..50b87b63b581 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -126,6 +126,7 @@ struct dw_edma_core_ops {
void (*start)(struct dw_edma_chunk *chunk, bool first);
void (*ch_config)(struct dw_edma_chan *chan);
void (*debugfs_on)(struct dw_edma *dw);
+ void (*ack_emulated_irq)(struct dw_edma *dw);
};
struct dw_edma_sg {
@@ -206,4 +207,13 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
dw->core->debugfs_on(dw);
}
+static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw)
+{
+ if (!dw->core->ack_emulated_irq)
+ return -EOPNOTSUPP;
+
+ dw->core->ack_emulated_irq(dw);
+ return 0;
+}
+
#endif /* _DW_EDMA_CORE_H */
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index b75fdaffad9a..82b9c063c10f 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -509,6 +509,16 @@ static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
dw_edma_v0_debugfs_on(dw);
}
+static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw)
+{
+ /*
+ * Interrupt emulation may assert the IRQ without setting
+ * DONE/ABORT status bits. A zero write to INT_CLEAR deasserts the
+ * emulated IRQ, while being a no-op for real interrupts.
+ */
+ SET_BOTH_32(dw, int_clear, 0);
+}
+
static const struct dw_edma_core_ops dw_edma_v0_core = {
.off = dw_edma_v0_core_off,
.ch_count = dw_edma_v0_core_ch_count,
@@ -517,6 +527,7 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
.start = dw_edma_v0_core_start,
.ch_config = dw_edma_v0_core_ch_config,
.debugfs_on = dw_edma_v0_core_debugfs_on,
+ .ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq,
};
void dw_edma_v0_core_register(struct dw_edma *dw)
--
2.51.0
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v6 2/8] dmaengine: dw-edma: Cache per-channel IRQ and emulation doorbell offset
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
2026-02-09 12:53 ` [PATCH v6 1/8] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler Koichiro Den
@ 2026-02-09 12:53 ` Koichiro Den
2026-02-09 16:13 ` Frank Li
2026-02-09 12:53 ` [PATCH v6 3/8] PCI: endpoint: Add auxiliary resource query API Koichiro Den
` (6 subsequent siblings)
8 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-09 12:53 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, cassel, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas, kishon, jdmason, allenbh
Cc: dmaengine, linux-pci, ntb, linux-kselftest, linux-kernel
Some DesignWare PCIe endpoint controllers integrate a DesignWare
eDMA/HDMA instance. In remote eDMA use cases (e.g. exposing the eDMA
MMIO window and per-channel linked-list regions to a peer via BARs),
consumers need a stable way to discover:
- the Linux IRQ number associated with a given channel's interrupt
vector,
- an offset within the eDMA register window that can be used as an
interrupt-emulation doorbell for that channel.
Store the requested Linux IRQ number in struct dw_edma_irq at IRQ
request time and cache per-channel metadata in struct dw_edma_chip
(ch_info_wr/rd) during channel setup. Add a core callback, .ch_info(),
to fill core-specific metadata such as the doorbell register offset;
implement it for the v0 eDMA core (use rd_int_status as a suitable
doorbell target) and provide a placeholder for HDMA until the correct
offset is known.
No functional change for normal DMA operation. This only makes the
metadata available to controller/platform drivers that need to expose or
consume eDMA-related resources.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/dma/dw-edma/dw-edma-core.c | 9 +++++++++
drivers/dma/dw-edma/dw-edma-core.h | 9 +++++++++
drivers/dma/dw-edma/dw-edma-v0-core.c | 11 +++++++++++
drivers/dma/dw-edma/dw-hdma-v0-core.c | 8 ++++++++
include/linux/dma/edma.h | 17 +++++++++++++++++
5 files changed, 54 insertions(+)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index fe131abf1ca3..bd5ff4a4431a 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -760,6 +760,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
{
struct dw_edma_chip *chip = dw->chip;
struct device *dev = chip->dev;
+ struct dw_edma_ch_info *info;
struct dw_edma_chan *chan;
struct dw_edma_irq *irq;
struct dma_device *dma;
@@ -779,9 +780,11 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
if (i < dw->wr_ch_cnt) {
chan->id = i;
chan->dir = EDMA_DIR_WRITE;
+ info = &chip->ch_info_wr[chan->id];
} else {
chan->id = i - dw->wr_ch_cnt;
chan->dir = EDMA_DIR_READ;
+ info = &chip->ch_info_rd[chan->id];
}
chan->configured = false;
@@ -807,6 +810,10 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
irq = &dw->irq[pos];
+ /* cache channel-specific info */
+ dw_edma_core_ch_info(dw, chan, info);
+ info->irq = irq->irq;
+
if (chan->dir == EDMA_DIR_WRITE)
irq->wr_mask |= BIT(chan->id);
else
@@ -910,6 +917,7 @@ static int dw_edma_irq_request(struct dw_edma *dw,
if (irq_get_msi_desc(irq))
get_cached_msi_msg(irq, &dw->irq[0].msi);
+ dw->irq[0].irq = irq;
dw->nr_irqs = 1;
} else {
/* Distribute IRQs equally among all channels */
@@ -936,6 +944,7 @@ static int dw_edma_irq_request(struct dw_edma *dw,
if (irq_get_msi_desc(irq))
get_cached_msi_msg(irq, &dw->irq[i].msi);
+ dw->irq[i].irq = irq;
}
dw->nr_irqs = i;
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 50b87b63b581..82f8f3b38752 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -93,6 +93,7 @@ struct dw_edma_irq {
u32 wr_mask;
u32 rd_mask;
struct dw_edma *dw;
+ int irq;
};
struct dw_edma {
@@ -127,6 +128,7 @@ struct dw_edma_core_ops {
void (*ch_config)(struct dw_edma_chan *chan);
void (*debugfs_on)(struct dw_edma *dw);
void (*ack_emulated_irq)(struct dw_edma *dw);
+ void (*ch_info)(struct dw_edma_chan *chan, struct dw_edma_ch_info *info);
};
struct dw_edma_sg {
@@ -216,4 +218,11 @@ static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw)
return 0;
}
+static inline void
+dw_edma_core_ch_info(struct dw_edma *dw, struct dw_edma_chan *chan,
+ struct dw_edma_ch_info *info)
+{
+ dw->core->ch_info(chan, info);
+}
+
#endif /* _DW_EDMA_CORE_H */
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index 82b9c063c10f..0b8d4b6a5e26 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -519,6 +519,16 @@ static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw)
SET_BOTH_32(dw, int_clear, 0);
}
+static void dw_edma_v0_core_ch_info(struct dw_edma_chan *chan,
+ struct dw_edma_ch_info *info)
+{
+ /*
+ * rd_int_status is chosen arbitrarily, but wr_int_status would be
+ * equally suitable.
+ */
+ info->db_offset = offsetof(struct dw_edma_v0_regs, rd_int_status);
+}
+
static const struct dw_edma_core_ops dw_edma_v0_core = {
.off = dw_edma_v0_core_off,
.ch_count = dw_edma_v0_core_ch_count,
@@ -528,6 +538,7 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
.ch_config = dw_edma_v0_core_ch_config,
.debugfs_on = dw_edma_v0_core_debugfs_on,
.ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq,
+ .ch_info = dw_edma_v0_core_ch_info,
};
void dw_edma_v0_core_register(struct dw_edma *dw)
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index e3f8db4fe909..1076b394c45f 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -283,6 +283,13 @@ static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
dw_hdma_v0_debugfs_on(dw);
}
+static void dw_hdma_v0_core_ch_info(struct dw_edma_chan *chan,
+ struct dw_edma_ch_info *info)
+{
+ /* Implement once the correct offset is known. */
+ info->db_offset = ~0;
+}
+
static const struct dw_edma_core_ops dw_hdma_v0_core = {
.off = dw_hdma_v0_core_off,
.ch_count = dw_hdma_v0_core_ch_count,
@@ -291,6 +298,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
.start = dw_hdma_v0_core_start,
.ch_config = dw_hdma_v0_core_ch_config,
.debugfs_on = dw_hdma_v0_core_debugfs_on,
+ .ch_info = dw_hdma_v0_core_ch_info,
};
void dw_hdma_v0_core_register(struct dw_edma *dw)
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 3080747689f6..921250204a08 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -60,6 +60,19 @@ enum dw_edma_chip_flags {
DW_EDMA_CHIP_LOCAL = BIT(0),
};
+/**
+ * struct dw_edma_ch_info - DW eDMA channel metadata
+ * @irq: Linux IRQ number used by this channel's interrupt vector
+ * @db_offset: offset within the eDMA register window that can be used as
+ * an interrupt-emulation doorbell for this channel
+ */
+struct dw_edma_ch_info {
+ int irq;
+
+ /* Fields below are filled in by dw_edma_core_ops->ch_info() */
+ resource_size_t db_offset;
+};
+
/**
* struct dw_edma_chip - representation of DesignWare eDMA controller hardware
* @dev: struct device of the eDMA controller
@@ -96,6 +109,10 @@ struct dw_edma_chip {
struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH];
struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH];
+ /* cached channel info */
+ struct dw_edma_ch_info ch_info_wr[EDMA_MAX_WR_CH];
+ struct dw_edma_ch_info ch_info_rd[EDMA_MAX_RD_CH];
+
enum dw_edma_map_format mf;
struct dw_edma *dw;
--
2.51.0
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v6 3/8] PCI: endpoint: Add auxiliary resource query API
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
2026-02-09 12:53 ` [PATCH v6 1/8] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler Koichiro Den
2026-02-09 12:53 ` [PATCH v6 2/8] dmaengine: dw-edma: Cache per-channel IRQ and emulation doorbell offset Koichiro Den
@ 2026-02-09 12:53 ` Koichiro Den
2026-02-09 15:59 ` Frank Li
2026-02-09 12:53 ` [PATCH v6 4/8] PCI: dwc: Record integrated eDMA register window Koichiro Den
` (5 subsequent siblings)
8 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-09 12:53 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, cassel, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas, kishon, jdmason, allenbh
Cc: dmaengine, linux-pci, ntb, linux-kselftest, linux-kernel
Endpoint controller drivers may integrate auxiliary blocks (e.g. DMA
engines) whose register windows and descriptor memories metadata need to
be exposed to a remote peer. Endpoint function drivers need a generic
way to discover such resources without hard-coding controller-specific
helpers.
Add pci_epc_get_aux_resources() and the corresponding pci_epc_ops
get_aux_resources() callback. The API returns a list of resources
described by type, physical address and size, plus type-specific
metadata.
Passing resources == NULL (or num_resources == 0) returns the required
number of entries.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/endpoint/pci-epc-core.c | 41 +++++++++++++++++++++++++
include/linux/pci-epc.h | 46 +++++++++++++++++++++++++++++
2 files changed, 87 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 068155819c57..01de4bd5047a 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -155,6 +155,47 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
}
EXPORT_SYMBOL_GPL(pci_epc_get_features);
+/**
+ * pci_epc_get_aux_resources() - query EPC-provided auxiliary resources
+ * @epc: EPC device
+ * @func_no: function number
+ * @vfunc_no: virtual function number
+ * @resources: output array (may be NULL to query required count)
+ * @num_resources: size of @resources array in entries (0 when querying count)
+ *
+ * Some EPC backends integrate auxiliary blocks (e.g. DMA engines) whose control
+ * registers and/or descriptor memories can be exposed to the host by mapping
+ * them into BAR space. This helper queries the backend for such resources.
+ *
+ * Return:
+ * * >= 0: number of resources returned (or required, if @resources is NULL)
+ * * -EOPNOTSUPP: backend does not support auxiliary resource queries
+ * * other -errno on failure
+ */
+int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_aux_resource *resources,
+ int num_resources)
+{
+ int ret;
+
+ if (!epc || !epc->ops)
+ return -EINVAL;
+
+ if (func_no >= epc->max_functions)
+ return -EINVAL;
+
+ if (!epc->ops->get_aux_resources)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&epc->lock);
+ ret = epc->ops->get_aux_resources(epc, func_no, vfunc_no, resources,
+ num_resources);
+ mutex_unlock(&epc->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources);
+
/**
* pci_epc_stop() - stop the PCI link
* @epc: the link of the EPC device that has to be stopped
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index c021c7af175f..5d3e1986b49f 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -61,6 +61,45 @@ struct pci_epc_map {
void __iomem *virt_addr;
};
+/**
+ * enum pci_epc_aux_resource_type - auxiliary resource type identifiers
+ * @PCI_EPC_AUX_DMA_CTRL_MMIO: Integrated DMA controller register window (MMIO)
+ * @PCI_EPC_AUX_DMA_CHAN_DESC: Per-channel DMA descriptor
+ *
+ * EPC backends may expose auxiliary blocks (e.g. DMA engines) by mapping their
+ * register windows and descriptor memories into BAR space. This enum
+ * identifies the type of each exposable resource.
+ */
+enum pci_epc_aux_resource_type {
+ PCI_EPC_AUX_DMA_CTRL_MMIO,
+ PCI_EPC_AUX_DMA_CHAN_DESC,
+};
+
+/**
+ * struct pci_epc_aux_resource - a physical auxiliary resource that may be
+ * exposed for peer use
+ * @type: resource type, see enum pci_epc_aux_resource_type
+ * @phys_addr: physical base address of the resource
+ * @size: size of the resource in bytes
+ * @u: type-specific metadata
+ *
+ * For @PCI_EPC_AUX_DMA_CHAN_DESC, @u.dma_chan_desc provides per-channel
+ * information.
+ */
+struct pci_epc_aux_resource {
+ enum pci_epc_aux_resource_type type;
+ phys_addr_t phys_addr;
+ resource_size_t size;
+
+ union {
+ /* PCI_EPC_AUX_DMA_CHAN_DESC */
+ struct {
+ int irq;
+ resource_size_t db_offset;
+ } dma_chan_desc;
+ } u;
+};
+
/**
* struct pci_epc_ops - set of function pointers for performing EPC operations
* @write_header: ops to populate configuration space header
@@ -84,6 +123,7 @@ struct pci_epc_map {
* @start: ops to start the PCI link
* @stop: ops to stop the PCI link
* @get_features: ops to get the features supported by the EPC
+ * @get_aux_resources: ops to retrieve controller-owned auxiliary resources
* @owner: the module owner containing the ops
*/
struct pci_epc_ops {
@@ -115,6 +155,9 @@ struct pci_epc_ops {
void (*stop)(struct pci_epc *epc);
const struct pci_epc_features* (*get_features)(struct pci_epc *epc,
u8 func_no, u8 vfunc_no);
+ int (*get_aux_resources)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_aux_resource *resources,
+ int num_resources);
struct module *owner;
};
@@ -309,6 +352,9 @@ int pci_epc_start(struct pci_epc *epc);
void pci_epc_stop(struct pci_epc *epc);
const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
u8 func_no, u8 vfunc_no);
+int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_aux_resource *resources,
+ int num_resources);
enum pci_barno
pci_epc_get_first_free_bar(const struct pci_epc_features *epc_features);
enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features
--
2.51.0
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v6 4/8] PCI: dwc: Record integrated eDMA register window
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
` (2 preceding siblings ...)
2026-02-09 12:53 ` [PATCH v6 3/8] PCI: endpoint: Add auxiliary resource query API Koichiro Den
@ 2026-02-09 12:53 ` Koichiro Den
2026-02-09 12:53 ` [PATCH v6 5/8] PCI: dwc: ep: Report integrated eDMA resources via EPC aux-resource API Koichiro Den
` (4 subsequent siblings)
8 siblings, 0 replies; 33+ messages in thread
From: Koichiro Den @ 2026-02-09 12:53 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, cassel, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas, kishon, jdmason, allenbh
Cc: dmaengine, linux-pci, ntb, linux-kselftest, linux-kernel
Some DesignWare PCIe controllers integrate an eDMA block whose registers
are located in a dedicated register window. Endpoint function drivers
may need the physical base and size of this window to map/expose it to a
peer.
Record the physical base and size of the integrated eDMA register window
in struct dw_pcie.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/pcie-designware.c | 4 ++++
drivers/pci/controller/dwc/pcie-designware.h | 2 ++
2 files changed, 6 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 5741c09dde7f..f82ed189f6ae 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -162,8 +162,12 @@ int dw_pcie_get_resources(struct dw_pcie *pci)
pci->edma.reg_base = devm_ioremap_resource(pci->dev, res);
if (IS_ERR(pci->edma.reg_base))
return PTR_ERR(pci->edma.reg_base);
+ pci->edma_reg_phys = res->start;
+ pci->edma_reg_size = resource_size(res);
} else if (pci->atu_size >= 2 * DEFAULT_DBI_DMA_OFFSET) {
pci->edma.reg_base = pci->atu_base + DEFAULT_DBI_DMA_OFFSET;
+ pci->edma_reg_phys = pci->atu_phys_addr + DEFAULT_DBI_DMA_OFFSET;
+ pci->edma_reg_size = pci->atu_size - DEFAULT_DBI_DMA_OFFSET;
}
}
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 43d7606bc987..88e4a9e514e8 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -542,6 +542,8 @@ struct dw_pcie {
int max_link_speed;
u8 n_fts[2];
struct dw_edma_chip edma;
+ phys_addr_t edma_reg_phys;
+ resource_size_t edma_reg_size;
bool l1ss_support; /* L1 PM Substates support */
struct clk_bulk_data app_clks[DW_PCIE_NUM_APP_CLKS];
struct clk_bulk_data core_clks[DW_PCIE_NUM_CORE_CLKS];
--
2.51.0
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v6 5/8] PCI: dwc: ep: Report integrated eDMA resources via EPC aux-resource API
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
` (3 preceding siblings ...)
2026-02-09 12:53 ` [PATCH v6 4/8] PCI: dwc: Record integrated eDMA register window Koichiro Den
@ 2026-02-09 12:53 ` Koichiro Den
2026-02-09 12:53 ` [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested Koichiro Den
` (3 subsequent siblings)
8 siblings, 0 replies; 33+ messages in thread
From: Koichiro Den @ 2026-02-09 12:53 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, cassel, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas, kishon, jdmason, allenbh
Cc: dmaengine, linux-pci, ntb, linux-kselftest, linux-kernel
Implement pci_epc_ops.get_aux_resources() for DesignWare PCIe endpoint
controllers with integrated eDMA.
Report:
- the eDMA controller MMIO window (physical base + size),
- each non-empty per-channel linked-list region, along with
per-channel metadata such as the Linux IRQ number and the
interrupt-emulation doorbell register offset.
This allows endpoint function drivers (e.g. pci-epf-test) to discover
the eDMA resources and map a suitable doorbell target into BAR space.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
.../pci/controller/dwc/pcie-designware-ep.c | 78 +++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7e7844ff0f7e..c99ba1b85da4 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -808,6 +808,83 @@ dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
return ep->ops->get_features(ep);
}
+static int
+dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_aux_resource *resources,
+ int num_resources)
+{
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct dw_edma_chip *edma = &pci->edma;
+ int ll_cnt = 0, needed, idx = 0;
+ resource_size_t dma_size;
+ phys_addr_t dma_phys;
+ unsigned int i;
+
+ if (!pci->edma_reg_size)
+ return 0;
+
+ dma_phys = pci->edma_reg_phys;
+ dma_size = pci->edma_reg_size;
+
+ for (i = 0; i < edma->ll_wr_cnt; i++)
+ if (edma->ll_region_wr[i].sz)
+ ll_cnt++;
+
+ for (i = 0; i < edma->ll_rd_cnt; i++)
+ if (edma->ll_region_rd[i].sz)
+ ll_cnt++;
+
+ needed = 1 + ll_cnt;
+
+ /* Count query mode */
+ if (!resources || !num_resources)
+ return needed;
+
+ if (num_resources < needed)
+ return -ENOSPC;
+
+ resources[idx++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_CTRL_MMIO,
+ .phys_addr = dma_phys,
+ .size = dma_size,
+ };
+
+ /* One LL region per write channel */
+ for (i = 0; i < edma->ll_wr_cnt; i++) {
+ if (!edma->ll_region_wr[i].sz)
+ continue;
+
+ resources[idx++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_CHAN_DESC,
+ .phys_addr = edma->ll_region_wr[i].paddr,
+ .size = edma->ll_region_wr[i].sz,
+ .u.dma_chan_desc = {
+ .irq = edma->ch_info_wr[i].irq,
+ .db_offset = edma->ch_info_wr[i].db_offset,
+ },
+ };
+ }
+
+ /* One LL region per read channel */
+ for (i = 0; i < edma->ll_rd_cnt; i++) {
+ if (!edma->ll_region_rd[i].sz)
+ continue;
+
+ resources[idx++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_CHAN_DESC,
+ .phys_addr = edma->ll_region_rd[i].paddr,
+ .size = edma->ll_region_rd[i].sz,
+ .u.dma_chan_desc = {
+ .irq = edma->ch_info_rd[i].irq,
+ .db_offset = edma->ch_info_rd[i].db_offset,
+ },
+ };
+ }
+
+ return idx;
+}
+
static const struct pci_epc_ops epc_ops = {
.write_header = dw_pcie_ep_write_header,
.set_bar = dw_pcie_ep_set_bar,
@@ -823,6 +900,7 @@ static const struct pci_epc_ops epc_ops = {
.start = dw_pcie_ep_start,
.stop = dw_pcie_ep_stop,
.get_features = dw_pcie_ep_get_features,
+ .get_aux_resources = dw_pcie_ep_get_aux_resources,
};
/**
--
2.51.0
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
` (4 preceding siblings ...)
2026-02-09 12:53 ` [PATCH v6 5/8] PCI: dwc: ep: Report integrated eDMA resources via EPC aux-resource API Koichiro Den
@ 2026-02-09 12:53 ` Koichiro Den
2026-02-09 15:57 ` Frank Li
2026-02-10 12:36 ` Niklas Cassel
2026-02-09 12:53 ` [PATCH v6 7/8] PCI: endpoint: pci-ep-msi: Fix error unwind and prevent double alloc Koichiro Den
` (2 subsequent siblings)
8 siblings, 2 replies; 33+ messages in thread
From: Koichiro Den @ 2026-02-09 12:53 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, cassel, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas, kishon, jdmason, allenbh
Cc: dmaengine, linux-pci, ntb, linux-kselftest, linux-kernel
pci_epf_test_enable_doorbell() allocates a doorbell and then installs
the interrupt handler with request_threaded_irq(). On failures before
the IRQ is successfully requested (e.g. no free BAR,
request_threaded_irq() failure), the error path jumps to
err_doorbell_cleanup and calls pci_epf_test_doorbell_cleanup().
pci_epf_test_doorbell_cleanup() unconditionally calls free_irq() for the
doorbell virq, which can trigger "Trying to free already-free IRQ"
warnings when the IRQ was never requested.
Track whether the doorbell IRQ has been successfully requested and only
call free_irq() when it has.
Fixes: eff0c286aa91 ("PCI: endpoint: pci-epf-test: Add doorbell test support")
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/endpoint/functions/pci-epf-test.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 6952ee418622..23034f548c90 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -86,6 +86,7 @@ struct pci_epf_test {
bool dma_private;
const struct pci_epc_features *epc_features;
struct pci_epf_bar db_bar;
+ bool db_irq_requested;
size_t bar_size[PCI_STD_NUM_BARS];
};
@@ -715,7 +716,10 @@ 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];
struct pci_epf *epf = epf_test->epf;
- free_irq(epf->db_msg[0].virq, epf_test);
+ if (epf_test->db_irq_requested && epf->db_msg) {
+ free_irq(epf->db_msg[0].virq, epf_test);
+ epf_test->db_irq_requested = false;
+ }
reg->doorbell_bar = cpu_to_le32(NO_BAR);
pci_epf_free_doorbell(epf);
@@ -741,6 +745,8 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
if (bar < BAR_0)
goto err_doorbell_cleanup;
+ epf_test->db_irq_requested = false;
+
ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
pci_epf_test_doorbell_handler, IRQF_ONESHOT,
"pci-ep-test-doorbell", epf_test);
@@ -751,6 +757,7 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
goto err_doorbell_cleanup;
}
+ epf_test->db_irq_requested = true;
reg->doorbell_data = cpu_to_le32(msg->data);
reg->doorbell_bar = cpu_to_le32(bar);
--
2.51.0
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v6 7/8] PCI: endpoint: pci-ep-msi: Fix error unwind and prevent double alloc
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
` (5 preceding siblings ...)
2026-02-09 12:53 ` [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested Koichiro Den
@ 2026-02-09 12:53 ` Koichiro Den
2026-02-09 15:53 ` Frank Li
2026-02-09 12:53 ` [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
2026-02-10 12:24 ` [PATCH v6 0/8] " Niklas Cassel
8 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-09 12:53 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, cassel, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas, kishon, jdmason, allenbh
Cc: dmaengine, linux-pci, ntb, linux-kselftest, linux-kernel
pci_epf_alloc_doorbell() stores the allocated doorbell message array in
epf->db_msg/epf->num_db before requesting MSI vectors. If MSI allocation
fails, the array is freed but the EPF state may still point to freed
memory.
Clear epf->db_msg and epf->num_db on the MSI allocation failure path so
that later cleanup cannot double-free the array and callers can retry
allocation.
Also return -EBUSY when doorbells have already been allocated to prevent
leaking or overwriting an existing allocation.
Fixes: 1c3b002c6bf6 ("PCI: endpoint: Add RC-to-EP doorbell support using platform MSI controller")
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/endpoint/pci-ep-msi.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c
index 1b58357b905f..ad8a81d6ad77 100644
--- a/drivers/pci/endpoint/pci-ep-msi.c
+++ b/drivers/pci/endpoint/pci-ep-msi.c
@@ -50,6 +50,9 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
return -EINVAL;
}
+ if (epf->db_msg)
+ return -EBUSY;
+
domain = of_msi_map_get_device_domain(epc->dev.parent, 0,
DOMAIN_BUS_PLATFORM_MSI);
if (!domain) {
@@ -79,6 +82,8 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
if (ret) {
dev_err(dev, "Failed to allocate MSI\n");
kfree(msg);
+ epf->db_msg = NULL;
+ epf->num_db = 0;
return ret;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
` (6 preceding siblings ...)
2026-02-09 12:53 ` [PATCH v6 7/8] PCI: endpoint: pci-ep-msi: Fix error unwind and prevent double alloc Koichiro Den
@ 2026-02-09 12:53 ` Koichiro Den
2026-02-09 15:51 ` Frank Li
2026-02-10 12:24 ` [PATCH v6 0/8] " Niklas Cassel
8 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-09 12:53 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, cassel, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas, kishon, jdmason, allenbh
Cc: dmaengine, linux-pci, ntb, linux-kselftest, linux-kernel
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 <den@valinux.co.jp>
---
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) {
+ 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)
+{
+ 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
^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-09 12:53 ` [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
@ 2026-02-09 15:51 ` Frank Li
2026-02-10 2:10 ` Koichiro Den
0 siblings, 1 reply; 33+ messages in thread
From: Frank Li @ 2026-02-09 15:51 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
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 <den@valinux.co.jp>
> ---
> 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
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 7/8] PCI: endpoint: pci-ep-msi: Fix error unwind and prevent double alloc
2026-02-09 12:53 ` [PATCH v6 7/8] PCI: endpoint: pci-ep-msi: Fix error unwind and prevent double alloc Koichiro Den
@ 2026-02-09 15:53 ` Frank Li
0 siblings, 0 replies; 33+ messages in thread
From: Frank Li @ 2026-02-09 15:53 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 09:53:15PM +0900, Koichiro Den wrote:
> pci_epf_alloc_doorbell() stores the allocated doorbell message array in
> epf->db_msg/epf->num_db before requesting MSI vectors. If MSI allocation
> fails, the array is freed but the EPF state may still point to freed
> memory.
>
> Clear epf->db_msg and epf->num_db on the MSI allocation failure path so
> that later cleanup cannot double-free the array and callers can retry
> allocation.
>
> Also return -EBUSY when doorbells have already been allocated to prevent
> leaking or overwriting an existing allocation.
>
> Fixes: 1c3b002c6bf6 ("PCI: endpoint: Add RC-to-EP doorbell support using platform MSI controller")
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Thanks, fix patch should be first.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Frank
> drivers/pci/endpoint/pci-ep-msi.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c
> index 1b58357b905f..ad8a81d6ad77 100644
> --- a/drivers/pci/endpoint/pci-ep-msi.c
> +++ b/drivers/pci/endpoint/pci-ep-msi.c
> @@ -50,6 +50,9 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
> return -EINVAL;
> }
>
> + if (epf->db_msg)
> + return -EBUSY;
> +
> domain = of_msi_map_get_device_domain(epc->dev.parent, 0,
> DOMAIN_BUS_PLATFORM_MSI);
> if (!domain) {
> @@ -79,6 +82,8 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
> if (ret) {
> dev_err(dev, "Failed to allocate MSI\n");
> kfree(msg);
> + epf->db_msg = NULL;
> + epf->num_db = 0;
> return ret;
> }
>
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested
2026-02-09 12:53 ` [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested Koichiro Den
@ 2026-02-09 15:57 ` Frank Li
2026-02-10 1:54 ` Koichiro Den
2026-02-10 12:36 ` Niklas Cassel
1 sibling, 1 reply; 33+ messages in thread
From: Frank Li @ 2026-02-09 15:57 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 09:53:14PM +0900, Koichiro Den wrote:
> pci_epf_test_enable_doorbell() allocates a doorbell and then installs
> the interrupt handler with request_threaded_irq(). On failures before
> the IRQ is successfully requested (e.g. no free BAR,
> request_threaded_irq() failure), the error path jumps to
> err_doorbell_cleanup and calls pci_epf_test_doorbell_cleanup().
>
> pci_epf_test_doorbell_cleanup() unconditionally calls free_irq() for the
> doorbell virq, which can trigger "Trying to free already-free IRQ"
> warnings when the IRQ was never requested.
>
> Track whether the doorbell IRQ has been successfully requested and only
> call free_irq() when it has.
>
> Fixes: eff0c286aa91 ("PCI: endpoint: pci-epf-test: Add doorbell test support")
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> drivers/pci/endpoint/functions/pci-epf-test.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> index 6952ee418622..23034f548c90 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> @@ -86,6 +86,7 @@ struct pci_epf_test {
> bool dma_private;
> const struct pci_epc_features *epc_features;
> struct pci_epf_bar db_bar;
> + bool db_irq_requested;
Prevous patch clean up epf->num_db = 0 at doorbell_free(). can you check
epf->num_db ?
Frank
> size_t bar_size[PCI_STD_NUM_BARS];
> };
>
> @@ -715,7 +716,10 @@ 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];
> struct pci_epf *epf = epf_test->epf;
>
> - free_irq(epf->db_msg[0].virq, epf_test);
> + if (epf_test->db_irq_requested && epf->db_msg) {
> + free_irq(epf->db_msg[0].virq, epf_test);
> + epf_test->db_irq_requested = false;
> + }
> reg->doorbell_bar = cpu_to_le32(NO_BAR);
>
> pci_epf_free_doorbell(epf);
> @@ -741,6 +745,8 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> if (bar < BAR_0)
> goto err_doorbell_cleanup;
>
> + epf_test->db_irq_requested = false;
> +
> ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
> pci_epf_test_doorbell_handler, IRQF_ONESHOT,
> "pci-ep-test-doorbell", epf_test);
> @@ -751,6 +757,7 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> goto err_doorbell_cleanup;
> }
>
> + epf_test->db_irq_requested = true;
> reg->doorbell_data = cpu_to_le32(msg->data);
> reg->doorbell_bar = cpu_to_le32(bar);
>
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 3/8] PCI: endpoint: Add auxiliary resource query API
2026-02-09 12:53 ` [PATCH v6 3/8] PCI: endpoint: Add auxiliary resource query API Koichiro Den
@ 2026-02-09 15:59 ` Frank Li
0 siblings, 0 replies; 33+ messages in thread
From: Frank Li @ 2026-02-09 15:59 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 09:53:11PM +0900, Koichiro Den wrote:
> Endpoint controller drivers may integrate auxiliary blocks (e.g. DMA
> engines) whose register windows and descriptor memories metadata need to
> be exposed to a remote peer. Endpoint function drivers need a generic
> way to discover such resources without hard-coding controller-specific
> helpers.
>
> Add pci_epc_get_aux_resources() and the corresponding pci_epc_ops
> get_aux_resources() callback. The API returns a list of resources
> described by type, physical address and size, plus type-specific
> metadata.
>
> Passing resources == NULL (or num_resources == 0) returns the required
> number of entries.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/pci/endpoint/pci-epc-core.c | 41 +++++++++++++++++++++++++
> include/linux/pci-epc.h | 46 +++++++++++++++++++++++++++++
> 2 files changed, 87 insertions(+)
>
> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
> index 068155819c57..01de4bd5047a 100644
> --- a/drivers/pci/endpoint/pci-epc-core.c
> +++ b/drivers/pci/endpoint/pci-epc-core.c
> @@ -155,6 +155,47 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
> }
> EXPORT_SYMBOL_GPL(pci_epc_get_features);
>
> +/**
> + * pci_epc_get_aux_resources() - query EPC-provided auxiliary resources
> + * @epc: EPC device
> + * @func_no: function number
> + * @vfunc_no: virtual function number
> + * @resources: output array (may be NULL to query required count)
> + * @num_resources: size of @resources array in entries (0 when querying count)
> + *
> + * Some EPC backends integrate auxiliary blocks (e.g. DMA engines) whose control
> + * registers and/or descriptor memories can be exposed to the host by mapping
> + * them into BAR space. This helper queries the backend for such resources.
> + *
> + * Return:
> + * * >= 0: number of resources returned (or required, if @resources is NULL)
> + * * -EOPNOTSUPP: backend does not support auxiliary resource queries
> + * * other -errno on failure
> + */
> +int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_aux_resource *resources,
> + int num_resources)
> +{
> + int ret;
> +
> + if (!epc || !epc->ops)
> + return -EINVAL;
> +
> + if (func_no >= epc->max_functions)
> + return -EINVAL;
> +
> + if (!epc->ops->get_aux_resources)
> + return -EOPNOTSUPP;
> +
> + mutex_lock(&epc->lock);
> + ret = epc->ops->get_aux_resources(epc, func_no, vfunc_no, resources,
> + num_resources);
> + mutex_unlock(&epc->lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources);
> +
> /**
> * pci_epc_stop() - stop the PCI link
> * @epc: the link of the EPC device that has to be stopped
> diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
> index c021c7af175f..5d3e1986b49f 100644
> --- a/include/linux/pci-epc.h
> +++ b/include/linux/pci-epc.h
> @@ -61,6 +61,45 @@ struct pci_epc_map {
> void __iomem *virt_addr;
> };
>
> +/**
> + * enum pci_epc_aux_resource_type - auxiliary resource type identifiers
> + * @PCI_EPC_AUX_DMA_CTRL_MMIO: Integrated DMA controller register window (MMIO)
> + * @PCI_EPC_AUX_DMA_CHAN_DESC: Per-channel DMA descriptor
> + *
> + * EPC backends may expose auxiliary blocks (e.g. DMA engines) by mapping their
> + * register windows and descriptor memories into BAR space. This enum
> + * identifies the type of each exposable resource.
> + */
> +enum pci_epc_aux_resource_type {
> + PCI_EPC_AUX_DMA_CTRL_MMIO,
> + PCI_EPC_AUX_DMA_CHAN_DESC,
> +};
> +
> +/**
> + * struct pci_epc_aux_resource - a physical auxiliary resource that may be
> + * exposed for peer use
> + * @type: resource type, see enum pci_epc_aux_resource_type
> + * @phys_addr: physical base address of the resource
> + * @size: size of the resource in bytes
> + * @u: type-specific metadata
> + *
> + * For @PCI_EPC_AUX_DMA_CHAN_DESC, @u.dma_chan_desc provides per-channel
> + * information.
> + */
> +struct pci_epc_aux_resource {
> + enum pci_epc_aux_resource_type type;
> + phys_addr_t phys_addr;
> + resource_size_t size;
> +
> + union {
> + /* PCI_EPC_AUX_DMA_CHAN_DESC */
> + struct {
> + int irq;
> + resource_size_t db_offset;
> + } dma_chan_desc;
> + } u;
> +};
> +
> /**
> * struct pci_epc_ops - set of function pointers for performing EPC operations
> * @write_header: ops to populate configuration space header
> @@ -84,6 +123,7 @@ struct pci_epc_map {
> * @start: ops to start the PCI link
> * @stop: ops to stop the PCI link
> * @get_features: ops to get the features supported by the EPC
> + * @get_aux_resources: ops to retrieve controller-owned auxiliary resources
> * @owner: the module owner containing the ops
> */
> struct pci_epc_ops {
> @@ -115,6 +155,9 @@ struct pci_epc_ops {
> void (*stop)(struct pci_epc *epc);
> const struct pci_epc_features* (*get_features)(struct pci_epc *epc,
> u8 func_no, u8 vfunc_no);
> + int (*get_aux_resources)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_aux_resource *resources,
> + int num_resources);
> struct module *owner;
> };
>
> @@ -309,6 +352,9 @@ int pci_epc_start(struct pci_epc *epc);
> void pci_epc_stop(struct pci_epc *epc);
> const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
> u8 func_no, u8 vfunc_no);
> +int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_aux_resource *resources,
> + int num_resources);
> enum pci_barno
> pci_epc_get_first_free_bar(const struct pci_epc_features *epc_features);
> enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 1/8] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler
2026-02-09 12:53 ` [PATCH v6 1/8] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler Koichiro Den
@ 2026-02-09 16:05 ` Frank Li
0 siblings, 0 replies; 33+ messages in thread
From: Frank Li @ 2026-02-09 16:05 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 09:53:09PM +0900, Koichiro Den wrote:
> Some DesignWare eDMA instances support "interrupt emulation", where a
> software write can assert the IRQ line without setting the normal
> DONE/ABORT status bits.
>
> With a shared IRQ handler the driver cannot reliably distinguish an
> emulated interrupt from a real one by only looking at DONE/ABORT status
> bits. Leaving the emulated IRQ asserted may leave a level-triggered IRQ
> line permanently asserted.
>
> Add a core callback, .ack_emulated_irq(), to perform the core-specific
> deassert sequence and call it from the read/write/common IRQ handlers.
> Note that previously a direct software write could assert the emulated
> IRQ without DMA activity, leading to the interrupt never getting
> deasserted. This patch resolves it.
>
> For v0, a zero write to INT_CLEAR deasserts the emulated IRQ and is a
> no-op for real interrupts. HDMA is not tested or verified and is
> therefore unsupported for now.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/dw-edma/dw-edma-core.c | 48 ++++++++++++++++++++++++---
> drivers/dma/dw-edma/dw-edma-core.h | 10 ++++++
> drivers/dma/dw-edma/dw-edma-v0-core.c | 11 ++++++
> 3 files changed, 64 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index 8e5f7defa6b6..fe131abf1ca3 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -663,7 +663,24 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
> chan->status = EDMA_ST_IDLE;
> }
>
> -static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
> +static inline irqreturn_t dw_edma_interrupt_emulated(void *data)
> +{
> + struct dw_edma_irq *dw_irq = data;
> + struct dw_edma *dw = dw_irq->dw;
> +
> + /*
> + * Interrupt emulation may assert the IRQ line without updating the
> + * normal DONE/ABORT status bits. With a shared IRQ handler we
> + * cannot reliably detect such events by status registers alone, so
> + * always perform the core-specific deassert sequence.
> + */
> + if (dw_edma_core_ack_emulated_irq(dw))
> + return IRQ_NONE;
> +
> + return IRQ_HANDLED;
> +}
> +
> +static inline irqreturn_t dw_edma_interrupt_write_inner(int irq, void *data)
> {
> struct dw_edma_irq *dw_irq = data;
>
> @@ -672,7 +689,7 @@ static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
> dw_edma_abort_interrupt);
> }
>
> -static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
> +static inline irqreturn_t dw_edma_interrupt_read_inner(int irq, void *data)
> {
> struct dw_edma_irq *dw_irq = data;
>
> @@ -681,12 +698,33 @@ static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
> dw_edma_abort_interrupt);
> }
>
> -static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
> +static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
> +{
> + irqreturn_t ret = IRQ_NONE;
> +
> + ret |= dw_edma_interrupt_write_inner(irq, data);
> + ret |= dw_edma_interrupt_emulated(data);
> +
> + return ret;
> +}
> +
> +static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
> +{
> + irqreturn_t ret = IRQ_NONE;
> +
> + ret |= dw_edma_interrupt_read_inner(irq, data);
> + ret |= dw_edma_interrupt_emulated(data);
> +
> + return ret;
> +}
> +
> +static inline irqreturn_t dw_edma_interrupt_common(int irq, void *data)
> {
> irqreturn_t ret = IRQ_NONE;
>
> - ret |= dw_edma_interrupt_write(irq, data);
> - ret |= dw_edma_interrupt_read(irq, data);
> + ret |= dw_edma_interrupt_write_inner(irq, data);
> + ret |= dw_edma_interrupt_read_inner(irq, data);
> + ret |= dw_edma_interrupt_emulated(data);
>
> return ret;
> }
> diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> index 71894b9e0b15..50b87b63b581 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-core.h
> @@ -126,6 +126,7 @@ struct dw_edma_core_ops {
> void (*start)(struct dw_edma_chunk *chunk, bool first);
> void (*ch_config)(struct dw_edma_chan *chan);
> void (*debugfs_on)(struct dw_edma *dw);
> + void (*ack_emulated_irq)(struct dw_edma *dw);
> };
>
> struct dw_edma_sg {
> @@ -206,4 +207,13 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
> dw->core->debugfs_on(dw);
> }
>
> +static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw)
> +{
> + if (!dw->core->ack_emulated_irq)
> + return -EOPNOTSUPP;
> +
> + dw->core->ack_emulated_irq(dw);
> + return 0;
> +}
> +
> #endif /* _DW_EDMA_CORE_H */
> diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> index b75fdaffad9a..82b9c063c10f 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -509,6 +509,16 @@ static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
> dw_edma_v0_debugfs_on(dw);
> }
>
> +static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw)
> +{
> + /*
> + * Interrupt emulation may assert the IRQ without setting
> + * DONE/ABORT status bits. A zero write to INT_CLEAR deasserts the
> + * emulated IRQ, while being a no-op for real interrupts.
> + */
> + SET_BOTH_32(dw, int_clear, 0);
> +}
> +
> static const struct dw_edma_core_ops dw_edma_v0_core = {
> .off = dw_edma_v0_core_off,
> .ch_count = dw_edma_v0_core_ch_count,
> @@ -517,6 +527,7 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
> .start = dw_edma_v0_core_start,
> .ch_config = dw_edma_v0_core_ch_config,
> .debugfs_on = dw_edma_v0_core_debugfs_on,
> + .ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq,
> };
>
> void dw_edma_v0_core_register(struct dw_edma *dw)
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 2/8] dmaengine: dw-edma: Cache per-channel IRQ and emulation doorbell offset
2026-02-09 12:53 ` [PATCH v6 2/8] dmaengine: dw-edma: Cache per-channel IRQ and emulation doorbell offset Koichiro Den
@ 2026-02-09 16:13 ` Frank Li
2026-02-10 1:48 ` Koichiro Den
0 siblings, 1 reply; 33+ messages in thread
From: Frank Li @ 2026-02-09 16:13 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 09:53:10PM +0900, Koichiro Den wrote:
> Some DesignWare PCIe endpoint controllers integrate a DesignWare
> eDMA/HDMA instance. In remote eDMA use cases (e.g. exposing the eDMA
> MMIO window and per-channel linked-list regions to a peer via BARs),
> consumers need a stable way to discover:
> - the Linux IRQ number associated with a given channel's interrupt
> vector,
> - an offset within the eDMA register window that can be used as an
> interrupt-emulation doorbell for that channel.
>
> Store the requested Linux IRQ number in struct dw_edma_irq at IRQ
> request time and cache per-channel metadata in struct dw_edma_chip
> (ch_info_wr/rd) during channel setup. Add a core callback, .ch_info(),
> to fill core-specific metadata such as the doorbell register offset;
> implement it for the v0 eDMA core (use rd_int_status as a suitable
> doorbell target) and provide a placeholder for HDMA until the correct
> offset is known.
>
> No functional change for normal DMA operation. This only makes the
> metadata available to controller/platform drivers that need to expose or
> consume eDMA-related resources.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> drivers/dma/dw-edma/dw-edma-core.c | 9 +++++++++
> drivers/dma/dw-edma/dw-edma-core.h | 9 +++++++++
> drivers/dma/dw-edma/dw-edma-v0-core.c | 11 +++++++++++
> drivers/dma/dw-edma/dw-hdma-v0-core.c | 8 ++++++++
> include/linux/dma/edma.h | 17 +++++++++++++++++
> 5 files changed, 54 insertions(+)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index fe131abf1ca3..bd5ff4a4431a 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -760,6 +760,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> {
> struct dw_edma_chip *chip = dw->chip;
> struct device *dev = chip->dev;
> + struct dw_edma_ch_info *info;
> struct dw_edma_chan *chan;
> struct dw_edma_irq *irq;
> struct dma_device *dma;
> @@ -779,9 +780,11 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> if (i < dw->wr_ch_cnt) {
> chan->id = i;
> chan->dir = EDMA_DIR_WRITE;
> + info = &chip->ch_info_wr[chan->id];
> } else {
> chan->id = i - dw->wr_ch_cnt;
> chan->dir = EDMA_DIR_READ;
> + info = &chip->ch_info_rd[chan->id];
> }
>
> chan->configured = false;
> @@ -807,6 +810,10 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
>
> irq = &dw->irq[pos];
>
> + /* cache channel-specific info */
> + dw_edma_core_ch_info(dw, chan, info);
> + info->irq = irq->irq;
> +
> if (chan->dir == EDMA_DIR_WRITE)
> irq->wr_mask |= BIT(chan->id);
> else
> @@ -910,6 +917,7 @@ static int dw_edma_irq_request(struct dw_edma *dw,
> if (irq_get_msi_desc(irq))
> get_cached_msi_msg(irq, &dw->irq[0].msi);
>
> + dw->irq[0].irq = irq;
> dw->nr_irqs = 1;
> } else {
> /* Distribute IRQs equally among all channels */
> @@ -936,6 +944,7 @@ static int dw_edma_irq_request(struct dw_edma *dw,
>
> if (irq_get_msi_desc(irq))
> get_cached_msi_msg(irq, &dw->irq[i].msi);
> + dw->irq[i].irq = irq;
> }
>
> dw->nr_irqs = i;
> diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> index 50b87b63b581..82f8f3b38752 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-core.h
> @@ -93,6 +93,7 @@ struct dw_edma_irq {
> u32 wr_mask;
> u32 rd_mask;
> struct dw_edma *dw;
> + int irq;
> };
>
> struct dw_edma {
> @@ -127,6 +128,7 @@ struct dw_edma_core_ops {
> void (*ch_config)(struct dw_edma_chan *chan);
> void (*debugfs_on)(struct dw_edma *dw);
> void (*ack_emulated_irq)(struct dw_edma *dw);
> + void (*ch_info)(struct dw_edma_chan *chan, struct dw_edma_ch_info *info);
> };
>
> struct dw_edma_sg {
> @@ -216,4 +218,11 @@ static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw)
> return 0;
> }
>
> +static inline void
> +dw_edma_core_ch_info(struct dw_edma *dw, struct dw_edma_chan *chan,
> + struct dw_edma_ch_info *info)
> +{
> + dw->core->ch_info(chan, info);
> +}
> +
> #endif /* _DW_EDMA_CORE_H */
> diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> index 82b9c063c10f..0b8d4b6a5e26 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -519,6 +519,16 @@ static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw)
> SET_BOTH_32(dw, int_clear, 0);
> }
>
> +static void dw_edma_v0_core_ch_info(struct dw_edma_chan *chan,
> + struct dw_edma_ch_info *info)
> +{
> + /*
> + * rd_int_status is chosen arbitrarily, but wr_int_status would be
> + * equally suitable.
> + */
> + info->db_offset = offsetof(struct dw_edma_v0_regs, rd_int_status);
> +}
> +
> static const struct dw_edma_core_ops dw_edma_v0_core = {
> .off = dw_edma_v0_core_off,
> .ch_count = dw_edma_v0_core_ch_count,
> @@ -528,6 +538,7 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
> .ch_config = dw_edma_v0_core_ch_config,
> .debugfs_on = dw_edma_v0_core_debugfs_on,
> .ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq,
> + .ch_info = dw_edma_v0_core_ch_info,
> };
>
> void dw_edma_v0_core_register(struct dw_edma *dw)
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> index e3f8db4fe909..1076b394c45f 100644
> --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> @@ -283,6 +283,13 @@ static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
> dw_hdma_v0_debugfs_on(dw);
> }
>
> +static void dw_hdma_v0_core_ch_info(struct dw_edma_chan *chan,
> + struct dw_edma_ch_info *info)
> +{
> + /* Implement once the correct offset is known. */
> + info->db_offset = ~0;
> +}
> +
> static const struct dw_edma_core_ops dw_hdma_v0_core = {
> .off = dw_hdma_v0_core_off,
> .ch_count = dw_hdma_v0_core_ch_count,
> @@ -291,6 +298,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
> .start = dw_hdma_v0_core_start,
> .ch_config = dw_hdma_v0_core_ch_config,
> .debugfs_on = dw_hdma_v0_core_debugfs_on,
> + .ch_info = dw_hdma_v0_core_ch_info,
> };
>
> void dw_hdma_v0_core_register(struct dw_edma *dw)
> diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> index 3080747689f6..921250204a08 100644
> --- a/include/linux/dma/edma.h
> +++ b/include/linux/dma/edma.h
> @@ -60,6 +60,19 @@ enum dw_edma_chip_flags {
> DW_EDMA_CHIP_LOCAL = BIT(0),
> };
>
> +/**
> + * struct dw_edma_ch_info - DW eDMA channel metadata
> + * @irq: Linux IRQ number used by this channel's interrupt vector
> + * @db_offset: offset within the eDMA register window that can be used as
> + * an interrupt-emulation doorbell for this channel
> + */
> +struct dw_edma_ch_info {
> + int irq;
> +
> + /* Fields below are filled in by dw_edma_core_ops->ch_info() */
> + resource_size_t db_offset;
> +};
> +
> /**
> * struct dw_edma_chip - representation of DesignWare eDMA controller hardware
> * @dev: struct device of the eDMA controller
> @@ -96,6 +109,10 @@ struct dw_edma_chip {
> struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH];
> struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH];
>
> + /* cached channel info */
> + struct dw_edma_ch_info ch_info_wr[EDMA_MAX_WR_CH];
> + struct dw_edma_ch_info ch_info_rd[EDMA_MAX_RD_CH];
> +
suppose this info only used in side dw edma driver, so it should be in
dw_edma.
dw_edma_chip is useful to exchange informaiton between EPC/PCI controller
and dma engine when call dw_edma_probe().
Frand
> enum dw_edma_map_format mf;
>
> struct dw_edma *dw;
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 2/8] dmaengine: dw-edma: Cache per-channel IRQ and emulation doorbell offset
2026-02-09 16:13 ` Frank Li
@ 2026-02-10 1:48 ` Koichiro Den
0 siblings, 0 replies; 33+ messages in thread
From: Koichiro Den @ 2026-02-10 1:48 UTC (permalink / raw)
To: Frank Li
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 11:13:40AM -0500, Frank Li wrote:
> On Mon, Feb 09, 2026 at 09:53:10PM +0900, Koichiro Den wrote:
> > Some DesignWare PCIe endpoint controllers integrate a DesignWare
> > eDMA/HDMA instance. In remote eDMA use cases (e.g. exposing the eDMA
> > MMIO window and per-channel linked-list regions to a peer via BARs),
> > consumers need a stable way to discover:
> > - the Linux IRQ number associated with a given channel's interrupt
> > vector,
> > - an offset within the eDMA register window that can be used as an
> > interrupt-emulation doorbell for that channel.
> >
> > Store the requested Linux IRQ number in struct dw_edma_irq at IRQ
> > request time and cache per-channel metadata in struct dw_edma_chip
> > (ch_info_wr/rd) during channel setup. Add a core callback, .ch_info(),
> > to fill core-specific metadata such as the doorbell register offset;
> > implement it for the v0 eDMA core (use rd_int_status as a suitable
> > doorbell target) and provide a placeholder for HDMA until the correct
> > offset is known.
> >
> > No functional change for normal DMA operation. This only makes the
> > metadata available to controller/platform drivers that need to expose or
> > consume eDMA-related resources.
> >
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > drivers/dma/dw-edma/dw-edma-core.c | 9 +++++++++
> > drivers/dma/dw-edma/dw-edma-core.h | 9 +++++++++
> > drivers/dma/dw-edma/dw-edma-v0-core.c | 11 +++++++++++
> > drivers/dma/dw-edma/dw-hdma-v0-core.c | 8 ++++++++
> > include/linux/dma/edma.h | 17 +++++++++++++++++
> > 5 files changed, 54 insertions(+)
> >
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > index fe131abf1ca3..bd5ff4a4431a 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > @@ -760,6 +760,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> > {
> > struct dw_edma_chip *chip = dw->chip;
> > struct device *dev = chip->dev;
> > + struct dw_edma_ch_info *info;
> > struct dw_edma_chan *chan;
> > struct dw_edma_irq *irq;
> > struct dma_device *dma;
> > @@ -779,9 +780,11 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> > if (i < dw->wr_ch_cnt) {
> > chan->id = i;
> > chan->dir = EDMA_DIR_WRITE;
> > + info = &chip->ch_info_wr[chan->id];
> > } else {
> > chan->id = i - dw->wr_ch_cnt;
> > chan->dir = EDMA_DIR_READ;
> > + info = &chip->ch_info_rd[chan->id];
> > }
> >
> > chan->configured = false;
> > @@ -807,6 +810,10 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> >
> > irq = &dw->irq[pos];
> >
> > + /* cache channel-specific info */
> > + dw_edma_core_ch_info(dw, chan, info);
> > + info->irq = irq->irq;
> > +
> > if (chan->dir == EDMA_DIR_WRITE)
> > irq->wr_mask |= BIT(chan->id);
> > else
> > @@ -910,6 +917,7 @@ static int dw_edma_irq_request(struct dw_edma *dw,
> > if (irq_get_msi_desc(irq))
> > get_cached_msi_msg(irq, &dw->irq[0].msi);
> >
> > + dw->irq[0].irq = irq;
> > dw->nr_irqs = 1;
> > } else {
> > /* Distribute IRQs equally among all channels */
> > @@ -936,6 +944,7 @@ static int dw_edma_irq_request(struct dw_edma *dw,
> >
> > if (irq_get_msi_desc(irq))
> > get_cached_msi_msg(irq, &dw->irq[i].msi);
> > + dw->irq[i].irq = irq;
> > }
> >
> > dw->nr_irqs = i;
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> > index 50b87b63b581..82f8f3b38752 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.h
> > +++ b/drivers/dma/dw-edma/dw-edma-core.h
> > @@ -93,6 +93,7 @@ struct dw_edma_irq {
> > u32 wr_mask;
> > u32 rd_mask;
> > struct dw_edma *dw;
> > + int irq;
> > };
> >
> > struct dw_edma {
> > @@ -127,6 +128,7 @@ struct dw_edma_core_ops {
> > void (*ch_config)(struct dw_edma_chan *chan);
> > void (*debugfs_on)(struct dw_edma *dw);
> > void (*ack_emulated_irq)(struct dw_edma *dw);
> > + void (*ch_info)(struct dw_edma_chan *chan, struct dw_edma_ch_info *info);
> > };
> >
> > struct dw_edma_sg {
> > @@ -216,4 +218,11 @@ static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw)
> > return 0;
> > }
> >
> > +static inline void
> > +dw_edma_core_ch_info(struct dw_edma *dw, struct dw_edma_chan *chan,
> > + struct dw_edma_ch_info *info)
> > +{
> > + dw->core->ch_info(chan, info);
> > +}
> > +
> > #endif /* _DW_EDMA_CORE_H */
> > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > index 82b9c063c10f..0b8d4b6a5e26 100644
> > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > @@ -519,6 +519,16 @@ static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw)
> > SET_BOTH_32(dw, int_clear, 0);
> > }
> >
> > +static void dw_edma_v0_core_ch_info(struct dw_edma_chan *chan,
> > + struct dw_edma_ch_info *info)
> > +{
> > + /*
> > + * rd_int_status is chosen arbitrarily, but wr_int_status would be
> > + * equally suitable.
> > + */
> > + info->db_offset = offsetof(struct dw_edma_v0_regs, rd_int_status);
> > +}
> > +
> > static const struct dw_edma_core_ops dw_edma_v0_core = {
> > .off = dw_edma_v0_core_off,
> > .ch_count = dw_edma_v0_core_ch_count,
> > @@ -528,6 +538,7 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
> > .ch_config = dw_edma_v0_core_ch_config,
> > .debugfs_on = dw_edma_v0_core_debugfs_on,
> > .ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq,
> > + .ch_info = dw_edma_v0_core_ch_info,
> > };
> >
> > void dw_edma_v0_core_register(struct dw_edma *dw)
> > diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > index e3f8db4fe909..1076b394c45f 100644
> > --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > @@ -283,6 +283,13 @@ static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
> > dw_hdma_v0_debugfs_on(dw);
> > }
> >
> > +static void dw_hdma_v0_core_ch_info(struct dw_edma_chan *chan,
> > + struct dw_edma_ch_info *info)
> > +{
> > + /* Implement once the correct offset is known. */
> > + info->db_offset = ~0;
> > +}
> > +
> > static const struct dw_edma_core_ops dw_hdma_v0_core = {
> > .off = dw_hdma_v0_core_off,
> > .ch_count = dw_hdma_v0_core_ch_count,
> > @@ -291,6 +298,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
> > .start = dw_hdma_v0_core_start,
> > .ch_config = dw_hdma_v0_core_ch_config,
> > .debugfs_on = dw_hdma_v0_core_debugfs_on,
> > + .ch_info = dw_hdma_v0_core_ch_info,
> > };
> >
> > void dw_hdma_v0_core_register(struct dw_edma *dw)
> > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> > index 3080747689f6..921250204a08 100644
> > --- a/include/linux/dma/edma.h
> > +++ b/include/linux/dma/edma.h
> > @@ -60,6 +60,19 @@ enum dw_edma_chip_flags {
> > DW_EDMA_CHIP_LOCAL = BIT(0),
> > };
> >
> > +/**
> > + * struct dw_edma_ch_info - DW eDMA channel metadata
> > + * @irq: Linux IRQ number used by this channel's interrupt vector
> > + * @db_offset: offset within the eDMA register window that can be used as
> > + * an interrupt-emulation doorbell for this channel
> > + */
> > +struct dw_edma_ch_info {
> > + int irq;
> > +
> > + /* Fields below are filled in by dw_edma_core_ops->ch_info() */
> > + resource_size_t db_offset;
> > +};
> > +
> > /**
> > * struct dw_edma_chip - representation of DesignWare eDMA controller hardware
> > * @dev: struct device of the eDMA controller
> > @@ -96,6 +109,10 @@ struct dw_edma_chip {
> > struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH];
> > struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH];
> >
> > + /* cached channel info */
> > + struct dw_edma_ch_info ch_info_wr[EDMA_MAX_WR_CH];
> > + struct dw_edma_ch_info ch_info_rd[EDMA_MAX_RD_CH];
> > +
>
> suppose this info only used in side dw edma driver, so it should be in
> dw_edma.
>
> dw_edma_chip is useful to exchange informaiton between EPC/PCI controller
> and dma engine when call dw_edma_probe().
These cached values are consumed on the EPC side, while struct dw_edma is
opaque there. Putting them into struct dw_edma would require an exported
accessor (again), which I was hoping to avoid. Does that sound reasonable
to you?
Thanks for the review,
Koichiro
>
> Frand
>
> > enum dw_edma_map_format mf;
> >
> > struct dw_edma *dw;
> > --
> > 2.51.0
> >
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested
2026-02-09 15:57 ` Frank Li
@ 2026-02-10 1:54 ` Koichiro Den
0 siblings, 0 replies; 33+ messages in thread
From: Koichiro Den @ 2026-02-10 1:54 UTC (permalink / raw)
To: Frank Li
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 10:57:17AM -0500, Frank Li wrote:
> On Mon, Feb 09, 2026 at 09:53:14PM +0900, Koichiro Den wrote:
> > pci_epf_test_enable_doorbell() allocates a doorbell and then installs
> > the interrupt handler with request_threaded_irq(). On failures before
> > the IRQ is successfully requested (e.g. no free BAR,
> > request_threaded_irq() failure), the error path jumps to
> > err_doorbell_cleanup and calls pci_epf_test_doorbell_cleanup().
> >
> > pci_epf_test_doorbell_cleanup() unconditionally calls free_irq() for the
> > doorbell virq, which can trigger "Trying to free already-free IRQ"
> > warnings when the IRQ was never requested.
> >
> > Track whether the doorbell IRQ has been successfully requested and only
> > call free_irq() when it has.
> >
> > Fixes: eff0c286aa91 ("PCI: endpoint: pci-epf-test: Add doorbell test support")
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > drivers/pci/endpoint/functions/pci-epf-test.c | 9 ++++++++-
> > 1 file changed, 8 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> > index 6952ee418622..23034f548c90 100644
> > --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> > +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> > @@ -86,6 +86,7 @@ struct pci_epf_test {
> > bool dma_private;
> > const struct pci_epc_features *epc_features;
> > struct pci_epf_bar db_bar;
> > + bool db_irq_requested;
>
> Prevous patch clean up epf->num_db = 0 at doorbell_free(). can you check
> epf->num_db ?
I don't think so. epf->num_db tracks how many dbs were allocated, but it
doesn't tell whether request_irq() actually succeeded. What I'm fixing here
is the case where the db allocation succeeeds (or was already done), but
the request_irq() fails. In that case we must not call free_irq(), even
though num_db is non-zero. That's why I introduced a separate
db_irq_requested flag.
Thanks for the review,
Koichiro
>
> Frank
>
> > size_t bar_size[PCI_STD_NUM_BARS];
> > };
> >
> > @@ -715,7 +716,10 @@ 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];
> > struct pci_epf *epf = epf_test->epf;
> >
> > - free_irq(epf->db_msg[0].virq, epf_test);
> > + if (epf_test->db_irq_requested && epf->db_msg) {
> > + free_irq(epf->db_msg[0].virq, epf_test);
> > + epf_test->db_irq_requested = false;
> > + }
> > reg->doorbell_bar = cpu_to_le32(NO_BAR);
> >
> > pci_epf_free_doorbell(epf);
> > @@ -741,6 +745,8 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> > if (bar < BAR_0)
> > goto err_doorbell_cleanup;
> >
> > + epf_test->db_irq_requested = false;
> > +
> > ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
> > pci_epf_test_doorbell_handler, IRQF_ONESHOT,
> > "pci-ep-test-doorbell", epf_test);
> > @@ -751,6 +757,7 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> > goto err_doorbell_cleanup;
> > }
> >
> > + epf_test->db_irq_requested = true;
> > reg->doorbell_data = cpu_to_le32(msg->data);
> > reg->doorbell_bar = cpu_to_le32(bar);
> >
> > --
> > 2.51.0
> >
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-09 15:51 ` Frank Li
@ 2026-02-10 2:10 ` Koichiro Den
2026-02-10 15:16 ` Frank Li
0 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-10 2:10 UTC (permalink / raw)
To: Frank Li
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 10:51:56AM -0500, Frank Li wrote:
> 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 <den@valinux.co.jp>
> > ---
> > 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.
That makes sense. I can extend enum pci_epc_aux_resource_type with a
dedicated doorbell MMIO resource type and use it, instead of assuming the
embedded doorbell lives inside the DMA control MMIO window
(PCI_EPC_AUX_DMA_CTRL_MMIO).
With that approach, I plan to drop u.dma_chan_desc.db_offset for now, since
it would always be zero in the current dw-edma v0 case (the INT_STATUS
offset is not channel-specific). If another vendor implementation needs a
channel-specific offset in the future, we can just extend this again when
it's actually needed.
In short, after your feedback, I'm thinking of the following change (diff
against v6):
enum pci_epc_aux_resource_type {
PCI_EPC_AUX_DMA_CTRL_MMIO,
PCI_EPC_AUX_DMA_CHAN_DESC,
+ PCI_EPC_AUX_DOORBELL_MMIO,
};
struct pci_epc_aux_resource {
enum pci_epc_aux_resource_type type;
phys_addr_t phys_addr;
resource_size_t size;
union {
/* PCI_EPC_AUX_DMA_CHAN_DESC */
struct {
int irq;
- resource_size_t db_offset;
} dma_chan_desc;
} u;
};
Thanks for the review,
Koichiro
>
> 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
> >
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
` (7 preceding siblings ...)
2026-02-09 12:53 ` [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
@ 2026-02-10 12:24 ` Niklas Cassel
2026-02-10 14:07 ` Koichiro Den
8 siblings, 1 reply; 33+ messages in thread
From: Niklas Cassel @ 2026-02-10 12:24 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 09:53:08PM +0900, Koichiro Den wrote:
> Tested on
> =========
>
> I tested the embedded (DMA) doorbell fallback path (via pci-epf-test) on
> R-Car Spider boards:
>
> $ ./pci_endpoint_test -t DOORBELL_TEST
> TAP version 13
> 1..1
> # Starting 1 tests from 1 test cases.
> # RUN pcie_ep_doorbell.DOORBELL_TEST ...
> # OK pcie_ep_doorbell.DOORBELL_TEST
> ok 1 pcie_ep_doorbell.DOORBELL_TEST
> # PASSED: 1 / 1 tests passed.
> # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
>
> with the following message observed on the EP side:
>
> [ 80.464653] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
>
> (Note: for the test to pass on R-Car Spider, one of the following was required:
> - echo 1048576 > functions/pci_epf_test/func1/pci_epf_test.0/bar2_size
> - apply https://lore.kernel.org/all/20251023072217.901888-1-den@valinux.co.jp/)
I applied this series on top of branch pci/controller/dwc
on Rock 5B (pcie-dw-rockchip.c).
On EP side:
[ 39.218533] pci_epf_test pci_epf_test.0: Can't find MSI domain for EPC
[ 39.219125] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
On RC side:
# RUN pcie_ep_doorbell.DOORBELL_TEST ...
[ 40.297892] pci-endpoint-test 0000:01:00.0: Failed to trigger doorbell in endpoint
# pci_endpoint_test.c:279:DOORBELL_TEST:Expected 0 (0) == ret (-22)
# pci_endpoint_test.c:279:DOORBELL_TEST:Test failed for Doorbell
# DOORBELL_TEST: Test failed
# FAIL pcie_ep_doorbell.DOORBELL_TEST
not ok 23 pcie_ep_doorbell.DOORBELL_TEST
Any suggestions?
(All BARs in pcie-dw-rockchip.c is marked as BAR_RESIZABLE.)
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested
2026-02-09 12:53 ` [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested Koichiro Den
2026-02-09 15:57 ` Frank Li
@ 2026-02-10 12:36 ` Niklas Cassel
2026-02-10 13:54 ` Koichiro Den
1 sibling, 1 reply; 33+ messages in thread
From: Niklas Cassel @ 2026-02-10 12:36 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Mon, Feb 09, 2026 at 09:53:14PM +0900, Koichiro Den wrote:
> pci_epf_test_enable_doorbell() allocates a doorbell and then installs
> the interrupt handler with request_threaded_irq(). On failures before
> the IRQ is successfully requested (e.g. no free BAR,
> request_threaded_irq() failure), the error path jumps to
> err_doorbell_cleanup and calls pci_epf_test_doorbell_cleanup().
>
> pci_epf_test_doorbell_cleanup() unconditionally calls free_irq() for the
> doorbell virq, which can trigger "Trying to free already-free IRQ"
> warnings when the IRQ was never requested.
>
> Track whether the doorbell IRQ has been successfully requested and only
> call free_irq() when it has.
>
> Fixes: eff0c286aa91 ("PCI: endpoint: pci-epf-test: Add doorbell test support")
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> drivers/pci/endpoint/functions/pci-epf-test.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> index 6952ee418622..23034f548c90 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> @@ -86,6 +86,7 @@ struct pci_epf_test {
> bool dma_private;
> const struct pci_epc_features *epc_features;
> struct pci_epf_bar db_bar;
> + bool db_irq_requested;
> size_t bar_size[PCI_STD_NUM_BARS];
> };
>
> @@ -715,7 +716,10 @@ 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];
> struct pci_epf *epf = epf_test->epf;
>
> - free_irq(epf->db_msg[0].virq, epf_test);
> + if (epf_test->db_irq_requested && epf->db_msg) {
> + free_irq(epf->db_msg[0].virq, epf_test);
> + epf_test->db_irq_requested = false;
> + }
> reg->doorbell_bar = cpu_to_le32(NO_BAR);
>
> pci_epf_free_doorbell(epf);
> @@ -741,6 +745,8 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> if (bar < BAR_0)
> goto err_doorbell_cleanup;
>
> + epf_test->db_irq_requested = false;
> +
> ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
> pci_epf_test_doorbell_handler, IRQF_ONESHOT,
> "pci-ep-test-doorbell", epf_test);
Another bug in pci_epf_test_enable_doorbell():
Since we reuse the BAR size, and use dynamic inbound mapping,
what if the returned DB offset is larger than epf->bar[bar].size ?
I think we need something like this before calling pci_epc_set_bar():
if (reg->doorbell_offset >= epf->bar[bar].size)
goto err_doorbell_cleanup;
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested
2026-02-10 12:36 ` Niklas Cassel
@ 2026-02-10 13:54 ` Koichiro Den
2026-02-10 16:38 ` Niklas Cassel
0 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-10 13:54 UTC (permalink / raw)
To: Niklas Cassel
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Tue, Feb 10, 2026 at 01:36:29PM +0100, Niklas Cassel wrote:
> On Mon, Feb 09, 2026 at 09:53:14PM +0900, Koichiro Den wrote:
> > pci_epf_test_enable_doorbell() allocates a doorbell and then installs
> > the interrupt handler with request_threaded_irq(). On failures before
> > the IRQ is successfully requested (e.g. no free BAR,
> > request_threaded_irq() failure), the error path jumps to
> > err_doorbell_cleanup and calls pci_epf_test_doorbell_cleanup().
> >
> > pci_epf_test_doorbell_cleanup() unconditionally calls free_irq() for the
> > doorbell virq, which can trigger "Trying to free already-free IRQ"
> > warnings when the IRQ was never requested.
> >
> > Track whether the doorbell IRQ has been successfully requested and only
> > call free_irq() when it has.
> >
> > Fixes: eff0c286aa91 ("PCI: endpoint: pci-epf-test: Add doorbell test support")
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > drivers/pci/endpoint/functions/pci-epf-test.c | 9 ++++++++-
> > 1 file changed, 8 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> > index 6952ee418622..23034f548c90 100644
> > --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> > +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> > @@ -86,6 +86,7 @@ struct pci_epf_test {
> > bool dma_private;
> > const struct pci_epc_features *epc_features;
> > struct pci_epf_bar db_bar;
> > + bool db_irq_requested;
> > size_t bar_size[PCI_STD_NUM_BARS];
> > };
> >
> > @@ -715,7 +716,10 @@ 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];
> > struct pci_epf *epf = epf_test->epf;
> >
> > - free_irq(epf->db_msg[0].virq, epf_test);
> > + if (epf_test->db_irq_requested && epf->db_msg) {
> > + free_irq(epf->db_msg[0].virq, epf_test);
> > + epf_test->db_irq_requested = false;
> > + }
> > reg->doorbell_bar = cpu_to_le32(NO_BAR);
> >
> > pci_epf_free_doorbell(epf);
> > @@ -741,6 +745,8 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> > if (bar < BAR_0)
> > goto err_doorbell_cleanup;
> >
> > + epf_test->db_irq_requested = false;
> > +
> > ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
> > pci_epf_test_doorbell_handler, IRQF_ONESHOT,
> > "pci-ep-test-doorbell", epf_test);
>
> Another bug in pci_epf_test_enable_doorbell():
>
> Since we reuse the BAR size, and use dynamic inbound mapping,
> what if the returned DB offset is larger than epf->bar[bar].size ?
>
> I think we need something like this before calling pci_epc_set_bar():
>
> if (reg->doorbell_offset >= epf->bar[bar].size)
> goto err_doorbell_cleanup;
Right, I remember this coming up in another thread.
The reason I didn't include the fix in this series, even though I added
Patch #6 and #7, is mainly about how those relate to Patch #8. The
doorbell_offset issue feels orthogonal to Patch #8 to me.
- the issue addressed by Patch #6 is more likely to be hit once Patch #8 is
applied, depending on the platform, compared to the existing MSI
doorbell-only setup.
- without Patch #7, Patch #8 could silently mask the issue, which might
make it look like it includes unrelated changes. I felt that keeping the
fix separate from the functional change would make the series clearer and
easier to reason about, so I added Patch #7.
If there are no objections from either of you, I'm happy to include a fix
patch for this in v7.
Best regards,
Koichiro
>
>
>
> Kind regards,
> Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-10 12:24 ` [PATCH v6 0/8] " Niklas Cassel
@ 2026-02-10 14:07 ` Koichiro Den
2026-02-10 16:30 ` Niklas Cassel
0 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-10 14:07 UTC (permalink / raw)
To: Niklas Cassel
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Tue, Feb 10, 2026 at 01:24:29PM +0100, Niklas Cassel wrote:
> On Mon, Feb 09, 2026 at 09:53:08PM +0900, Koichiro Den wrote:
> > Tested on
> > =========
> >
> > I tested the embedded (DMA) doorbell fallback path (via pci-epf-test) on
> > R-Car Spider boards:
> >
> > $ ./pci_endpoint_test -t DOORBELL_TEST
> > TAP version 13
> > 1..1
> > # Starting 1 tests from 1 test cases.
> > # RUN pcie_ep_doorbell.DOORBELL_TEST ...
> > # OK pcie_ep_doorbell.DOORBELL_TEST
> > ok 1 pcie_ep_doorbell.DOORBELL_TEST
> > # PASSED: 1 / 1 tests passed.
> > # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
> >
> > with the following message observed on the EP side:
> >
> > [ 80.464653] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
> >
> > (Note: for the test to pass on R-Car Spider, one of the following was required:
> > - echo 1048576 > functions/pci_epf_test/func1/pci_epf_test.0/bar2_size
> > - apply https://lore.kernel.org/all/20251023072217.901888-1-den@valinux.co.jp/)
>
> I applied this series on top of branch pci/controller/dwc
> on Rock 5B (pcie-dw-rockchip.c).
>
> On EP side:
> [ 39.218533] pci_epf_test pci_epf_test.0: Can't find MSI domain for EPC
> [ 39.219125] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
>
> On RC side:
> # RUN pcie_ep_doorbell.DOORBELL_TEST ...
> [ 40.297892] pci-endpoint-test 0000:01:00.0: Failed to trigger doorbell in endpoint
> # pci_endpoint_test.c:279:DOORBELL_TEST:Expected 0 (0) == ret (-22)
> # pci_endpoint_test.c:279:DOORBELL_TEST:Test failed for Doorbell
>
> # DOORBELL_TEST: Test failed
> # FAIL pcie_ep_doorbell.DOORBELL_TEST
> not ok 23 pcie_ep_doorbell.DOORBELL_TEST
>
> Any suggestions?
>
> (All BARs in pcie-dw-rockchip.c is marked as BAR_RESIZABLE.)
Thank you for testing.
If the failure was observed in a scenario other than a plain
`./pci_endpoint_test -t DOORBELL_TEST`, could you please try again with [1]
applied as well?
[1] https://lore.kernel.org/linux-pci/20260202145407.503348-1-den@valinux.co.jp/
Best regards,
Koichiro
>
>
> Kind regards,
> Niklas
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-10 2:10 ` Koichiro Den
@ 2026-02-10 15:16 ` Frank Li
0 siblings, 0 replies; 33+ messages in thread
From: Frank Li @ 2026-02-10 15:16 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, cassel, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Tue, Feb 10, 2026 at 11:10:52AM +0900, Koichiro Den wrote:
> On Mon, Feb 09, 2026 at 10:51:56AM -0500, Frank Li wrote:
> > 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 <den@valinux.co.jp>
> > > ---
> > > 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.
>
> That makes sense. I can extend enum pci_epc_aux_resource_type with a
> dedicated doorbell MMIO resource type and use it, instead of assuming the
> embedded doorbell lives inside the DMA control MMIO window
> (PCI_EPC_AUX_DMA_CTRL_MMIO).
>
> With that approach, I plan to drop u.dma_chan_desc.db_offset for now, since
> it would always be zero in the current dw-edma v0 case (the INT_STATUS
> offset is not channel-specific). If another vendor implementation needs a
> channel-specific offset in the future, we can just extend this again when
> it's actually needed.
>
> In short, after your feedback, I'm thinking of the following change (diff
> against v6):
>
> enum pci_epc_aux_resource_type {
> PCI_EPC_AUX_DMA_CTRL_MMIO,
> PCI_EPC_AUX_DMA_CHAN_DESC,
> + PCI_EPC_AUX_DOORBELL_MMIO,
Yes, thanks
Frank
> };
>
> struct pci_epc_aux_resource {
> enum pci_epc_aux_resource_type type;
> phys_addr_t phys_addr;
> resource_size_t size;
>
> union {
> /* PCI_EPC_AUX_DMA_CHAN_DESC */
> struct {
> int irq;
> - resource_size_t db_offset;
> } dma_chan_desc;
> } u;
> };
>
> Thanks for the review,
> Koichiro
>
> >
> > 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
> > >
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-10 14:07 ` Koichiro Den
@ 2026-02-10 16:30 ` Niklas Cassel
2026-02-11 15:57 ` Koichiro Den
0 siblings, 1 reply; 33+ messages in thread
From: Niklas Cassel @ 2026-02-10 16:30 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Tue, Feb 10, 2026 at 11:07:16PM +0900, Koichiro Den wrote:
> On Tue, Feb 10, 2026 at 01:24:29PM +0100, Niklas Cassel wrote:
> > On Mon, Feb 09, 2026 at 09:53:08PM +0900, Koichiro Den wrote:
> > > Tested on
> > > =========
> > >
> > > I tested the embedded (DMA) doorbell fallback path (via pci-epf-test) on
> > > R-Car Spider boards:
> > >
> > > $ ./pci_endpoint_test -t DOORBELL_TEST
> > > TAP version 13
> > > 1..1
> > > # Starting 1 tests from 1 test cases.
> > > # RUN pcie_ep_doorbell.DOORBELL_TEST ...
> > > # OK pcie_ep_doorbell.DOORBELL_TEST
> > > ok 1 pcie_ep_doorbell.DOORBELL_TEST
> > > # PASSED: 1 / 1 tests passed.
> > > # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
> > >
> > > with the following message observed on the EP side:
> > >
> > > [ 80.464653] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
> > >
> > > (Note: for the test to pass on R-Car Spider, one of the following was required:
> > > - echo 1048576 > functions/pci_epf_test/func1/pci_epf_test.0/bar2_size
> > > - apply https://lore.kernel.org/all/20251023072217.901888-1-den@valinux.co.jp/)
> >
> > I applied this series on top of branch pci/controller/dwc
> > on Rock 5B (pcie-dw-rockchip.c).
> >
> > On EP side:
> > [ 39.218533] pci_epf_test pci_epf_test.0: Can't find MSI domain for EPC
> > [ 39.219125] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
> >
> > On RC side:
> > # RUN pcie_ep_doorbell.DOORBELL_TEST ...
> > [ 40.297892] pci-endpoint-test 0000:01:00.0: Failed to trigger doorbell in endpoint
> > # pci_endpoint_test.c:279:DOORBELL_TEST:Expected 0 (0) == ret (-22)
> > # pci_endpoint_test.c:279:DOORBELL_TEST:Test failed for Doorbell
> >
> > # DOORBELL_TEST: Test failed
> > # FAIL pcie_ep_doorbell.DOORBELL_TEST
> > not ok 23 pcie_ep_doorbell.DOORBELL_TEST
> >
> > Any suggestions?
> >
> > (All BARs in pcie-dw-rockchip.c is marked as BAR_RESIZABLE.)
>
> Thank you for testing.
>
> If the failure was observed in a scenario other than a plain
> `./pci_endpoint_test -t DOORBELL_TEST`, could you please try again with [1]
> applied as well?
>
> [1] https://lore.kernel.org/linux-pci/20260202145407.503348-1-den@valinux.co.jp/
I applied that series, but I got the same problem.
I added debug, and the EP side does use the correct address for the eDMA:
[ 26.279457] msg_addr: 0xa403800a0
[ 26.279898] phys_addr: 0xa40300000 offset: 0x800a0
If I write to the msg_addr directly on the EP using devmem, I do see the print
that I added in the IRQ handler:
# devmem 0xa403800a0 32 0
[ 155.861989] dw_edma_interrupt_emulated:696
# devmem 0xa403800a0 32 0
[ 158.809160] dw_edma_interrupt_emulated:696
[ 158.809543] pci_epf_test_doorbell_primary:729
# [ 158.809986] pci_epf_test_doorbell_handler:703
# devmem 0xa403800a0 32 0
[ 161.241326] dw_edma_interrupt_emulated:696
# devmem 0xa403800a0 32 0
[ 163.466054] dw_edma_interrupt_emulated:696
# devmem 0xa403800a0 32 0
[ 167.378662] dw_edma_interrupt_emulated:696
[ 167.379045] pci_epf_test_doorbell_primary:729
# [ 167.379512] pci_epf_test_doorbell_handler:703
# devmem 0xa403800a0 32 0
[ 168.880179] dw_edma_interrupt_emulated:696
# devmem 0xa403800a0 32 0
[ 170.492176] dw_edma_interrupt_emulated:696
# devmem 0xa403800a0 32 0
[ 171.729154] dw_edma_interrupt_emulated:696
# devmem 0xa403800a0 32 0
[ 173.481271] dw_edma_interrupt_emulated:696
# devmem 0xa403800a0 32 0
[ 174.985787] dw_edma_interrupt_emulated:696
# devmem 0xa403800a0 32 0
[ 176.517131] dw_edma_interrupt_emulated:696
[ 176.517511] pci_epf_test_doorbell_primary:729
# [ 176.517963] pci_epf_test_doorbell_handler:703
But not on every write....
I'm not sure, but could this perhaps be because we are missing this patch:
https://lore.kernel.org/dmaengine/20260105075904.1254012-1-den@valinux.co.jp/
# dmesg | grep eDMA
[ 1.243339] rockchip-dw-pcie a40000000.pcie-ep: eDMA: unroll T, 2 wr, 2 rd
# cat /proc/interrupts | grep edma
53: 8 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
54: 7 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
55: 15 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
56: 7 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
Anyway, I was still curious why this did never worked when writing from the
host side, even when running the test case many, many times.
AFAICT, the inbound translation looks correct.
RK3588 (pcie-dw-rockchip.c) exposes the DMA registers in BAR4 by default.
If I hack pci-epf-test on top of your patch to unconditionally return BAR4 with
offset 0xa0, it works. So my best guess is that the fixed inbound translation
in BAR4 (to the eDMA registers) somehow messes with the inbound translation if
another BAR tries to use an inbound translation to the eDMA registers as well.
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested
2026-02-10 13:54 ` Koichiro Den
@ 2026-02-10 16:38 ` Niklas Cassel
2026-02-11 16:08 ` Koichiro Den
0 siblings, 1 reply; 33+ messages in thread
From: Niklas Cassel @ 2026-02-10 16:38 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Tue, Feb 10, 2026 at 10:54:20PM +0900, Koichiro Den wrote:
> On Tue, Feb 10, 2026 at 01:36:29PM +0100, Niklas Cassel wrote:
> > On Mon, Feb 09, 2026 at 09:53:14PM +0900, Koichiro Den wrote:
> > > pci_epf_test_enable_doorbell() allocates a doorbell and then installs
> > > the interrupt handler with request_threaded_irq(). On failures before
> > > the IRQ is successfully requested (e.g. no free BAR,
> > > request_threaded_irq() failure), the error path jumps to
> > > err_doorbell_cleanup and calls pci_epf_test_doorbell_cleanup().
> > >
> > > pci_epf_test_doorbell_cleanup() unconditionally calls free_irq() for the
> > > doorbell virq, which can trigger "Trying to free already-free IRQ"
> > > warnings when the IRQ was never requested.
> > >
> > > Track whether the doorbell IRQ has been successfully requested and only
> > > call free_irq() when it has.
> > >
> > > Fixes: eff0c286aa91 ("PCI: endpoint: pci-epf-test: Add doorbell test support")
> > > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > > ---
> > > drivers/pci/endpoint/functions/pci-epf-test.c | 9 ++++++++-
> > > 1 file changed, 8 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> > > index 6952ee418622..23034f548c90 100644
> > > --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> > > +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> > > @@ -86,6 +86,7 @@ struct pci_epf_test {
> > > bool dma_private;
> > > const struct pci_epc_features *epc_features;
> > > struct pci_epf_bar db_bar;
> > > + bool db_irq_requested;
> > > size_t bar_size[PCI_STD_NUM_BARS];
> > > };
> > >
> > > @@ -715,7 +716,10 @@ 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];
> > > struct pci_epf *epf = epf_test->epf;
> > >
> > > - free_irq(epf->db_msg[0].virq, epf_test);
> > > + if (epf_test->db_irq_requested && epf->db_msg) {
> > > + free_irq(epf->db_msg[0].virq, epf_test);
> > > + epf_test->db_irq_requested = false;
> > > + }
> > > reg->doorbell_bar = cpu_to_le32(NO_BAR);
> > >
> > > pci_epf_free_doorbell(epf);
> > > @@ -741,6 +745,8 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> > > if (bar < BAR_0)
> > > goto err_doorbell_cleanup;
> > >
> > > + epf_test->db_irq_requested = false;
> > > +
> > > ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
> > > pci_epf_test_doorbell_handler, IRQF_ONESHOT,
> > > "pci-ep-test-doorbell", epf_test);
> >
> > Another bug in pci_epf_test_enable_doorbell():
> >
> > Since we reuse the BAR size, and use dynamic inbound mapping,
> > what if the returned DB offset is larger than epf->bar[bar].size ?
> >
> > I think we need something like this before calling pci_epc_set_bar():
> >
> > if (reg->doorbell_offset >= epf->bar[bar].size)
> > goto err_doorbell_cleanup;
>
> Right, I remember this coming up in another thread.
>
> If there are no objections from either of you, I'm happy to include a fix
> patch for this in v7.
No objection from me.
Ideally I would also like:
if (!(test->ep_caps & CAP_DYNAMIC_INBOUND_MAPPING))
return -EOPNOTSUPP;
and that the pci_endpoint_test selftest would return skip on -EOPNOTSUPP,
since the doorbell test currently relies on CAP_DYNAMIC_INBOUND_MAPPING,
but that might make your series too big.
Thus, I'm happy if you add a safety check for:
reg->doorbell_offset >= epf->bar[bar].size
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-10 16:30 ` Niklas Cassel
@ 2026-02-11 15:57 ` Koichiro Den
2026-02-11 16:52 ` Niklas Cassel
2026-02-12 16:31 ` Koichiro Den
0 siblings, 2 replies; 33+ messages in thread
From: Koichiro Den @ 2026-02-11 15:57 UTC (permalink / raw)
To: Niklas Cassel
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Tue, Feb 10, 2026 at 05:30:10PM +0100, Niklas Cassel wrote:
> On Tue, Feb 10, 2026 at 11:07:16PM +0900, Koichiro Den wrote:
> > On Tue, Feb 10, 2026 at 01:24:29PM +0100, Niklas Cassel wrote:
> > > On Mon, Feb 09, 2026 at 09:53:08PM +0900, Koichiro Den wrote:
> > > > Tested on
> > > > =========
> > > >
> > > > I tested the embedded (DMA) doorbell fallback path (via pci-epf-test) on
> > > > R-Car Spider boards:
> > > >
> > > > $ ./pci_endpoint_test -t DOORBELL_TEST
> > > > TAP version 13
> > > > 1..1
> > > > # Starting 1 tests from 1 test cases.
> > > > # RUN pcie_ep_doorbell.DOORBELL_TEST ...
> > > > # OK pcie_ep_doorbell.DOORBELL_TEST
> > > > ok 1 pcie_ep_doorbell.DOORBELL_TEST
> > > > # PASSED: 1 / 1 tests passed.
> > > > # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
> > > >
> > > > with the following message observed on the EP side:
> > > >
> > > > [ 80.464653] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
> > > >
> > > > (Note: for the test to pass on R-Car Spider, one of the following was required:
> > > > - echo 1048576 > functions/pci_epf_test/func1/pci_epf_test.0/bar2_size
> > > > - apply https://lore.kernel.org/all/20251023072217.901888-1-den@valinux.co.jp/)
> > >
> > > I applied this series on top of branch pci/controller/dwc
> > > on Rock 5B (pcie-dw-rockchip.c).
> > >
> > > On EP side:
> > > [ 39.218533] pci_epf_test pci_epf_test.0: Can't find MSI domain for EPC
> > > [ 39.219125] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
> > >
> > > On RC side:
> > > # RUN pcie_ep_doorbell.DOORBELL_TEST ...
> > > [ 40.297892] pci-endpoint-test 0000:01:00.0: Failed to trigger doorbell in endpoint
> > > # pci_endpoint_test.c:279:DOORBELL_TEST:Expected 0 (0) == ret (-22)
> > > # pci_endpoint_test.c:279:DOORBELL_TEST:Test failed for Doorbell
> > >
> > > # DOORBELL_TEST: Test failed
> > > # FAIL pcie_ep_doorbell.DOORBELL_TEST
> > > not ok 23 pcie_ep_doorbell.DOORBELL_TEST
> > >
> > > Any suggestions?
> > >
> > > (All BARs in pcie-dw-rockchip.c is marked as BAR_RESIZABLE.)
> >
> > Thank you for testing.
> >
> > If the failure was observed in a scenario other than a plain
> > `./pci_endpoint_test -t DOORBELL_TEST`, could you please try again with [1]
> > applied as well?
> >
> > [1] https://lore.kernel.org/linux-pci/20260202145407.503348-1-den@valinux.co.jp/
>
> I applied that series, but I got the same problem.
>
> I added debug, and the EP side does use the correct address for the eDMA:
> [ 26.279457] msg_addr: 0xa403800a0
> [ 26.279898] phys_addr: 0xa40300000 offset: 0x800a0
>
>
> If I write to the msg_addr directly on the EP using devmem, I do see the print
> that I added in the IRQ handler:
> # devmem 0xa403800a0 32 0
> [ 155.861989] dw_edma_interrupt_emulated:696
> # devmem 0xa403800a0 32 0
> [ 158.809160] dw_edma_interrupt_emulated:696
> [ 158.809543] pci_epf_test_doorbell_primary:729
> # [ 158.809986] pci_epf_test_doorbell_handler:703
> # devmem 0xa403800a0 32 0
> [ 161.241326] dw_edma_interrupt_emulated:696
> # devmem 0xa403800a0 32 0
> [ 163.466054] dw_edma_interrupt_emulated:696
> # devmem 0xa403800a0 32 0
> [ 167.378662] dw_edma_interrupt_emulated:696
> [ 167.379045] pci_epf_test_doorbell_primary:729
> # [ 167.379512] pci_epf_test_doorbell_handler:703
> # devmem 0xa403800a0 32 0
> [ 168.880179] dw_edma_interrupt_emulated:696
> # devmem 0xa403800a0 32 0
> [ 170.492176] dw_edma_interrupt_emulated:696
> # devmem 0xa403800a0 32 0
> [ 171.729154] dw_edma_interrupt_emulated:696
> # devmem 0xa403800a0 32 0
> [ 173.481271] dw_edma_interrupt_emulated:696
> # devmem 0xa403800a0 32 0
> [ 174.985787] dw_edma_interrupt_emulated:696
> # devmem 0xa403800a0 32 0
> [ 176.517131] dw_edma_interrupt_emulated:696
> [ 176.517511] pci_epf_test_doorbell_primary:729
> # [ 176.517963] pci_epf_test_doorbell_handler:703
>
> But not on every write....
>
> I'm not sure, but could this perhaps be because we are missing this patch:
> https://lore.kernel.org/dmaengine/20260105075904.1254012-1-den@valinux.co.jp/
Thank you for the detailed debugging.
I don't have a Rock 5B to reproduce locally, but your log indicates that
the emulated interrupt is not always delivered on the same eDMA IRQ line.
On RK3588 (rk3588-extra.dtsi) there are multiple eDMA IRQs (dma0-4), while
pci-epf-test requests only epf->db_msg[0].virq (IRQ 53 in your
/proc/interrupts). For R-Car S4 Spider, chip->nr_irqs == 1 and I wasn't
able to verify whether my earlier concern here:
https://lore.kernel.org/linux-pci/p4ommmpcjegvb4lafzecf67tzmdodtuqexeoifcn5eh7xqyp2y@ss76d3ubbsw7/
> The proposed dmaengine_{register,unregister}_selfirq() APIs are
> device-wide (i.e. not per channel), so I'm not sure which "channel" you
> refer to here. Also, when chip->nr_irqs > 1 on EP, dw-edma distributes
> channels across multiple IRQ vectors, and it's unclear (at least to me)
> which IRQ vector the emulated interrupt ("fake irq") is expected to be
> delivered on.
actually holds true in practice. Your report makes it clear that the
emulated interrupt can indeed be delivered on different IRQ vectors.
One hypothesis is that: we currently program msg.data = 0 for the
"embedded" doorbell, and we write to DMA_READ_INT_STATUS_OFF. The register
field (RD_DONE_INT_STATUS) is defined per-channel, and the register
supports interrupt emulation by writes, so it might be that writing BIT(n)
selects the channel/irq line, while writing 0 does not consistently map to
a specific one.
Could you try a quick experiment on the Rock 5B EP side?
devmem 0xa403800a0 32 1
devmem 0xa403800a0 32 2
devmem 0xa403800a0 32 4
devmem 0xa403800a0 32 8
and see if the interrupt consistently lands on a specific one of IRQ53-56
for each value?
If that is the case, we can make msg.data non-zero value instead of always 0.
If that is not the case, then we may need to consider two less ideal
options:
- switch back to the ~v3 approach, where we run the registered hook
exactly at the time when the emulated interrupt is deasserted. (ref.
https://lore.kernel.org/linux-pci/20260204145440.950609-6-den@valinux.co.jp/)
- or, require users to request_irq() for all irq vectors associated with
all channels. However, this would not be very attractive from a design
perspective.
>
> # dmesg | grep eDMA
> [ 1.243339] rockchip-dw-pcie a40000000.pcie-ep: eDMA: unroll T, 2 wr, 2 rd
>
> # cat /proc/interrupts | grep edma
> 53: 8 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
> 54: 7 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
> 55: 15 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
> 56: 7 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
>
>
>
> Anyway, I was still curious why this did never worked when writing from the
> host side, even when running the test case many, many times.
> AFAICT, the inbound translation looks correct.
>
> RK3588 (pcie-dw-rockchip.c) exposes the DMA registers in BAR4 by default.
> If I hack pci-epf-test on top of your patch to unconditionally return BAR4 with
> offset 0xa0, it works. So my best guess is that the fixed inbound translation
> in BAR4 (to the eDMA registers) somehow messes with the inbound translation if
> another BAR tries to use an inbound translation to the eDMA registers as well.
Thanks a lot for letting me know that. I see two possible ways forward:
(a) extend PCI_EPC_AUX_DMA_CTRL_MMIO to optionally describe that the DMA
MMIO window is already mapped to a fixed BAR and should be reused, so
EPFs avoid creating a second mapping to the same target. I guess it
could be treated as a quirk for "rockchip,rk3588-pcie-ep".
(b) alternatively, clear the default BAR4 mapping on RK3588 at least
temporarily when using the pci-epf-msi doorbell fallback.
Koichiro
>
>
> Kind regards,
> Niklas
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested
2026-02-10 16:38 ` Niklas Cassel
@ 2026-02-11 16:08 ` Koichiro Den
0 siblings, 0 replies; 33+ messages in thread
From: Koichiro Den @ 2026-02-11 16:08 UTC (permalink / raw)
To: Niklas Cassel
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Tue, Feb 10, 2026 at 05:38:19PM +0100, Niklas Cassel wrote:
> On Tue, Feb 10, 2026 at 10:54:20PM +0900, Koichiro Den wrote:
> > On Tue, Feb 10, 2026 at 01:36:29PM +0100, Niklas Cassel wrote:
> > > On Mon, Feb 09, 2026 at 09:53:14PM +0900, Koichiro Den wrote:
> > > > pci_epf_test_enable_doorbell() allocates a doorbell and then installs
> > > > the interrupt handler with request_threaded_irq(). On failures before
> > > > the IRQ is successfully requested (e.g. no free BAR,
> > > > request_threaded_irq() failure), the error path jumps to
> > > > err_doorbell_cleanup and calls pci_epf_test_doorbell_cleanup().
> > > >
> > > > pci_epf_test_doorbell_cleanup() unconditionally calls free_irq() for the
> > > > doorbell virq, which can trigger "Trying to free already-free IRQ"
> > > > warnings when the IRQ was never requested.
> > > >
> > > > Track whether the doorbell IRQ has been successfully requested and only
> > > > call free_irq() when it has.
> > > >
> > > > Fixes: eff0c286aa91 ("PCI: endpoint: pci-epf-test: Add doorbell test support")
> > > > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > > > ---
> > > > drivers/pci/endpoint/functions/pci-epf-test.c | 9 ++++++++-
> > > > 1 file changed, 8 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> > > > index 6952ee418622..23034f548c90 100644
> > > > --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> > > > +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> > > > @@ -86,6 +86,7 @@ struct pci_epf_test {
> > > > bool dma_private;
> > > > const struct pci_epc_features *epc_features;
> > > > struct pci_epf_bar db_bar;
> > > > + bool db_irq_requested;
> > > > size_t bar_size[PCI_STD_NUM_BARS];
> > > > };
> > > >
> > > > @@ -715,7 +716,10 @@ 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];
> > > > struct pci_epf *epf = epf_test->epf;
> > > >
> > > > - free_irq(epf->db_msg[0].virq, epf_test);
> > > > + if (epf_test->db_irq_requested && epf->db_msg) {
> > > > + free_irq(epf->db_msg[0].virq, epf_test);
> > > > + epf_test->db_irq_requested = false;
> > > > + }
> > > > reg->doorbell_bar = cpu_to_le32(NO_BAR);
> > > >
> > > > pci_epf_free_doorbell(epf);
> > > > @@ -741,6 +745,8 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> > > > if (bar < BAR_0)
> > > > goto err_doorbell_cleanup;
> > > >
> > > > + epf_test->db_irq_requested = false;
> > > > +
> > > > ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
> > > > pci_epf_test_doorbell_handler, IRQF_ONESHOT,
> > > > "pci-ep-test-doorbell", epf_test);
> > >
> > > Another bug in pci_epf_test_enable_doorbell():
> > >
> > > Since we reuse the BAR size, and use dynamic inbound mapping,
> > > what if the returned DB offset is larger than epf->bar[bar].size ?
> > >
> > > I think we need something like this before calling pci_epc_set_bar():
> > >
> > > if (reg->doorbell_offset >= epf->bar[bar].size)
> > > goto err_doorbell_cleanup;
> >
> > Right, I remember this coming up in another thread.
> >
> > If there are no objections from either of you, I'm happy to include a fix
> > patch for this in v7.
>
> No objection from me.
>
>
> Ideally I would also like:
>
> if (!(test->ep_caps & CAP_DYNAMIC_INBOUND_MAPPING))
> return -EOPNOTSUPP;
>
> and that the pci_endpoint_test selftest would return skip on -EOPNOTSUPP,
> since the doorbell test currently relies on CAP_DYNAMIC_INBOUND_MAPPING,
> but that might make your series too big.
That makes sense. I'll consider posting a stand-alone patch for that
separately.
Best regards,
Koichiro
>
>
> Thus, I'm happy if you add a safety check for:
> reg->doorbell_offset >= epf->bar[bar].size
>
>
> Kind regards,
> Niklas
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-11 15:57 ` Koichiro Den
@ 2026-02-11 16:52 ` Niklas Cassel
2026-02-12 3:26 ` Koichiro Den
2026-02-12 16:31 ` Koichiro Den
1 sibling, 1 reply; 33+ messages in thread
From: Niklas Cassel @ 2026-02-11 16:52 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel, Manikanta Maddireddy
On Thu, Feb 12, 2026 at 12:57:52AM +0900, Koichiro Den wrote:
> Could you try a quick experiment on the Rock 5B EP side?
>
> devmem 0xa403800a0 32 1
> devmem 0xa403800a0 32 2
> devmem 0xa403800a0 32 4
> devmem 0xa403800a0 32 8
Here it goes:
# cat /proc/interrupts | grep edma
53: 0 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
54: 0 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
55: 0 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
# devmem 0xa403800a0 32 1
# cat /proc/interrupts | grep edma
53: 0 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
54: 1 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
55: 0 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
# devmem 0xa403800a0 32 1
# cat /proc/interrupts | grep edma
53: 0 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
54: 2 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
55: 0 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
# devmem 0xa403800a0 32 1
[ 104.217632] pci_epf_test_doorbell_primary
# cat /proc/interrupts | grep edma
53: 1 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
54: 2 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
55: 0 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
# devmem 0xa403800a0 32 1
# cat /proc/interrupts | grep edma
53: 1 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
54: 2 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
55: 1 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
Seems very random if you ask me.
Same randomness is observed when I write other values, e.g. 2.
Could this randomness be because I do not have:
https://lore.kernel.org/dmaengine/20260105075904.1254012-1-den@valinux.co.jp/
Or do you think that is completely unrelated?
> > RK3588 (pcie-dw-rockchip.c) exposes the DMA registers in BAR4 by default.
> > If I hack pci-epf-test on top of your patch to unconditionally return BAR4 with
> > offset 0xa0, it works. So my best guess is that the fixed inbound translation
> > in BAR4 (to the eDMA registers) somehow messes with the inbound translation if
> > another BAR tries to use an inbound translation to the eDMA registers as well.
Adding Manikanta to CC.
RK3588 is not the only SoC that has the eDMA registers exposed via a BAR,
some other SoCs like Tegra234 also has the eDMA registers exposed via a BAR.
Me and Manikanta were discussing this a few days ago:
https://lore.kernel.org/linux-pci/aYsQu9lQi4IzfBiP@ryzen/
>
> Thanks a lot for letting me know that. I see two possible ways forward:
>
> (a) extend PCI_EPC_AUX_DMA_CTRL_MMIO to optionally describe that the DMA
> MMIO window is already mapped to a fixed BAR and should be reused, so
> EPFs avoid creating a second mapping to the same target. I guess it
> could be treated as a quirk for "rockchip,rk3588-pcie-ep".
I do like the idea that an EPC driver can somehow provide the BAR number
which the eDMA registers are already exposed in, together with the offset
and the size within that BAR. After that pci-epf-test could still work with
the emulated doorbell (only difference is that it would not program the iATU).
(This would also require Manikanta suggestion of having both a BAR_RESERVED and
a BAR_DISABLED type, and support in pci_endpoint_test to ignore RESERVED BARs.)
>
> (b) alternatively, clear the default BAR4 mapping on RK3588 at least
> temporarily when using the pci-epf-msi doorbell fallback.
So for these SoCs like RK3588 and Tegra234, it appears that the option to
expose the eDMA registers in one of the BARs is a Synopsys DWC config that
you set when you synthesize the DWC core.
When you do this, it appear that that BAR will always some kind of fixed
inbound address translation (i.e. even when you disable all inbound iATUs
this translation for BAR4 to eDMA registers is still there).
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-11 16:52 ` Niklas Cassel
@ 2026-02-12 3:26 ` Koichiro Den
2026-02-12 10:26 ` Niklas Cassel
0 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-12 3:26 UTC (permalink / raw)
To: Niklas Cassel
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel, Manikanta Maddireddy
On Wed, Feb 11, 2026 at 05:52:53PM +0100, Niklas Cassel wrote:
> On Thu, Feb 12, 2026 at 12:57:52AM +0900, Koichiro Den wrote:
> > Could you try a quick experiment on the Rock 5B EP side?
> >
> > devmem 0xa403800a0 32 1
> > devmem 0xa403800a0 32 2
> > devmem 0xa403800a0 32 4
> > devmem 0xa403800a0 32 8
>
> Here it goes:
>
> # cat /proc/interrupts | grep edma
> 53: 0 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
> 54: 0 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
> 55: 0 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
> 56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
> # devmem 0xa403800a0 32 1
> # cat /proc/interrupts | grep edma
> 53: 0 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
> 54: 1 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
> 55: 0 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
> 56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
> # devmem 0xa403800a0 32 1
> # cat /proc/interrupts | grep edma
> 53: 0 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
> 54: 2 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
> 55: 0 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
> 56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
> # devmem 0xa403800a0 32 1
> [ 104.217632] pci_epf_test_doorbell_primary
> # cat /proc/interrupts | grep edma
> 53: 1 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
> 54: 2 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
> 55: 0 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
> 56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
> # devmem 0xa403800a0 32 1
> # cat /proc/interrupts | grep edma
> 53: 1 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
> 54: 2 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
> 55: 1 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
> 56: 0 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
>
>
> Seems very random if you ask me.
>
> Same randomness is observed when I write other values, e.g. 2.
Thanks for the testing and sharing the actual behavior. That helps a lot.
>
>
> Could this randomness be because I do not have:
> https://lore.kernel.org/dmaengine/20260105075904.1254012-1-den@valinux.co.jp/
>
> Or do you think that is completely unrelated?
I didn't clearly answer this in my previous response, apologies.
I believe that patch is unrelated. It addresses MSI data handling for
multi-vector IMWr generation (i.e. remote interrupts), whereas here we are
dealing with the emulated interrupt path for the embedded doorbell.
If applying that patch changes the emulated interrupt behavior in your
environment, please let me know. That would be very interesting.
>
>
>
> > > RK3588 (pcie-dw-rockchip.c) exposes the DMA registers in BAR4 by default.
> > > If I hack pci-epf-test on top of your patch to unconditionally return BAR4 with
> > > offset 0xa0, it works. So my best guess is that the fixed inbound translation
> > > in BAR4 (to the eDMA registers) somehow messes with the inbound translation if
> > > another BAR tries to use an inbound translation to the eDMA registers as well.
>
> Adding Manikanta to CC.
>
> RK3588 is not the only SoC that has the eDMA registers exposed via a BAR,
> some other SoCs like Tegra234 also has the eDMA registers exposed via a BAR.
>
> Me and Manikanta were discussing this a few days ago:
> https://lore.kernel.org/linux-pci/aYsQu9lQi4IzfBiP@ryzen/
>
>
> >
> > Thanks a lot for letting me know that. I see two possible ways forward:
> >
> > (a) extend PCI_EPC_AUX_DMA_CTRL_MMIO to optionally describe that the DMA
> > MMIO window is already mapped to a fixed BAR and should be reused, so
> > EPFs avoid creating a second mapping to the same target. I guess it
> > could be treated as a quirk for "rockchip,rk3588-pcie-ep".
>
> I do like the idea that an EPC driver can somehow provide the BAR number
> which the eDMA registers are already exposed in, together with the offset
> and the size within that BAR. After that pci-epf-test could still work with
> the emulated doorbell (only difference is that it would not program the iATU).
>
> (This would also require Manikanta suggestion of having both a BAR_RESERVED and
> a BAR_DISABLED type, and support in pci_endpoint_test to ignore RESERVED BARs.)
Thanks for the additional context.
Even if we introduce the distinction between BAR_RESERVED and BAR_DISABLED,
as I understand it, we currently lack a way to describe what actually
resides behind a BAR_RESERVED region.
Perhaps extending pci_epc_bar_desc to describe what a reserved BAR
contains (e.g. DMA register block) might allow us to handle this in a
cleaner and more generic way. It would at least be cleaner than treating it
as a quirk and hard-code the reserved BAR+offset+contents.
>
>
> >
> > (b) alternatively, clear the default BAR4 mapping on RK3588 at least
> > temporarily when using the pci-epf-msi doorbell fallback.
>
> So for these SoCs like RK3588 and Tegra234, it appears that the option to
> expose the eDMA registers in one of the BARs is a Synopsys DWC config that
> you set when you synthesize the DWC core.
> When you do this, it appear that that BAR will always some kind of fixed
> inbound address translation (i.e. even when you disable all inbound iATUs
> this translation for BAR4 to eDMA registers is still there).
Thanks a lot for this input, so (b) would not be viable from the beginning.
Best regards,
Koichiro
>
>
> Kind regards,
> Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-12 3:26 ` Koichiro Den
@ 2026-02-12 10:26 ` Niklas Cassel
2026-02-12 15:14 ` Koichiro Den
0 siblings, 1 reply; 33+ messages in thread
From: Niklas Cassel @ 2026-02-12 10:26 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel, Manikanta Maddireddy
On Thu, Feb 12, 2026 at 12:26:49PM +0900, Koichiro Den wrote:
> On Wed, Feb 11, 2026 at 05:52:53PM +0100, Niklas Cassel wrote:
>
> Thanks for the additional context.
> Even if we introduce the distinction between BAR_RESERVED and BAR_DISABLED,
> as I understand it, we currently lack a way to describe what actually
> resides behind a BAR_RESERVED region.
>
> Perhaps extending pci_epc_bar_desc to describe what a reserved BAR
> contains (e.g. DMA register block) might allow us to handle this in a
> cleaner and more generic way. It would at least be cleaner than treating it
> as a quirk and hard-code the reserved BAR+offset+contents.
Yes, you are absolutely right.
Improving struct pci_epc_bar_desc to be able give more information about a
BAR_RESERVED BAR would be a next logical step.
If we take RK3588 as an example:
BAR4 offset 0x0 is eDMA registers.
BAR4 offset 0x2000 is ATU registers.
BAR4 offset 0x4000 is MSI-X table.
BAR4 offset 0x5000 is MSI-X PBA.
Many different SoCs have the MSI-X table in one of the BARs.
pci-epf-test always puts the MSI-X table in BAR0 (test_reg_bar), after the
pci_epf_test_reg registers.
On RK3588, this mostly works fine, e.g. the MSI-X test case in the
pci_endpoint_test selftest passes with the MSI-X table in BAR0, however,
e.g. dw_pcie_ep_raise_msix_irq_doorbell() does not work when the MSI-X
table is in BAR0. If I hack the pci-epf-test code to have the MSI-X table
in BAR4 (as it is by default), then dw_pcie_ep_raise_msix_irq_doorbell()
works fine.
This would however require an EPF driver to be able to get an EPC drivers'
"desired" MSI-X table and MSI-X PBA location, so that it could call
pci_epc_set_msix() with these "desired" locations.
I guess we would just need to add a new "get desired MSI-X location" API
in epc->ops. However, I have been too busy to work on this, so right now it
is only an idea. (Anyone with some spare cycles are free to implement it.)
I know for a fact that many other SoCs with the DWC PCIe EP controller have
the MSI-X table in one of the BARs by default, so this would also allow
them to use dw_pcie_ep_raise_msix_irq_doorbell(). (And would also allow us
to no longer force disable these BAR_RESERVED BARs, as the PCI endpoint
currently has no way to make use for these BAR_RESERVED BARs.)
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-12 10:26 ` Niklas Cassel
@ 2026-02-12 15:14 ` Koichiro Den
2026-02-12 15:38 ` Niklas Cassel
0 siblings, 1 reply; 33+ messages in thread
From: Koichiro Den @ 2026-02-12 15:14 UTC (permalink / raw)
To: Niklas Cassel
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel, Manikanta Maddireddy
On Thu, Feb 12, 2026 at 11:26:59AM +0100, Niklas Cassel wrote:
> On Thu, Feb 12, 2026 at 12:26:49PM +0900, Koichiro Den wrote:
> > On Wed, Feb 11, 2026 at 05:52:53PM +0100, Niklas Cassel wrote:
> >
> > Thanks for the additional context.
> > Even if we introduce the distinction between BAR_RESERVED and BAR_DISABLED,
> > as I understand it, we currently lack a way to describe what actually
> > resides behind a BAR_RESERVED region.
> >
> > Perhaps extending pci_epc_bar_desc to describe what a reserved BAR
> > contains (e.g. DMA register block) might allow us to handle this in a
> > cleaner and more generic way. It would at least be cleaner than treating it
> > as a quirk and hard-code the reserved BAR+offset+contents.
>
> Yes, you are absolutely right.
>
> Improving struct pci_epc_bar_desc to be able give more information about a
> BAR_RESERVED BAR would be a next logical step.
>
> If we take RK3588 as an example:
> BAR4 offset 0x0 is eDMA registers.
> BAR4 offset 0x2000 is ATU registers.
> BAR4 offset 0x4000 is MSI-X table.
> BAR4 offset 0x5000 is MSI-X PBA.
>
>
> Many different SoCs have the MSI-X table in one of the BARs.
>
> pci-epf-test always puts the MSI-X table in BAR0 (test_reg_bar), after the
> pci_epf_test_reg registers.
>
> On RK3588, this mostly works fine, e.g. the MSI-X test case in the
> pci_endpoint_test selftest passes with the MSI-X table in BAR0, however,
> e.g. dw_pcie_ep_raise_msix_irq_doorbell() does not work when the MSI-X
> table is in BAR0. If I hack the pci-epf-test code to have the MSI-X table
> in BAR4 (as it is by default), then dw_pcie_ep_raise_msix_irq_doorbell()
> works fine.
Yes, I remember this was discussed in another patch thread.
>
> This would however require an EPF driver to be able to get an EPC drivers'
> "desired" MSI-X table and MSI-X PBA location, so that it could call
> pci_epc_set_msix() with these "desired" locations.
>
>
> I guess we would just need to add a new "get desired MSI-X location" API
> in epc->ops. However, I have been too busy to work on this, so right now it
> is only an idea. (Anyone with some spare cycles are free to implement it.)
While I think the MSI-X table placement issue is not directly relevant to
the eDMA register case (which needs to be addressed in this patch series),
they similarly appear to be consequences of SoC-level integration
decisions.
From that perspective, I feel that extending pci_epc_bar_desc so it can
describe (1) what resides behind a BAR_RESERVED region and (2) whether it
must be reused rather than accessed via a new iATU inbound mapping, would
be a clearer and more generic, extensible solution than adding a dedicated
epc->ops API for each such case.
Anyway, I think that the MSI-X Table case should be handled in another
patch series.
Best regards,
Koichiro
>
> I know for a fact that many other SoCs with the DWC PCIe EP controller have
> the MSI-X table in one of the BARs by default, so this would also allow
> them to use dw_pcie_ep_raise_msix_irq_doorbell(). (And would also allow us
> to no longer force disable these BAR_RESERVED BARs, as the PCI endpoint
> currently has no way to make use for these BAR_RESERVED BARs.)
>
>
> Kind regards,
> Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-12 15:14 ` Koichiro Den
@ 2026-02-12 15:38 ` Niklas Cassel
0 siblings, 0 replies; 33+ messages in thread
From: Niklas Cassel @ 2026-02-12 15:38 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel, Manikanta Maddireddy
On Fri, Feb 13, 2026 at 12:14:15AM +0900, Koichiro Den wrote:
>
> Anyway, I think that the MSI-X Table case should be handled in another
> patch series.
Yes, of course.
I just wanted to give some extra context.
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
2026-02-11 15:57 ` Koichiro Den
2026-02-11 16:52 ` Niklas Cassel
@ 2026-02-12 16:31 ` Koichiro Den
1 sibling, 0 replies; 33+ messages in thread
From: Koichiro Den @ 2026-02-12 16:31 UTC (permalink / raw)
To: Niklas Cassel
Cc: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, kishon, jdmason, allenbh, dmaengine, linux-pci, ntb,
linux-kselftest, linux-kernel
On Thu, Feb 12, 2026 at 12:57:52AM +0900, Koichiro Den wrote:
> On Tue, Feb 10, 2026 at 05:30:10PM +0100, Niklas Cassel wrote:
> > On Tue, Feb 10, 2026 at 11:07:16PM +0900, Koichiro Den wrote:
> > > On Tue, Feb 10, 2026 at 01:24:29PM +0100, Niklas Cassel wrote:
> > > > On Mon, Feb 09, 2026 at 09:53:08PM +0900, Koichiro Den wrote:
> > > > > Tested on
> > > > > =========
> > > > >
> > > > > I tested the embedded (DMA) doorbell fallback path (via pci-epf-test) on
> > > > > R-Car Spider boards:
> > > > >
> > > > > $ ./pci_endpoint_test -t DOORBELL_TEST
> > > > > TAP version 13
> > > > > 1..1
> > > > > # Starting 1 tests from 1 test cases.
> > > > > # RUN pcie_ep_doorbell.DOORBELL_TEST ...
> > > > > # OK pcie_ep_doorbell.DOORBELL_TEST
> > > > > ok 1 pcie_ep_doorbell.DOORBELL_TEST
> > > > > # PASSED: 1 / 1 tests passed.
> > > > > # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
> > > > >
> > > > > with the following message observed on the EP side:
> > > > >
> > > > > [ 80.464653] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
> > > > >
> > > > > (Note: for the test to pass on R-Car Spider, one of the following was required:
> > > > > - echo 1048576 > functions/pci_epf_test/func1/pci_epf_test.0/bar2_size
> > > > > - apply https://lore.kernel.org/all/20251023072217.901888-1-den@valinux.co.jp/)
> > > >
> > > > I applied this series on top of branch pci/controller/dwc
> > > > on Rock 5B (pcie-dw-rockchip.c).
> > > >
> > > > On EP side:
> > > > [ 39.218533] pci_epf_test pci_epf_test.0: Can't find MSI domain for EPC
> > > > [ 39.219125] pci_epf_test pci_epf_test.0: Using embedded (DMA) doorbell fallback
> > > >
> > > > On RC side:
> > > > # RUN pcie_ep_doorbell.DOORBELL_TEST ...
> > > > [ 40.297892] pci-endpoint-test 0000:01:00.0: Failed to trigger doorbell in endpoint
> > > > # pci_endpoint_test.c:279:DOORBELL_TEST:Expected 0 (0) == ret (-22)
> > > > # pci_endpoint_test.c:279:DOORBELL_TEST:Test failed for Doorbell
> > > >
> > > > # DOORBELL_TEST: Test failed
> > > > # FAIL pcie_ep_doorbell.DOORBELL_TEST
> > > > not ok 23 pcie_ep_doorbell.DOORBELL_TEST
> > > >
> > > > Any suggestions?
> > > >
> > > > (All BARs in pcie-dw-rockchip.c is marked as BAR_RESIZABLE.)
> > >
> > > Thank you for testing.
> > >
> > > If the failure was observed in a scenario other than a plain
> > > `./pci_endpoint_test -t DOORBELL_TEST`, could you please try again with [1]
> > > applied as well?
> > >
> > > [1] https://lore.kernel.org/linux-pci/20260202145407.503348-1-den@valinux.co.jp/
> >
> > I applied that series, but I got the same problem.
> >
> > I added debug, and the EP side does use the correct address for the eDMA:
> > [ 26.279457] msg_addr: 0xa403800a0
> > [ 26.279898] phys_addr: 0xa40300000 offset: 0x800a0
> >
> >
> > If I write to the msg_addr directly on the EP using devmem, I do see the print
> > that I added in the IRQ handler:
> > # devmem 0xa403800a0 32 0
> > [ 155.861989] dw_edma_interrupt_emulated:696
> > # devmem 0xa403800a0 32 0
> > [ 158.809160] dw_edma_interrupt_emulated:696
> > [ 158.809543] pci_epf_test_doorbell_primary:729
> > # [ 158.809986] pci_epf_test_doorbell_handler:703
> > # devmem 0xa403800a0 32 0
> > [ 161.241326] dw_edma_interrupt_emulated:696
> > # devmem 0xa403800a0 32 0
> > [ 163.466054] dw_edma_interrupt_emulated:696
> > # devmem 0xa403800a0 32 0
> > [ 167.378662] dw_edma_interrupt_emulated:696
> > [ 167.379045] pci_epf_test_doorbell_primary:729
> > # [ 167.379512] pci_epf_test_doorbell_handler:703
> > # devmem 0xa403800a0 32 0
> > [ 168.880179] dw_edma_interrupt_emulated:696
> > # devmem 0xa403800a0 32 0
> > [ 170.492176] dw_edma_interrupt_emulated:696
> > # devmem 0xa403800a0 32 0
> > [ 171.729154] dw_edma_interrupt_emulated:696
> > # devmem 0xa403800a0 32 0
> > [ 173.481271] dw_edma_interrupt_emulated:696
> > # devmem 0xa403800a0 32 0
> > [ 174.985787] dw_edma_interrupt_emulated:696
> > # devmem 0xa403800a0 32 0
> > [ 176.517131] dw_edma_interrupt_emulated:696
> > [ 176.517511] pci_epf_test_doorbell_primary:729
> > # [ 176.517963] pci_epf_test_doorbell_handler:703
> >
> > But not on every write....
> >
> > I'm not sure, but could this perhaps be because we are missing this patch:
> > https://lore.kernel.org/dmaengine/20260105075904.1254012-1-den@valinux.co.jp/
>
> Thank you for the detailed debugging.
>
> I don't have a Rock 5B to reproduce locally, but your log indicates that
> the emulated interrupt is not always delivered on the same eDMA IRQ line.
> On RK3588 (rk3588-extra.dtsi) there are multiple eDMA IRQs (dma0-4), while
> pci-epf-test requests only epf->db_msg[0].virq (IRQ 53 in your
> /proc/interrupts). For R-Car S4 Spider, chip->nr_irqs == 1 and I wasn't
> able to verify whether my earlier concern here:
> https://lore.kernel.org/linux-pci/p4ommmpcjegvb4lafzecf67tzmdodtuqexeoifcn5eh7xqyp2y@ss76d3ubbsw7/
>
> > The proposed dmaengine_{register,unregister}_selfirq() APIs are
> > device-wide (i.e. not per channel), so I'm not sure which "channel" you
> > refer to here. Also, when chip->nr_irqs > 1 on EP, dw-edma distributes
> > channels across multiple IRQ vectors, and it's unclear (at least to me)
> > which IRQ vector the emulated interrupt ("fake irq") is expected to be
> > delivered on.
>
> actually holds true in practice. Your report makes it clear that the
> emulated interrupt can indeed be delivered on different IRQ vectors.
>
> One hypothesis is that: we currently program msg.data = 0 for the
> "embedded" doorbell, and we write to DMA_READ_INT_STATUS_OFF. The register
> field (RD_DONE_INT_STATUS) is defined per-channel, and the register
> supports interrupt emulation by writes, so it might be that writing BIT(n)
> selects the channel/irq line, while writing 0 does not consistently map to
> a specific one.
>
> Could you try a quick experiment on the Rock 5B EP side?
>
> devmem 0xa403800a0 32 1
> devmem 0xa403800a0 32 2
> devmem 0xa403800a0 32 4
> devmem 0xa403800a0 32 8
>
> and see if the interrupt consistently lands on a specific one of IRQ53-56
> for each value?
>
> If that is the case, we can make msg.data non-zero value instead of always 0.
>
> If that is not the case, [...]
Thanks to the experiment by Niklas, it turns out that this is not the case.
> [...] then we may need to consider two less ideal
> options:
> - switch back to the ~v3 approach, where we run the registered hook
> exactly at the time when the emulated interrupt is deasserted. (ref.
> https://lore.kernel.org/linux-pci/20260204145440.950609-6-den@valinux.co.jp/)
> - or, require users to request_irq() for all irq vectors associated with
> all channels. However, this would not be very attractive from a design
> perspective.
So now revisiting this, in hindsight, this part (the two options) was
premature. I'm considering taking a different approach, like:
- alloc a single virtual IRQ dedicated to the emulated interrupt,
associate it with dummy_irq_chip + handle_simple_irq, and have the
parent handlers clear the emulated interrupt (i.e. write 0 to
INT_CLEAR) before invoking generic_handle_irq() from parent handlers
unconditionally (*). Or, create a dedicated irq_chip and use
handle_level_irq(), where the emulated interrupt is cleared in the
irq_ack path. The virtual IRQ is taught via
pci_epc_get_aux_resources().
(*): Because the Done/Abort bits are not set by the write into the
INT_STATUS, and because a DMA transfer completion may set those
bits between the write and the IRQ handling, as I understand it,
we still need to call generic_handle_irq() unconditionally.
I'm planning to rework the relevant code for v7.
Kind regards,
Koichiro
>
> >
> > # dmesg | grep eDMA
> > [ 1.243339] rockchip-dw-pcie a40000000.pcie-ep: eDMA: unroll T, 2 wr, 2 rd
> >
> > # cat /proc/interrupts | grep edma
> > 53: 8 0 0 0 0 0 0 0 GICv3 303 Level dw-edma-core:a40000000.pcie-ep, pci-ep-test-doorbell
> > 54: 7 0 0 0 0 0 0 0 GICv3 304 Level dw-edma-core:a40000000.pcie-ep
> > 55: 15 0 0 0 0 0 0 0 GICv3 301 Level dw-edma-core:a40000000.pcie-ep
> > 56: 7 0 0 0 0 0 0 0 GICv3 302 Level dw-edma-core:a40000000.pcie-ep
> >
> >
> >
> > Anyway, I was still curious why this did never worked when writing from the
> > host side, even when running the test case many, many times.
> > AFAICT, the inbound translation looks correct.
> >
> > RK3588 (pcie-dw-rockchip.c) exposes the DMA registers in BAR4 by default.
> > If I hack pci-epf-test on top of your patch to unconditionally return BAR4 with
> > offset 0xa0, it works. So my best guess is that the fixed inbound translation
> > in BAR4 (to the eDMA registers) somehow messes with the inbound translation if
> > another BAR tries to use an inbound translation to the eDMA registers as well.
>
> Thanks a lot for letting me know that. I see two possible ways forward:
>
> (a) extend PCI_EPC_AUX_DMA_CTRL_MMIO to optionally describe that the DMA
> MMIO window is already mapped to a fixed BAR and should be reused, so
> EPFs avoid creating a second mapping to the same target. I guess it
> could be treated as a quirk for "rockchip,rk3588-pcie-ep".
>
> (b) alternatively, clear the default BAR4 mapping on RK3588 at least
> temporarily when using the pci-epf-msi doorbell fallback.
>
> Koichiro
>
> >
> >
> > Kind regards,
> > Niklas
> >
>
^ permalink raw reply [flat|nested] 33+ messages in thread
end of thread, other threads:[~2026-02-12 16:32 UTC | newest]
Thread overview: 33+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-09 12:53 [PATCH v6 0/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
2026-02-09 12:53 ` [PATCH v6 1/8] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler Koichiro Den
2026-02-09 16:05 ` Frank Li
2026-02-09 12:53 ` [PATCH v6 2/8] dmaengine: dw-edma: Cache per-channel IRQ and emulation doorbell offset Koichiro Den
2026-02-09 16:13 ` Frank Li
2026-02-10 1:48 ` Koichiro Den
2026-02-09 12:53 ` [PATCH v6 3/8] PCI: endpoint: Add auxiliary resource query API Koichiro Den
2026-02-09 15:59 ` Frank Li
2026-02-09 12:53 ` [PATCH v6 4/8] PCI: dwc: Record integrated eDMA register window Koichiro Den
2026-02-09 12:53 ` [PATCH v6 5/8] PCI: dwc: ep: Report integrated eDMA resources via EPC aux-resource API Koichiro Den
2026-02-09 12:53 ` [PATCH v6 6/8] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested Koichiro Den
2026-02-09 15:57 ` Frank Li
2026-02-10 1:54 ` Koichiro Den
2026-02-10 12:36 ` Niklas Cassel
2026-02-10 13:54 ` Koichiro Den
2026-02-10 16:38 ` Niklas Cassel
2026-02-11 16:08 ` Koichiro Den
2026-02-09 12:53 ` [PATCH v6 7/8] PCI: endpoint: pci-ep-msi: Fix error unwind and prevent double alloc Koichiro Den
2026-02-09 15:53 ` Frank Li
2026-02-09 12:53 ` [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Koichiro Den
2026-02-09 15:51 ` Frank Li
2026-02-10 2:10 ` Koichiro Den
2026-02-10 15:16 ` Frank Li
2026-02-10 12:24 ` [PATCH v6 0/8] " Niklas Cassel
2026-02-10 14:07 ` Koichiro Den
2026-02-10 16:30 ` Niklas Cassel
2026-02-11 15:57 ` Koichiro Den
2026-02-11 16:52 ` Niklas Cassel
2026-02-12 3:26 ` Koichiro Den
2026-02-12 10:26 ` Niklas Cassel
2026-02-12 15:14 ` Koichiro Den
2026-02-12 15:38 ` Niklas Cassel
2026-02-12 16:31 ` Koichiro Den
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox