public inbox for dmaengine@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support
@ 2026-02-15 15:22 Koichiro Den
  2026-02-15 15:22 ` [PATCH 1/2] dmaengine: dw-edma: Add interrupt-emulation hooks Koichiro Den
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Koichiro Den @ 2026-02-15 15:22 UTC (permalink / raw)
  To: mani, vkoul, Frank.Li; +Cc: dmaengine, linux-kernel

Hi,

Some DesignWare eDMA instances support "interrupt emulation", where a
software write can assert the IRQ line without setting the normal
DONE/ABORT status bits.

In the current mainline, on implementations that support interrupt
emulation, writing once to DMA_{WRITE,READ}_INT_STATUS_OFF is sufficient
to leave the level-triggered IRQ line asserted. Since the shared dw-edma
IRQ handlers only look at DONE/ABORT bits and do not perform any
deassertion sequence for interrupt emulation, the IRQ remains asserted
and is eventually disabled by the generic IRQ layer:

  $ sudo devmem2 0xe65d50a0 w 0

  [   47.189557] irq 48: nobody cared (try booting with the "irqpoll" option)
  ...
  [   47.190383] handlers:
  [   47.199837] [<00000000a5ecb36e>] dw_edma_interrupt_common
  [   47.200214] Disabling IRQ #48

In other words, a single interrupt-emulation write can leave the IRQ
line stuck asserted and render the DMA engine unusable until reboot.

This series fixes the problem by:

  - adding a core hook to deassert an emulated interrupt
  - wiring a requestable Linux virtual IRQ whose .irq_ack performs the
    deassert sequence
  - raising that virtual IRQ from the dw-edma IRQ path to ensure the
    deassert sequence is always executed

This makes interrupt emulation safe and also enables platform users to
expose it as a doorbell via the exported db_irq and db_offset.

This is a spin-off from:
https://lore.kernel.org/linux-pci/20260209125316.2132589-1-den@valinux.co.jp/

Based on dmaengine.git next branch latest:
Commit ab736ed52e34 ("dmaengine: add Frank Li as reviewer")

Thanks for reviewing,


Koichiro Den (2):
  dmaengine: dw-edma: Add interrupt-emulation hooks
  dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells

 drivers/dma/dw-edma/dw-edma-core.c    | 127 +++++++++++++++++++++++++-
 drivers/dma/dw-edma/dw-edma-core.h    |  17 ++++
 drivers/dma/dw-edma/dw-edma-v0-core.c |  21 +++++
 drivers/dma/dw-edma/dw-hdma-v0-core.c |   7 ++
 include/linux/dma/edma.h              |   6 ++
 5 files changed, 173 insertions(+), 5 deletions(-)

-- 
2.51.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/2] dmaengine: dw-edma: Add interrupt-emulation hooks
  2026-02-15 15:22 [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support Koichiro Den
@ 2026-02-15 15:22 ` Koichiro Den
  2026-02-16 16:38   ` Frank Li
  2026-03-24  2:10   ` Koichiro Den
  2026-02-15 15:22 ` [PATCH 2/2] dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells Koichiro Den
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 9+ messages in thread
From: Koichiro Den @ 2026-02-15 15:22 UTC (permalink / raw)
  To: mani, vkoul, Frank.Li; +Cc: dmaengine, linux-kernel

DesignWare eDMA instances support "interrupt emulation", where a
software write can assert the IRQ line without setting the normal
DONE/ABORT status bits.

Introduce core callbacks needed to support this feature:

  - .ack_emulated_irq(): core-specific sequence to deassert an emulated
    IRQ
  - .db_offset(): offset from the DMA register base that is suitable as a
    host-writable doorbell target for interrupt emulation

Implement both hooks for the v0 register map. For dw-hdma-v0, provide a
stub .db_offset() returning ~0 until the correct offset is known.

The next patch wires these hooks into the dw-edma IRQ path and exports
the doorbell resources to platform users.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
 drivers/dma/dw-edma/dw-edma-core.h    | 17 +++++++++++++++++
 drivers/dma/dw-edma/dw-edma-v0-core.c | 21 +++++++++++++++++++++
 drivers/dma/dw-edma/dw-hdma-v0-core.c |  7 +++++++
 3 files changed, 45 insertions(+)

diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 71894b9e0b15..59b24973fa7d 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -126,6 +126,8 @@ struct dw_edma_core_ops {
 	void (*start)(struct dw_edma_chunk *chunk, bool first);
 	void (*ch_config)(struct dw_edma_chan *chan);
 	void (*debugfs_on)(struct dw_edma *dw);
+	void (*ack_emulated_irq)(struct dw_edma *dw);
+	resource_size_t (*db_offset)(struct dw_edma *dw);
 };
 
 struct dw_edma_sg {
@@ -206,4 +208,19 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
 	dw->core->debugfs_on(dw);
 }
 
+static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw)
+{
+	if (!dw->core->ack_emulated_irq)
+		return -EOPNOTSUPP;
+
+	dw->core->ack_emulated_irq(dw);
+	return 0;
+}
+
+static inline resource_size_t
+dw_edma_core_db_offset(struct dw_edma *dw)
+{
+	return dw->core->db_offset(dw);
+}
+
 #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..69e8279adec8 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -509,6 +509,25 @@ static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
 	dw_edma_v0_debugfs_on(dw);
 }
 
+static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw)
+{
+	/*
+	 * Interrupt emulation may assert the IRQ without setting
+	 * DONE/ABORT status bits. A zero write to INT_CLEAR deasserts the
+	 * emulated IRQ, while being a no-op for real interrupts.
+	 */
+	SET_BOTH_32(dw, int_clear, 0);
+}
+
+static resource_size_t dw_edma_v0_core_db_offset(struct dw_edma *dw)
+{
+	/*
+	 * rd_int_status is chosen arbitrarily, but wr_int_status would be
+	 * equally suitable.
+	 */
+	return 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,
@@ -517,6 +536,8 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
 	.start = dw_edma_v0_core_start,
 	.ch_config = dw_edma_v0_core_ch_config,
 	.debugfs_on = dw_edma_v0_core_debugfs_on,
+	.ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq,
+	.db_offset = dw_edma_v0_core_db_offset,
 };
 
 void dw_edma_v0_core_register(struct dw_edma *dw)
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index e3f8db4fe909..1ae8e44f0a67 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -283,6 +283,12 @@ static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
 	dw_hdma_v0_debugfs_on(dw);
 }
 
+static resource_size_t dw_hdma_v0_core_db_offset(struct dw_edma *dw)
+{
+	/* Implement once the correct offset is known. */
+	return ~0;
+}
+
 static const struct dw_edma_core_ops dw_hdma_v0_core = {
 	.off = dw_hdma_v0_core_off,
 	.ch_count = dw_hdma_v0_core_ch_count,
@@ -291,6 +297,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
 	.start = dw_hdma_v0_core_start,
 	.ch_config = dw_hdma_v0_core_ch_config,
 	.debugfs_on = dw_hdma_v0_core_debugfs_on,
+	.db_offset = dw_hdma_v0_core_db_offset,
 };
 
 void dw_hdma_v0_core_register(struct dw_edma *dw)
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/2] dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells
  2026-02-15 15:22 [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support Koichiro Den
  2026-02-15 15:22 ` [PATCH 1/2] dmaengine: dw-edma: Add interrupt-emulation hooks Koichiro Den
