mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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
>

  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®