From: Frank Li <Frank.li@nxp.com>
To: Koichiro Den <den@valinux.co.jp>, Thomas Gleixner <tglx@kernel.org>
Cc: mani@kernel.org, vkoul@kernel.org, Frank.Li@kernel.org,
dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2/2] dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells
Date: Mon, 16 Feb 2026 11:38:06 -0500 [thread overview]
Message-ID: <aZNH7oup-ne77twC@lizhi-Precision-Tower-5810> (raw)
In-Reply-To: <20260215152216.3393561-3-den@valinux.co.jp>
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
>
next prev parent reply other threads:[~2026-02-16 16:38 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aZNH7oup-ne77twC@lizhi-Precision-Tower-5810 \
--to=frank.li@nxp.com \
--cc=Frank.Li@kernel.org \
--cc=den@valinux.co.jp \
--cc=dmaengine@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mani@kernel.org \
--cc=tglx@kernel.org \
--cc=vkoul@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®