in xilinx/xilinx_dma.c [2884:3037]
static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
struct device_node *node)
{
struct xilinx_dma_chan *chan;
bool has_dre = false;
u32 value, width;
int err;
/* Allocate and initialize the channel structure */
chan = devm_kzalloc(xdev->dev, sizeof(*chan), GFP_KERNEL);
if (!chan)
return -ENOMEM;
chan->dev = xdev->dev;
chan->xdev = xdev;
chan->desc_pendingcount = 0x0;
chan->ext_addr = xdev->ext_addr;
/* This variable ensures that descriptors are not
* Submitted when dma engine is in progress. This variable is
* Added to avoid polling for a bit in the status register to
* Know dma state in the driver hot path.
*/
chan->idle = true;
spin_lock_init(&chan->lock);
INIT_LIST_HEAD(&chan->pending_list);
INIT_LIST_HEAD(&chan->done_list);
INIT_LIST_HEAD(&chan->active_list);
INIT_LIST_HEAD(&chan->free_seg_list);
/* Retrieve the channel properties from the device tree */
has_dre = of_property_read_bool(node, "xlnx,include-dre");
chan->genlock = of_property_read_bool(node, "xlnx,genlock-mode");
err = of_property_read_u32(node, "xlnx,datawidth", &value);
if (err) {
dev_err(xdev->dev, "missing xlnx,datawidth property\n");
return err;
}
width = value >> 3; /* Convert bits to bytes */
/* If data width is greater than 8 bytes, DRE is not in hw */
if (width > 8)
has_dre = false;
if (!has_dre)
xdev->common.copy_align = (enum dmaengine_alignment)fls(width - 1);
if (of_device_is_compatible(node, "xlnx,axi-vdma-mm2s-channel") ||
of_device_is_compatible(node, "xlnx,axi-dma-mm2s-channel") ||
of_device_is_compatible(node, "xlnx,axi-cdma-channel")) {
chan->direction = DMA_MEM_TO_DEV;
chan->id = xdev->mm2s_chan_id++;
chan->tdest = chan->id;
chan->ctrl_offset = XILINX_DMA_MM2S_CTRL_OFFSET;
if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
chan->desc_offset = XILINX_VDMA_MM2S_DESC_OFFSET;
chan->config.park = 1;
if (xdev->flush_on_fsync == XILINX_DMA_FLUSH_BOTH ||
xdev->flush_on_fsync == XILINX_DMA_FLUSH_MM2S)
chan->flush_on_fsync = true;
}
} else if (of_device_is_compatible(node,
"xlnx,axi-vdma-s2mm-channel") ||
of_device_is_compatible(node,
"xlnx,axi-dma-s2mm-channel")) {
chan->direction = DMA_DEV_TO_MEM;
chan->id = xdev->s2mm_chan_id++;
chan->tdest = chan->id - xdev->dma_config->max_channels / 2;
chan->has_vflip = of_property_read_bool(node,
"xlnx,enable-vert-flip");
if (chan->has_vflip) {
chan->config.vflip_en = dma_read(chan,
XILINX_VDMA_REG_ENABLE_VERTICAL_FLIP) &
XILINX_VDMA_ENABLE_VERTICAL_FLIP;
}
if (xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA)
chan->ctrl_offset = XILINX_MCDMA_S2MM_CTRL_OFFSET;
else
chan->ctrl_offset = XILINX_DMA_S2MM_CTRL_OFFSET;
if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
chan->desc_offset = XILINX_VDMA_S2MM_DESC_OFFSET;
chan->config.park = 1;
if (xdev->flush_on_fsync == XILINX_DMA_FLUSH_BOTH ||
xdev->flush_on_fsync == XILINX_DMA_FLUSH_S2MM)
chan->flush_on_fsync = true;
}
} else {
dev_err(xdev->dev, "Invalid channel compatible node\n");
return -EINVAL;
}
/* Request the interrupt */
chan->irq = of_irq_get(node, chan->tdest);
if (chan->irq < 0)
return dev_err_probe(xdev->dev, chan->irq, "failed to get irq\n");
err = request_irq(chan->irq, xdev->dma_config->irq_handler,
IRQF_SHARED, "xilinx-dma-controller", chan);
if (err) {
dev_err(xdev->dev, "unable to request IRQ %d\n", chan->irq);
return err;
}
if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
chan->start_transfer = xilinx_dma_start_transfer;
chan->stop_transfer = xilinx_dma_stop_transfer;
} else if (xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
chan->start_transfer = xilinx_mcdma_start_transfer;
chan->stop_transfer = xilinx_dma_stop_transfer;
} else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA) {
chan->start_transfer = xilinx_cdma_start_transfer;
chan->stop_transfer = xilinx_cdma_stop_transfer;
} else {
chan->start_transfer = xilinx_vdma_start_transfer;
chan->stop_transfer = xilinx_dma_stop_transfer;
}
/* check if SG is enabled (only for AXIDMA, AXIMCDMA, and CDMA) */
if (xdev->dma_config->dmatype != XDMA_TYPE_VDMA) {
if (xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA ||
dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) &
XILINX_DMA_DMASR_SG_MASK)
chan->has_sg = true;
dev_dbg(chan->dev, "ch %d: SG %s\n", chan->id,
chan->has_sg ? "enabled" : "disabled");
}
/* Initialize the tasklet */
tasklet_setup(&chan->tasklet, xilinx_dma_do_tasklet);
/*
* Initialize the DMA channel and add it to the DMA engine channels
* list.
*/
chan->common.device = &xdev->common;
list_add_tail(&chan->common.device_node, &xdev->common.channels);
xdev->chan[chan->id] = chan;
/* Reset the channel */
err = xilinx_dma_chan_reset(chan);
if (err < 0) {
dev_err(xdev->dev, "Reset channel failed\n");
return err;
}
return 0;
}