@ 2026-02-15 15:22 ` Koichiro Den
  2026-02-16 16:38   ` Frank Li
  2026-02-18 16:19 ` [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support Koichiro Den
  2026-02-25 11:24 ` Vinod Koul
  3 siblings, 1 reply; 9+ messages in thread
From: Koichiro Den @ 2026-02-15 15:22 UTC (permalink / raw)
  To: mani, vkoul, Frank.Li; +Cc: dmaengine, linux-kernel

Interrupt emulation can assert the dw-edma IRQ line without updating the
DONE/ABORT bits. With the shared read/write/common IRQ handlers, the
driver cannot reliably distinguish such an emulated interrupt from a
real one and leaving a level IRQ asserted may wedge the line.

Allocate a dedicated, requestable Linux virtual IRQ (db_irq) for
interrupt emulation and attach an irq_chip whose .irq_ack runs the
core-specific deassert sequence (.ack_emulated_irq()). The physical
dw-edma interrupt handlers raise this virtual IRQ via
generic_handle_irq(), ensuring emulated IRQs are always deasserted.

Export the virtual IRQ number (db_irq) and the doorbell register offset
(db_offset) via struct dw_edma_chip so platform users can expose
interrupt emulation as a doorbell.

Without this, a single interrupt-emulation write can leave the level IRQ
line asserted and cause the generic IRQ layer to disable it.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
 drivers/dma/dw-edma/dw-edma-core.c | 127 +++++++++++++++++++++++++++--
 include/linux/dma/edma.h           |   6 ++
 2 files changed, 128 insertions(+), 5 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 8e5f7defa6b6..51c1ea99c584 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -663,7 +663,96 @@ 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 void dw_edma_emul_irq_ack(struct irq_data *d)
