Skip to content

Commit

Permalink
dmaengine: xilinx: fix device_terminate_all() callback for AXI CDMA
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Akinobu Mita authored and Vinod Koul committed Mar 14, 2017
1 parent c1ae3cf commit 676f9c2
Showing 1 changed file with 33 additions and 16 deletions.
49 changes: 33 additions & 16 deletions drivers/dma/xilinx/xilinx_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
};

Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 676f9c2

Please sign in to comment.