[PATCH 04/15] dmaengine: dw-edma: Add per-channel interrupt routing control

Koichiro Den posted 15 patches 3 weeks, 4 days ago
[PATCH 04/15] dmaengine: dw-edma: Add per-channel interrupt routing control
Posted by Koichiro Den 3 weeks, 4 days ago
DesignWare endpoint eDMA can signal completion both locally and
remotely through LIE/RIE. A remotely controlled channel needs a
per-channel policy for whether completions are handled locally,
remotely, or both; otherwise the endpoint and host can race to
acknowledge the interrupt.

Add dw_edma_peripheral_config, carried through dma_slave_config, to let
a frontend select the interrupt routing mode for each channel. Update
the v0 programming path so linked-list interrupt generation and
DONE/ABORT masking follow the selected mode. If a frontend does nothing,
the default keeps the existing behavior.

For now reject the new peripheral_config on HDMA, where the routing
model has not been implemented or validated yet, instead of silently
misprogramming interrupts.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
 drivers/dma/dw-edma/dw-edma-core.c    | 55 +++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-core.h    | 13 +++++++
 drivers/dma/dw-edma/dw-edma-v0-core.c | 26 +++++++++----
 include/linux/dma/edma.h              | 38 ++++++++++++++++++
 4 files changed, 124 insertions(+), 8 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index a13beacce2e7..6341bda4c303 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -219,11 +219,60 @@ static void dw_edma_device_caps(struct dma_chan *dchan,
 	}
 }
 
+static enum dw_edma_ch_irq_mode
+dw_edma_get_default_irq_mode(struct dw_edma_chan *chan)
+{
+	switch (chan->dw->chip->default_irq_mode) {
+	case DW_EDMA_CH_IRQ_DEFAULT:
+	case DW_EDMA_CH_IRQ_LOCAL:
+	case DW_EDMA_CH_IRQ_REMOTE:
+		return chan->dw->chip->default_irq_mode;
+	default:
+		return DW_EDMA_CH_IRQ_DEFAULT;
+	}
+}
+
+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, fall back to the frontend default. */
+	*mode = dw_edma_get_default_irq_mode(chan);
+	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;
 
@@ -808,11 +857,14 @@ 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_default_irq_mode(chan);
+
 	return 0;
 }
 
 static void dw_edma_free_chan_resources(struct dma_chan *dchan)
 {
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
 	unsigned long timeout = jiffies + msecs_to_jiffies(5000);
 	int ret;
 
@@ -826,6 +878,8 @@ static void dw_edma_free_chan_resources(struct dma_chan *dchan)
 
 		cpu_relax();
 	}
+
+	chan->irq_mode = dw_edma_get_default_irq_mode(chan);
 }
 
 static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
@@ -860,6 +914,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_default_irq_mode(chan);
 
 		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 59b24973fa7d..e021551b0b9f 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;
@@ -223,4 +225,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;
+	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 69e8279adec8..2e95da0d6fc2 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 0b861e8d305e..e4a6302bd04c 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -60,6 +60,41 @@ 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:   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,
+};
+
+/**
+ * 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
@@ -76,6 +111,8 @@ 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
+ * @default_irq_mode:	 default per-channel interrupt routing when client
+ *			 does not supply dw_edma_peripheral_config
  * @dw:			 struct dw_edma that is filled by dw_edma_probe()
  */
 struct dw_edma_chip {
@@ -105,6 +142,7 @@ struct dw_edma_chip {
 	int			chan_ids_rd[EDMA_MAX_RD_CH];
 
 	enum dw_edma_map_format	mf;
+	enum dw_edma_ch_irq_mode	default_irq_mode;
 
 	struct dw_edma		*dw;
 };
-- 
2.51.0
Re: [PATCH 04/15] dmaengine: dw-edma: Add per-channel interrupt routing control
Posted by Frank Li 3 weeks, 4 days ago
On Fri, Mar 13, 2026 at 01:49:54AM +0900, Koichiro Den wrote:
> DesignWare endpoint eDMA can signal completion both locally and
> remotely through LIE/RIE. A remotely controlled channel needs a
> per-channel policy for whether completions are handled locally,
> remotely, or both; otherwise the endpoint and host can race to
> acknowledge the interrupt.
>
> Add dw_edma_peripheral_config, carried through dma_slave_config, to let
> a frontend select the interrupt routing mode for each channel. Update
> the v0 programming path so linked-list interrupt generation and
> DONE/ABORT masking follow the selected mode. If a frontend does nothing,
> the default keeps the existing behavior.
>
> For now reject the new peripheral_config on HDMA, where the routing
> model has not been implemented or validated yet, instead of silently
> misprogramming interrupts.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>  drivers/dma/dw-edma/dw-edma-core.c    | 55 +++++++++++++++++++++++++++
>  drivers/dma/dw-edma/dw-edma-core.h    | 13 +++++++
>  drivers/dma/dw-edma/dw-edma-v0-core.c | 26 +++++++++----
>  include/linux/dma/edma.h              | 38 ++++++++++++++++++
>  4 files changed, 124 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index a13beacce2e7..6341bda4c303 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -219,11 +219,60 @@ static void dw_edma_device_caps(struct dma_chan *dchan,
>  	}
>  }
>
> +static enum dw_edma_ch_irq_mode
> +dw_edma_get_default_irq_mode(struct dw_edma_chan *chan)
> +{
> +	switch (chan->dw->chip->default_irq_mode) {
> +	case DW_EDMA_CH_IRQ_DEFAULT:
> +	case DW_EDMA_CH_IRQ_LOCAL:
> +	case DW_EDMA_CH_IRQ_REMOTE:
> +		return chan->dw->chip->default_irq_mode;
> +	default:
> +		return DW_EDMA_CH_IRQ_DEFAULT;
> +	}
> +}
> +
> +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, fall back to the frontend default. */
> +	*mode = dw_edma_get_default_irq_mode(chan);
> +	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;
>
> @@ -808,11 +857,14 @@ 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_default_irq_mode(chan);
> +
>  	return 0;
>  }
>
>  static void dw_edma_free_chan_resources(struct dma_chan *dchan)
>  {
> +	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
>  	unsigned long timeout = jiffies + msecs_to_jiffies(5000);
>  	int ret;
>
> @@ -826,6 +878,8 @@ static void dw_edma_free_chan_resources(struct dma_chan *dchan)
>
>  		cpu_relax();
>  	}
> +
> +	chan->irq_mode = dw_edma_get_default_irq_mode(chan);
>  }
>
>  static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> @@ -860,6 +914,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_default_irq_mode(chan);
>
>  		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 59b24973fa7d..e021551b0b9f 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;
> @@ -223,4 +225,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;
> +	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 69e8279adec8..2e95da0d6fc2 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 0b861e8d305e..e4a6302bd04c 100644
> --- a/include/linux/dma/edma.h
> +++ b/include/linux/dma/edma.h
> @@ -60,6 +60,41 @@ 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:   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,
> +};
> +
> +/**
> + * 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
> @@ -76,6 +111,8 @@ 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
> + * @default_irq_mode:	 default per-channel interrupt routing when client
> + *			 does not supply dw_edma_peripheral_config
>   * @dw:			 struct dw_edma that is filled by dw_edma_probe()
>   */
>  struct dw_edma_chip {
> @@ -105,6 +142,7 @@ struct dw_edma_chip {
>  	int			chan_ids_rd[EDMA_MAX_RD_CH];
>
>  	enum dw_edma_map_format	mf;
> +	enum dw_edma_ch_irq_mode	default_irq_mode;
>
>  	struct dw_edma		*dw;
>  };
> --
> 2.51.0
>