+{
+	struct dw_edma *dw = irq_data_get_irq_chip_data(d);
+
+	dw_edma_core_ack_emulated_irq(dw);
+}
+
+/*
+ * irq_chip implementation for interrupt-emulation doorbells.
+ *
+ * The emulated source has no mask/unmask mechanism. With handle_level_irq(),
+ * the flow is therefore:
+ *   1) .irq_ack() deasserts the source
+ *   2) registered handlers (if any) are dispatched
+ * Since deassertion is already done in .irq_ack(), handlers do not need to take
+ * care of it, hence IRQCHIP_ONESHOT_SAFE.
+ */
+static struct irq_chip dw_edma_emul_irqchip = {
+	.name		= "dw-edma-emul",
+	.irq_ack	= dw_edma_emul_irq_ack,
+	.flags		= IRQCHIP_ONESHOT_SAFE | IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int dw_edma_emul_irq_alloc(struct dw_edma *dw)
+{
+	struct dw_edma_chip *chip = dw->chip;
+	int virq;
+
+	chip->db_irq = 0;
+	chip->db_offset = ~0;
+
+	/*
+	 * Only meaningful when the core provides the deassert sequence
+	 * for interrupt emulation.
+	 */
+	if (!dw->core->ack_emulated_irq)
+		return 0;
+
+	/*
+	 * Allocate a single, requestable Linux virtual IRQ number.
+	 * Use >= 1 so that 0 can remain a "not available" sentinel.
+	 */
+	virq = irq_alloc_desc(NUMA_NO_NODE);
+	if (virq < 0)
+		return virq;
+
+	irq_set_chip_and_handler(virq, &dw_edma_emul_irqchip, handle_level_irq);
+	irq_set_chip_data(virq, dw);
+	irq_set_noprobe(virq);
+
+	chip->db_irq = virq;
+	chip->db_offset = dw_edma_core_db_offset(dw);
+
+	return 0;
+}
+
+static void dw_edma_emul_irq_free(struct dw_edma *dw)
+{
+	struct dw_edma_chip *chip = dw->chip;
+
+	if (!chip)
+		return;
+	if (chip->db_irq <= 0)
+		return;
+
+	irq_free_descs(chip->db_irq, 1);
+	chip->db_irq = 0;
+	chip->db_offset = ~0;
+}
+
+static inline irqreturn_t dw_edma_interrupt_emulated(void *data)
+{
+	struct dw_edma_irq *dw_irq = data;
+	struct dw_edma *dw = dw_irq->dw;
+	int db_irq = dw->chip->db_irq;
+
+	if (db_irq > 0) {
+		/*
+		 * 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.
+		 */
+		generic_handle_irq(db_irq);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
+static inline irqreturn_t dw_edma_interrupt_write_inner(int irq, void *data)
 {
 	struct dw_edma_irq *dw_irq = data;
 
@@ -672,7 +761,7 @@ static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
 				       dw_edma_abort_interrupt);
 }
 
-static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
+static inline irqreturn_t dw_edma_interrupt_read_inner(int irq, void *data)
 {
 	struct dw_edma_irq *dw_irq = data;
 
@@ -681,12 +770,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_write(irq, data);
-	ret |= dw_edma_interrupt_read(irq, data);
+	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_inner(irq, data);
+	ret |= dw_edma_interrupt_read_inner(irq, data);
+	ret |= dw_edma_interrupt_emulated(data);
 
 	return ret;
 }
@@ -973,6 +1083,11 @@ int dw_edma_probe(struct dw_edma_chip *chip)
 	if (err)
 		return err;
 
+	/* Allocate a dedicated virtual IRQ for interrupt-emulation doorbells */
+	err = dw_edma_emul_irq_alloc(dw);
+	if (err)
+		dev_warn(dev, "Failed to allocate emulation IRQ: %d\n", err);
+
 	/* Setup write/read channels */
 	err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc);
 	if (err)
@@ -988,6 +1103,7 @@ int dw_edma_probe(struct dw_edma_chip *chip)
 err_irq_free:
 	for (i = (dw->nr_irqs - 1); i >= 0; i--)
 		free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
+	dw_edma_emul_irq_free(dw);
 
 	return err;
 }
@@ -1010,6 +1126,7 @@ int dw_edma_remove(struct dw_edma_chip *chip)
 	/* Free irqs */
 	for (i = (dw->nr_irqs - 1); i >= 0; i--)
 		free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
+	dw_edma_emul_irq_free(dw);
 
 	/* Deregister eDMA device */
 	dma_async_device_unregister(&dw->dma);
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 270b5458aecf..9da53c75e49b 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -73,6 +73,8 @@ enum dw_edma_chip_flags {
  * @ll_region_rd:	 DMA descriptor link list memory for read channel
  * @dt_region_wr:	 DMA data memory for write channel
  * @dt_region_rd:	 DMA data memory for read channel
+ * @db_irq:		 Virtual IRQ dedicated to interrupt emulation
+ * @db_offset:		 Offset from DMA register base
  * @mf:			 DMA register map format
  * @dw:			 struct dw_edma that is filled by dw_edma_probe()
  */
@@ -94,6 +96,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];
 
+	/* interrupt emulation */
+	int			db_irq;
+	resource_size_t		db_offset;
+
 	enum dw_edma_map_format	mf;
 
 	struct dw_edma		*dw;
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2] dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells
  2026-02-15 15:22 ` [PATCH 2/2] dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells Koichiro Den
@ 2026-02-16 16:38   ` Frank Li
  0 siblings, 0 replies; 9+ messages in thread
From: Frank Li @ 2026-02-16 16:38 UTC (permalink / raw)
  To: Koichiro Den, Thomas Gleixner
  Cc: mani, vkoul, Frank.Li, dmaengine, linux-kernel

On Mon, Feb 16, 2026 at 12:22:16AM +0900, Koichiro Den wrote:
> Interrupt emulation can assert the dw-edma IRQ line without updating the
> DONE/ABORT bits. With the shared read/write/common IRQ handlers, the
> driver cannot reliably distinguish such an emulated interrupt from a
> real one and leaving a level IRQ asserted may wedge the line.
>
> Allocate a dedicated, requestable Linux virtual IRQ (db_irq) for
> interrupt emulation and attach an irq_chip whose .irq_ack runs the
> core-specific deassert sequence (.ack_emulated_irq()). The physical
> dw-edma interrupt handlers raise this virtual IRQ via
> generic_handle_irq(), ensuring emulated IRQs are always deasserted.
>
> Export the virtual IRQ number (db_irq) and the doorbell register offset
> (db_offset) via struct dw_edma_chip so platform users can expose
> interrupt emulation as a doorbell.
>
> Without this, a single interrupt-emulation write can leave the level IRQ
> line asserted and cause the generic IRQ layer to disable it.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---

