dmaengine: dw-edma: Enable HDMA 64R/W Channels
Subsystems (2)
PATCH RESEND v4 1/1 dmaengine: dw-edma: Enable HDMA 64R/W Channels
As per 'Designware Cores PCI Express Controller Databook', Section 7.1 - Overview, HDMA supports 64 Read and 64 Write channels. Current controller driver supports up to 8 read and write channels only. In order to utilize all the channels the controller driver need to have the channel related structs and variables as per the number of channels supported by IP. Following changes are made to enable 64 Read / 64 Write channel support:
o Defined HDMA specific macros to reflect the channel count.
o The count of ll_regions and dt_regions in dw_edma_chip and dw_edma_pcie_data shall be in accordance to number of read and write channels.
o In dw_edma_probe() configure the channels as per the channels of the IP used.
o Changed mask types to u64 for higher channel counts.
---
Changes in v3:
o Reverted the FIX for AI reported GET_CH_32() issue, as per the recommendation of reviewers, need to create separate patch for it.
Changes in v2:
o Fixed the pre-existing bug related to GET_CH_32 interchanging the channel direction and id. This bug was not caused by any version of this patch.
o Fixed the issue when using for_each_set_bit() for mask of u64 type.
Changes in v1:
o On review recommendation of sashiko bot, in the function dw_hdma_v0_core_off(), the loop iterates over registers as per the number of channels enabled and not on total number of channels supported.
o Changed mask types to u64 for higher channel counts.
--- drivers/dma/dw-edma/dw-edma-core.c | 19 ++++++++++----- drivers/dma/dw-edma/dw-edma-core.h | 4 ++-- drivers/dma/dw-edma/dw-edma-pcie.c | 8 +++---- drivers/dma/dw-edma/dw-hdma-v0-core.c | 33 ++++++++++++++++++++------- drivers/dma/dw-edma/dw-hdma-v0-regs.h | 2 +- include/linux/dma/edma.h | 10 ++++---- 6 files changed, 51 insertions(+), 25 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +13 -6
@@ -925,9 +925,9 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) irq = &dw->irq[pos];if (chan->dir ==EDMA_DIR_WRITE ) - irq->wr_mask |=BIT (chan->id); + irq->wr_mask |=BIT_ULL (chan->id);else - irq->rd_mask |=BIT (chan->id); + irq->rd_mask |=BIT_ULL (chan->id); irq->dw = dw;memcpy (&chan->msi, &irq->msi,sizeof (chan->msi));
@@ -1079,6 +1079,8 @@ int dw_edma_probe(struct dw_edma_chip *chip)struct dw_edma *dw;u32 wr_alloc =0 ;u32 rd_alloc =0 ; +u16 max_wr_cnt; +u16 max_rd_cnt;int i, err;if (!chip)
@@ -1094,20 +1096,25 @@ int dw_edma_probe(struct dw_edma_chip *chip) dw->chip = chip; -if (dw->chip->mf ==EDMA_MF_HDMA_NATIVE ) +if (dw->chip->mf ==EDMA_MF_HDMA_NATIVE ) {dw_hdma_v0_core_register (dw); -else + max_wr_cnt =HDMA_MAX_WR_CH ; + max_rd_cnt =HDMA_MAX_RD_CH ; + }else {dw_edma_v0_core_register (dw); + max_wr_cnt =EDMA_MAX_WR_CH ; + max_rd_cnt =EDMA_MAX_RD_CH ; + }raw_spin_lock_init (&dw->lock); dw->wr_ch_cnt =min_t (u16, chip->ll_wr_cnt,dw_edma_core_ch_count (dw,EDMA_DIR_WRITE )); - dw->wr_ch_cnt =min_t (u16, dw->wr_ch_cnt,EDMA_MAX_WR_CH ); + dw->wr_ch_cnt =min_t (u16, dw->wr_ch_cnt, max_wr_cnt); dw->rd_ch_cnt =min_t (u16, chip->ll_rd_cnt,dw_edma_core_ch_count (dw,EDMA_DIR_READ )); - dw->rd_ch_cnt =min_t (u16, dw->rd_ch_cnt,EDMA_MAX_RD_CH ); + dw->rd_ch_cnt =min_t (u16, dw->rd_ch_cnt, max_rd_cnt);if (!dw->wr_ch_cnt && !dw->rd_ch_cnt)return -EINVAL ;
drivers/dma/dw-edma/dw-edma-core.h +2 -2
@@ -91,8 +91,8 @@ struct dw_edma_chan {struct dw_edma_irq {struct msi_msg msi; -u32 wr_mask; -u32 rd_mask; +u64 wr_mask; +u64 rd_mask;struct dw_edma *dw; };
drivers/dma/dw-edma/dw-edma-pcie.c +4 -4
@@ -61,11 +61,11 @@ struct dw_edma_pcie_data {/* eDMA registers location */ struct dw_edma_block rg;/* eDMA memory linked list location */ -struct dw_edma_block ll_wr[EDMA_MAX_WR_CH ]; -struct dw_edma_block ll_rd[EDMA_MAX_RD_CH ]; +struct dw_edma_block ll_wr[HDMA_MAX_WR_CH ]; +struct dw_edma_block ll_rd[HDMA_MAX_RD_CH ];/* eDMA memory data location */ -struct dw_edma_block dt_wr[EDMA_MAX_WR_CH ]; -struct dw_edma_block dt_rd[EDMA_MAX_RD_CH ]; +struct dw_edma_block dt_wr[HDMA_MAX_WR_CH ]; +struct dw_edma_block dt_rd[HDMA_MAX_RD_CH ];/* Other */ enum dw_edma_map_format mf;u8 irqs;
drivers/dma/dw-edma/dw-hdma-v0-core.c +25 -8
@@ -53,13 +53,24 @@ __dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch)static void dw_hdma_v0_core_off (struct dw_edma *dw) {int id; +enum dw_edma_dir dir; + + dir =EDMA_DIR_WRITE ; +for (id =0 ; id < dw->wr_ch_cnt; id++) { +SET_CH_32 (dw, dir, id, int_setup, +HDMA_V0_STOP_INT_MASK |HDMA_V0_ABORT_INT_MASK ); +SET_CH_32 (dw, dir, id, int_clear, +HDMA_V0_STOP_INT_MASK |HDMA_V0_ABORT_INT_MASK ); +SET_CH_32 (dw, dir, id, ch_en,0 ); + } -for (id =0 ; id <HDMA_V0_MAX_NR_CH ; id++) { -SET_BOTH_CH_32 (dw, id, int_setup, -HDMA_V0_STOP_INT_MASK |HDMA_V0_ABORT_INT_MASK ); -SET_BOTH_CH_32 (dw, id, int_clear, -HDMA_V0_STOP_INT_MASK |HDMA_V0_ABORT_INT_MASK ); -SET_BOTH_CH_32 (dw, id, ch_en,0 ); + dir =EDMA_DIR_READ ; +for (id =0 ; id < dw->rd_ch_cnt; id++) { +SET_CH_32 (dw, dir, id, int_setup, +HDMA_V0_STOP_INT_MASK |HDMA_V0_ABORT_INT_MASK ); +SET_CH_32 (dw, dir, id, int_clear, +HDMA_V0_STOP_INT_MASK |HDMA_V0_ABORT_INT_MASK ); +SET_CH_32 (dw, dir, id, ch_en,0 ); } }
@@ -118,7 +129,8 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,unsigned long total, pos, val;irqreturn_t ret =IRQ_NONE ;struct dw_edma_chan *chan; -unsigned long off, mask; +unsigned long off; +u64 mask;if (dir ==EDMA_DIR_WRITE ) { total = dw->wr_ch_cnt;
@@ -130,7 +142,11 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, mask = dw_irq->rd_mask; } -for_each_set_bit (pos, &mask, total) { +while (mask) { + pos =__ffs64 (mask); +if (pos >= total) +break ; + chan = &dw->chan[pos + off]; val =dw_hdma_v0_core_status_int (chan);
@@ -147,6 +163,7 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, ret =IRQ_HANDLED ; } + mask &= mask -1 ; }return ret;
drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -1
@@ -11,7 +11,7 @@#include <linux/dmaengine.h> -#define HDMA_V0_MAX_NR_CH 8 +#define HDMA_V0_MAX_NR_CH 64#define HDMA_V0_CH_EN BIT(0)#define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6)#define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5)
include/linux/dma/edma.h +6 -4
@@ -14,6 +14,8 @@#define EDMA_MAX_WR_CH 8#define EDMA_MAX_RD_CH 8 +#define HDMA_MAX_WR_CH 64 +#define HDMA_MAX_RD_CH 64struct dw_edma ;
@@ -89,12 +91,12 @@ struct dw_edma_chip {u16 ll_wr_cnt;u16 ll_rd_cnt;/* link list address */ -struct dw_edma_region ll_region_wr[EDMA_MAX_WR_CH ]; -struct dw_edma_region ll_region_rd[EDMA_MAX_RD_CH ]; +struct dw_edma_region ll_region_wr[HDMA_MAX_WR_CH ]; +struct dw_edma_region ll_region_rd[HDMA_MAX_RD_CH ];/* data region */ -struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH ]; -struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH ]; +struct dw_edma_region dt_region_wr[HDMA_MAX_WR_CH ]; +struct dw_edma_region dt_region_rd[HDMA_MAX_RD_CH ];/* interrupt emulation */ int db_irq;
Signature · 1 line
2.43.0
[email protected]
AI reviewThank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] The 64-channel HDMA feature is unreachable due to hardcoded legacy limits in the driver bindings.
Pre-existing issues:
- [High] Potential NULL pointer dereference if a shared IRQ fires before dw_irq->dw is initialized. --
commit ae4f94060e0e6e5d4419cd4f95926541afa41dd0
Author: Devendra K Verma <[email protected]>
dmaengine: dw-edma: Enable HDMA 64R/W Channels
This patch expands the designware eDMA driver to support up to 64 read and 64 write channels for the HDMA controller, aligning with the hardware databook. It introduces new macros for the HDMA channel counts, expands the layout of the channel-related structures, updates channel masks to 64-bit, and updates the probe sequence to configure the core based on the controller type.
Quoted 5 lines
drivers/dma/dw-edma/dw-edma-core.c +0
-0
drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) · line 932
Quoted 10 lines
drivers/dma/dw-edma/dw-edma-core.c +2
-2@@ -925,9 +925,9 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) irq = &dw->irq[pos];if (chan->dir ==EDMA_DIR_WRITE ) - irq->wr_mask |=BIT (chan->id); + irq->wr_mask |=BIT_ULL (chan->id);else - irq->rd_mask |=BIT (chan->id); + irq->rd_mask |=BIT_ULL (chan->id); irq->dw = dw;
[Severity: High]
This is a pre-existing issue, but could a shared interrupt fire before irq->dw is initialized here?
In dw_edma_probe(), dw_edma_irq_request() is called before dw_edma_channel_setup(). The IRQs are requested with IRQF_SHARED:
dw_edma_probe() {
...
err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc);
...
err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc);
}If the shared IRQ fires immediately after registration, the interrupt handler might dereference a NULL dw_irq->dw:
dw_edma_interrupt_write()
dw_edma_core_handle_int()
return dw_irq->dw->core->handle_int(dw_irq, dir, done, abort);
Should irq->dw be initialized prior to calling request_irq()?
[ ... ]
include/linux/dma/edma.h line 18
Quoted 10 lines
include/linux/dma/edma.h +2
-0@@ -14,6 +14,8 @@#define EDMA_MAX_WR_CH 8#define EDMA_MAX_RD_CH 8 +#define HDMA_MAX_WR_CH 64 +#define HDMA_MAX_RD_CH 64
[Severity: Medium]
Will these expanded 64-channel limits actually be reachable with the current driver bindings?
Looking at drivers/pci/controller/dwc/pcie-designware.c, dw_pcie_edma_find_channels() still explicitly enforces the legacy 8-channel limit and returns -EINVAL if the configuration exceeds it:
if (!pci->edma.ll_wr_cnt || pci->edma.ll_wr_cnt > EDMA_MAX_WR_CH ||
!pci->edma.ll_rd_cnt || pci->edma.ll_rd_cnt > EDMA_MAX_RD_CH)
return -EINVAL;Similarly, in drivers/dma/dw-edma/dw-edma-pcie.c, the Xilinx MDB HDMA data statically declares 8 channels and dw_edma_pcie_get_xilinx_dma_data() caps the write channels:
pdata->wr_ch_cnt = min(pdata->wr_ch_cnt,
FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH, val));Do the hardcoded legacy limits in the driver bindings prevent the new 64-channel feature from being fully utilized?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Hi
Please check my response inline.
regards,
Devendra
Quoted 35 lines · On 23-Jun-26 17:11, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] The 64-channel HDMA feature is unreachable due to hardcoded legacy limits in the driver bindings.Pre-existing issues:
- [High] Potential NULL pointer dereference if a shared IRQ fires beforedw_irq->dwis initialized. --commit
ae4f94060e0e6e5d4419cd4f95926541afa41dd0
Author: Devendra K Verma <[email protected]>dmaengine: dw-edma: Enable HDMA 64R/W Channels
This patch expands the designware eDMA driver to support up to 64 read and 64 write channels for the HDMA controller, aligning with the hardware databook. It introduces new macros for the HDMA channel counts, expands the layout of the channel-related structures, updates channel masks to 64-bit, and updates the probe sequence to configure the core based on the controller type.
Quoted 14 lines
drivers/dma/dw-edma/dw-edma-core.c +2
-2@@ -925,9 +925,9 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) irq = &dw->irq[pos];if (chan->dir ==EDMA_DIR_WRITE ) - irq->wr_mask |=BIT (chan->id); + irq->wr_mask |=BIT_ULL (chan->id);else - irq->rd_mask |=BIT (chan->id); + irq->rd_mask |=BIT_ULL (chan->id);
Quoted 6 lines
drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) · line 932
Quoted 1 line
drivers/dma/dw-edma/dw-edma-core.c +0
-0irq->dw = dw;[Severity: High]
This is a pre-existing issue, but could a shared interrupt fire beforeirq->dwis initialized here?
As this is pre-existing issue it shall be taken in a separate patch series.
Quoted 13 lines
In
dw_edma_probe(),dw_edma_irq_request()is called beforedw_edma_channel_setup(). The IRQs are requested withIRQF_SHARED:dw_edma_probe() { ... err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc); ... err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc); }If the shared IRQ fires immediately after registration, the interrupt handler might dereference a NULL
dw_irq->dw:
Quoted 7 lines
dw_edma_interrupt_write()dw_edma_core_handle_int() return dw_irq->dw->core->handle_int(dw_irq, dir, done, abort);Should
irq->dwbe initialized prior to callingrequest_irq()?[ ... ]
As the issue existed before any fix of this patch, it shall be taken in a separate patch series.
Quoted 16 lines
include/linux/dma/edma.h line 18
Quoted 11 lines
include/linux/dma/edma.h +2
-0@@ -14,6 +14,8 @@#define EDMA_MAX_WR_CH 8#define EDMA_MAX_RD_CH 8 +#define HDMA_MAX_WR_CH 64 +#define HDMA_MAX_RD_CH 64[Severity: Medium]
Will these expanded 64-channel limits actually be reachable with the current driver bindings?
Quoted 8 lines
Looking at
drivers/pci/controller/dwc/pcie-designware.c,dw_pcie_edma_find_channels()still explicitly enforces the legacy 8-channel limit and returns-EINVALif the configuration exceeds it:if (!pci->edma.ll_wr_cnt || pci->edma.ll_wr_cnt > EDMA_MAX_WR_CH || !pci->edma.ll_rd_cnt || pci->edma.ll_rd_cnt > EDMA_MAX_RD_CH) return -EINVAL;
This particular function targets non-HDMA platforms which suggests that increasing the channel limit will not affect anything here. This code shall be retained as is. It is not affected by increase in limit for HDMA channels.
Quoted 10 lines
Similarly, in
drivers/dma/dw-edma/dw-edma-pcie.c, the Xilinx MDB HDMA data statically declares 8 channels anddw_edma_pcie_get_xilinx_dma_data()caps the write channels:pdata->wr_ch_cnt = min(pdata->wr_ch_cnt, FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH, val));Do the hardcoded legacy limits in the driver bindings prevent the new 64-channel feature from being fully utilized?
It depends on the user, how many channels it enables. If user wishes to enable more that 8 channels, be it read/write, then the current code limits the channels to a ceiling of 8 channels which is a case of under-utilization of HDMA channels. This patch enables the code to handle the max supported HDMA channels which is 64 Read/Write. Thus, using any number of channels below <= 64 is fine when using this patch. This patch would not create any issue for the existing use cases.
Quoted 19 lines · Devendra K Verma
As per 'Designware Cores PCI Express Controller Databook', Section 7.1 - Overview, HDMA supports 64 Read and 64 Write channels. Current controller driver supports up to 8 read and write channels only. In order to utilize all the channels the controller driver need to have the channel related structs and variables as per the number of channels supported by IP. Following changes are made to enable 64 Read / 64 Write channel support:
o Defined HDMA specific macros to reflect the channel count.
o The count ofll_regionsanddt_regionsindw_edma_chipanddw_edma_pcie_datashall be in accordance to number of read and write channels.
o Indw_edma_probe()configure the channels as per the channels of the IP used.
o Changed mask types to u64 for higher channel counts.Signed-off-by Devendra K Verma <[email protected]>---
...
Quoted 12 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +2
-1@@ -118,7 +129,8 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,unsigned long total, pos, val;irqreturn_t ret =IRQ_NONE ;struct dw_edma_chan *chan; -unsigned long off, mask; +unsigned long off; +u64 mask;if (dir ==EDMA_DIR_WRITE ) { total = dw->wr_ch_cnt;
drivers/dma/dw-edma/dw-hdma-v0-core.c dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, · line 145
Quoted 5 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +1
-1@@ -130,7 +142,11 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, mask = dw_irq->rd_mask; } -for_each_set_bit (pos, &mask, total) { +while (mask) {
can you use DECLARE_BITMAP(status_mask, 64); and keep original for_each_set_bit() ref:
https://lore.kernel.org/dmaengine/aj1JrufD1vIZH06s@lizhi-Precision-Tower-5810/T/#u
Frank
Quoted 42 lines · Frank Li
Quoted 19 lines · Devendra K Verma
As per 'Designware Cores PCI Express Controller Databook', Section 7.1 - Overview, HDMA supports 64 Read and 64 Write channels. Current controller driver supports up to 8 read and write channels only. In order to utilize all the channels the controller driver need to have the channel related structs and variables as per the number of channels supported by IP. Following changes are made to enable 64 Read / 64 Write channel support:
o Defined HDMA specific macros to reflect the channel count.
o The count ofll_regionsanddt_regionsindw_edma_chipanddw_edma_pcie_datashall be in accordance to number of read and write channels.
o Indw_edma_probe()configure the channels as per the channels of the IP used.
o Changed mask types to u64 for higher channel counts.Signed-off-by Devendra K Verma <[email protected]>---
...
Quoted 12 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +2
-1@@ -118,7 +129,8 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,unsigned long total, pos, val;irqreturn_t ret =IRQ_NONE ;struct dw_edma_chan *chan; -unsigned long off, mask; +unsigned long off; +u64 mask;if (dir ==EDMA_DIR_WRITE ) { total = dw->wr_ch_cnt;drivers/dma/dw-edma/dw-hdma-v0-core.c dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, · line 145
Quoted 5 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +1
-1@@ -130,7 +142,11 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, mask = dw_irq->rd_mask; } -for_each_set_bit (pos, &mask, total) { +while (mask) {can you use
DECLARE_BITMAP(status_mask, 64); and keep originalfor_each_set_bit()ref:
Quoted 4 lines
https://lore.kernel.org/dmaengine/aj1JrufD1vIZH06s@lizhi-Precision-Tower-5810/T/#u
Frank
Hi Frank, thank you for the suggestion!
This is also appreciation comment in the way you review and provide example for the ease of reviewer and patch developers which helps in speed up evaluation and implementation for that particular suggestion.
I will push the changes in next review.
-Devendra