From: Frank Li <Frank.li@oss.nxp.com>
To: Koichiro Den <den@valinux.co.jp>
Cc: Vinod Koul <vkoul@kernel.org>, Frank Li <Frank.Li@kernel.org>,
Manivannan Sadhasivam <mani@kernel.org>,
Marek Vasut <marek.vasut+renesas@mailbox.org>,
Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>,
dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3 01/13] dmaengine: dw-edma: Add per-channel interrupt routing control
Date: Mon, 22 Jun 2026 10:34:33 -0500 [thread overview]
Message-ID: <ajlWCeFlcGFbeLwY@SMW015318> (raw)
In-Reply-To: <20260620170040.3756043-2-den@valinux.co.jp>
On Sun, Jun 21, 2026 at 02:00:28AM +0900, Koichiro Den wrote:
> DesignWare eDMA can signal completion locally through edma_int[] and
> remotely through IMWr/MSI. When channels are delegated to a remote
> frontend, the local endpoint side and the remote host side must not both
> service the same DONE/ABORT status.
>
> Add channel interrupt routing state and initialize it from the
> controller instance configuration. Update the v0 eDMA and HDMA native
> paths so linked-list interrupt generation, HDMA non-linked-list
> interrupt enables, and DONE/ABORT masking follow the selected mode. For
> HDMA native non-linked-list channels, use the dedicated remote
> stop/abort enables without local stop/abort enables.
>
> Keep the existing dw-edma-pcie host-side instances in remote interrupt
> routing mode so their IMWr/MSI completion model remains unchanged after
> local routing becomes the zero value.
>
> Note that the routing mode describes where a channel should report
> completion. It does not, by itself, say whether this dw-edma instance
> owns the interrupt status. A local instance must ignore remote-only
> channels, and a remote instance must ignore local-only channels, even if
> such interrupts are unexpectedly delivered. Otherwise the non-owner side
> could steal the interrupt from the owner by clearing shared DONE/ABORT
> status.
>
> Suggested-by: Frank Li <Frank.Li@nxp.com>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> Changes in v3:
> - Remove DW_EDMA_CH_IRQ_DEFAULT; local routing is the zero value
> (Frank).
> - Set existing dw-edma-pcie host-side instances to remote interrupt
> routing in this patch, preserving the legacy IMWr completion model.
> - Remove an unreachable HDMA native check (Sashiko).
> - Clarify local/remote instance ownership after Frank's question.
> - Mark non-owner IRQ handling guard paths unlikely.
> - Add HDMA native interrupt routing while keeping the existing non-LL
> int config ABI.
> - Keep HDMA native linked-list local interrupt generation enabled for
> remote-routed channels while masking the local edma_int[] output.
> - Use remote-only stop/abort enables for HDMA native non-LL remote-routed
> channels.
> - Drop the peripheral_config IRQ-routing ABI; initial routing comes from
> chip setup and channel ownership handoff can override it.
> - Keep dma_slave_config from resetting channel ownership routing.
>
> drivers/dma/dw-edma/dw-edma-core.c | 14 +++++++++
> drivers/dma/dw-edma/dw-edma-core.h | 13 +++++++++
> drivers/dma/dw-edma/dw-edma-pcie.c | 1 +
> drivers/dma/dw-edma/dw-edma-v0-core.c | 22 ++++++++++----
> drivers/dma/dw-edma/dw-hdma-v0-core.c | 41 ++++++++++++++++-----------
> include/linux/dma/edma.h | 30 ++++++++++++++++++++
> 6 files changed, 99 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index 89a4c498a17b..7a24248b84e9 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -219,6 +219,17 @@ static void dw_edma_device_caps(struct dma_chan *dchan,
> }
> }
>
> +static enum dw_edma_ch_irq_mode dw_edma_get_irq_mode(struct dw_edma_chan *chan)
> +{
dw_edma_get_default_irq_mode() ?
> + struct dw_edma_chip *chip = chan->dw->chip;
> +
> + if (chip->irq_mode == DW_EDMA_CH_IRQ_REMOTE &&
> + !(chip->flags & DW_EDMA_CHIP_LOCAL))
> + return DW_EDMA_CH_IRQ_REMOTE;
> +
return chip->flags & DW_EDMA_CHIP_LOCAL ? DW_EDMA_CH_IRQ_LOCAL : DW_EDMA_CH_IRQ_LOCAL
> + return DW_EDMA_CH_IRQ_LOCAL;
> +}
> +
> static int dw_edma_device_config(struct dma_chan *dchan,
> struct dma_slave_config *config)
> {
> @@ -853,6 +864,8 @@ static int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
> if (chan->status != EDMA_ST_IDLE)
> return -EBUSY;
>
> + chan->irq_mode = dw_edma_get_irq_mode(chan);
> +
> return 0;
> }
>
> @@ -904,6 +917,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_get_irq_mode(chan);
if already set in dw_edma_alloc_chan_resources(), needn't set again?
>
> 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 6474cacf7195..42f2f25ef377 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;
> @@ -224,4 +226,15 @@ dw_edma_core_db_offset(struct dw_edma *dw)
> return dw->core->db_offset(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;
is it okay to simple return false here?
> + else
> + return chan->irq_mode == DW_EDMA_CH_IRQ_LOCAL;
> +}
> +
> #endif /* _DW_EDMA_CORE_H */
> diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
> index 791c46e8ae4c..70ea031147d1 100644
> --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> @@ -419,6 +419,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
> chip->dev = dev;
>
> chip->mf = vsec_data->mf;
> + chip->irq_mode = DW_EDMA_CH_IRQ_REMOTE;
> chip->nr_irqs = nr_irqs;
> chip->ops = &dw_edma_pcie_plat_ops;
> chip->cfg_non_ll = non_ll;
> diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> index cfdd6463252e..1781ba4f022e 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -256,9 +256,11 @@ 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];
>
> + if (unlikely(dw_edma_core_ch_ignore_irq(chan)))
> + continue;
> +
> dw_edma_v0_core_clear_done_int(chan);
> done(chan);
> -
clean up these unncessary chagnes.
> ret = IRQ_HANDLED;
> }
>
> @@ -267,9 +269,11 @@ 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];
>
> + if (unlikely(dw_edma_core_ch_ignore_irq(chan)))
> + continue;
> +
> 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)
you define DW_EMDA_CH_IRQ_REMOTE, sugget don't use reverise logic.
chan->irq_mode == chan->irq_mode
> control |= DW_EDMA_V0_RIE;
> }
>
> @@ -408,12 +413,17 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> break;
> }
> }
> - /* Interrupt unmask - done, abort */
> + /* Interrupt mask/unmask - done, abort */
> raw_spin_lock_irqsave(&dw->lock, flags);
>
> 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) {
I think need also check chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL
> + 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/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> index 2beec876b184..7ba6bdbffc17 100644
> --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> @@ -49,6 +49,26 @@ __dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch)
> writel(value, &(__dw_ch_regs(dw, EDMA_DIR_READ, ch)->name)); \
> } while (0)
>
> +static u32 dw_hdma_v0_core_int_setup(struct dw_edma_chan *chan, u32 val)
> +{
> + val &= ~(HDMA_V0_LOCAL_ABORT_INT_EN | HDMA_V0_REMOTE_ABORT_INT_EN |
> + HDMA_V0_LOCAL_STOP_INT_EN | HDMA_V0_REMOTE_STOP_INT_EN |
> + HDMA_V0_ABORT_INT_MASK | HDMA_V0_STOP_INT_MASK);
> +
> + if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE && chan->non_ll)
> + return val | HDMA_V0_REMOTE_ABORT_INT_EN |
> + HDMA_V0_REMOTE_STOP_INT_EN;
> +
> + if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE)
> + return val | HDMA_V0_LOCAL_ABORT_INT_EN |
> + HDMA_V0_REMOTE_ABORT_INT_EN |
> + HDMA_V0_LOCAL_STOP_INT_EN |
> + HDMA_V0_REMOTE_STOP_INT_EN | HDMA_V0_ABORT_INT_MASK |
> + HDMA_V0_STOP_INT_MASK;
> +
> + return val | HDMA_V0_LOCAL_ABORT_INT_EN | HDMA_V0_LOCAL_STOP_INT_EN;
> +}
> +
> /* HDMA management callbacks */
> static void dw_hdma_v0_core_off(struct dw_edma *dw)
> {
> @@ -132,6 +152,8 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
>
> for_each_set_bit(pos, &mask, total) {
> chan = &dw->chan[pos + off];
> + if (unlikely(dw_edma_core_ch_ignore_irq(chan)))
> + continue;
>
> val = dw_hdma_v0_core_status_int(chan);
> if (FIELD_GET(HDMA_V0_STOP_INT_MASK, val)) {
> @@ -238,11 +260,7 @@ static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk, bool first)
> SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0));
> /* Interrupt unmask - stop, abort */
> tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup);
> - tmp &= ~(HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK);
> - /* Interrupt enable - stop, abort */
> - tmp |= HDMA_V0_LOCAL_STOP_INT_EN | HDMA_V0_LOCAL_ABORT_INT_EN;
> - if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> - tmp |= HDMA_V0_REMOTE_STOP_INT_EN | HDMA_V0_REMOTE_ABORT_INT_EN;
> + tmp = dw_hdma_v0_core_int_setup(chan, tmp);
Can you use small patch to create helper dw_hdma_v0_core_int_setup() only
> SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp);
> /* Channel control */
> SET_CH_32(dw, chan->dir, chan->id, control1, HDMA_V0_LINKLIST_EN);
> @@ -293,17 +311,8 @@ static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk)
> SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz);
>
> /* Interrupt setup */
> - val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> - HDMA_V0_STOP_INT_MASK |
> - HDMA_V0_ABORT_INT_MASK |
> - HDMA_V0_LOCAL_STOP_INT_EN |
> - HDMA_V0_LOCAL_ABORT_INT_EN;
> -
> - if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> - val |= HDMA_V0_REMOTE_STOP_INT_EN |
> - HDMA_V0_REMOTE_ABORT_INT_EN;
> - }
> -
> + val = GET_CH_32(dw, chan->dir, chan->id, int_setup);
> + val = dw_hdma_v0_core_int_setup(chan, val);
> SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
>
> /* Channel control setup */
> diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> index 1fafd5b0e315..c0906221a7c7 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_LOCAL: local interrupt only (edma_int[])
> + * @DW_EDMA_CH_IRQ_REMOTE: remote interrupt only (IMWr/MSI), without
> + * delivering local edma_int[].
> + *
> + * 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.
> + *
> + * For the v0 eDMA linked-list programming path, 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. See the
> + * DesignWare endpoint databook 6.30a, Linked List Mode interrupt handling
> + * ("Software Programming of an Endpoint's LIE and RIE Bits for Linked List
> + * Transfers", Attention).
> + *
> + * HDMA linked-list watermark interrupts have the same LWIE/RWIE guidance. HDMA
> + * non-linked-list mode has dedicated local and remote stop/abort interrupt
> + * enables, and the remote CPU programming examples use remote enables without
> + * local enables.
> + */
> +enum dw_edma_ch_irq_mode {
> + DW_EDMA_CH_IRQ_LOCAL = 0,
> + DW_EDMA_CH_IRQ_REMOTE,
> +};
> +
> /**
> * struct dw_edma_chip - representation of DesignWare eDMA controller hardware
> * @dev: struct device of the eDMA controller
> @@ -76,6 +104,7 @@ enum dw_edma_chip_flags {
> * @db_irq: Virtual IRQ dedicated to interrupt emulation
> * @db_offset: Offset from DMA register base
> * @mf: DMA register map format
> + * @irq_mode: default per-channel interrupt routing
> * @dw: struct dw_edma that is filled by dw_edma_probe()
> */
> struct dw_edma_chip {
> @@ -101,6 +130,7 @@ struct dw_edma_chip {
> resource_size_t db_offset;
>
> enum dw_edma_map_format mf;
> + enum dw_edma_ch_irq_mode irq_mode;
This are already have flags dw_edma_chip_flags, not sure why need irq_mode,
suppose, if set DW_EDMA_CHIP_LOCAL, pre channel should be default as
DW_EDMA_CH_IRQ_LOCAL
Frank
>
> struct dw_edma *dw;
> bool cfg_non_ll;
> --
> 2.51.0
>
next prev parent reply other threads:[~2026-06-22 15:34 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-20 17:00 [PATCH v3 00/13] dmaengine: dw-edma: Prepare for PCI EP DMA (part 1/3) Koichiro Den
2026-06-20 17:00 ` [PATCH v3 01/13] dmaengine: dw-edma: Add per-channel interrupt routing control Koichiro Den
2026-06-20 17:13 ` sashiko-bot
2026-06-21 14:35 ` Koichiro Den
2026-06-22 15:34 ` Frank Li [this message]
2026-06-20 17:00 ` [PATCH v3 02/13] dmaengine: dw-edma: Add core quiesce operations Koichiro Den
2026-06-20 17:15 ` sashiko-bot
2026-06-22 1:43 ` Koichiro Den
2026-06-22 15:45 ` Frank Li
2026-06-20 17:00 ` [PATCH v3 03/13] dmaengine: dw-edma: Add delegated channel request helpers Koichiro Den
2026-06-20 17:25 ` sashiko-bot
2026-06-22 4:38 ` Koichiro Den
2026-06-22 16:06 ` Frank Li
2026-06-20 17:00 ` [PATCH v3 04/13] dmaengine: dw-edma: Initialize IRQ data before requesting IRQs Koichiro Den
2026-06-20 17:16 ` sashiko-bot
2026-06-22 4:58 ` Koichiro Den
2026-06-20 17:00 ` [PATCH v3 05/13] dmaengine: dw-edma: Add partial channel ownership mode Koichiro Den
2026-06-20 17:16 ` sashiko-bot
2026-06-22 6:14 ` Koichiro Den
2026-06-22 15:59 ` Frank Li
2026-06-20 17:00 ` [PATCH v3 06/13] dmaengine: dw-edma-pcie: Track non-LL mode in DMA data Koichiro Den
2026-06-20 17:15 ` sashiko-bot
2026-06-20 17:00 ` [PATCH v3 07/13] dmaengine: dw-edma-pcie: Add capability match data Koichiro Den
2026-06-20 17:11 ` sashiko-bot
2026-06-20 17:00 ` [PATCH v3 08/13] dmaengine: dw-edma-pcie: Rename vsec_data to dma_data Koichiro Den
2026-06-20 17:11 ` sashiko-bot
2026-06-20 17:00 ` [PATCH v3 09/13] dmaengine: dw-edma-pcie: Add platform ops to match data Koichiro Den
2026-06-20 17:13 ` sashiko-bot
2026-06-20 17:00 ` [PATCH v3 10/13] dmaengine: dw-edma-pcie: Add register offset match flag Koichiro Den
2026-06-20 17:18 ` sashiko-bot
2026-06-20 17:00 ` [PATCH v3 11/13] dmaengine: dw-edma-pcie: Factor out descriptor block address lookup Koichiro Den
2026-06-20 17:00 ` [PATCH v3 12/13] dmaengine: dw-edma-pcie: Handle optional data blocks Koichiro Den
2026-06-20 17:14 ` sashiko-bot
2026-06-20 17:00 ` [PATCH v3 13/13] dmaengine: dw-edma-pcie: Add chip flags to match data Koichiro Den
2026-06-20 17:16 ` sashiko-bot
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=ajlWCeFlcGFbeLwY@SMW015318 \
--to=frank.li@oss.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=marek.vasut+renesas@mailbox.org \
--cc=vkoul@kernel.org \
--cc=yoshihiro.shimoda.uh@renesas.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.