I think it is good. Add Thomas Gleixner for irq part to do double check.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
>  drivers/dma/dw-edma/dw-edma-core.c | 127 +++++++++++++++++++++++++++--
>  include/linux/dma/edma.h           |   6 ++
>  2 files changed, 128 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index 8e5f7defa6b6..51c1ea99c584 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -663,7 +663,96 @@ 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 void dw_edma_emul_irq_ack(struct irq_data *d)
> +{
> +	struct dw_edma *dw = irq_data_get_irq_chip_data(d);
> +
> +	dw_edma_core_ack_emulated_irq(dw);
> +}
> +
> +/*
> + * irq_chip implementation for interrupt-emulation doorbells.
> + *
> + * The emulated source has no mask/unmask mechanism. With handle_level_irq(),
> + * the flow is therefore:
> + *   1) .irq_ack() deasserts the source
> + *   2) registered handlers (if any) are dispatched
> + * Since deassertion is already done in .irq_ack(), handlers do not need to take
> + * care of it, hence IRQCHIP_ONESHOT_SAFE.
> + */
> +static struct irq_chip dw_edma_emul_irqchip = {
> +	.name		= "dw-edma-emul",
> +	.irq_ack	= dw_edma_emul_irq_ack,
> +	.flags		= IRQCHIP_ONESHOT_SAFE | IRQCHIP_SKIP_SET_WAKE,
> +};
> +
> +static int dw_edma_emul_irq_alloc(struct dw_edma *dw)
> +{
> +	struct dw_edma_chip *chip = dw->chip;
> +	int virq;
> +
> +	chip->db_irq = 0;
> +	chip->db_offset = ~0;
> +
> +	/*
> +	 * Only meaningful when the core provides the deassert sequence
> +	 * for interrupt emulation.
> +	 */
> +	if (!dw->core->ack_emulated_irq)
> +		return 0;
> +
> +	/*
> +	 * Allocate a single, requestable Linux virtual IRQ number.
> +	 * Use >= 1 so that 0 can remain a "not available" sentinel.
> +	 */
> +	virq = irq_alloc_desc(NUMA_NO_NODE);
> +	if (virq < 0)
> +		return virq;
> +
> +	irq_set_chip_and_handler(virq, &dw_edma_emul_irqchip, handle_level_irq);
> +	irq_set_chip_data(virq, dw);
> +	irq_set_noprobe(virq);
> +
> +	chip->db_irq = virq;
> +	chip->db_offset = dw_edma_core_db_offset(dw);
> +
> +	return 0;
> +}
> +
> +static void dw_edma_emul_irq_free(struct dw_edma *dw)
> +{
> +	struct dw_edma_chip *chip = dw->chip;
> +
> +	if (!chip)
> +		return;
> +	if (chip->db_irq <= 0)
> +		return;
> +
> +	irq_free_descs(chip->db_irq, 1);
> +	chip->db_irq = 0;
> +	chip->db_offset = ~0;
> +}
> +
> +static inline irqreturn_t dw_edma_interrupt_emulated(void *data)
> +{
> +	struct dw_edma_irq *dw_irq = data;
> +	struct dw_edma *dw = dw_irq->dw;
> +	int db_irq = dw->chip->db_irq;
> +
> +	if (db_irq > 0) {
> +		/*
> +		 * 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.
> +		 */
> +		generic_handle_irq(db_irq);
> +		return IRQ_HANDLED;
> +	}
> +	return IRQ_NONE;
> +}
> +
> +static inline irqreturn_t dw_edma_interrupt_write_inner(int irq, void *data)
>  {
>  	struct dw_edma_irq *dw_irq = data;
>
> @@ -672,7 +761,7 @@ static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
>  				       dw_edma_abort_interrupt);
>  }
>
> -static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
> +static inline irqreturn_t dw_edma_interrupt_read_inner(int irq, void *data)
>  {
>  	struct dw_edma_irq *dw_irq = data;
>
> @@ -681,12 +770,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_write(irq, data);
> -	ret |= dw_edma_interrupt_read(irq, data);
> +	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_inner(irq, data);
> +	ret |= dw_edma_interrupt_read_inner(irq, data);
> +	ret |= dw_edma_interrupt_emulated(data);
>
>  	return ret;
>  }
> @@ -973,6 +1083,11 @@ int dw_edma_probe(struct dw_edma_chip *chip)
>  	if (err)
>  		return err;
>
> +	/* Allocate a dedicated virtual IRQ for interrupt-emulation doorbells */
> +	err = dw_edma_emul_irq_alloc(dw);
> +	if (err)
> +		dev_warn(dev, "Failed to allocate emulation IRQ: %d\n", err);
> +
>  	/* Setup write/read channels */
>  	err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc);
>  	if (err)
> @@ -988,6 +1103,7 @@ int dw_edma_probe(struct dw_edma_chip *chip)
>  err_irq_free:
>  	for (i = (dw->nr_irqs - 1); i >= 0; i--)
>  		free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
> +	dw_edma_emul_irq_free(dw);
>
>  	return err;
>  }
> @@ -1010,6 +1126,7 @@ int dw_edma_remove(struct dw_edma_chip *chip)
>  	/* Free irqs */
>  	for (i = (dw->nr_irqs - 1); i >= 0; i--)
>  		free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
> +	dw_edma_emul_irq_free(dw);
>
>  	/* Deregister eDMA device */
>  	dma_async_device_unregister(&dw->dma);
> diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> index 270b5458aecf..9da53c75e49b 100644
> --- a/include/linux/dma/edma.h
> +++ b/include/linux/dma/edma.h
> @@ -73,6 +73,8 @@ enum dw_edma_chip_flags {
>   * @ll_region_rd:	 DMA descriptor link list memory for read channel
>   * @dt_region_wr:	 DMA data memory for write channel
>   * @dt_region_rd:	 DMA data memory for read channel
> + * @db_irq:		 Virtual IRQ dedicated to interrupt emulation
> + * @db_offset:		 Offset from DMA register base
>   * @mf:			 DMA register map format
>   * @dw:			 struct dw_edma that is filled by dw_edma_probe()
>   */
> @@ -94,6 +96,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];
>
> +	/* interrupt emulation */
> +	int			db_irq;
> +	resource_size_t		db_offset;
> +
>  	enum dw_edma_map_format	mf;
>
>  	struct dw_edma		*dw;
> --
> 2.51.0
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] dmaengine: dw-edma: Add interrupt-emulation hooks
  2026-02-15 15:22 ` [PATCH 1/2] dmaengine: dw-edma: Add interrupt-emulation hooks Koichiro Den
@ 2026-02-16 16:38   ` Frank Li
  2026-03-24  2:10   ` Koichiro Den
  1 sibling, 0 replies; 9+ messages in thread
From: Frank Li @ 2026-02-16 16:38 UTC (permalink / raw)
  To: Koichiro Den; +Cc: mani, vkoul, Frank.Li, dmaengine, linux-kernel

On Mon, Feb 16, 2026 at 12:22:15AM +0900, Koichiro Den wrote:
> DesignWare eDMA instances support "interrupt emulation", where a
> software write can assert the IRQ line without setting the normal
> DONE/ABORT status bits.
>
> Introduce core callbacks needed to support this feature:
>
>   - .ack_emulated_irq(): core-specific sequence to deassert an emulated
>     IRQ
>   - .db_offset(): offset from the DMA register base that is suitable as a
>     host-writable doorbell target for interrupt emulation
>
> Implement both hooks for the v0 register map. For dw-hdma-v0, provide a
> stub .db_offset() returning ~0 until the correct offset is known.
>
> The next patch wires these hooks into the dw-edma IRQ path and exports
> the doorbell resources to platform users.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>
>  drivers/dma/dw-edma/dw-edma-core.h    | 17 +++++++++++++++++
>  drivers/dma/dw-edma/dw-edma-v0-core.c | 21 +++++++++++++++++++++
>  drivers/dma/dw-edma/dw-hdma-v0-core.c |  7 +++++++
>  3 files changed, 45 insertions(+)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> index 71894b9e0b15..59b24973fa7d 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-core.h
> @@ -126,6 +126,8 @@ struct dw_edma_core_ops {
>  	void (*start)(struct dw_edma_chunk *chunk, bool first);
>  	void (*ch_config)(struct dw_edma_chan *chan);
>  	void (*debugfs_on)(struct dw_edma *dw);
> +	void (*ack_emulated_irq)(struct dw_edma *dw);
> +	resource_size_t (*db_offset)(struct dw_edma *dw);
>  };
>
>  struct dw_edma_sg {
> @@ -206,4 +208,19 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
>  	dw->core->debugfs_on(dw);
>  }
>
> +static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw)
> +{
> +	if (!dw->core->ack_emulated_irq)
> +		return -EOPNOTSUPP;
> +
> +	dw->core->ack_emulated_irq(dw);
> +	return 0;
> +}
> +
> +static inline resource_size_t
> +dw_edma_core_db_offset(struct dw_edma *dw)
> +{
> +	return dw->core->db_offset(dw);
> +}
> +
>  #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..69e8279adec8 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -509,6 +509,25 @@ static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
>  	dw_edma_v0_debugfs_on(dw);
>  }
>
> +static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw)
> +{
> +	/*
> +	 * Interrupt emulation may assert the IRQ without setting
> +	 * DONE/ABORT status bits. A zero write to INT_CLEAR deasserts the
> +	 * emulated IRQ, while being a no-op for real interrupts.
> +	 */
> +	SET_BOTH_32(dw, int_clear, 0);
> +}
> +
> +static resource_size_t dw_edma_v0_core_db_offset(struct dw_edma *dw)
> +{
> +	/*
> +	 * rd_int_status is chosen arbitrarily, but wr_int_status would be
> +	 * equally suitable.
> +	 */
> +	return 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,
> @@ -517,6 +536,8 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
>  	.start = dw_edma_v0_core_start,
>  	.ch_config = dw_edma_v0_core_ch_config,
>  	.debugfs_on = dw_edma_v0_core_debugfs_on,
> +	.ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq,
> +	.db_offset = dw_edma_v0_core_db_offset,
>  };
>
>  void dw_edma_v0_core_register(struct dw_edma *dw)
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> index e3f8db4fe909..1ae8e44f0a67 100644
> --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> @@ -283,6 +283,12 @@ static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
>  	dw_hdma_v0_debugfs_on(dw);
>  }
>
> +static resource_size_t dw_hdma_v0_core_db_offset(struct dw_edma *dw)
> +{
> +	/* Implement once the correct offset is known. */
> +	return ~0;
> +}
> +
>  static const struct dw_edma_core_ops dw_hdma_v0_core = {
>  	.off = dw_hdma_v0_core_off,
>  	.ch_count = dw_hdma_v0_core_ch_count,
> @@ -291,6 +297,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
>  	.start = dw_hdma_v0_core_start,
>  	.ch_config = dw_hdma_v0_core_ch_config,
>  	.debugfs_on = dw_hdma_v0_core_debugfs_on,
> +	.db_offset = dw_hdma_v0_core_db_offset,
>  };
>
>  void dw_hdma_v0_core_register(struct dw_edma *dw)
> --
> 2.51.0
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support
  2026-02-15 15:22 [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support Koichiro Den
  2026-02-15 15:22 ` [PATCH 1/2] dmaengine: dw-edma: Add interrupt-emulation hooks Koichiro Den
  2026-02-15 15:22 ` [PATCH 2/2] dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells Koichiro Den
@ 2026-02-18 16:19 ` Koichiro Den
  2026-02-18 16:50   ` Niklas Cassel
  2026-02-25 11:24 ` Vinod Koul
  3 siblings, 1 reply; 9+ messages in thread
