Skip to content

Commit a17ce4b

Browse files
tombavinodkoul
authored andcommitted
dmaengine: xilinx_dma: Fix reset related timeout with two-channel AXIDMA
A single AXIDMA controller can have one or two channels. When it has two channels, the reset for both are tied together: resetting one channel resets the other as well. This creates a problem where resetting one channel will reset the registers for both channels, including clearing interrupt enable bits for the other channel, which can then lead to timeouts as the driver is waiting for an interrupt which never comes. The driver currently has a probe-time work around for this: when a channel is created, the driver also resets and enables the interrupts. With two channels the reset for the second channel will clear the interrupt enables for the first one. The work around in the driver is just to manually enable the interrupts again in xilinx_dma_alloc_chan_resources(). This workaround only addresses the probe-time issue. When channels are reset at runtime (e.g., in xilinx_dma_terminate_all() or during error recovery), there's no corresponding mechanism to restore the other channel's interrupt enables. This leads to one channel having its interrupts disabled while the driver expects them to work, causing timeouts and DMA failures. A proper fix is a complicated matter, as we should not reset the other channel when it's operating normally. So, perhaps, there should be some kind of synchronization for a common reset, which is not trivial to implement. To add to the complexity, the driver also supports other DMA types, like VDMA, CDMA and MCDMA, which don't have a shared reset. However, when the two-channel AXIDMA is used in the (assumably) normal use case, providing DMA for a single memory-to-memory device, the common reset is a bit smaller issue: when something bad happens on one channel, or when one channel is terminated, the assumption is that we also want to terminate the other channel. And thus resetting both at the same time is "ok". With that line of thinking we can implement a bit better work around than just the current probe time work around: let's enable the AXIDMA interrupts at xilinx_dma_start_transfer() instead. This ensures interrupts are enabled whenever a transfer starts, regardless of any prior resets that may have cleared them. This approach is also more logical: enable interrupts only when needed for a transfer, rather than at resource allocation time, and, I think, all the other DMA types should also use this model, but I'm reluctant to do such changes as I cannot test them. The reset function still enables interrupts even though it's not needed for AXIDMA anymore, but it's common code for all DMA types (VDMA, CDMA, MCDMA), so leave it unchanged to avoid affecting other variants. Signed-off-by: Tomi Valkeinen <[email protected]> Fixes: c0bba3a ("dmaengine: vdma: Add Support for Xilinx AXI Direct Memory Access Engine") Link: https://patch.msgid.link/[email protected] Signed-off-by: Vinod Koul <[email protected]>
1 parent c7d812e commit a17ce4b

1 file changed

Lines changed: 1 addition & 8 deletions

File tree

drivers/dma/xilinx/xilinx_dma.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,14 +1235,6 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
12351235

12361236
dma_cookie_init(dchan);
12371237

1238-
if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
1239-
/* For AXI DMA resetting once channel will reset the
1240-
* other channel as well so enable the interrupts here.
1241-
*/
1242-
dma_ctrl_set(chan, XILINX_DMA_REG_DMACR,
1243-
XILINX_DMA_DMAXR_ALL_IRQ_MASK);
1244-
}
1245-
12461238
if ((chan->xdev->dma_config->dmatype == XDMA_TYPE_CDMA) && chan->has_sg)
12471239
dma_ctrl_set(chan, XILINX_DMA_REG_DMACR,
12481240
XILINX_CDMA_CR_SGMODE);
@@ -1612,6 +1604,7 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
16121604
head_desc->async_tx.phys);
16131605
reg &= ~XILINX_DMA_CR_DELAY_MAX;
16141606
reg |= chan->irq_delay << XILINX_DMA_CR_DELAY_SHIFT;
1607+
reg |= XILINX_DMA_DMAXR_ALL_IRQ_MASK;
16151608
dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg);
16161609

16171610
xilinx_dma_start(chan);

0 commit comments

Comments
 (0)