* [PATCH v4 1/9] dmaengine: dw-edma: Add per-channel interrupt routing control
2026-02-06 17:26 [PATCH v4 0/9] dmaengine, PCI: endpoint: Enable remote use of integrated DesignWare eDMA Koichiro Den
@ 2026-02-06 17:26 ` Koichiro Den
2026-02-06 17:43 ` Frank Li
2026-02-06 17:26 ` [PATCH v4 2/9] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler Koichiro Den
` (7 subsequent siblings)
8 siblings, 1 reply; 24+ messages in thread
From: Koichiro Den @ 2026-02-06 17:26 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas
Cc: dmaengine, linux-pci, linux-kselftest, linux-kernel
DesignWare EP eDMA can generate interrupts both locally and remotely
(LIE/RIE). Remote eDMA users need to decide, per channel, whether
completions should be handled locally, remotely, or both. Unless
carefully configured, the endpoint and host would race to ack the
interrupt.
Introduce a dw_edma_peripheral_config that holds per-channel interrupt
routing mode. Update v0 programming so that RIE and local done/abort
interrupt masking follow the selected mode. The default mode keeps the
original behavior, so unless the new peripheral_config is explicitly
used and set, no functional changes.
For now, HDMA is not supported for the peripheral_config. Until the
support is implemented and validated, explicitly reject it for HDMA to
avoid silently misconfiguring interrupt routing.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/dma/dw-edma/dw-edma-core.c | 37 +++++++++++++++++++++++++++
drivers/dma/dw-edma/dw-edma-core.h | 13 ++++++++++
drivers/dma/dw-edma/dw-edma-v0-core.c | 26 +++++++++++++------
include/linux/dma/edma.h | 28 ++++++++++++++++++++
4 files changed, 96 insertions(+), 8 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 8e5f7defa6b6..0c3461f9174a 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -219,11 +219,47 @@ static void dw_edma_device_caps(struct dma_chan *dchan,
}
}
+static int dw_edma_parse_irq_mode(struct dw_edma_chan *chan,
+ const struct dma_slave_config *config,
+ enum dw_edma_ch_irq_mode *mode)
+{
+ const struct dw_edma_peripheral_config *pcfg;
+
+ /* peripheral_config is optional, default keeps legacy behaviour. */
+ *mode = DW_EDMA_CH_IRQ_DEFAULT;
+ if (!config || !config->peripheral_config)
+ return 0;
+
+ if (chan->dw->chip->mf == EDMA_MF_HDMA_NATIVE)
+ return -EOPNOTSUPP;
+
+ if (config->peripheral_size < sizeof(*pcfg))
+ return -EINVAL;
+
+ pcfg = config->peripheral_config;
+ switch (pcfg->irq_mode) {
+ case DW_EDMA_CH_IRQ_DEFAULT:
+ case DW_EDMA_CH_IRQ_LOCAL:
+ case DW_EDMA_CH_IRQ_REMOTE:
+ *mode = pcfg->irq_mode;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
static int dw_edma_device_config(struct dma_chan *dchan,
struct dma_slave_config *config)
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+ enum dw_edma_ch_irq_mode mode;
+ int ret;
+
+ ret = dw_edma_parse_irq_mode(chan, config, &mode);
+ if (ret)
+ return ret;
+ chan->irq_mode = mode;
memcpy(&chan->config, config, sizeof(*config));
chan->configured = true;
@@ -749,6 +785,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
chan->configured = false;
chan->request = EDMA_REQ_NONE;
chan->status = EDMA_ST_IDLE;
+ chan->irq_mode = DW_EDMA_CH_IRQ_DEFAULT;
if (chan->dir == EDMA_DIR_WRITE)
chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 71894b9e0b15..0608b9044a08 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -81,6 +81,8 @@ struct dw_edma_chan {
struct msi_msg msi;
+ enum dw_edma_ch_irq_mode irq_mode;
+
enum dw_edma_request request;
enum dw_edma_status status;
u8 configured;
@@ -206,4 +208,15 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
dw->core->debugfs_on(dw);
}
+static inline
+bool dw_edma_core_ch_ignore_irq(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ if (dw->chip->flags & DW_EDMA_CHIP_LOCAL)
+ return chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE;
+ else
+ return chan->irq_mode == DW_EDMA_CH_IRQ_LOCAL;
+}
+
#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..a0441e8aa3b3 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -256,8 +256,10 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
for_each_set_bit(pos, &val, total) {
chan = &dw->chan[pos + off];
- dw_edma_v0_core_clear_done_int(chan);
- done(chan);
+ if (!dw_edma_core_ch_ignore_irq(chan)) {
+ dw_edma_v0_core_clear_done_int(chan);
+ done(chan);
+ }
ret = IRQ_HANDLED;
}
@@ -267,8 +269,10 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
for_each_set_bit(pos, &val, total) {
chan = &dw->chan[pos + off];
- dw_edma_v0_core_clear_abort_int(chan);
- abort(chan);
+ if (!dw_edma_core_ch_ignore_irq(chan)) {
+ dw_edma_v0_core_clear_abort_int(chan);
+ abort(chan);
+ }
ret = IRQ_HANDLED;
}
@@ -331,7 +335,8 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
j--;
if (!j) {
control |= DW_EDMA_V0_LIE;
- if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
+ if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) &&
+ chan->irq_mode != DW_EDMA_CH_IRQ_LOCAL)
control |= DW_EDMA_V0_RIE;
}
@@ -407,10 +412,15 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
break;
}
}
- /* Interrupt unmask - done, abort */
+ /* Interrupt mask/unmask - done, abort */
tmp = GET_RW_32(dw, chan->dir, int_mask);
- tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
- tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
+ if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) {
+ tmp |= FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
+ tmp |= FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
+ } else {
+ tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
+ tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
+ }
SET_RW_32(dw, chan->dir, int_mask, tmp);
/* Linked list error */
tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 3080747689f6..53b31a974331 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -60,6 +60,34 @@ enum dw_edma_chip_flags {
DW_EDMA_CHIP_LOCAL = BIT(0),
};
+/*
+ * enum dw_edma_ch_irq_mode - per-channel interrupt routing control
+ * @DW_EDMA_CH_IRQ_DEFAULT: LIE=1/RIE=1, local interrupt unmasked
+ * @DW_EDMA_CH_IRQ_LOCAL: LIE=1/RIE=0, local interrupt unmasked
+ * @DW_EDMA_CH_IRQ_REMOTE: LIE=1/RIE=1, local interrupt masked
+ *
+ * Some implementations require using LIE=1/RIE=1 with the local interrupt
+ * masked to generate a remote-only interrupt (rather than LIE=0/RIE=1).
+ * See the DesignWare endpoint databook 5.40, "Hint" below "Figure 8-22
+ * Write Interrupt Generation".
+ */
+enum dw_edma_ch_irq_mode {
+ DW_EDMA_CH_IRQ_DEFAULT = 0,
+ DW_EDMA_CH_IRQ_LOCAL,
+ DW_EDMA_CH_IRQ_REMOTE,
+};
+
+/**
+ * struct dw_edma_peripheral_config - dw-edma specific slave configuration
+ * @irq_mode: per-channel interrupt routing control.
+ *
+ * Pass this structure via dma_slave_config.peripheral_config and
+ * dma_slave_config.peripheral_size.
+ */
+struct dw_edma_peripheral_config {
+ enum dw_edma_ch_irq_mode irq_mode;
+};
+
/**
* struct dw_edma_chip - representation of DesignWare eDMA controller hardware
* @dev: struct device of the eDMA controller
--
2.51.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v4 1/9] dmaengine: dw-edma: Add per-channel interrupt routing control
2026-02-06 17:26 ` [PATCH v4 1/9] dmaengine: dw-edma: Add per-channel interrupt routing control Koichiro Den
@ 2026-02-06 17:43 ` Frank Li
2026-02-09 4:06 ` Koichiro Den
0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-02-06 17:43 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Sat, Feb 07, 2026 at 02:26:38AM +0900, Koichiro Den wrote:
> DesignWare EP eDMA can generate interrupts both locally and remotely
> (LIE/RIE). Remote eDMA users need to decide, per channel, whether
> completions should be handled locally, remotely, or both. Unless
> carefully configured, the endpoint and host would race to ack the
> interrupt.
>
> Introduce a dw_edma_peripheral_config that holds per-channel interrupt
> routing mode. Update v0 programming so that RIE and local done/abort
> interrupt masking follow the selected mode. The default mode keeps the
> original behavior, so unless the new peripheral_config is explicitly
> used and set, no functional changes.
>
> For now, HDMA is not supported for the peripheral_config. Until the
> support is implemented and validated, explicitly reject it for HDMA to
> avoid silently misconfiguring interrupt routing.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> drivers/dma/dw-edma/dw-edma-core.c | 37 +++++++++++++++++++++++++++
> drivers/dma/dw-edma/dw-edma-core.h | 13 ++++++++++
> drivers/dma/dw-edma/dw-edma-v0-core.c | 26 +++++++++++++------
> include/linux/dma/edma.h | 28 ++++++++++++++++++++
> 4 files changed, 96 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index 8e5f7defa6b6..0c3461f9174a 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -219,11 +219,47 @@ static void dw_edma_device_caps(struct dma_chan *dchan,
> }
> }
>
> +static int dw_edma_parse_irq_mode(struct dw_edma_chan *chan,
> + const struct dma_slave_config *config,
> + enum dw_edma_ch_irq_mode *mode)
> +{
> + const struct dw_edma_peripheral_config *pcfg;
> +
> + /* peripheral_config is optional, default keeps legacy behaviour. */
> + *mode = DW_EDMA_CH_IRQ_DEFAULT;
> + if (!config || !config->peripheral_config)
> + return 0;
> +
> + if (chan->dw->chip->mf == EDMA_MF_HDMA_NATIVE)
> + return -EOPNOTSUPP;
> +
> + if (config->peripheral_size < sizeof(*pcfg))
> + return -EINVAL;
> +
> + pcfg = config->peripheral_config;
> + switch (pcfg->irq_mode) {
> + case DW_EDMA_CH_IRQ_DEFAULT:
> + case DW_EDMA_CH_IRQ_LOCAL:
> + case DW_EDMA_CH_IRQ_REMOTE:
> + *mode = pcfg->irq_mode;
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static int dw_edma_device_config(struct dma_chan *dchan,
> struct dma_slave_config *config)
> {
> struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> + enum dw_edma_ch_irq_mode mode;
> + int ret;
> +
> + ret = dw_edma_parse_irq_mode(chan, config, &mode);
> + if (ret)
> + return ret;
>
> + chan->irq_mode = mode;
> memcpy(&chan->config, config, sizeof(*config));
> chan->configured = true;
>
> @@ -749,6 +785,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> chan->configured = false;
> chan->request = EDMA_REQ_NONE;
> chan->status = EDMA_ST_IDLE;
> + chan->irq_mode = DW_EDMA_CH_IRQ_DEFAULT;
>
> if (chan->dir == EDMA_DIR_WRITE)
> chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
> diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> index 71894b9e0b15..0608b9044a08 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-core.h
> @@ -81,6 +81,8 @@ struct dw_edma_chan {
>
> struct msi_msg msi;
>
> + enum dw_edma_ch_irq_mode irq_mode;
> +
> enum dw_edma_request request;
> enum dw_edma_status status;
> u8 configured;
> @@ -206,4 +208,15 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
> dw->core->debugfs_on(dw);
> }
>
> +static inline
> +bool dw_edma_core_ch_ignore_irq(struct dw_edma_chan *chan)
nit:
static inline bool
dw_edma_core_ch_ignore_irq()
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + if (dw->chip->flags & DW_EDMA_CHIP_LOCAL)
> + return chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE;
> + else
> + return chan->irq_mode == DW_EDMA_CH_IRQ_LOCAL;
> +}
> +
> #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..a0441e8aa3b3 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -256,8 +256,10 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
> for_each_set_bit(pos, &val, total) {
> chan = &dw->chan[pos + off];
>
> - dw_edma_v0_core_clear_done_int(chan);
> - done(chan);
> + if (!dw_edma_core_ch_ignore_irq(chan)) {
> + dw_edma_v0_core_clear_done_int(chan);
> + done(chan);
> + }
>
> ret = IRQ_HANDLED;
> }
> @@ -267,8 +269,10 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
> for_each_set_bit(pos, &val, total) {
> chan = &dw->chan[pos + off];
>
> - dw_edma_v0_core_clear_abort_int(chan);
> - abort(chan);
> + if (!dw_edma_core_ch_ignore_irq(chan)) {
> + dw_edma_v0_core_clear_abort_int(chan);
> + abort(chan);
> + }
>
> ret = IRQ_HANDLED;
> }
> @@ -331,7 +335,8 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
> j--;
> if (!j) {
> control |= DW_EDMA_V0_LIE;
> - if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> + if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) &&
> + chan->irq_mode != DW_EDMA_CH_IRQ_LOCAL)
> control |= DW_EDMA_V0_RIE;
> }
>
> @@ -407,10 +412,15 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> break;
> }
> }
> - /* Interrupt unmask - done, abort */
> + /* Interrupt mask/unmask - done, abort */
> tmp = GET_RW_32(dw, chan->dir, int_mask);
> - tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> - tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> + if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) {
> + tmp |= FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> + tmp |= FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> + } else {
> + tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> + tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> + }
> SET_RW_32(dw, chan->dir, int_mask, tmp);
> /* Linked list error */
> tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
> diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> index 3080747689f6..53b31a974331 100644
> --- a/include/linux/dma/edma.h
> +++ b/include/linux/dma/edma.h
> @@ -60,6 +60,34 @@ enum dw_edma_chip_flags {
> DW_EDMA_CHIP_LOCAL = BIT(0),
> };
>
> +/*
> + * enum dw_edma_ch_irq_mode - per-channel interrupt routing control
> + * @DW_EDMA_CH_IRQ_DEFAULT: LIE=1/RIE=1, local interrupt unmasked
> + * @DW_EDMA_CH_IRQ_LOCAL: LIE=1/RIE=0, local interrupt unmasked
> + * @DW_EDMA_CH_IRQ_REMOTE: LIE=1/RIE=1, local interrupt masked
You'd better descript remote interrupt also.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> + *
> + * Some implementations require using LIE=1/RIE=1 with the local interrupt
> + * masked to generate a remote-only interrupt (rather than LIE=0/RIE=1).
> + * See the DesignWare endpoint databook 5.40, "Hint" below "Figure 8-22
> + * Write Interrupt Generation".
> + */
> +enum dw_edma_ch_irq_mode {
> + DW_EDMA_CH_IRQ_DEFAULT = 0,
> + DW_EDMA_CH_IRQ_LOCAL,
> + DW_EDMA_CH_IRQ_REMOTE,
> +};
> +
> +/**
> + * struct dw_edma_peripheral_config - dw-edma specific slave configuration
> + * @irq_mode: per-channel interrupt routing control.
> + *
> + * Pass this structure via dma_slave_config.peripheral_config and
> + * dma_slave_config.peripheral_size.
> + */
> +struct dw_edma_peripheral_config {
> + enum dw_edma_ch_irq_mode irq_mode;
> +};
> +
> /**
> * struct dw_edma_chip - representation of DesignWare eDMA controller hardware
> * @dev: struct device of the eDMA controller
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v4 1/9] dmaengine: dw-edma: Add per-channel interrupt routing control
2026-02-06 17:43 ` Frank Li
@ 2026-02-09 4:06 ` Koichiro Den
0 siblings, 0 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-09 4:06 UTC (permalink / raw)
To: Frank Li
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Fri, Feb 06, 2026 at 12:43:54PM -0500, Frank Li wrote:
> On Sat, Feb 07, 2026 at 02:26:38AM +0900, Koichiro Den wrote:
> > DesignWare EP eDMA can generate interrupts both locally and remotely
> > (LIE/RIE). Remote eDMA users need to decide, per channel, whether
> > completions should be handled locally, remotely, or both. Unless
> > carefully configured, the endpoint and host would race to ack the
> > interrupt.
> >
> > Introduce a dw_edma_peripheral_config that holds per-channel interrupt
> > routing mode. Update v0 programming so that RIE and local done/abort
> > interrupt masking follow the selected mode. The default mode keeps the
> > original behavior, so unless the new peripheral_config is explicitly
> > used and set, no functional changes.
> >
> > For now, HDMA is not supported for the peripheral_config. Until the
> > support is implemented and validated, explicitly reject it for HDMA to
> > avoid silently misconfiguring interrupt routing.
> >
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > drivers/dma/dw-edma/dw-edma-core.c | 37 +++++++++++++++++++++++++++
> > drivers/dma/dw-edma/dw-edma-core.h | 13 ++++++++++
> > drivers/dma/dw-edma/dw-edma-v0-core.c | 26 +++++++++++++------
> > include/linux/dma/edma.h | 28 ++++++++++++++++++++
> > 4 files changed, 96 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > index 8e5f7defa6b6..0c3461f9174a 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > @@ -219,11 +219,47 @@ static void dw_edma_device_caps(struct dma_chan *dchan,
> > }
> > }
> >
> > +static int dw_edma_parse_irq_mode(struct dw_edma_chan *chan,
> > + const struct dma_slave_config *config,
> > + enum dw_edma_ch_irq_mode *mode)
> > +{
> > + const struct dw_edma_peripheral_config *pcfg;
> > +
> > + /* peripheral_config is optional, default keeps legacy behaviour. */
> > + *mode = DW_EDMA_CH_IRQ_DEFAULT;
> > + if (!config || !config->peripheral_config)
> > + return 0;
> > +
> > + if (chan->dw->chip->mf == EDMA_MF_HDMA_NATIVE)
> > + return -EOPNOTSUPP;
> > +
> > + if (config->peripheral_size < sizeof(*pcfg))
> > + return -EINVAL;
> > +
> > + pcfg = config->peripheral_config;
> > + switch (pcfg->irq_mode) {
> > + case DW_EDMA_CH_IRQ_DEFAULT:
> > + case DW_EDMA_CH_IRQ_LOCAL:
> > + case DW_EDMA_CH_IRQ_REMOTE:
> > + *mode = pcfg->irq_mode;
> > + return 0;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > static int dw_edma_device_config(struct dma_chan *dchan,
> > struct dma_slave_config *config)
> > {
> > struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > + enum dw_edma_ch_irq_mode mode;
> > + int ret;
> > +
> > + ret = dw_edma_parse_irq_mode(chan, config, &mode);
> > + if (ret)
> > + return ret;
> >
> > + chan->irq_mode = mode;
> > memcpy(&chan->config, config, sizeof(*config));
> > chan->configured = true;
> >
> > @@ -749,6 +785,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> > chan->configured = false;
> > chan->request = EDMA_REQ_NONE;
> > chan->status = EDMA_ST_IDLE;
> > + chan->irq_mode = DW_EDMA_CH_IRQ_DEFAULT;
> >
> > if (chan->dir == EDMA_DIR_WRITE)
> > chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> > index 71894b9e0b15..0608b9044a08 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.h
> > +++ b/drivers/dma/dw-edma/dw-edma-core.h
> > @@ -81,6 +81,8 @@ struct dw_edma_chan {
> >
> > struct msi_msg msi;
> >
> > + enum dw_edma_ch_irq_mode irq_mode;
> > +
> > enum dw_edma_request request;
> > enum dw_edma_status status;
> > u8 configured;
> > @@ -206,4 +208,15 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
> > dw->core->debugfs_on(dw);
> > }
> >
> > +static inline
> > +bool dw_edma_core_ch_ignore_irq(struct dw_edma_chan *chan)
>
> nit:
>
> static inline bool
> dw_edma_core_ch_ignore_irq()
Thanks for the nit. I followed the style of the neighboring codes, but I
agree it's better on its own. Let me fix that in the next revision.
>
> > +{
> > + struct dw_edma *dw = chan->dw;
> > +
> > + if (dw->chip->flags & DW_EDMA_CHIP_LOCAL)
> > + return chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE;
> > + else
> > + return chan->irq_mode == DW_EDMA_CH_IRQ_LOCAL;
> > +}
> > +
> > #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..a0441e8aa3b3 100644
> > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > @@ -256,8 +256,10 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
> > for_each_set_bit(pos, &val, total) {
> > chan = &dw->chan[pos + off];
> >
> > - dw_edma_v0_core_clear_done_int(chan);
> > - done(chan);
> > + if (!dw_edma_core_ch_ignore_irq(chan)) {
> > + dw_edma_v0_core_clear_done_int(chan);
> > + done(chan);
> > + }
> >
> > ret = IRQ_HANDLED;
> > }
> > @@ -267,8 +269,10 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
> > for_each_set_bit(pos, &val, total) {
> > chan = &dw->chan[pos + off];
> >
> > - dw_edma_v0_core_clear_abort_int(chan);
> > - abort(chan);
> > + if (!dw_edma_core_ch_ignore_irq(chan)) {
> > + dw_edma_v0_core_clear_abort_int(chan);
> > + abort(chan);
> > + }
> >
> > ret = IRQ_HANDLED;
> > }
> > @@ -331,7 +335,8 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
> > j--;
> > if (!j) {
> > control |= DW_EDMA_V0_LIE;
> > - if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> > + if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) &&
> > + chan->irq_mode != DW_EDMA_CH_IRQ_LOCAL)
> > control |= DW_EDMA_V0_RIE;
> > }
> >
> > @@ -407,10 +412,15 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > break;
> > }
> > }
> > - /* Interrupt unmask - done, abort */
> > + /* Interrupt mask/unmask - done, abort */
> > tmp = GET_RW_32(dw, chan->dir, int_mask);
> > - tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> > - tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> > + if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) {
> > + tmp |= FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> > + tmp |= FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> > + } else {
> > + tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> > + tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> > + }
> > SET_RW_32(dw, chan->dir, int_mask, tmp);
> > /* Linked list error */
> > tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
> > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> > index 3080747689f6..53b31a974331 100644
> > --- a/include/linux/dma/edma.h
> > +++ b/include/linux/dma/edma.h
> > @@ -60,6 +60,34 @@ enum dw_edma_chip_flags {
> > DW_EDMA_CHIP_LOCAL = BIT(0),
> > };
> >
> > +/*
> > + * enum dw_edma_ch_irq_mode - per-channel interrupt routing control
> > + * @DW_EDMA_CH_IRQ_DEFAULT: LIE=1/RIE=1, local interrupt unmasked
> > + * @DW_EDMA_CH_IRQ_LOCAL: LIE=1/RIE=0, local interrupt unmasked
> > + * @DW_EDMA_CH_IRQ_REMOTE: LIE=1/RIE=1, local interrupt masked
>
> You'd better descript remote interrupt also.
Thanks for the comment. Hm, while revisiting this I noticed the current
enum comment is overly LIE/RIE-specific and can even be misleading (e.g.
*_IRQ_DEFAULT does not imply a RIE=1 behavior when the eDMA instance is
operated purely in a local-only setup).
I think it’s clearer to describe the modes in terms of behavior and add a
short explanation of what each mode means outside the individual field
descriptions. Below is a draft of the updated kernel-doc comment. How does
this look to you?
/**
* enum dw_edma_ch_irq_mode - per-channel interrupt routing control
* @DW_EDMA_CH_IRQ_DEFAULT: Keep legacy behavior.
* @DW_EDMA_CH_IRQ_LOCAL: Local interrupt only (edma_int[]).
* @DW_EDMA_CH_IRQ_REMOTE: Remote interrupt only (IMWr/MSI),
* while masking local DONE/ABORT output.
*
* DesignWare EP eDMA can signal interrupts locally through the edma_int[]
* bus, and remotely using posted memory writes (IMWr) that may be
* interpreted as MSI/MSI-X by the RC.
*
* DMA_*_INT_MASK gates the local edma_int[] assertion, while there is no
* dedicated per-channel mask for IMWr generation. To request a remote-only
* interrupt, Synopsys recommends setting both LIE and RIE, and masking the
* local interrupt in DMA_*_INT_MASK (rather than relying on LIE=0/RIE=1).
* See the DesignWare endpoint databook 5.40a, Non Linked List Mode
* interrupt handling ("Hint").
*/
enum dw_edma_ch_irq_mode {
DW_EDMA_CH_IRQ_DEFAULT = 0,
DW_EDMA_CH_IRQ_LOCAL,
DW_EDMA_CH_IRQ_REMOTE,
};
Also, for v5 I think I can drop this patch 1/9 to keep the series minimal
and focused on the doorbell fallback implementation (and its testing). I
can repost this patch later together with the actual consumer. Please let
me know if you have any objections.
If you're ok with the updated comment above, I believe it should also be
fine to keep your Reviewed-by tag on the repost.
Thanks for the review,
Koichiro
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> > + *
> > + * Some implementations require using LIE=1/RIE=1 with the local interrupt
> > + * masked to generate a remote-only interrupt (rather than LIE=0/RIE=1).
> > + * See the DesignWare endpoint databook 5.40, "Hint" below "Figure 8-22
> > + * Write Interrupt Generation".
> > + */
> > +enum dw_edma_ch_irq_mode {
> > + DW_EDMA_CH_IRQ_DEFAULT = 0,
> > + DW_EDMA_CH_IRQ_LOCAL,
> > + DW_EDMA_CH_IRQ_REMOTE,
> > +};
> > +
> > +/**
> > + * struct dw_edma_peripheral_config - dw-edma specific slave configuration
> > + * @irq_mode: per-channel interrupt routing control.
> > + *
> > + * Pass this structure via dma_slave_config.peripheral_config and
> > + * dma_slave_config.peripheral_size.
> > + */
> > +struct dw_edma_peripheral_config {
> > + enum dw_edma_ch_irq_mode irq_mode;
> > +};
> > +
> > /**
> > * struct dw_edma_chip - representation of DesignWare eDMA controller hardware
> > * @dev: struct device of the eDMA controller
> > --
> > 2.51.0
> >
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v4 2/9] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler
2026-02-06 17:26 [PATCH v4 0/9] dmaengine, PCI: endpoint: Enable remote use of integrated DesignWare eDMA Koichiro Den
2026-02-06 17:26 ` [PATCH v4 1/9] dmaengine: dw-edma: Add per-channel interrupt routing control Koichiro Den
@ 2026-02-06 17:26 ` Koichiro Den
2026-02-06 17:26 ` [PATCH v4 3/9] dmaengine: dw-edma: Export per-channel IRQ and doorbell register offset Koichiro Den
` (6 subsequent siblings)
8 siblings, 0 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-06 17:26 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas
Cc: dmaengine, linux-pci, 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_selfirq(), 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 | 11 ++++++
drivers/dma/dw-edma/dw-edma-v0-core.c | 11 ++++++
3 files changed, 65 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 0c3461f9174a..dd01a9aa8ad8 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -699,7 +699,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_selfirq(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;
@@ -708,7 +725,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;
@@ -717,12 +734,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 0608b9044a08..abc97e375484 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -128,6 +128,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_selfirq)(struct dw_edma *dw);
};
struct dw_edma_sg {
@@ -208,6 +209,16 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
dw->core->debugfs_on(dw);
}
+static inline
+int dw_edma_core_ack_selfirq(struct dw_edma *dw)
+{
+ if (!dw->core->ack_selfirq)
+ return -EOPNOTSUPP;
+
+ dw->core->ack_selfirq(dw);
+ return 0;
+}
+
static inline
bool dw_edma_core_ch_ignore_irq(struct dw_edma_chan *chan)
{
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index a0441e8aa3b3..68e0d088570d 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_debugfs_on(struct dw_edma *dw)
dw_edma_v0_debugfs_on(dw);
}
+static void dw_edma_v0_core_ack_selfirq(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,
@@ -527,6 +537,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_selfirq = dw_edma_v0_core_ack_selfirq,
};
void dw_edma_v0_core_register(struct dw_edma *dw)
--
2.51.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH v4 3/9] dmaengine: dw-edma: Export per-channel IRQ and doorbell register offset
2026-02-06 17:26 [PATCH v4 0/9] dmaengine, PCI: endpoint: Enable remote use of integrated DesignWare eDMA Koichiro Den
2026-02-06 17:26 ` [PATCH v4 1/9] dmaengine: dw-edma: Add per-channel interrupt routing control Koichiro Den
2026-02-06 17:26 ` [PATCH v4 2/9] dmaengine: dw-edma: Deassert emulated interrupts in the IRQ handler Koichiro Den
@ 2026-02-06 17:26 ` Koichiro Den
2026-02-06 17:56 ` Frank Li
2026-02-08 8:57 ` kernel test robot
2026-02-06 17:26 ` [PATCH v4 4/9] PCI: endpoint: Add remote resource query API Koichiro Den
` (5 subsequent siblings)
8 siblings, 2 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-06 17:26 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas
Cc: dmaengine, linux-pci, linux-kselftest, linux-kernel
Endpoint controller drivers may need to expose or consume eDMA-related
resources (e.g. for remote programming or doorbell/self-tests). For
that, consumers need a stable way to discover the Linux IRQ used by a
given eDMA channel/vector and a register offset suitable for interrupt
emulation.
Record each channel's IRQ-vector index and store the requested Linux IRQ
number. Add a core callback .ch_info() to provide core-specific metadata
and implement it for v0.
Export dw_edma_chan_info() so that platform drivers can retrieve:
- per-channel device name
- Linux IRQ number for the channel's interrupt vector
- offset of the register used as an emulated-interrupt doorbell within
the eDMA register window.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/dma/dw-edma/dw-edma-core.c | 31 +++++++++++++++++++++++++++
drivers/dma/dw-edma/dw-edma-core.h | 14 ++++++++++++
drivers/dma/dw-edma/dw-edma-v0-core.c | 7 ++++++
include/linux/dma/edma.h | 20 +++++++++++++++++
4 files changed, 72 insertions(+)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index dd01a9aa8ad8..147a5466e4e7 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -842,6 +842,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
else
pos = wr_alloc + chan->id % rd_alloc;
+ chan->irq_idx = pos;
irq = &dw->irq[pos];
if (chan->dir == EDMA_DIR_WRITE)
@@ -947,6 +948,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 */
@@ -973,6 +975,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;
@@ -1098,6 +1101,34 @@ int dw_edma_remove(struct dw_edma_chip *chip)
}
EXPORT_SYMBOL_GPL(dw_edma_remove);
+int dw_edma_chan_info(struct dw_edma_chip *chip, unsigned int ch_idx,
+ struct dw_edma_chan_info *info)
+{
+ struct dw_edma *dw = chip->dw;
+ struct dw_edma_chan *chan;
+ struct dma_chan *dchan;
+ u32 ch_cnt;
+ int ret;
+
+ if (!chip || !info || !dw)
+ return -EINVAL;
+
+ ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
+ if (ch_idx >= ch_cnt)
+ return -EINVAL;
+
+ chan = &dw->chan[ch_idx];
+ dchan = &chan->vc.chan;
+
+ ret = dw_edma_core_ch_info(dw, chan, info);
+ if (ret)
+ return ret;
+
+ info->irq = dw->irq[chan->irq_idx].irq;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dw_edma_chan_info);
+
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index abc97e375484..e92891ed5536 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -82,6 +82,7 @@ struct dw_edma_chan {
struct msi_msg msi;
enum dw_edma_ch_irq_mode irq_mode;
+ u32 irq_idx;
enum dw_edma_request request;
enum dw_edma_status status;
@@ -95,6 +96,7 @@ struct dw_edma_irq {
u32 wr_mask;
u32 rd_mask;
struct dw_edma *dw;
+ int irq;
};
struct dw_edma {
@@ -129,6 +131,7 @@ struct dw_edma_core_ops {
void (*ch_config)(struct dw_edma_chan *chan);
void (*debugfs_on)(struct dw_edma *dw);
void (*ack_selfirq)(struct dw_edma *dw);
+ void (*ch_info)(struct dw_edma_chan *chan, struct dw_edma_chan_info *info);
};
struct dw_edma_sg {
@@ -219,6 +222,17 @@ int dw_edma_core_ack_selfirq(struct dw_edma *dw)
return 0;
}
+static inline
+int dw_edma_core_ch_info(struct dw_edma *dw, struct dw_edma_chan *chan,
+ struct dw_edma_chan_info *info)
+{
+ if (!dw->core->ch_info)
+ return -EOPNOTSUPP;
+
+ dw->core->ch_info(chan, info);
+ return 0;
+}
+
static inline
bool dw_edma_core_ch_ignore_irq(struct dw_edma_chan *chan)
{
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index 68e0d088570d..9c7908a76fff 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -529,6 +529,12 @@ static void dw_edma_v0_core_ack_selfirq(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_chan_info *info)
+{
+ 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,
@@ -538,6 +544,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_selfirq = dw_edma_v0_core_ack_selfirq,
+ .ch_info = dw_edma_v0_core_ch_info,
};
void dw_edma_v0_core_register(struct dw_edma *dw)
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 53b31a974331..9fd78dc313e5 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -129,10 +129,23 @@ struct dw_edma_chip {
struct dw_edma *dw;
};
+/**
+ * struct dw_edma_chan_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_chan_info {
+ int irq;
+ resource_size_t db_offset;
+};
+
/* Export to the platform drivers */
#if IS_REACHABLE(CONFIG_DW_EDMA)
int dw_edma_probe(struct dw_edma_chip *chip);
int dw_edma_remove(struct dw_edma_chip *chip);
+int dw_edma_chan_info(struct dw_edma_chip *chip, unsigned int ch_idx,
+ struct dw_edma_chan_info *info);
#else
static inline int dw_edma_probe(struct dw_edma_chip *chip)
{
@@ -143,6 +156,13 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
{
return 0;
}
+
+static inline int dw_edma_chan_info(struct dw_edma_chip *chip,
+ unsigned int ch_idx,
+ struct dw_edma_chan_info *info)
+{
+ return -ENODEV;
+}
#endif /* CONFIG_DW_EDMA */
#endif /* _DW_EDMA_H */
--
2.51.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v4 3/9] dmaengine: dw-edma: Export per-channel IRQ and doorbell register offset
2026-02-06 17:26 ` [PATCH v4 3/9] dmaengine: dw-edma: Export per-channel IRQ and doorbell register offset Koichiro Den
@ 2026-02-06 17:56 ` Frank Li
2026-02-09 4:14 ` Koichiro Den
2026-02-08 8:57 ` kernel test robot
1 sibling, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-02-06 17:56 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Sat, Feb 07, 2026 at 02:26:40AM +0900, Koichiro Den wrote:
> Endpoint controller drivers may need to expose or consume eDMA-related
> resources (e.g. for remote programming or doorbell/self-tests). For
> that, consumers need a stable way to discover the Linux IRQ used by a
> given eDMA channel/vector and a register offset suitable for interrupt
> emulation.
>
> Record each channel's IRQ-vector index and store the requested Linux IRQ
> number. Add a core callback .ch_info() to provide core-specific metadata
> and implement it for v0.
>
> Export dw_edma_chan_info() so that platform drivers can retrieve:
> - per-channel device name
> - Linux IRQ number for the channel's interrupt vector
> - offset of the register used as an emulated-interrupt doorbell within
> the eDMA register window.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> drivers/dma/dw-edma/dw-edma-core.c | 31 +++++++++++++++++++++++++++
> drivers/dma/dw-edma/dw-edma-core.h | 14 ++++++++++++
> drivers/dma/dw-edma/dw-edma-v0-core.c | 7 ++++++
> include/linux/dma/edma.h | 20 +++++++++++++++++
> 4 files changed, 72 insertions(+)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index dd01a9aa8ad8..147a5466e4e7 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -842,6 +842,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> else
> pos = wr_alloc + chan->id % rd_alloc;
>
> + chan->irq_idx = pos;
> irq = &dw->irq[pos];
>
> if (chan->dir == EDMA_DIR_WRITE)
> @@ -947,6 +948,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 */
> @@ -973,6 +975,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;
> @@ -1098,6 +1101,34 @@ int dw_edma_remove(struct dw_edma_chip *chip)
> }
> EXPORT_SYMBOL_GPL(dw_edma_remove);
>
> +int dw_edma_chan_info(struct dw_edma_chip *chip, unsigned int ch_idx,
> + struct dw_edma_chan_info *info)
> +{
> + struct dw_edma *dw = chip->dw;
> + struct dw_edma_chan *chan;
> + struct dma_chan *dchan;
> + u32 ch_cnt;
> + int ret;
> +
> + if (!chip || !info || !dw)
> + return -EINVAL;
> +
> + ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
> + if (ch_idx >= ch_cnt)
> + return -EINVAL;
> +
> + chan = &dw->chan[ch_idx];
> + dchan = &chan->vc.chan;
> +
> + ret = dw_edma_core_ch_info(dw, chan, info);
> + if (ret)
> + return ret;
> +
> + info->irq = dw->irq[chan->irq_idx].irq;
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(dw_edma_chan_info);
> +
> MODULE_LICENSE("GPL v2");
> MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
> MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
> diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> index abc97e375484..e92891ed5536 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-core.h
> @@ -82,6 +82,7 @@ struct dw_edma_chan {
> struct msi_msg msi;
>
> enum dw_edma_ch_irq_mode irq_mode;
> + u32 irq_idx;
can we directly save irq_number?
>
> enum dw_edma_request request;
> enum dw_edma_status status;
> @@ -95,6 +96,7 @@ struct dw_edma_irq {
> u32 wr_mask;
> u32 rd_mask;
> struct dw_edma *dw;
> + int irq;
> };
>
> struct dw_edma {
> @@ -129,6 +131,7 @@ struct dw_edma_core_ops {
> void (*ch_config)(struct dw_edma_chan *chan);
> void (*debugfs_on)(struct dw_edma *dw);
> void (*ack_selfirq)(struct dw_edma *dw);
> + void (*ch_info)(struct dw_edma_chan *chan, struct dw_edma_chan_info *info);
> };
>
> struct dw_edma_sg {
> @@ -219,6 +222,17 @@ int dw_edma_core_ack_selfirq(struct dw_edma *dw)
> return 0;
> }
>
> +static inline
> +int dw_edma_core_ch_info(struct dw_edma *dw, struct dw_edma_chan *chan,
> + struct dw_edma_chan_info *info)
wrap int to previous line, check others.
Frank
> +{
> + if (!dw->core->ch_info)
> + return -EOPNOTSUPP;
> +
> + dw->core->ch_info(chan, info);
> + return 0;
> +}
> +
> static inline
> bool dw_edma_core_ch_ignore_irq(struct dw_edma_chan *chan)
> {
> diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> index 68e0d088570d..9c7908a76fff 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -529,6 +529,12 @@ static void dw_edma_v0_core_ack_selfirq(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_chan_info *info)
> +{
> + 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,
> @@ -538,6 +544,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_selfirq = dw_edma_v0_core_ack_selfirq,
> + .ch_info = dw_edma_v0_core_ch_info,
> };
>
> void dw_edma_v0_core_register(struct dw_edma *dw)
> diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> index 53b31a974331..9fd78dc313e5 100644
> --- a/include/linux/dma/edma.h
> +++ b/include/linux/dma/edma.h
> @@ -129,10 +129,23 @@ struct dw_edma_chip {
> struct dw_edma *dw;
> };
>
> +/**
> + * struct dw_edma_chan_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_chan_info {
> + int irq;
> + resource_size_t db_offset;
> +};
> +
> /* Export to the platform drivers */
> #if IS_REACHABLE(CONFIG_DW_EDMA)
> int dw_edma_probe(struct dw_edma_chip *chip);
> int dw_edma_remove(struct dw_edma_chip *chip);
> +int dw_edma_chan_info(struct dw_edma_chip *chip, unsigned int ch_idx,
> + struct dw_edma_chan_info *info);
> #else
> static inline int dw_edma_probe(struct dw_edma_chip *chip)
> {
> @@ -143,6 +156,13 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
> {
> return 0;
> }
> +
> +static inline int dw_edma_chan_info(struct dw_edma_chip *chip,
> + unsigned int ch_idx,
> + struct dw_edma_chan_info *info)
> +{
> + return -ENODEV;
> +}
> #endif /* CONFIG_DW_EDMA */
>
> #endif /* _DW_EDMA_H */
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v4 3/9] dmaengine: dw-edma: Export per-channel IRQ and doorbell register offset
2026-02-06 17:56 ` Frank Li
@ 2026-02-09 4:14 ` Koichiro Den
0 siblings, 0 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-09 4:14 UTC (permalink / raw)
To: Frank Li
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Fri, Feb 06, 2026 at 12:56:24PM -0500, Frank Li wrote:
> On Sat, Feb 07, 2026 at 02:26:40AM +0900, Koichiro Den wrote:
> > Endpoint controller drivers may need to expose or consume eDMA-related
> > resources (e.g. for remote programming or doorbell/self-tests). For
> > that, consumers need a stable way to discover the Linux IRQ used by a
> > given eDMA channel/vector and a register offset suitable for interrupt
> > emulation.
> >
> > Record each channel's IRQ-vector index and store the requested Linux IRQ
> > number. Add a core callback .ch_info() to provide core-specific metadata
> > and implement it for v0.
> >
> > Export dw_edma_chan_info() so that platform drivers can retrieve:
> > - per-channel device name
> > - Linux IRQ number for the channel's interrupt vector
> > - offset of the register used as an emulated-interrupt doorbell within
> > the eDMA register window.
> >
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > drivers/dma/dw-edma/dw-edma-core.c | 31 +++++++++++++++++++++++++++
> > drivers/dma/dw-edma/dw-edma-core.h | 14 ++++++++++++
> > drivers/dma/dw-edma/dw-edma-v0-core.c | 7 ++++++
> > include/linux/dma/edma.h | 20 +++++++++++++++++
> > 4 files changed, 72 insertions(+)
> >
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > index dd01a9aa8ad8..147a5466e4e7 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > @@ -842,6 +842,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> > else
> > pos = wr_alloc + chan->id % rd_alloc;
> >
> > + chan->irq_idx = pos;
> > irq = &dw->irq[pos];
> >
> > if (chan->dir == EDMA_DIR_WRITE)
> > @@ -947,6 +948,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 */
> > @@ -973,6 +975,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;
> > @@ -1098,6 +1101,34 @@ int dw_edma_remove(struct dw_edma_chip *chip)
> > }
> > EXPORT_SYMBOL_GPL(dw_edma_remove);
> >
> > +int dw_edma_chan_info(struct dw_edma_chip *chip, unsigned int ch_idx,
> > + struct dw_edma_chan_info *info)
> > +{
> > + struct dw_edma *dw = chip->dw;
> > + struct dw_edma_chan *chan;
> > + struct dma_chan *dchan;
> > + u32 ch_cnt;
> > + int ret;
> > +
> > + if (!chip || !info || !dw)
> > + return -EINVAL;
> > +
> > + ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
> > + if (ch_idx >= ch_cnt)
> > + return -EINVAL;
> > +
> > + chan = &dw->chan[ch_idx];
> > + dchan = &chan->vc.chan;
> > +
> > + ret = dw_edma_core_ch_info(dw, chan, info);
> > + if (ret)
> > + return ret;
> > +
> > + info->irq = dw->irq[chan->irq_idx].irq;
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(dw_edma_chan_info);
> > +
> > MODULE_LICENSE("GPL v2");
> > MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
> > MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> > index abc97e375484..e92891ed5536 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.h
> > +++ b/drivers/dma/dw-edma/dw-edma-core.h
> > @@ -82,6 +82,7 @@ struct dw_edma_chan {
> > struct msi_msg msi;
> >
> > enum dw_edma_ch_irq_mode irq_mode;
> > + u32 irq_idx;
>
> can we directly save irq_number?
Yes, that makes sense. I'll address this in v5 together with the changes
based on the discussion here:
https://lore.kernel.org/dmaengine/cuihp4wo5bcku75myq7mfbfvyddwptitiyy6pz5ldq2l6robxk@kghlibxpr7wf/
>
> >
> > enum dw_edma_request request;
> > enum dw_edma_status status;
> > @@ -95,6 +96,7 @@ struct dw_edma_irq {
> > u32 wr_mask;
> > u32 rd_mask;
> > struct dw_edma *dw;
> > + int irq;
> > };
> >
> > struct dw_edma {
> > @@ -129,6 +131,7 @@ struct dw_edma_core_ops {
> > void (*ch_config)(struct dw_edma_chan *chan);
> > void (*debugfs_on)(struct dw_edma *dw);
> > void (*ack_selfirq)(struct dw_edma *dw);
> > + void (*ch_info)(struct dw_edma_chan *chan, struct dw_edma_chan_info *info);
> > };
> >
> > struct dw_edma_sg {
> > @@ -219,6 +222,17 @@ int dw_edma_core_ack_selfirq(struct dw_edma *dw)
> > return 0;
> > }
> >
> > +static inline
> > +int dw_edma_core_ch_info(struct dw_edma *dw, struct dw_edma_chan *chan,
> > + struct dw_edma_chan_info *info)
>
> wrap int to previous line, check others.
Thanks for pointing that out. I'll update it for the same reasons as
discussed here:
https://lore.kernel.org/dmaengine/qktad6ggosznbznej7n2luxwkhr34f3egtojzjnkry4v2balbh@sfxxoii5yxoa/
Thanks,
Koichiro
>
> Frank
> > +{
> > + if (!dw->core->ch_info)
> > + return -EOPNOTSUPP;
> > +
> > + dw->core->ch_info(chan, info);
> > + return 0;
> > +}
> > +
> > static inline
> > bool dw_edma_core_ch_ignore_irq(struct dw_edma_chan *chan)
> > {
> > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > index 68e0d088570d..9c7908a76fff 100644
> > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > @@ -529,6 +529,12 @@ static void dw_edma_v0_core_ack_selfirq(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_chan_info *info)
> > +{
> > + 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,
> > @@ -538,6 +544,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_selfirq = dw_edma_v0_core_ack_selfirq,
> > + .ch_info = dw_edma_v0_core_ch_info,
> > };
> >
> > void dw_edma_v0_core_register(struct dw_edma *dw)
> > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> > index 53b31a974331..9fd78dc313e5 100644
> > --- a/include/linux/dma/edma.h
> > +++ b/include/linux/dma/edma.h
> > @@ -129,10 +129,23 @@ struct dw_edma_chip {
> > struct dw_edma *dw;
> > };
> >
> > +/**
> > + * struct dw_edma_chan_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_chan_info {
> > + int irq;
> > + resource_size_t db_offset;
> > +};
> > +
> > /* Export to the platform drivers */
> > #if IS_REACHABLE(CONFIG_DW_EDMA)
> > int dw_edma_probe(struct dw_edma_chip *chip);
> > int dw_edma_remove(struct dw_edma_chip *chip);
> > +int dw_edma_chan_info(struct dw_edma_chip *chip, unsigned int ch_idx,
> > + struct dw_edma_chan_info *info);
> > #else
> > static inline int dw_edma_probe(struct dw_edma_chip *chip)
> > {
> > @@ -143,6 +156,13 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
> > {
> > return 0;
> > }
> > +
> > +static inline int dw_edma_chan_info(struct dw_edma_chip *chip,
> > + unsigned int ch_idx,
> > + struct dw_edma_chan_info *info)
> > +{
> > + return -ENODEV;
> > +}
> > #endif /* CONFIG_DW_EDMA */
> >
> > #endif /* _DW_EDMA_H */
> > --
> > 2.51.0
> >
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v4 3/9] dmaengine: dw-edma: Export per-channel IRQ and doorbell register offset
2026-02-06 17:26 ` [PATCH v4 3/9] dmaengine: dw-edma: Export per-channel IRQ and doorbell register offset Koichiro Den
2026-02-06 17:56 ` Frank Li
@ 2026-02-08 8:57 ` kernel test robot
1 sibling, 0 replies; 24+ messages in thread
From: kernel test robot @ 2026-02-08 8:57 UTC (permalink / raw)
To: Koichiro Den, vkoul, mani, Frank.Li, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas
Cc: oe-kbuild-all, dmaengine, linux-pci, linux-kselftest,
linux-kernel
Hi Koichiro,
kernel test robot noticed the following build warnings:
[auto build test WARNING on pci/next]
[also build test WARNING on pci/for-linus linus/master v6.19-rc8 next-20260205]
[cannot apply to vkoul-dmaengine/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Koichiro-Den/dmaengine-dw-edma-Add-per-channel-interrupt-routing-control/20260207-013042
base: https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git next
patch link: https://lore.kernel.org/r/20260206172646.1556847-4-den%40valinux.co.jp
patch subject: [PATCH v4 3/9] dmaengine: dw-edma: Export per-channel IRQ and doorbell register offset
config: arc-randconfig-002-20260208 (https://download.01.org/0day-ci/archive/20260208/202602081640.ChGzNbe8-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 12.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260208/202602081640.ChGzNbe8-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602081640.ChGzNbe8-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/dma/dw-edma/dw-edma-core.c: In function 'dw_edma_chan_info':
>> drivers/dma/dw-edma/dw-edma-core.c:1109:26: warning: variable 'dchan' set but not used [-Wunused-but-set-variable]
1109 | struct dma_chan *dchan;
| ^~~~~
Kconfig warnings: (for reference only)
WARNING: unmet direct dependencies detected for CAN_DEV
Depends on [n]: NETDEVICES [=n] && CAN [=y]
Selected by [y]:
- CAN [=y] && NET [=y]
vim +/dchan +1109 drivers/dma/dw-edma/dw-edma-core.c
1103
1104 int dw_edma_chan_info(struct dw_edma_chip *chip, unsigned int ch_idx,
1105 struct dw_edma_chan_info *info)
1106 {
1107 struct dw_edma *dw = chip->dw;
1108 struct dw_edma_chan *chan;
> 1109 struct dma_chan *dchan;
1110 u32 ch_cnt;
1111 int ret;
1112
1113 if (!chip || !info || !dw)
1114 return -EINVAL;
1115
1116 ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
1117 if (ch_idx >= ch_cnt)
1118 return -EINVAL;
1119
1120 chan = &dw->chan[ch_idx];
1121 dchan = &chan->vc.chan;
1122
1123 ret = dw_edma_core_ch_info(dw, chan, info);
1124 if (ret)
1125 return ret;
1126
1127 info->irq = dw->irq[chan->irq_idx].irq;
1128 return 0;
1129 }
1130 EXPORT_SYMBOL_GPL(dw_edma_chan_info);
1131
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v4 4/9] PCI: endpoint: Add remote resource query API
2026-02-06 17:26 [PATCH v4 0/9] dmaengine, PCI: endpoint: Enable remote use of integrated DesignWare eDMA Koichiro Den
` (2 preceding siblings ...)
2026-02-06 17:26 ` [PATCH v4 3/9] dmaengine: dw-edma: Export per-channel IRQ and doorbell register offset Koichiro Den
@ 2026-02-06 17:26 ` Koichiro Den
2026-02-06 18:01 ` Frank Li
2026-02-06 17:26 ` [PATCH v4 5/9] PCI: dwc: Record integrated eDMA register window Koichiro Den
` (4 subsequent siblings)
8 siblings, 1 reply; 24+ messages in thread
From: Koichiro Den @ 2026-02-06 17:26 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas
Cc: dmaengine, linux-pci, 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_remote_resources() and the corresponding pci_epc_ops
get_remote_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..fa161113e24c 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_remote_resources() - query EPC-provided remote 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 remote resource queries
+ * * other -errno on failure
+ */
+int pci_epc_get_remote_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_remote_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_remote_resources)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&epc->lock);
+ ret = epc->ops->get_remote_resources(epc, func_no, vfunc_no,
+ resources, num_resources);
+ mutex_unlock(&epc->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_get_remote_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..7d2fce9f3a63 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -61,6 +61,44 @@ struct pci_epc_map {
void __iomem *virt_addr;
};
+/**
+ * enum pci_epc_remote_resource_type - remote resource type identifiers
+ * @PCI_EPC_RR_DMA_CTRL_MMIO: Integrated DMA controller register window (MMIO)
+ * @PCI_EPC_RR_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_remote_resource_type {
+ PCI_EPC_RR_DMA_CTRL_MMIO,
+ PCI_EPC_RR_DMA_CHAN_DESC,
+};
+
+/**
+ * struct pci_epc_remote_resource - a physical resource that can be exposed
+ * @type: resource type, see enum pci_epc_remote_resource_type
+ * @phys_addr: physical base address of the resource
+ * @size: size of the resource in bytes
+ * @u: type-specific metadata
+ *
+ * For @PCI_EPC_RR_DMA_CHAN_DESC, @u.dma_chan_desc provides per-channel
+ * information.
+ */
+struct pci_epc_remote_resource {
+ enum pci_epc_remote_resource_type type;
+ phys_addr_t phys_addr;
+ resource_size_t size;
+
+ union {
+ /* PCI_EPC_RR_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 +122,8 @@ 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_remote_resources: ops to retrieve controller-owned resources that can be
+ * exposed to the remote host (optional)
* @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_remote_resources)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_remote_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_remote_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_remote_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] 24+ messages in thread* Re: [PATCH v4 4/9] PCI: endpoint: Add remote resource query API
2026-02-06 17:26 ` [PATCH v4 4/9] PCI: endpoint: Add remote resource query API Koichiro Den
@ 2026-02-06 18:01 ` Frank Li
2026-02-07 16:39 ` Koichiro Den
0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-02-06 18:01 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Sat, Feb 07, 2026 at 02:26:41AM +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_remote_resources() and the corresponding pci_epc_ops
> get_remote_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..fa161113e24c 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_remote_resources() - query EPC-provided remote resources
I am not sure if it good naming, pci_epc_get_additional_resources().
Niklas Cassel may have good suggest, I just find you forget cc him.
Frank
> + * @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 remote resource queries
> + * * other -errno on failure
> + */
> +int pci_epc_get_remote_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_remote_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_remote_resources)
> + return -EOPNOTSUPP;
> +
> + mutex_lock(&epc->lock);
> + ret = epc->ops->get_remote_resources(epc, func_no, vfunc_no,
> + resources, num_resources);
> + mutex_unlock(&epc->lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_get_remote_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..7d2fce9f3a63 100644
> --- a/include/linux/pci-epc.h
> +++ b/include/linux/pci-epc.h
> @@ -61,6 +61,44 @@ struct pci_epc_map {
> void __iomem *virt_addr;
> };
>
> +/**
> + * enum pci_epc_remote_resource_type - remote resource type identifiers
> + * @PCI_EPC_RR_DMA_CTRL_MMIO: Integrated DMA controller register window (MMIO)
> + * @PCI_EPC_RR_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_remote_resource_type {
> + PCI_EPC_RR_DMA_CTRL_MMIO,
> + PCI_EPC_RR_DMA_CHAN_DESC,
> +};
> +
> +/**
> + * struct pci_epc_remote_resource - a physical resource that can be exposed
> + * @type: resource type, see enum pci_epc_remote_resource_type
> + * @phys_addr: physical base address of the resource
> + * @size: size of the resource in bytes
> + * @u: type-specific metadata
> + *
> + * For @PCI_EPC_RR_DMA_CHAN_DESC, @u.dma_chan_desc provides per-channel
> + * information.
> + */
> +struct pci_epc_remote_resource {
> + enum pci_epc_remote_resource_type type;
> + phys_addr_t phys_addr;
> + resource_size_t size;
> +
> + union {
> + /* PCI_EPC_RR_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 +122,8 @@ 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_remote_resources: ops to retrieve controller-owned resources that can be
> + * exposed to the remote host (optional)
> * @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_remote_resources)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_remote_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_remote_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_remote_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] 24+ messages in thread* Re: [PATCH v4 4/9] PCI: endpoint: Add remote resource query API
2026-02-06 18:01 ` Frank Li
@ 2026-02-07 16:39 ` Koichiro Den
2026-02-10 12:20 ` Niklas Cassel
0 siblings, 1 reply; 24+ messages in thread
From: Koichiro Den @ 2026-02-07 16:39 UTC (permalink / raw)
To: Frank Li, cassel
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Fri, Feb 06, 2026 at 01:01:33PM -0500, Frank Li wrote:
> On Sat, Feb 07, 2026 at 02:26:41AM +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_remote_resources() and the corresponding pci_epc_ops
> > get_remote_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..fa161113e24c 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_remote_resources() - query EPC-provided remote resources
>
> I am not sure if it good naming, pci_epc_get_additional_resources().
> Niklas Cassel may have good suggest, I just find you forget cc him.
That's true, I just naively followed the get_maintainers.pl output.
Niklas, I'd be happy to hear your thoughts on the naming here.
One other option I had in mind after Frank's feedback is
pci_epc_get_aux_resources().
Thanks,
Koichiro
>
> Frank
> > + * @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 remote resource queries
> > + * * other -errno on failure
> > + */
> > +int pci_epc_get_remote_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> > + struct pci_epc_remote_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_remote_resources)
> > + return -EOPNOTSUPP;
> > +
> > + mutex_lock(&epc->lock);
> > + ret = epc->ops->get_remote_resources(epc, func_no, vfunc_no,
> > + resources, num_resources);
> > + mutex_unlock(&epc->lock);
> > +
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_epc_get_remote_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..7d2fce9f3a63 100644
> > --- a/include/linux/pci-epc.h
> > +++ b/include/linux/pci-epc.h
> > @@ -61,6 +61,44 @@ struct pci_epc_map {
> > void __iomem *virt_addr;
> > };
> >
> > +/**
> > + * enum pci_epc_remote_resource_type - remote resource type identifiers
> > + * @PCI_EPC_RR_DMA_CTRL_MMIO: Integrated DMA controller register window (MMIO)
> > + * @PCI_EPC_RR_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_remote_resource_type {
> > + PCI_EPC_RR_DMA_CTRL_MMIO,
> > + PCI_EPC_RR_DMA_CHAN_DESC,
> > +};
> > +
> > +/**
> > + * struct pci_epc_remote_resource - a physical resource that can be exposed
> > + * @type: resource type, see enum pci_epc_remote_resource_type
> > + * @phys_addr: physical base address of the resource
> > + * @size: size of the resource in bytes
> > + * @u: type-specific metadata
> > + *
> > + * For @PCI_EPC_RR_DMA_CHAN_DESC, @u.dma_chan_desc provides per-channel
> > + * information.
> > + */
> > +struct pci_epc_remote_resource {
> > + enum pci_epc_remote_resource_type type;
> > + phys_addr_t phys_addr;
> > + resource_size_t size;
> > +
> > + union {
> > + /* PCI_EPC_RR_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 +122,8 @@ 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_remote_resources: ops to retrieve controller-owned resources that can be
> > + * exposed to the remote host (optional)
> > * @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_remote_resources)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> > + struct pci_epc_remote_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_remote_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> > + struct pci_epc_remote_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] 24+ messages in thread* Re: [PATCH v4 4/9] PCI: endpoint: Add remote resource query API
2026-02-07 16:39 ` Koichiro Den
@ 2026-02-10 12:20 ` Niklas Cassel
2026-02-10 14:18 ` Koichiro Den
0 siblings, 1 reply; 24+ messages in thread
From: Niklas Cassel @ 2026-02-10 12:20 UTC (permalink / raw)
To: Koichiro Den
Cc: Frank Li, vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, dmaengine, linux-pci, linux-kselftest, linux-kernel
On Sun, Feb 08, 2026 at 01:39:40AM +0900, Koichiro Den wrote:
> On Fri, Feb 06, 2026 at 01:01:33PM -0500, Frank Li wrote:
> > On Sat, Feb 07, 2026 at 02:26:41AM +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_remote_resources() and the corresponding pci_epc_ops
> > > get_remote_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..fa161113e24c 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_remote_resources() - query EPC-provided remote resources
> >
> > I am not sure if it good naming, pci_epc_get_additional_resources().
> > Niklas Cassel may have good suggest, I just find you forget cc him.
>
> That's true, I just naively followed the get_maintainers.pl output.
>
> Niklas, I'd be happy to hear your thoughts on the naming here.
> One other option I had in mind after Frank's feedback is
> pci_epc_get_aux_resources().
Name looks good to me, but I think the cover-letter can be improved a lot.
(Even when looking at the cover-letter in V6.)
It seems like in v6, the main motivation is basically what is described in
PATCH v6 8/8.
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).
I think the first sentence in the cover-letter should be that this series,
for SoCs that cannot use GIC ITS, provides an alternate doorbell using DWC
eDMA.
So first describe the problem, then describe how you solve it.
Perhaps also show some performance numbers. E.g. vntb, you can show the
latency of ping against the ntb netdev, see e.g. the commit message for:
dc693d606644 ("PCI: endpoint: pci-epf-vntb: Add MSI doorbell support")
(I.e. compare R-Car Spider with no doorbell vs eDMA doorbell when doing
ping against the ntb netdev.)
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v4 4/9] PCI: endpoint: Add remote resource query API
2026-02-10 12:20 ` Niklas Cassel
@ 2026-02-10 14:18 ` Koichiro Den
0 siblings, 0 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-10 14:18 UTC (permalink / raw)
To: Niklas Cassel
Cc: Frank Li, vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas, dmaengine, linux-pci, linux-kselftest, linux-kernel
On Tue, Feb 10, 2026 at 01:20:08PM +0100, Niklas Cassel wrote:
> On Sun, Feb 08, 2026 at 01:39:40AM +0900, Koichiro Den wrote:
> > On Fri, Feb 06, 2026 at 01:01:33PM -0500, Frank Li wrote:
> > > On Sat, Feb 07, 2026 at 02:26:41AM +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_remote_resources() and the corresponding pci_epc_ops
> > > > get_remote_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..fa161113e24c 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_remote_resources() - query EPC-provided remote resources
> > >
> > > I am not sure if it good naming, pci_epc_get_additional_resources().
> > > Niklas Cassel may have good suggest, I just find you forget cc him.
> >
> > That's true, I just naively followed the get_maintainers.pl output.
> >
> > Niklas, I'd be happy to hear your thoughts on the naming here.
> > One other option I had in mind after Frank's feedback is
> > pci_epc_get_aux_resources().
>
> Name looks good to me, but I think the cover-letter can be improved a lot.
> (Even when looking at the cover-letter in V6.)
>
> It seems like in v6, the main motivation is basically what is described in
> PATCH v6 8/8.
>
> 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).
>
> I think the first sentence in the cover-letter should be that this series,
> for SoCs that cannot use GIC ITS, provides an alternate doorbell using DWC
> eDMA.
>
> So first describe the problem, then describe how you solve it.
Thanks for pointing that out. Makes sense. Let me polish up the cover
letter accordingly.
>
> Perhaps also show some performance numbers. E.g. vntb, you can show the
> latency of ping against the ntb netdev, see e.g. the commit message for:
> dc693d606644 ("PCI: endpoint: pci-epf-vntb: Add MSI doorbell support")
>
> (I.e. compare R-Car Spider with no doorbell vs eDMA doorbell when doing
> ping against the ntb netdev.)
That makes sense as well, I'll do some performance measurements and include
them in the cover letter.
I'll send v7 once the remaining discussions in v6
(https://lore.kernel.org/linux-pci/20260209125316.2132589-1-den@valinux.co.jp/)
have converged.
Best regards,
Koichiro
>
>
> Kind regards,
> Niklas
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v4 5/9] PCI: dwc: Record integrated eDMA register window
2026-02-06 17:26 [PATCH v4 0/9] dmaengine, PCI: endpoint: Enable remote use of integrated DesignWare eDMA Koichiro Den
` (3 preceding siblings ...)
2026-02-06 17:26 ` [PATCH v4 4/9] PCI: endpoint: Add remote resource query API Koichiro Den
@ 2026-02-06 17:26 ` Koichiro Den
2026-02-06 17:26 ` [PATCH v4 6/9] PCI: dwc: ep: Report integrated eDMA resources via EPC remote-resource API Koichiro Den
` (3 subsequent siblings)
8 siblings, 0 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-06 17:26 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas
Cc: dmaengine, linux-pci, 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] 24+ messages in thread* [PATCH v4 6/9] PCI: dwc: ep: Report integrated eDMA resources via EPC remote-resource API
2026-02-06 17:26 [PATCH v4 0/9] dmaengine, PCI: endpoint: Enable remote use of integrated DesignWare eDMA Koichiro Den
` (4 preceding siblings ...)
2026-02-06 17:26 ` [PATCH v4 5/9] PCI: dwc: Record integrated eDMA register window Koichiro Den
@ 2026-02-06 17:26 ` Koichiro Den
2026-02-06 18:06 ` Frank Li
2026-02-06 17:26 ` [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant Koichiro Den
` (2 subsequent siblings)
8 siblings, 1 reply; 24+ messages in thread
From: Koichiro Den @ 2026-02-06 17:26 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas
Cc: dmaengine, linux-pci, linux-kselftest, linux-kernel
Implement pci_epc_ops.get_remote_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 | 85 +++++++++++++++++++
1 file changed, 85 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7e7844ff0f7e..29dedac86190 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -8,6 +8,7 @@
#include <linux/align.h>
#include <linux/bitfield.h>
+#include <linux/dma/edma.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -808,6 +809,89 @@ 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_remote_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_remote_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;
+ struct dw_edma_chan_info info;
+ int ll_cnt = 0, needed, idx = 0;
+ resource_size_t dma_size;
+ phys_addr_t dma_phys;
+ unsigned int i;
+ int ret;
+
+ 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_remote_resource) {
+ .type = PCI_EPC_RR_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;
+
+ ret = dw_edma_chan_info(edma, i, &info);
+ if (ret)
+ return ret;
+
+ resources[idx++] = (struct pci_epc_remote_resource) {
+ .type = PCI_EPC_RR_DMA_CHAN_DESC,
+ .phys_addr = edma->ll_region_wr[i].paddr,
+ .size = edma->ll_region_wr[i].sz,
+ .u.dma_chan_desc.irq = info.irq,
+ .u.dma_chan_desc.db_offset = info.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;
+
+ ret = dw_edma_chan_info(edma, i + edma->ll_wr_cnt, &info);
+ if (ret)
+ return ret;
+
+ resources[idx++] = (struct pci_epc_remote_resource) {
+ .type = PCI_EPC_RR_DMA_CHAN_DESC,
+ .phys_addr = edma->ll_region_rd[i].paddr,
+ .size = edma->ll_region_rd[i].sz,
+ .u.dma_chan_desc.irq = info.irq,
+ .u.dma_chan_desc.db_offset = info.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 +907,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_remote_resources = dw_pcie_ep_get_remote_resources,
};
/**
--
2.51.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v4 6/9] PCI: dwc: ep: Report integrated eDMA resources via EPC remote-resource API
2026-02-06 17:26 ` [PATCH v4 6/9] PCI: dwc: ep: Report integrated eDMA resources via EPC remote-resource API Koichiro Den
@ 2026-02-06 18:06 ` Frank Li
2026-02-07 15:59 ` Koichiro Den
0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-02-06 18:06 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Sat, Feb 07, 2026 at 02:26:43AM +0900, Koichiro Den wrote:
> Implement pci_epc_ops.get_remote_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 | 85 +++++++++++++++++++
> 1 file changed, 85 insertions(+)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 7e7844ff0f7e..29dedac86190 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -8,6 +8,7 @@
>
> #include <linux/align.h>
> #include <linux/bitfield.h>
> +#include <linux/dma/edma.h>
> #include <linux/of.h>
> #include <linux/platform_device.h>
>
> @@ -808,6 +809,89 @@ 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_remote_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_remote_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;
> + struct dw_edma_chan_info info;
> + int ll_cnt = 0, needed, idx = 0;
> + resource_size_t dma_size;
> + phys_addr_t dma_phys;
> + unsigned int i;
> + int ret;
> +
> + 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_remote_resource) {
> + .type = PCI_EPC_RR_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;
> +
> + ret = dw_edma_chan_info(edma, i, &info);
> + if (ret)
> + return ret;
> +
> + resources[idx++] = (struct pci_epc_remote_resource) {
> + .type = PCI_EPC_RR_DMA_CHAN_DESC,
> + .phys_addr = edma->ll_region_wr[i].paddr,
> + .size = edma->ll_region_wr[i].sz,
> + .u.dma_chan_desc.irq = info.irq,
> + .u.dma_chan_desc.db_offset = info.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;
> +
> + ret = dw_edma_chan_info(edma, i + edma->ll_wr_cnt, &info);
edma's information is what dw-EP pass to edma driver, supposed dw-ep know
irq and HDMI or EDMA's information, I think needn't go around to EDMA again
to fetch this information back.
Frank
> + if (ret)
> + return ret;
> +
> + resources[idx++] = (struct pci_epc_remote_resource) {
> + .type = PCI_EPC_RR_DMA_CHAN_DESC,
> + .phys_addr = edma->ll_region_rd[i].paddr,
> + .size = edma->ll_region_rd[i].sz,
> + .u.dma_chan_desc.irq = info.irq,
> + .u.dma_chan_desc.db_offset = info.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 +907,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_remote_resources = dw_pcie_ep_get_remote_resources,
> };
>
> /**
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v4 6/9] PCI: dwc: ep: Report integrated eDMA resources via EPC remote-resource API
2026-02-06 18:06 ` Frank Li
@ 2026-02-07 15:59 ` Koichiro Den
0 siblings, 0 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-07 15:59 UTC (permalink / raw)
To: Frank Li
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Fri, Feb 06, 2026 at 01:06:46PM -0500, Frank Li wrote:
> On Sat, Feb 07, 2026 at 02:26:43AM +0900, Koichiro Den wrote:
> > Implement pci_epc_ops.get_remote_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 | 85 +++++++++++++++++++
> > 1 file changed, 85 insertions(+)
> >
> > diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > index 7e7844ff0f7e..29dedac86190 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> > +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > @@ -8,6 +8,7 @@
> >
> > #include <linux/align.h>
> > #include <linux/bitfield.h>
> > +#include <linux/dma/edma.h>
> > #include <linux/of.h>
> > #include <linux/platform_device.h>
> >
> > @@ -808,6 +809,89 @@ 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_remote_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> > + struct pci_epc_remote_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;
> > + struct dw_edma_chan_info info;
> > + int ll_cnt = 0, needed, idx = 0;
> > + resource_size_t dma_size;
> > + phys_addr_t dma_phys;
> > + unsigned int i;
> > + int ret;
> > +
> > + 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_remote_resource) {
> > + .type = PCI_EPC_RR_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;
> > +
> > + ret = dw_edma_chan_info(edma, i, &info);
> > + if (ret)
> > + return ret;
> > +
> > + resources[idx++] = (struct pci_epc_remote_resource) {
> > + .type = PCI_EPC_RR_DMA_CHAN_DESC,
> > + .phys_addr = edma->ll_region_wr[i].paddr,
> > + .size = edma->ll_region_wr[i].sz,
> > + .u.dma_chan_desc.irq = info.irq,
> > + .u.dma_chan_desc.db_offset = info.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;
> > +
> > + ret = dw_edma_chan_info(edma, i + edma->ll_wr_cnt, &info);
>
> edma's information is what dw-EP pass to edma driver, supposed dw-ep know
> irq and HDMI or EDMA's information, I think needn't go around to EDMA again
> to fetch this information back.
Thanks for the feedback. As I understand it, yes, dw-EP passes information
such as nr_irqs/ll_wr_cnt/ll_rd_cnt to dw-edma, but the per-channel
chan-to-irq mapping is decided inside dw-edma. The doorbell offset is also
derived from the dw-(e|h)dma v0 core registers.
To avoid having dw-EP call back into dw-edma just to fetch the mapping
(i.e. get rid of dw_edma_chan_info()), we could cache the per-channel
metadata in struct dw_edma_chip and have dw-edma fill it at probe time.
Then get_remote_resources() can just read the cached values.
Something like:
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 53b31a974331..83503aacaf5f 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -88,6 +88,17 @@ struct dw_edma_peripheral_config {
enum dw_edma_ch_irq_mode irq_mode;
};
+/**
+ * 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;
+ resource_size_t db_offset;
+};
+
/**
* struct dw_edma_chip - representation of DesignWare eDMA controller hardware
* @dev: struct device of the eDMA controller
@@ -124,6 +135,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;
Best regards,
Koichiro
>
> Frank
> > + if (ret)
> > + return ret;
> > +
> > + resources[idx++] = (struct pci_epc_remote_resource) {
> > + .type = PCI_EPC_RR_DMA_CHAN_DESC,
> > + .phys_addr = edma->ll_region_rd[i].paddr,
> > + .size = edma->ll_region_rd[i].sz,
> > + .u.dma_chan_desc.irq = info.irq,
> > + .u.dma_chan_desc.db_offset = info.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 +907,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_remote_resources = dw_pcie_ep_get_remote_resources,
> > };
> >
> > /**
> > --
> > 2.51.0
> >
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant
2026-02-06 17:26 [PATCH v4 0/9] dmaengine, PCI: endpoint: Enable remote use of integrated DesignWare eDMA Koichiro Den
` (5 preceding siblings ...)
2026-02-06 17:26 ` [PATCH v4 6/9] PCI: dwc: ep: Report integrated eDMA resources via EPC remote-resource API Koichiro Den
@ 2026-02-06 17:26 ` Koichiro Den
2026-02-06 19:20 ` Frank Li
2026-02-08 12:35 ` kernel test robot
2026-02-06 17:26 ` [PATCH v4 8/9] misc: pci_endpoint_test: Allow selecting embedded doorbell Koichiro Den
2026-02-06 17:26 ` [PATCH v4 9/9] selftests: pci_endpoint: Exercise MSI and " Koichiro Den
8 siblings, 2 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-06 17:26 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas
Cc: dmaengine, linux-pci, linux-kselftest, linux-kernel
Extend pci-epf-test with an "embedded doorbell" variant that does not
rely on the EPC doorbell/MSI mechanism.
When the host sets FLAG_DB_EMBEDDED, query EPC remote resources to
locate the embedded DMA MMIO window and a per-channel
interrupt-emulation doorbell register offset. Map the MMIO window into a
free BAR and return BAR+offset to the host as the doorbell target.
Handle the resulting shared IRQ by deferring completion signalling to a
work item, then update the test status and raise the completion IRQ back
to the host.
The existing MSI doorbell remains the default when FLAG_DB_EMBEDDED is
not set.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/endpoint/functions/pci-epf-test.c | 193 +++++++++++++++++-
1 file changed, 185 insertions(+), 8 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 6952ee418622..5871da8cbddf 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -6,6 +6,7 @@
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
+#include <linux/bitops.h>
#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
@@ -56,6 +57,7 @@
#define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17)
#define FLAG_USE_DMA BIT(0)
+#define FLAG_DB_EMBEDDED BIT(1)
#define TIMER_RESOLUTION 1
@@ -69,6 +71,12 @@
static struct workqueue_struct *kpcitest_workqueue;
+enum pci_epf_test_doorbell_variant {
+ PCI_EPF_TEST_DB_NONE = 0,
+ PCI_EPF_TEST_DB_MSI,
+ PCI_EPF_TEST_DB_EMBEDDED,
+};
+
struct pci_epf_test {
void *reg[PCI_STD_NUM_BARS];
struct pci_epf *epf;
@@ -85,7 +93,11 @@ struct pci_epf_test {
bool dma_supported;
bool dma_private;
const struct pci_epc_features *epc_features;
+ enum pci_epf_test_doorbell_variant db_variant;
struct pci_epf_bar db_bar;
+ int db_irq;
+ unsigned long db_irq_pending;
+ struct work_struct db_work;
size_t bar_size[PCI_STD_NUM_BARS];
};
@@ -696,7 +708,7 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,
}
}
-static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
+static irqreturn_t pci_epf_test_doorbell_msi_handler(int irq, void *data)
{
struct pci_epf_test *epf_test = data;
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
@@ -710,19 +722,58 @@ static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
return IRQ_HANDLED;
}
+static void pci_epf_test_doorbell_embedded_work(struct work_struct *work)
+{
+ struct pci_epf_test *epf_test =
+ container_of(work, struct pci_epf_test, db_work);
+ enum pci_barno test_reg_bar = epf_test->test_reg_bar;
+ struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
+ u32 status = le32_to_cpu(reg->status);
+
+ status |= STATUS_DOORBELL_SUCCESS;
+ reg->status = cpu_to_le32(status);
+ pci_epf_test_raise_irq(epf_test, reg);
+
+ clear_bit(0, &epf_test->db_irq_pending);
+}
+
+static irqreturn_t pci_epf_test_doorbell_embedded_irq_handler(int irq, void *data)
+{
+ struct pci_epf_test *epf_test = data;
+
+ if (READ_ONCE(epf_test->db_variant) != PCI_EPF_TEST_DB_EMBEDDED)
+ return IRQ_NONE;
+
+ if (test_and_set_bit(0, &epf_test->db_irq_pending))
+ return IRQ_HANDLED;
+
+ queue_work(kpcitest_workqueue, &epf_test->db_work);
+ return IRQ_HANDLED;
+}
+
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);
- reg->doorbell_bar = cpu_to_le32(NO_BAR);
+ if (epf_test->db_irq) {
+ free_irq(epf_test->db_irq, epf_test);
+ epf_test->db_irq = 0;
+ }
+
+ if (epf_test->db_variant == PCI_EPF_TEST_DB_EMBEDDED) {
+ cancel_work_sync(&epf_test->db_work);
+ clear_bit(0, &epf_test->db_irq_pending);
+ } else if (epf_test->db_variant == PCI_EPF_TEST_DB_MSI) {
+ pci_epf_free_doorbell(epf);
+ }
- pci_epf_free_doorbell(epf);
+ reg->doorbell_bar = cpu_to_le32(NO_BAR);
+ epf_test->db_variant = PCI_EPF_TEST_DB_NONE;
}
-static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
- struct pci_epf_test_reg *reg)
+static void pci_epf_test_enable_doorbell_msi(struct pci_epf_test *epf_test,
+ struct pci_epf_test_reg *reg)
{
u32 status = le32_to_cpu(reg->status);
struct pci_epf *epf = epf_test->epf;
@@ -736,20 +787,23 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
if (ret)
goto set_status_err;
+ epf_test->db_variant = PCI_EPF_TEST_DB_MSI;
msg = &epf->db_msg[0].msg;
bar = pci_epc_get_next_free_bar(epf_test->epc_features, epf_test->test_reg_bar + 1);
if (bar < BAR_0)
goto err_doorbell_cleanup;
ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
- pci_epf_test_doorbell_handler, IRQF_ONESHOT,
- "pci-ep-test-doorbell", epf_test);
+ pci_epf_test_doorbell_msi_handler,
+ IRQF_ONESHOT, "pci-ep-test-doorbell",
+ epf_test);
if (ret) {
dev_err(&epf->dev,
"Failed to request doorbell IRQ: %d\n",
epf->db_msg[0].virq);
goto err_doorbell_cleanup;
}
+ epf_test->db_irq = epf->db_msg[0].virq;
reg->doorbell_data = cpu_to_le32(msg->data);
reg->doorbell_bar = cpu_to_le32(bar);
@@ -782,6 +836,125 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
reg->status = cpu_to_le32(status);
}
+static void pci_epf_test_enable_doorbell_embedded(struct pci_epf_test *epf_test,
+ struct pci_epf_test_reg *reg)
+{
+ struct pci_epc_remote_resource *dma_ctrl = NULL, *chan0 = NULL;
+ const char *irq_name = "pci-ep-test-doorbell-embedded";
+ u32 status = le32_to_cpu(reg->status);
+ struct pci_epf *epf = epf_test->epf;
+ struct pci_epc *epc = epf->epc;
+ struct device *dev = &epf->dev;
+ enum pci_barno bar;
+ size_t align_off;
+ unsigned int i;
+ int cnt, ret;
+ u32 db_off;
+
+ cnt = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
+ NULL, 0);
+ if (cnt <= 0) {
+ dev_err(dev, "No remote resources available for embedded doorbell\n");
+ goto set_status_err;
+ }
+
+ struct pci_epc_remote_resource *resources __free(kfree) =
+ kcalloc(cnt, sizeof(*resources), GFP_KERNEL);
+ if (!resources)
+ goto set_status_err;
+
+ ret = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
+ resources, cnt);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get remote resources: %d\n", ret);
+ goto set_status_err;
+ }
+ cnt = ret;
+
+ for (i = 0; i < cnt; i++) {
+ if (resources[i].type == PCI_EPC_RR_DMA_CTRL_MMIO)
+ dma_ctrl = &resources[i];
+ else if (resources[i].type == PCI_EPC_RR_DMA_CHAN_DESC &&
+ !chan0)
+ chan0 = &resources[i];
+ }
+
+ if (!dma_ctrl || !chan0) {
+ dev_err(dev, "Missing DMA ctrl MMIO or channel #0 info\n");
+ goto set_status_err;
+ }
+
+ bar = pci_epc_get_next_free_bar(epf_test->epc_features,
+ epf_test->test_reg_bar + 1);
+ if (bar < BAR_0) {
+ dev_err(dev, "No free BAR for embedded doorbell\n");
+ goto set_status_err;
+ }
+
+ ret = pci_epf_align_inbound_addr(epf, bar, dma_ctrl->phys_addr,
+ &epf_test->db_bar.phys_addr,
+ &align_off);
+ if (ret)
+ goto set_status_err;
+
+ db_off = chan0->u.dma_chan_desc.db_offset;
+ if (db_off >= dma_ctrl->size ||
+ align_off + db_off >= epf->bar[bar].size) {
+ dev_err(dev, "BAR%d too small for embedded doorbell (off %#zx + %#x)\n",
+ bar, align_off, db_off);
+ goto set_status_err;
+ }
+
+ epf_test->db_variant = PCI_EPF_TEST_DB_EMBEDDED;
+
+ ret = request_irq(chan0->u.dma_chan_desc.irq,
+ pci_epf_test_doorbell_embedded_irq_handler,
+ IRQF_SHARED, irq_name, epf_test);
+ if (ret) {
+ dev_err(dev, "Failed to request embedded doorbell IRQ: %d\n",
+ chan0->u.dma_chan_desc.irq);
+ goto err_cleanup;
+ }
+ epf_test->db_irq = chan0->u.dma_chan_desc.irq;
+
+ reg->doorbell_data = cpu_to_le32(0);
+ reg->doorbell_bar = cpu_to_le32(bar);
+ reg->doorbell_offset = cpu_to_le32(align_off + db_off);
+
+ epf_test->db_bar.barno = bar;
+ epf_test->db_bar.size = epf->bar[bar].size;
+ epf_test->db_bar.flags = epf->bar[bar].flags;
+
+ ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
+ if (ret)
+ goto err_cleanup;
+
+ status |= STATUS_DOORBELL_ENABLE_SUCCESS;
+ reg->status = cpu_to_le32(status);
+ return;
+
+err_cleanup:
+ pci_epf_test_doorbell_cleanup(epf_test);
+set_status_err:
+ status |= STATUS_DOORBELL_ENABLE_FAIL;
+ reg->status = cpu_to_le32(status);
+}
+
+static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
+ struct pci_epf_test_reg *reg)
+{
+ u32 flags = le32_to_cpu(reg->flags);
+
+ /* If already enabled, drop previous setup first. */
+ if (epf_test->db_variant != PCI_EPF_TEST_DB_NONE)
+ pci_epf_test_doorbell_cleanup(epf_test);
+
+ if (flags & FLAG_DB_EMBEDDED)
+ pci_epf_test_enable_doorbell_embedded(epf_test, reg);
+ else
+ pci_epf_test_enable_doorbell_msi(epf_test, reg);
+}
+
static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
@@ -1309,6 +1482,9 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
cancel_delayed_work_sync(&epf_test->cmd_handler);
if (epc->init_complete) {
+ /* In case userspace never disabled doorbell explicitly. */
+ if (epf_test->db_variant != PCI_EPF_TEST_DB_NONE)
+ pci_epf_test_doorbell_cleanup(epf_test);
pci_epf_test_clean_dma_chan(epf_test);
pci_epf_test_clear_bar(epf);
}
@@ -1427,6 +1603,7 @@ static int pci_epf_test_probe(struct pci_epf *epf,
epf_test->bar_size[bar] = default_bar_size[bar];
INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);
+ INIT_WORK(&epf_test->db_work, pci_epf_test_doorbell_embedded_work);
epf->event_ops = &pci_epf_test_event_ops;
--
2.51.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant
2026-02-06 17:26 ` [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant Koichiro Den
@ 2026-02-06 19:20 ` Frank Li
2026-02-07 16:25 ` Koichiro Den
2026-02-08 12:35 ` kernel test robot
1 sibling, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-02-06 19:20 UTC (permalink / raw)
To: Koichiro Den
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Sat, Feb 07, 2026 at 02:26:44AM +0900, Koichiro Den wrote:
> Extend pci-epf-test with an "embedded doorbell" variant that does not
> rely on the EPC doorbell/MSI mechanism.
>
> When the host sets FLAG_DB_EMBEDDED, query EPC remote resources to
> locate the embedded DMA MMIO window and a per-channel
> interrupt-emulation doorbell register offset. Map the MMIO window into a
> free BAR and return BAR+offset to the host as the doorbell target.
>
> Handle the resulting shared IRQ by deferring completion signalling to a
> work item, then update the test status and raise the completion IRQ back
> to the host.
>
> The existing MSI doorbell remains the default when FLAG_DB_EMBEDDED is
> not set.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Can you change pci_epf_alloc_doorbell() directly? Let it fall back to
edma implement.
Frank
> drivers/pci/endpoint/functions/pci-epf-test.c | 193 +++++++++++++++++-
> 1 file changed, 185 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> index 6952ee418622..5871da8cbddf 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> @@ -6,6 +6,7 @@
> * Author: Kishon Vijay Abraham I <kishon@ti.com>
> */
>
> +#include <linux/bitops.h>
> #include <linux/crc32.h>
> #include <linux/delay.h>
> #include <linux/dmaengine.h>
> @@ -56,6 +57,7 @@
> #define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17)
>
> #define FLAG_USE_DMA BIT(0)
> +#define FLAG_DB_EMBEDDED BIT(1)
>
> #define TIMER_RESOLUTION 1
>
> @@ -69,6 +71,12 @@
>
> static struct workqueue_struct *kpcitest_workqueue;
>
> +enum pci_epf_test_doorbell_variant {
> + PCI_EPF_TEST_DB_NONE = 0,
> + PCI_EPF_TEST_DB_MSI,
> + PCI_EPF_TEST_DB_EMBEDDED,
> +};
> +
> struct pci_epf_test {
> void *reg[PCI_STD_NUM_BARS];
> struct pci_epf *epf;
> @@ -85,7 +93,11 @@ struct pci_epf_test {
> bool dma_supported;
> bool dma_private;
> const struct pci_epc_features *epc_features;
> + enum pci_epf_test_doorbell_variant db_variant;
> struct pci_epf_bar db_bar;
> + int db_irq;
> + unsigned long db_irq_pending;
> + struct work_struct db_work;
> size_t bar_size[PCI_STD_NUM_BARS];
> };
>
> @@ -696,7 +708,7 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,
> }
> }
>
> -static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
> +static irqreturn_t pci_epf_test_doorbell_msi_handler(int irq, void *data)
> {
> struct pci_epf_test *epf_test = data;
> enum pci_barno test_reg_bar = epf_test->test_reg_bar;
> @@ -710,19 +722,58 @@ static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
> return IRQ_HANDLED;
> }
>
> +static void pci_epf_test_doorbell_embedded_work(struct work_struct *work)
> +{
> + struct pci_epf_test *epf_test =
> + container_of(work, struct pci_epf_test, db_work);
> + enum pci_barno test_reg_bar = epf_test->test_reg_bar;
> + struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
> + u32 status = le32_to_cpu(reg->status);
> +
> + status |= STATUS_DOORBELL_SUCCESS;
> + reg->status = cpu_to_le32(status);
> + pci_epf_test_raise_irq(epf_test, reg);
> +
> + clear_bit(0, &epf_test->db_irq_pending);
> +}
> +
> +static irqreturn_t pci_epf_test_doorbell_embedded_irq_handler(int irq, void *data)
> +{
> + struct pci_epf_test *epf_test = data;
> +
> + if (READ_ONCE(epf_test->db_variant) != PCI_EPF_TEST_DB_EMBEDDED)
> + return IRQ_NONE;
> +
> + if (test_and_set_bit(0, &epf_test->db_irq_pending))
> + return IRQ_HANDLED;
> +
> + queue_work(kpcitest_workqueue, &epf_test->db_work);
> + return IRQ_HANDLED;
> +}
> +
> 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);
> - reg->doorbell_bar = cpu_to_le32(NO_BAR);
> + if (epf_test->db_irq) {
> + free_irq(epf_test->db_irq, epf_test);
> + epf_test->db_irq = 0;
> + }
> +
> + if (epf_test->db_variant == PCI_EPF_TEST_DB_EMBEDDED) {
> + cancel_work_sync(&epf_test->db_work);
> + clear_bit(0, &epf_test->db_irq_pending);
> + } else if (epf_test->db_variant == PCI_EPF_TEST_DB_MSI) {
> + pci_epf_free_doorbell(epf);
> + }
>
> - pci_epf_free_doorbell(epf);
> + reg->doorbell_bar = cpu_to_le32(NO_BAR);
> + epf_test->db_variant = PCI_EPF_TEST_DB_NONE;
> }
>
> -static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> - struct pci_epf_test_reg *reg)
> +static void pci_epf_test_enable_doorbell_msi(struct pci_epf_test *epf_test,
> + struct pci_epf_test_reg *reg)
> {
> u32 status = le32_to_cpu(reg->status);
> struct pci_epf *epf = epf_test->epf;
> @@ -736,20 +787,23 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> if (ret)
> goto set_status_err;
>
> + epf_test->db_variant = PCI_EPF_TEST_DB_MSI;
> msg = &epf->db_msg[0].msg;
> bar = pci_epc_get_next_free_bar(epf_test->epc_features, epf_test->test_reg_bar + 1);
> if (bar < BAR_0)
> goto err_doorbell_cleanup;
>
> ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
> - pci_epf_test_doorbell_handler, IRQF_ONESHOT,
> - "pci-ep-test-doorbell", epf_test);
> + pci_epf_test_doorbell_msi_handler,
> + IRQF_ONESHOT, "pci-ep-test-doorbell",
> + epf_test);
> if (ret) {
> dev_err(&epf->dev,
> "Failed to request doorbell IRQ: %d\n",
> epf->db_msg[0].virq);
> goto err_doorbell_cleanup;
> }
> + epf_test->db_irq = epf->db_msg[0].virq;
>
> reg->doorbell_data = cpu_to_le32(msg->data);
> reg->doorbell_bar = cpu_to_le32(bar);
> @@ -782,6 +836,125 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> reg->status = cpu_to_le32(status);
> }
>
> +static void pci_epf_test_enable_doorbell_embedded(struct pci_epf_test *epf_test,
> + struct pci_epf_test_reg *reg)
> +{
> + struct pci_epc_remote_resource *dma_ctrl = NULL, *chan0 = NULL;
> + const char *irq_name = "pci-ep-test-doorbell-embedded";
> + u32 status = le32_to_cpu(reg->status);
> + struct pci_epf *epf = epf_test->epf;
> + struct pci_epc *epc = epf->epc;
> + struct device *dev = &epf->dev;
> + enum pci_barno bar;
> + size_t align_off;
> + unsigned int i;
> + int cnt, ret;
> + u32 db_off;
> +
> + cnt = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
> + NULL, 0);
> + if (cnt <= 0) {
> + dev_err(dev, "No remote resources available for embedded doorbell\n");
> + goto set_status_err;
> + }
> +
> + struct pci_epc_remote_resource *resources __free(kfree) =
> + kcalloc(cnt, sizeof(*resources), GFP_KERNEL);
> + if (!resources)
> + goto set_status_err;
> +
> + ret = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
> + resources, cnt);
> + if (ret < 0) {
> + dev_err(dev, "Failed to get remote resources: %d\n", ret);
> + goto set_status_err;
> + }
> + cnt = ret;
> +
> + for (i = 0; i < cnt; i++) {
> + if (resources[i].type == PCI_EPC_RR_DMA_CTRL_MMIO)
> + dma_ctrl = &resources[i];
> + else if (resources[i].type == PCI_EPC_RR_DMA_CHAN_DESC &&
> + !chan0)
> + chan0 = &resources[i];
> + }
> +
> + if (!dma_ctrl || !chan0) {
> + dev_err(dev, "Missing DMA ctrl MMIO or channel #0 info\n");
> + goto set_status_err;
> + }
> +
> + bar = pci_epc_get_next_free_bar(epf_test->epc_features,
> + epf_test->test_reg_bar + 1);
> + if (bar < BAR_0) {
> + dev_err(dev, "No free BAR for embedded doorbell\n");
> + goto set_status_err;
> + }
> +
> + ret = pci_epf_align_inbound_addr(epf, bar, dma_ctrl->phys_addr,
> + &epf_test->db_bar.phys_addr,
> + &align_off);
> + if (ret)
> + goto set_status_err;
> +
> + db_off = chan0->u.dma_chan_desc.db_offset;
> + if (db_off >= dma_ctrl->size ||
> + align_off + db_off >= epf->bar[bar].size) {
> + dev_err(dev, "BAR%d too small for embedded doorbell (off %#zx + %#x)\n",
> + bar, align_off, db_off);
> + goto set_status_err;
> + }
> +
> + epf_test->db_variant = PCI_EPF_TEST_DB_EMBEDDED;
> +
> + ret = request_irq(chan0->u.dma_chan_desc.irq,
> + pci_epf_test_doorbell_embedded_irq_handler,
> + IRQF_SHARED, irq_name, epf_test);
> + if (ret) {
> + dev_err(dev, "Failed to request embedded doorbell IRQ: %d\n",
> + chan0->u.dma_chan_desc.irq);
> + goto err_cleanup;
> + }
> + epf_test->db_irq = chan0->u.dma_chan_desc.irq;
> +
> + reg->doorbell_data = cpu_to_le32(0);
> + reg->doorbell_bar = cpu_to_le32(bar);
> + reg->doorbell_offset = cpu_to_le32(align_off + db_off);
> +
> + epf_test->db_bar.barno = bar;
> + epf_test->db_bar.size = epf->bar[bar].size;
> + epf_test->db_bar.flags = epf->bar[bar].flags;
> +
> + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
> + if (ret)
> + goto err_cleanup;
> +
> + status |= STATUS_DOORBELL_ENABLE_SUCCESS;
> + reg->status = cpu_to_le32(status);
> + return;
> +
> +err_cleanup:
> + pci_epf_test_doorbell_cleanup(epf_test);
> +set_status_err:
> + status |= STATUS_DOORBELL_ENABLE_FAIL;
> + reg->status = cpu_to_le32(status);
> +}
> +
> +static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> + struct pci_epf_test_reg *reg)
> +{
> + u32 flags = le32_to_cpu(reg->flags);
> +
> + /* If already enabled, drop previous setup first. */
> + if (epf_test->db_variant != PCI_EPF_TEST_DB_NONE)
> + pci_epf_test_doorbell_cleanup(epf_test);
> +
> + if (flags & FLAG_DB_EMBEDDED)
> + pci_epf_test_enable_doorbell_embedded(epf_test, reg);
> + else
> + pci_epf_test_enable_doorbell_msi(epf_test, reg);
> +}
> +
> static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test,
> struct pci_epf_test_reg *reg)
> {
> @@ -1309,6 +1482,9 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
>
> cancel_delayed_work_sync(&epf_test->cmd_handler);
> if (epc->init_complete) {
> + /* In case userspace never disabled doorbell explicitly. */
> + if (epf_test->db_variant != PCI_EPF_TEST_DB_NONE)
> + pci_epf_test_doorbell_cleanup(epf_test);
> pci_epf_test_clean_dma_chan(epf_test);
> pci_epf_test_clear_bar(epf);
> }
> @@ -1427,6 +1603,7 @@ static int pci_epf_test_probe(struct pci_epf *epf,
> epf_test->bar_size[bar] = default_bar_size[bar];
>
> INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);
> + INIT_WORK(&epf_test->db_work, pci_epf_test_doorbell_embedded_work);
>
> epf->event_ops = &pci_epf_test_event_ops;
>
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant
2026-02-06 19:20 ` Frank Li
@ 2026-02-07 16:25 ` Koichiro Den
0 siblings, 0 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-07 16:25 UTC (permalink / raw)
To: Frank Li
Cc: vkoul, mani, jingoohan1, lpieralisi, kwilczynski, robh, bhelgaas,
dmaengine, linux-pci, linux-kselftest, linux-kernel
On Fri, Feb 06, 2026 at 02:20:58PM -0500, Frank Li wrote:
> On Sat, Feb 07, 2026 at 02:26:44AM +0900, Koichiro Den wrote:
> > Extend pci-epf-test with an "embedded doorbell" variant that does not
> > rely on the EPC doorbell/MSI mechanism.
> >
> > When the host sets FLAG_DB_EMBEDDED, query EPC remote resources to
> > locate the embedded DMA MMIO window and a per-channel
> > interrupt-emulation doorbell register offset. Map the MMIO window into a
> > free BAR and return BAR+offset to the host as the doorbell target.
> >
> > Handle the resulting shared IRQ by deferring completion signalling to a
> > work item, then update the test status and raise the completion IRQ back
> > to the host.
> >
> > The existing MSI doorbell remains the default when FLAG_DB_EMBEDDED is
> > not set.
> >
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
>
> Can you change pci_epf_alloc_doorbell() directly? Let it fall back to
> edma implement.
Thanks for the suggestion.
Yes, while I think doing so would require some extra care and constraints
for the users (i.e. the pci_epf_alloc_doorbell() -> request_irq flow).
Also, separate testing would no longer be possible, but I think it's ok,
because choosing the "fake irq" when a normal MSI doorbell is available
would not be very useful anyway.
For the fallback case, the interrupt source is not a normal MSI doorbell,
but a level-triggered, shared platform IRQ. Due to that, the caller needs
to know how the IRQ can be requested safely (e.g. usable IRQF_* flags,
or whether a primary handler is required for request_threaded_irq).
I think we need to expose such hints via pci_epf_doorbell_msg by adding new
fields (e.g. doorbell type, IRQ flags to be OR-ed), and have
pci_epf_alloc_doorbell() fill them in, rather than completely hiding the
fallback internally.
Let me send v5 accordingly. Please let me know if you see any issues.
Koichiro
>
> Frank
>
> > drivers/pci/endpoint/functions/pci-epf-test.c | 193 +++++++++++++++++-
> > 1 file changed, 185 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> > index 6952ee418622..5871da8cbddf 100644
> > --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> > +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> > @@ -6,6 +6,7 @@
> > * Author: Kishon Vijay Abraham I <kishon@ti.com>
> > */
> >
> > +#include <linux/bitops.h>
> > #include <linux/crc32.h>
> > #include <linux/delay.h>
> > #include <linux/dmaengine.h>
> > @@ -56,6 +57,7 @@
> > #define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17)
> >
> > #define FLAG_USE_DMA BIT(0)
> > +#define FLAG_DB_EMBEDDED BIT(1)
> >
> > #define TIMER_RESOLUTION 1
> >
> > @@ -69,6 +71,12 @@
> >
> > static struct workqueue_struct *kpcitest_workqueue;
> >
> > +enum pci_epf_test_doorbell_variant {
> > + PCI_EPF_TEST_DB_NONE = 0,
> > + PCI_EPF_TEST_DB_MSI,
> > + PCI_EPF_TEST_DB_EMBEDDED,
> > +};
> > +
> > struct pci_epf_test {
> > void *reg[PCI_STD_NUM_BARS];
> > struct pci_epf *epf;
> > @@ -85,7 +93,11 @@ struct pci_epf_test {
> > bool dma_supported;
> > bool dma_private;
> > const struct pci_epc_features *epc_features;
> > + enum pci_epf_test_doorbell_variant db_variant;
> > struct pci_epf_bar db_bar;
> > + int db_irq;
> > + unsigned long db_irq_pending;
> > + struct work_struct db_work;
> > size_t bar_size[PCI_STD_NUM_BARS];
> > };
> >
> > @@ -696,7 +708,7 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,
> > }
> > }
> >
> > -static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
> > +static irqreturn_t pci_epf_test_doorbell_msi_handler(int irq, void *data)
> > {
> > struct pci_epf_test *epf_test = data;
> > enum pci_barno test_reg_bar = epf_test->test_reg_bar;
> > @@ -710,19 +722,58 @@ static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
> > return IRQ_HANDLED;
> > }
> >
> > +static void pci_epf_test_doorbell_embedded_work(struct work_struct *work)
> > +{
> > + struct pci_epf_test *epf_test =
> > + container_of(work, struct pci_epf_test, db_work);
> > + enum pci_barno test_reg_bar = epf_test->test_reg_bar;
> > + struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
> > + u32 status = le32_to_cpu(reg->status);
> > +
> > + status |= STATUS_DOORBELL_SUCCESS;
> > + reg->status = cpu_to_le32(status);
> > + pci_epf_test_raise_irq(epf_test, reg);
> > +
> > + clear_bit(0, &epf_test->db_irq_pending);
> > +}
> > +
> > +static irqreturn_t pci_epf_test_doorbell_embedded_irq_handler(int irq, void *data)
> > +{
> > + struct pci_epf_test *epf_test = data;
> > +
> > + if (READ_ONCE(epf_test->db_variant) != PCI_EPF_TEST_DB_EMBEDDED)
> > + return IRQ_NONE;
> > +
> > + if (test_and_set_bit(0, &epf_test->db_irq_pending))
> > + return IRQ_HANDLED;
> > +
> > + queue_work(kpcitest_workqueue, &epf_test->db_work);
> > + return IRQ_HANDLED;
> > +}
> > +
> > 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);
> > - reg->doorbell_bar = cpu_to_le32(NO_BAR);
> > + if (epf_test->db_irq) {
> > + free_irq(epf_test->db_irq, epf_test);
> > + epf_test->db_irq = 0;
> > + }
> > +
> > + if (epf_test->db_variant == PCI_EPF_TEST_DB_EMBEDDED) {
> > + cancel_work_sync(&epf_test->db_work);
> > + clear_bit(0, &epf_test->db_irq_pending);
> > + } else if (epf_test->db_variant == PCI_EPF_TEST_DB_MSI) {
> > + pci_epf_free_doorbell(epf);
> > + }
> >
> > - pci_epf_free_doorbell(epf);
> > + reg->doorbell_bar = cpu_to_le32(NO_BAR);
> > + epf_test->db_variant = PCI_EPF_TEST_DB_NONE;
> > }
> >
> > -static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> > - struct pci_epf_test_reg *reg)
> > +static void pci_epf_test_enable_doorbell_msi(struct pci_epf_test *epf_test,
> > + struct pci_epf_test_reg *reg)
> > {
> > u32 status = le32_to_cpu(reg->status);
> > struct pci_epf *epf = epf_test->epf;
> > @@ -736,20 +787,23 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> > if (ret)
> > goto set_status_err;
> >
> > + epf_test->db_variant = PCI_EPF_TEST_DB_MSI;
> > msg = &epf->db_msg[0].msg;
> > bar = pci_epc_get_next_free_bar(epf_test->epc_features, epf_test->test_reg_bar + 1);
> > if (bar < BAR_0)
> > goto err_doorbell_cleanup;
> >
> > ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
> > - pci_epf_test_doorbell_handler, IRQF_ONESHOT,
> > - "pci-ep-test-doorbell", epf_test);
> > + pci_epf_test_doorbell_msi_handler,
> > + IRQF_ONESHOT, "pci-ep-test-doorbell",
> > + epf_test);
> > if (ret) {
> > dev_err(&epf->dev,
> > "Failed to request doorbell IRQ: %d\n",
> > epf->db_msg[0].virq);
> > goto err_doorbell_cleanup;
> > }
> > + epf_test->db_irq = epf->db_msg[0].virq;
> >
> > reg->doorbell_data = cpu_to_le32(msg->data);
> > reg->doorbell_bar = cpu_to_le32(bar);
> > @@ -782,6 +836,125 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> > reg->status = cpu_to_le32(status);
> > }
> >
> > +static void pci_epf_test_enable_doorbell_embedded(struct pci_epf_test *epf_test,
> > + struct pci_epf_test_reg *reg)
> > +{
> > + struct pci_epc_remote_resource *dma_ctrl = NULL, *chan0 = NULL;
> > + const char *irq_name = "pci-ep-test-doorbell-embedded";
> > + u32 status = le32_to_cpu(reg->status);
> > + struct pci_epf *epf = epf_test->epf;
> > + struct pci_epc *epc = epf->epc;
> > + struct device *dev = &epf->dev;
> > + enum pci_barno bar;
> > + size_t align_off;
> > + unsigned int i;
> > + int cnt, ret;
> > + u32 db_off;
> > +
> > + cnt = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
> > + NULL, 0);
> > + if (cnt <= 0) {
> > + dev_err(dev, "No remote resources available for embedded doorbell\n");
> > + goto set_status_err;
> > + }
> > +
> > + struct pci_epc_remote_resource *resources __free(kfree) =
> > + kcalloc(cnt, sizeof(*resources), GFP_KERNEL);
> > + if (!resources)
> > + goto set_status_err;
> > +
> > + ret = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
> > + resources, cnt);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to get remote resources: %d\n", ret);
> > + goto set_status_err;
> > + }
> > + cnt = ret;
> > +
> > + for (i = 0; i < cnt; i++) {
> > + if (resources[i].type == PCI_EPC_RR_DMA_CTRL_MMIO)
> > + dma_ctrl = &resources[i];
> > + else if (resources[i].type == PCI_EPC_RR_DMA_CHAN_DESC &&
> > + !chan0)
> > + chan0 = &resources[i];
> > + }
> > +
> > + if (!dma_ctrl || !chan0) {
> > + dev_err(dev, "Missing DMA ctrl MMIO or channel #0 info\n");
> > + goto set_status_err;
> > + }
> > +
> > + bar = pci_epc_get_next_free_bar(epf_test->epc_features,
> > + epf_test->test_reg_bar + 1);
> > + if (bar < BAR_0) {
> > + dev_err(dev, "No free BAR for embedded doorbell\n");
> > + goto set_status_err;
> > + }
> > +
> > + ret = pci_epf_align_inbound_addr(epf, bar, dma_ctrl->phys_addr,
> > + &epf_test->db_bar.phys_addr,
> > + &align_off);
> > + if (ret)
> > + goto set_status_err;
> > +
> > + db_off = chan0->u.dma_chan_desc.db_offset;
> > + if (db_off >= dma_ctrl->size ||
> > + align_off + db_off >= epf->bar[bar].size) {
> > + dev_err(dev, "BAR%d too small for embedded doorbell (off %#zx + %#x)\n",
> > + bar, align_off, db_off);
> > + goto set_status_err;
> > + }
> > +
> > + epf_test->db_variant = PCI_EPF_TEST_DB_EMBEDDED;
> > +
> > + ret = request_irq(chan0->u.dma_chan_desc.irq,
> > + pci_epf_test_doorbell_embedded_irq_handler,
> > + IRQF_SHARED, irq_name, epf_test);
> > + if (ret) {
> > + dev_err(dev, "Failed to request embedded doorbell IRQ: %d\n",
> > + chan0->u.dma_chan_desc.irq);
> > + goto err_cleanup;
> > + }
> > + epf_test->db_irq = chan0->u.dma_chan_desc.irq;
> > +
> > + reg->doorbell_data = cpu_to_le32(0);
> > + reg->doorbell_bar = cpu_to_le32(bar);
> > + reg->doorbell_offset = cpu_to_le32(align_off + db_off);
> > +
> > + epf_test->db_bar.barno = bar;
> > + epf_test->db_bar.size = epf->bar[bar].size;
> > + epf_test->db_bar.flags = epf->bar[bar].flags;
> > +
> > + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
> > + if (ret)
> > + goto err_cleanup;
> > +
> > + status |= STATUS_DOORBELL_ENABLE_SUCCESS;
> > + reg->status = cpu_to_le32(status);
> > + return;
> > +
> > +err_cleanup:
> > + pci_epf_test_doorbell_cleanup(epf_test);
> > +set_status_err:
> > + status |= STATUS_DOORBELL_ENABLE_FAIL;
> > + reg->status = cpu_to_le32(status);
> > +}
> > +
> > +static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
> > + struct pci_epf_test_reg *reg)
> > +{
> > + u32 flags = le32_to_cpu(reg->flags);
> > +
> > + /* If already enabled, drop previous setup first. */
> > + if (epf_test->db_variant != PCI_EPF_TEST_DB_NONE)
> > + pci_epf_test_doorbell_cleanup(epf_test);
> > +
> > + if (flags & FLAG_DB_EMBEDDED)
> > + pci_epf_test_enable_doorbell_embedded(epf_test, reg);
> > + else
> > + pci_epf_test_enable_doorbell_msi(epf_test, reg);
> > +}
> > +
> > static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test,
> > struct pci_epf_test_reg *reg)
> > {
> > @@ -1309,6 +1482,9 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
> >
> > cancel_delayed_work_sync(&epf_test->cmd_handler);
> > if (epc->init_complete) {
> > + /* In case userspace never disabled doorbell explicitly. */
> > + if (epf_test->db_variant != PCI_EPF_TEST_DB_NONE)
> > + pci_epf_test_doorbell_cleanup(epf_test);
> > pci_epf_test_clean_dma_chan(epf_test);
> > pci_epf_test_clear_bar(epf);
> > }
> > @@ -1427,6 +1603,7 @@ static int pci_epf_test_probe(struct pci_epf *epf,
> > epf_test->bar_size[bar] = default_bar_size[bar];
> >
> > INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);
> > + INIT_WORK(&epf_test->db_work, pci_epf_test_doorbell_embedded_work);
> >
> > epf->event_ops = &pci_epf_test_event_ops;
> >
> > --
> > 2.51.0
> >
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant
2026-02-06 17:26 ` [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant Koichiro Den
2026-02-06 19:20 ` Frank Li
@ 2026-02-08 12:35 ` kernel test robot
1 sibling, 0 replies; 24+ messages in thread
From: kernel test robot @ 2026-02-08 12:35 UTC (permalink / raw)
To: Koichiro Den, vkoul, mani, Frank.Li, jingoohan1, lpieralisi,
kwilczynski, robh, bhelgaas
Cc: llvm, oe-kbuild-all, dmaengine, linux-pci, linux-kselftest,
linux-kernel
Hi Koichiro,
kernel test robot noticed the following build errors:
[auto build test ERROR on pci/next]
[also build test ERROR on next-20260205]
[cannot apply to vkoul-dmaengine/next pci/for-linus linus/master v6.19-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Koichiro-Den/dmaengine-dw-edma-Add-per-channel-interrupt-routing-control/20260207-013042
base: https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git next
patch link: https://lore.kernel.org/r/20260206172646.1556847-8-den%40valinux.co.jp
patch subject: [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant
config: um-randconfig-002-20260208 (https://download.01.org/0day-ci/archive/20260208/202602082040.oSU5mO1p-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260208/202602082040.oSU5mO1p-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602082040.oSU5mO1p-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from drivers/pci/endpoint/functions/pci-epf-test.c:12:
In file included from include/linux/dmaengine.h:12:
In file included from include/linux/scatterlist.h:9:
In file included from arch/um/include/asm/io.h:24:
include/asm-generic/io.h:1209:55: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
1209 | return (port > MMIO_UPPER_LIMIT) ? NULL : PCI_IOBASE + port;
| ~~~~~~~~~~ ^
>> drivers/pci/endpoint/functions/pci-epf-test.c:858:3: error: cannot jump from this goto statement to its label
858 | goto set_status_err;
| ^
drivers/pci/endpoint/functions/pci-epf-test.c:861:34: note: jump bypasses initialization of variable with __attribute__((cleanup))
861 | struct pci_epc_remote_resource *resources __free(kfree) =
| ^
1 warning and 1 error generated.
vim +858 drivers/pci/endpoint/functions/pci-epf-test.c
838
839 static void pci_epf_test_enable_doorbell_embedded(struct pci_epf_test *epf_test,
840 struct pci_epf_test_reg *reg)
841 {
842 struct pci_epc_remote_resource *dma_ctrl = NULL, *chan0 = NULL;
843 const char *irq_name = "pci-ep-test-doorbell-embedded";
844 u32 status = le32_to_cpu(reg->status);
845 struct pci_epf *epf = epf_test->epf;
846 struct pci_epc *epc = epf->epc;
847 struct device *dev = &epf->dev;
848 enum pci_barno bar;
849 size_t align_off;
850 unsigned int i;
851 int cnt, ret;
852 u32 db_off;
853
854 cnt = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
855 NULL, 0);
856 if (cnt <= 0) {
857 dev_err(dev, "No remote resources available for embedded doorbell\n");
> 858 goto set_status_err;
859 }
860
861 struct pci_epc_remote_resource *resources __free(kfree) =
862 kcalloc(cnt, sizeof(*resources), GFP_KERNEL);
863 if (!resources)
864 goto set_status_err;
865
866 ret = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
867 resources, cnt);
868 if (ret < 0) {
869 dev_err(dev, "Failed to get remote resources: %d\n", ret);
870 goto set_status_err;
871 }
872 cnt = ret;
873
874 for (i = 0; i < cnt; i++) {
875 if (resources[i].type == PCI_EPC_RR_DMA_CTRL_MMIO)
876 dma_ctrl = &resources[i];
877 else if (resources[i].type == PCI_EPC_RR_DMA_CHAN_DESC &&
878 !chan0)
879 chan0 = &resources[i];
880 }
881
882 if (!dma_ctrl || !chan0) {
883 dev_err(dev, "Missing DMA ctrl MMIO or channel #0 info\n");
884 goto set_status_err;
885 }
886
887 bar = pci_epc_get_next_free_bar(epf_test->epc_features,
888 epf_test->test_reg_bar + 1);
889 if (bar < BAR_0) {
890 dev_err(dev, "No free BAR for embedded doorbell\n");
891 goto set_status_err;
892 }
893
894 ret = pci_epf_align_inbound_addr(epf, bar, dma_ctrl->phys_addr,
895 &epf_test->db_bar.phys_addr,
896 &align_off);
897 if (ret)
898 goto set_status_err;
899
900 db_off = chan0->u.dma_chan_desc.db_offset;
901 if (db_off >= dma_ctrl->size ||
902 align_off + db_off >= epf->bar[bar].size) {
903 dev_err(dev, "BAR%d too small for embedded doorbell (off %#zx + %#x)\n",
904 bar, align_off, db_off);
905 goto set_status_err;
906 }
907
908 epf_test->db_variant = PCI_EPF_TEST_DB_EMBEDDED;
909
910 ret = request_irq(chan0->u.dma_chan_desc.irq,
911 pci_epf_test_doorbell_embedded_irq_handler,
912 IRQF_SHARED, irq_name, epf_test);
913 if (ret) {
914 dev_err(dev, "Failed to request embedded doorbell IRQ: %d\n",
915 chan0->u.dma_chan_desc.irq);
916 goto err_cleanup;
917 }
918 epf_test->db_irq = chan0->u.dma_chan_desc.irq;
919
920 reg->doorbell_data = cpu_to_le32(0);
921 reg->doorbell_bar = cpu_to_le32(bar);
922 reg->doorbell_offset = cpu_to_le32(align_off + db_off);
923
924 epf_test->db_bar.barno = bar;
925 epf_test->db_bar.size = epf->bar[bar].size;
926 epf_test->db_bar.flags = epf->bar[bar].flags;
927
928 ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
929 if (ret)
930 goto err_cleanup;
931
932 status |= STATUS_DOORBELL_ENABLE_SUCCESS;
933 reg->status = cpu_to_le32(status);
934 return;
935
936 err_cleanup:
937 pci_epf_test_doorbell_cleanup(epf_test);
938 set_status_err:
939 status |= STATUS_DOORBELL_ENABLE_FAIL;
940 reg->status = cpu_to_le32(status);
941 }
942
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v4 8/9] misc: pci_endpoint_test: Allow selecting embedded doorbell
2026-02-06 17:26 [PATCH v4 0/9] dmaengine, PCI: endpoint: Enable remote use of integrated DesignWare eDMA Koichiro Den
` (6 preceding siblings ...)
2026-02-06 17:26 ` [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant Koichiro Den
@ 2026-02-06 17:26 ` Koichiro Den
2026-02-06 17:26 ` [PATCH v4 9/9] selftests: pci_endpoint: Exercise MSI and " Koichiro Den
8 siblings, 0 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-06 17:26 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas
Cc: dmaengine, linux-pci, linux-kselftest, linux-kernel
Allow users to request the embedded doorbell variant via the
PCITEST_DOORBELL ioctl argument.
If the argument requests embedded doorbell, program the endpoint-test
FLAGS register accordingly before enabling doorbell. Otherwise keep the
existing MSI doorbell behaviour.
This is used by selftests to exercise both doorbell implementations.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/misc/pci_endpoint_test.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 74ab5b5b9011..e484bd47c7fe 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -77,6 +77,7 @@
#define PCI_ENDPOINT_TEST_FLAGS 0x2c
#define FLAG_USE_DMA BIT(0)
+#define FLAG_DB_EMBEDDED BIT(1)
#define PCI_ENDPOINT_TEST_CAPS 0x30
#define CAP_UNALIGNED_ACCESS BIT(0)
@@ -1050,13 +1051,15 @@ static int pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
return 0;
}
-static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
+static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test,
+ unsigned long arg)
{
struct pci_dev *pdev = test->pdev;
struct device *dev = &pdev->dev;
int irq_type = test->irq_type;
enum pci_barno bar;
u32 data, status;
+ u32 flags = 0;
u32 addr;
int left;
@@ -1066,8 +1069,12 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
return -EINVAL;
}
+ if (arg)
+ flags |= FLAG_DB_EMBEDDED;
+
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
COMMAND_ENABLE_DOORBELL);
@@ -1173,7 +1180,7 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
ret = pci_endpoint_test_clear_irq(test);
break;
case PCITEST_DOORBELL:
- ret = pci_endpoint_test_doorbell(test);
+ ret = pci_endpoint_test_doorbell(test, arg);
break;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH v4 9/9] selftests: pci_endpoint: Exercise MSI and embedded doorbell
2026-02-06 17:26 [PATCH v4 0/9] dmaengine, PCI: endpoint: Enable remote use of integrated DesignWare eDMA Koichiro Den
` (7 preceding siblings ...)
2026-02-06 17:26 ` [PATCH v4 8/9] misc: pci_endpoint_test: Allow selecting embedded doorbell Koichiro Den
@ 2026-02-06 17:26 ` Koichiro Den
8 siblings, 0 replies; 24+ messages in thread
From: Koichiro Den @ 2026-02-06 17:26 UTC (permalink / raw)
To: vkoul, mani, Frank.Li, jingoohan1, lpieralisi, kwilczynski, robh,
bhelgaas
Cc: dmaengine, linux-pci, linux-kselftest, linux-kernel
Extend the pci_endpoint_test doorbell selftest to run in two variants:
- default MSI doorbell,
- embedded doorbell (requested via the PCITEST_DOORBELL ioctl
argument).
This improves coverage of EPC backends that implement embedded doorbell
support.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
.../selftests/pci_endpoint/pci_endpoint_test.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c
index eecb776c33af..b3c79fe3b828 100644
--- a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c
+++ b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c
@@ -268,6 +268,21 @@ FIXTURE_TEARDOWN(pcie_ep_doorbell)
close(self->fd);
};
+FIXTURE_VARIANT(pcie_ep_doorbell)
+{
+ bool use_embedded_db;
+};
+
+FIXTURE_VARIANT_ADD(pcie_ep_doorbell, msi)
+{
+ .use_embedded_db = false,
+};
+
+FIXTURE_VARIANT_ADD(pcie_ep_doorbell, embedded)
+{
+ .use_embedded_db = true,
+};
+
TEST_F(pcie_ep_doorbell, DOORBELL_TEST)
{
int ret;
@@ -275,7 +290,7 @@ TEST_F(pcie_ep_doorbell, DOORBELL_TEST)
pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_AUTO);
ASSERT_EQ(0, ret) TH_LOG("Can't set AUTO IRQ type");
- pci_ep_ioctl(PCITEST_DOORBELL, 0);
+ pci_ep_ioctl(PCITEST_DOORBELL, variant->use_embedded_db);
EXPECT_FALSE(ret) TH_LOG("Test failed for Doorbell\n");
}
TEST_HARNESS_MAIN
--
2.51.0
^ permalink raw reply related [flat|nested] 24+ messages in thread