From: Koichiro Den @ 2026-02-18 16:19 UTC (permalink / raw)
  To: mani, vkoul, Frank.Li; +Cc: cassel, dmaengine, linux-kernel

On Mon, Feb 16, 2026 at 12:22:14AM +0900, Koichiro Den wrote:
> Hi,
> 
> Some DesignWare eDMA instances support "interrupt emulation", where a
> software write can assert the IRQ line without setting the normal
> DONE/ABORT status bits.
> 
> In the current mainline, on implementations that support interrupt
> emulation, writing once to DMA_{WRITE,READ}_INT_STATUS_OFF is sufficient
> to leave the level-triggered IRQ line asserted. Since the shared dw-edma
> IRQ handlers only look at DONE/ABORT bits and do not perform any
> deassertion sequence for interrupt emulation, the IRQ remains asserted
> and is eventually disabled by the generic IRQ layer:
> 
>   $ sudo devmem2 0xe65d50a0 w 0
> 
>   [   47.189557] irq 48: nobody cared (try booting with the "irqpoll" option)
>   ...
>   [   47.190383] handlers:
>   [   47.199837] [<00000000a5ecb36e>] dw_edma_interrupt_common
>   [   47.200214] Disabling IRQ #48
> 
> In other words, a single interrupt-emulation write can leave the IRQ
> line stuck asserted and render the DMA engine unusable until reboot.
> 
> This series fixes the problem by:
> 
>   - adding a core hook to deassert an emulated interrupt
>   - wiring a requestable Linux virtual IRQ whose .irq_ack performs the
>     deassert sequence
>   - raising that virtual IRQ from the dw-edma IRQ path to ensure the
>     deassert sequence is always executed
> 
> This makes interrupt emulation safe and also enables platform users to
> expose it as a doorbell via the exported db_irq and db_offset.
> 
> This is a spin-off from:
> https://lore.kernel.org/linux-pci/20260209125316.2132589-1-den@valinux.co.jp/
> 
> Based on dmaengine.git next branch latest:
> Commit ab736ed52e34 ("dmaengine: add Frank Li as reviewer")
> 
> Thanks for reviewing,
> 
> 
> Koichiro Den (2):
>   dmaengine: dw-edma: Add interrupt-emulation hooks
>   dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells
> 
>  drivers/dma/dw-edma/dw-edma-core.c    | 127 +++++++++++++++++++++++++-
>  drivers/dma/dw-edma/dw-edma-core.h    |  17 ++++
>  drivers/dma/dw-edma/dw-edma-v0-core.c |  21 +++++
>  drivers/dma/dw-edma/dw-hdma-v0-core.c |   7 ++
>  include/linux/dma/edma.h              |   6 ++
>  5 files changed, 173 insertions(+), 5 deletions(-)

