The device_terminate_all() callback for this driver stops current DMA operations by clearing RUNSTOP bit in the control register and waiting HALTED bit set in the status register. But AXI CDMA which is one of the supported DMA engine by this driver does not provide the run / stop controls and those bits in the control and status registers are reserved. So when device_terminate_all() is called, the error message is printed and the channel is marked as having errors in xilinx_dma_halt(). This change adds stop_transfer() callback which differentiates CDMA and other DMA engine. The CDMA's one avoids the unsupported operations and instead polls the status register to check if the DMA operations are in progress for AXI CDMA. Cc: Vinod Koul <vinod.koul@xxxxxxxxx> Cc: Kedareswara rao Appana <appana.durga.rao@xxxxxxxxxx> Cc: Michal Simek <monstr@xxxxxxxxx> Signed-off-by: Akinobu Mita <akinobu.mita@xxxxxxxxx> --- * v2 - introduce stop_transfer() callback to differentiate DMA IP's halt drivers/dma/xilinx/xilinx_dma.c | 49 +++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index 8288fe4..df1676e 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -331,6 +331,7 @@ struct xilinx_dma_tx_descriptor { * @seg_v: Statically allocated segments base * @cyclic_seg_v: Statically allocated segment base for cyclic transfers * @start_transfer: Differentiate b/w DMA IP's transfer + * @stop_transfer: Differentiate b/w DMA IP's quiesce */ struct xilinx_dma_chan { struct xilinx_dma_device *xdev; @@ -361,6 +362,7 @@ struct xilinx_dma_chan { struct xilinx_axidma_tx_segment *seg_v; struct xilinx_axidma_tx_segment *cyclic_seg_v; void (*start_transfer)(struct xilinx_dma_chan *chan); + int (*stop_transfer)(struct xilinx_dma_chan *chan); u16 tdest; }; @@ -946,26 +948,32 @@ static bool xilinx_dma_is_idle(struct xilinx_dma_chan *chan) } /** - * xilinx_dma_halt - Halt DMA channel + * xilinx_dma_stop_transfer - Halt DMA channel * @chan: Driver specific DMA channel */ -static void xilinx_dma_halt(struct xilinx_dma_chan *chan) +static int xilinx_dma_stop_transfer(struct xilinx_dma_chan *chan) { - int err; u32 val; dma_ctrl_clr(chan, XILINX_DMA_REG_DMACR, XILINX_DMA_DMACR_RUNSTOP); /* Wait for the hardware to halt */ - err = xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, - (val & XILINX_DMA_DMASR_HALTED), 0, - XILINX_DMA_LOOP_COUNT); + return xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, + val & XILINX_DMA_DMASR_HALTED, 0, + XILINX_DMA_LOOP_COUNT); +} - if (err) { - dev_err(chan->dev, "Cannot stop channel %p: %x\n", - chan, dma_ctrl_read(chan, XILINX_DMA_REG_DMASR)); - chan->err = true; - } +/** + * xilinx_cdma_stop_transfer - Wait for the current transfer to complete + * @chan: Driver specific DMA channel + */ +static int xilinx_cdma_stop_transfer(struct xilinx_dma_chan *chan) +{ + u32 val; + + return xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, + val & XILINX_DMA_DMASR_IDLE, 0, + XILINX_DMA_LOOP_COUNT); } /** @@ -2003,12 +2011,17 @@ static int xilinx_dma_terminate_all(struct dma_chan *dchan) { struct xilinx_dma_chan *chan = to_xilinx_chan(dchan); u32 reg; + int err; if (chan->cyclic) xilinx_dma_chan_reset(chan); - /* Halt the DMA engine */ - xilinx_dma_halt(chan); + err = chan->stop_transfer(chan); + if (err) { + dev_err(chan->dev, "Cannot stop channel %p: %x\n", + chan, dma_ctrl_read(chan, XILINX_DMA_REG_DMASR)); + chan->err = true; + } /* Remove and free all of the descriptors in the lists */ xilinx_dma_free_descriptors(chan); @@ -2397,12 +2410,16 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, return err; } - if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) + if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { chan->start_transfer = xilinx_dma_start_transfer; - else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA) + chan->stop_transfer = xilinx_dma_stop_transfer; + } else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA) { chan->start_transfer = xilinx_cdma_start_transfer; - else + chan->stop_transfer = xilinx_cdma_stop_transfer; + } else { chan->start_transfer = xilinx_vdma_start_transfer; + chan->stop_transfer = xilinx_dma_stop_transfer; + } /* Initialize the tasklet */ tasklet_init(&chan->tasklet, xilinx_dma_do_tasklet, -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe dmaengine" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html