dmaengine: xilinx: fix device_terminate_all() callback for AXI CDMA
authorAkinobu Mita <akinobu.mita@gmail.com>
Mon, 13 Mar 2017 15:59:11 +0000 (00:59 +0900)
committerVinod Koul <vinod.koul@intel.com>
Tue, 14 Mar 2017 10:44:29 +0000 (16:14 +0530)
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@intel.com>
Cc: Kedareswara rao Appana <appana.durga.rao@xilinx.com>
Cc: Michal Simek <monstr@monstr.eu>
Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
drivers/dma/xilinx/xilinx_dma.c

index 8288fe4..df1676e 100644 (file)
@@ -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,