+CC: Niklas

Niklas provided valuable feedback in the previous iterations and also helped
test the earlier (non-split) series mentioned above. During testing, he
identified that on platforms where chip->nr_irqs > 1, the interrupt emulation
IRQ could be randomly delivered to one of the shared channel IRQs [1]. That
observation was the main motivation for the rework.

I missed Niklas in CC earlier, sorry about that. I had naively relied on
get_maintainers.pl. I'll be more careful.

In this series, the implementation has been reworked around a dedicated
dw_edma_emul_irqchip combined with handle_level_irq(), and generic_handle_irq()
is now invoked unconditionally from the channel IRQ handlers to ensure the
deassert sequence is always executed.

[1] the discussion started at https://lore.kernel.org/linux-pci/aYsjfTtA0EsXwh69@ryzen/

Best regards,
Koichiro

> 
> -- 
> 2.51.0
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support
  2026-02-18 16:19 ` [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support Koichiro Den
@ 2026-02-18 16:50   ` Niklas Cassel
  0 siblings, 0 replies; 9+ messages in thread
From: Niklas Cassel @ 2026-02-18 16:50 UTC (permalink / raw)
  To: Koichiro Den; +Cc: mani, vkoul, Frank.Li, dmaengine, linux-kernel

Hello Koichiro,

On Thu, Feb 19, 2026 at 01:19:02AM +0900, Koichiro Den wrote:
> On Mon, Feb 16, 2026 at 12:22:14AM +0900, Koichiro Den wrote:
> > Hi,
> > 
> > Some DesignWare eDMA instances support "interrupt emulation", where a
> > software write can assert the IRQ line without setting the normal
> > DONE/ABORT status bits.
> > 
> > In the current mainline, on implementations that support interrupt
> > emulation, writing once to DMA_{WRITE,READ}_INT_STATUS_OFF is sufficient
> > to leave the level-triggered IRQ line asserted. Since the shared dw-edma
> > IRQ handlers only look at DONE/ABORT bits and do not perform any
> > deassertion sequence for interrupt emulation, the IRQ remains asserted
> > and is eventually disabled by the generic IRQ layer:
> > 
> >   $ sudo devmem2 0xe65d50a0 w 0
> > 
> >   [   47.189557] irq 48: nobody cared (try booting with the "irqpoll" option)
> >   ...
> >   [   47.190383] handlers:
> >   [   47.199837] [<00000000a5ecb36e>] dw_edma_interrupt_common
> >   [   47.200214] Disabling IRQ #48
> > 
> > In other words, a single interrupt-emulation write can leave the IRQ
> > line stuck asserted and render the DMA engine unusable until reboot.
> > 
> > This series fixes the problem by:
> > 
> >   - adding a core hook to deassert an emulated interrupt
> >   - wiring a requestable Linux virtual IRQ whose .irq_ack performs the
> >     deassert sequence
> >   - raising that virtual IRQ from the dw-edma IRQ path to ensure the
> >     deassert sequence is always executed
> > 
> > This makes interrupt emulation safe and also enables platform users to
> > expose it as a doorbell via the exported db_irq and db_offset.
> > 
> > This is a spin-off from:
> > https://lore.kernel.org/linux-pci/20260209125316.2132589-1-den@valinux.co.jp/
> > 
> > Based on dmaengine.git next branch latest:
> > Commit ab736ed52e34 ("dmaengine: add Frank Li as reviewer")
> > 
> > Thanks for reviewing,
> > 
> > 
> > Koichiro Den (2):
> >   dmaengine: dw-edma: Add interrupt-emulation hooks
> >   dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells
> > 
> >  drivers/dma/dw-edma/dw-edma-core.c    | 127 +++++++++++++++++++++++++-
> >  drivers/dma/dw-edma/dw-edma-core.h    |  17 ++++
> >  drivers/dma/dw-edma/dw-edma-v0-core.c |  21 +++++
> >  drivers/dma/dw-edma/dw-hdma-v0-core.c |   7 ++
> >  include/linux/dma/edma.h              |   6 ++
> >  5 files changed, 173 insertions(+), 5 deletions(-)
> 
> +CC: Niklas
> 
> Niklas provided valuable feedback in the previous iterations and also helped
> test the earlier (non-split) series mentioned above. During testing, he
> identified that on platforms where chip->nr_irqs > 1, the interrupt emulation
> IRQ could be randomly delivered to one of the shared channel IRQs [1]. That
> observation was the main motivation for the rework.
> 
> I missed Niklas in CC earlier, sorry about that. I had naively relied on
> get_maintainers.pl. I'll be more careful.

Thank you for CC:ing me.

I'm happy to help with testing.

For reviewing, I will limit myself to drivers/pci/endpoint.
(I don't really have spare cycles to also review drivers/dma/dw-edma patches.)


Kind regards,
Niklas


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support
  2026-02-15 15:22 [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support Koichiro Den
                   ` (2 preceding siblings ...)
  2026-02-18 16:19 ` [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support Koichiro Den
@ 2026-02-25 11:24 ` Vinod Koul
  3 siblings, 0 replies; 9+ messages in thread
From: Vinod Koul @ 2026-02-25 11:24 UTC (permalink / raw)
  To: mani, Frank.Li, Koichiro Den; +Cc: dmaengine, linux-kernel


On Mon, 16 Feb 2026 00:22:14 +0900, Koichiro Den wrote:
> Some DesignWare eDMA instances support "interrupt emulation", where a
> software write can assert the IRQ line without setting the normal
> DONE/ABORT status bits.
> 
> In the current mainline, on implementations that support interrupt
> emulation, writing once to DMA_{WRITE,READ}_INT_STATUS_OFF is sufficient
> to leave the level-triggered IRQ line asserted. Since the shared dw-edma
> IRQ handlers only look at DONE/ABORT bits and do not perform any
> deassertion sequence for interrupt emulation, the IRQ remains asserted
> and is eventually disabled by the generic IRQ layer:
> 
> [...]

Applied, thanks!

[1/2] dmaengine: dw-edma: Add interrupt-emulation hooks
      commit: 2e8726879559144b4582fa059780e452b3a1ad41
[2/2] dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells
      commit: d9d5e1bdd18074ea27985c777ddc3a8a0b007468

Best regards,
-- 
~Vinod



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] dmaengine: dw-edma: Add interrupt-emulation hooks
  2026-02-15 15:22 ` [PATCH 1/2] dmaengine: dw-edma: Add interrupt-emulation hooks Koichiro Den
  2026-02-16 16:38   ` Frank Li
@ 2026-03-24  2:10   ` Koichiro Den
  1 sibling, 0 replies; 9+ messages in thread
From: Koichiro Den @ 2026-03-24  2:10 UTC (permalink / raw)
  To: mani, vkoul, Frank.Li; +Cc: dmaengine, linux-kernel

On Mon, Feb 16, 2026 at 12:22:15AM +0900, Koichiro Den wrote:
> DesignWare eDMA instances support "interrupt emulation", where a
> software write can assert the IRQ line without setting the normal
> DONE/ABORT status bits.
> 
> Introduce core callbacks needed to support this feature:
> 
>   - .ack_emulated_irq(): core-specific sequence to deassert an emulated
>     IRQ
>   - .db_offset(): offset from the DMA register base that is suitable as a
>     host-writable doorbell target for interrupt emulation
> 
> Implement both hooks for the v0 register map. For dw-hdma-v0, provide a
> stub .db_offset() returning ~0 until the correct offset is known.
> 
> The next patch wires these hooks into the dw-edma IRQ path and exports
> the doorbell resources to platform users.
> 
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
>  drivers/dma/dw-edma/dw-edma-core.h    | 17 +++++++++++++++++
>  drivers/dma/dw-edma/dw-edma-v0-core.c | 21 +++++++++++++++++++++
>  drivers/dma/dw-edma/dw-hdma-v0-core.c |  7 +++++++
>  3 files changed, 45 insertions(+)
> 
> diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> index 71894b9e0b15..59b24973fa7d 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-core.h
> @@ -126,6 +126,8 @@ struct dw_edma_core_ops {
>  	void (*start)(struct dw_edma_chunk *chunk, bool first);
>  	void (*ch_config)(struct dw_edma_chan *chan);
>  	void (*debugfs_on)(struct dw_edma *dw);
> +	void (*ack_emulated_irq)(struct dw_edma *dw);
> +	resource_size_t (*db_offset)(struct dw_edma *dw);
>  };
>  
>  struct dw_edma_sg {
> @@ -206,4 +208,19 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
>  	dw->core->debugfs_on(dw);
>  }
>  
> +static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw)
> +{
> +	if (!dw->core->ack_emulated_irq)
> +		return -EOPNOTSUPP;
> +
> +	dw->core->ack_emulated_irq(dw);
> +	return 0;
> +}
> +
> +static inline resource_size_t
> +dw_edma_core_db_offset(struct dw_edma *dw)
> +{
> +	return dw->core->db_offset(dw);
> +}
> +
>  #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..69e8279adec8 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -509,6 +509,25 @@ static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
>  	dw_edma_v0_debugfs_on(dw);
>  }
>  
> +static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw)
> +{
> +	/*
> +	 * Interrupt emulation may assert the IRQ without setting
> +	 * DONE/ABORT status bits. A zero write to INT_CLEAR deasserts the
> +	 * emulated IRQ, while being a no-op for real interrupts.
> +	 */
> +	SET_BOTH_32(dw, int_clear, 0);
> +}
> +
> +static resource_size_t dw_edma_v0_core_db_offset(struct dw_edma *dw)
> +{
> +	/*
> +	 * rd_int_status is chosen arbitrarily, but wr_int_status would be
> +	 * equally suitable.
> +	 */
> +	return 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,
> @@ -517,6 +536,8 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
>  	.start = dw_edma_v0_core_start,
>  	.ch_config = dw_edma_v0_core_ch_config,
>  	.debugfs_on = dw_edma_v0_core_debugfs_on,
> +	.ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq,
> +	.db_offset = dw_edma_v0_core_db_offset,
>  };
>  
>  void dw_edma_v0_core_register(struct dw_edma *dw)
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> index e3f8db4fe909..1ae8e44f0a67 100644
> --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> @@ -283,6 +283,12 @@ static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
>  	dw_hdma_v0_debugfs_on(dw);
>  }
>  
> +static resource_size_t dw_hdma_v0_core_db_offset(struct dw_edma *dw)
> +{
> +	/* Implement once the correct offset is known. */
> +	return ~0;

I now have access to an HDMA-capable device (not set up yet though) and the
databook. According to the PCIe DM Controller databook (6.10a-lca06/6.14a-lca02,
section 7.4.4 "HDMA Debug"):

  > Test Interrupt
  > HDMA does not support this legacy DMA feature in this release.

It's unclear whether this holds true for all revisions, but assuming so, perhaps
the comment above should be updated for clarity.

Best regards,
Koichiro

> +}
> +
>  static const struct dw_edma_core_ops dw_hdma_v0_core = {
>  	.off = dw_hdma_v0_core_off,
>  	.ch_count = dw_hdma_v0_core_ch_count,
> @@ -291,6 +297,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
>  	.start = dw_hdma_v0_core_start,
>  	.ch_config = dw_hdma_v0_core_ch_config,
>  	.debugfs_on = dw_hdma_v0_core_debugfs_on,
> +	.db_offset = dw_hdma_v0_core_db_offset,
>  };
>  
>  void dw_hdma_v0_core_register(struct dw_edma *dw)
> -- 
> 2.51.0
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-03-24  2:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-15 15:22 [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support Koichiro Den
2026-02-15 15:22 ` [PATCH 1/2] dmaengine: dw-edma: Add interrupt-emulation hooks Koichiro Den
2026-02-16 16:38   ` Frank Li
2026-03-24  2:10   ` Koichiro Den
2026-02-15 15:22 ` [PATCH 2/2] dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells Koichiro Den
2026-02-16 16:38   ` Frank Li
2026-02-18 16:19 ` [PATCH 0/2] dmaengine: dw-edma: Interrupt-emulation doorbell support Koichiro Den
2026-02-18 16:50   ` Niklas Cassel
2026-02-25 11:24 ` Vinod Koul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox