1fda8d26eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20e3b67b3SLars-Peter Clausen /*
30e3b67b3SLars-Peter Clausen * Driver for the Analog Devices AXI-DMAC core
40e3b67b3SLars-Peter Clausen *
5f4a9fe97SAlexandru Ardelean * Copyright 2013-2019 Analog Devices Inc.
60e3b67b3SLars-Peter Clausen * Author: Lars-Peter Clausen <lars@metafoo.de>
70e3b67b3SLars-Peter Clausen */
80e3b67b3SLars-Peter Clausen
978a2f92eSAlexandru Ardelean #include <linux/bitfield.h>
100e3b67b3SLars-Peter Clausen #include <linux/clk.h>
110e3b67b3SLars-Peter Clausen #include <linux/device.h>
120e3b67b3SLars-Peter Clausen #include <linux/dma-mapping.h>
130e3b67b3SLars-Peter Clausen #include <linux/dmaengine.h>
140e3b67b3SLars-Peter Clausen #include <linux/err.h>
150e3b67b3SLars-Peter Clausen #include <linux/interrupt.h>
160e3b67b3SLars-Peter Clausen #include <linux/io.h>
170e3b67b3SLars-Peter Clausen #include <linux/kernel.h>
180e3b67b3SLars-Peter Clausen #include <linux/module.h>
190e3b67b3SLars-Peter Clausen #include <linux/of.h>
200e3b67b3SLars-Peter Clausen #include <linux/of_dma.h>
219327c7e7SMathias Tausen #include <linux/of_address.h>
220e3b67b3SLars-Peter Clausen #include <linux/platform_device.h>
23fc15be39SAlexandru Ardelean #include <linux/regmap.h>
240e3b67b3SLars-Peter Clausen #include <linux/slab.h>
25a5b20600SLars-Peter Clausen #include <linux/fpga/adi-axi-common.h>
260e3b67b3SLars-Peter Clausen
270e3b67b3SLars-Peter Clausen #include <dt-bindings/dma/axi-dmac.h>
280e3b67b3SLars-Peter Clausen
290e3b67b3SLars-Peter Clausen #include "dmaengine.h"
300e3b67b3SLars-Peter Clausen #include "virt-dma.h"
310e3b67b3SLars-Peter Clausen
320e3b67b3SLars-Peter Clausen /*
330e3b67b3SLars-Peter Clausen * The AXI-DMAC is a soft IP core that is used in FPGA designs. The core has
340e3b67b3SLars-Peter Clausen * various instantiation parameters which decided the exact feature set support
350e3b67b3SLars-Peter Clausen * by the core.
360e3b67b3SLars-Peter Clausen *
370e3b67b3SLars-Peter Clausen * Each channel of the core has a source interface and a destination interface.
380e3b67b3SLars-Peter Clausen * The number of channels and the type of the channel interfaces is selected at
390e3b67b3SLars-Peter Clausen * configuration time. A interface can either be a connected to a central memory
400e3b67b3SLars-Peter Clausen * interconnect, which allows access to system memory, or it can be connected to
410e3b67b3SLars-Peter Clausen * a dedicated bus which is directly connected to a data port on a peripheral.
420e3b67b3SLars-Peter Clausen * Given that those are configuration options of the core that are selected when
430e3b67b3SLars-Peter Clausen * it is instantiated this means that they can not be changed by software at
440e3b67b3SLars-Peter Clausen * runtime. By extension this means that each channel is uni-directional. It can
450e3b67b3SLars-Peter Clausen * either be device to memory or memory to device, but not both. Also since the
460e3b67b3SLars-Peter Clausen * device side is a dedicated data bus only connected to a single peripheral
470e3b67b3SLars-Peter Clausen * there is no address than can or needs to be configured for the device side.
480e3b67b3SLars-Peter Clausen */
490e3b67b3SLars-Peter Clausen
5078a2f92eSAlexandru Ardelean #define AXI_DMAC_REG_INTERFACE_DESC 0x10
5178a2f92eSAlexandru Ardelean #define AXI_DMAC_DMA_SRC_TYPE_MSK GENMASK(13, 12)
5278a2f92eSAlexandru Ardelean #define AXI_DMAC_DMA_SRC_TYPE_GET(x) FIELD_GET(AXI_DMAC_DMA_SRC_TYPE_MSK, x)
5378a2f92eSAlexandru Ardelean #define AXI_DMAC_DMA_SRC_WIDTH_MSK GENMASK(11, 8)
5478a2f92eSAlexandru Ardelean #define AXI_DMAC_DMA_SRC_WIDTH_GET(x) FIELD_GET(AXI_DMAC_DMA_SRC_WIDTH_MSK, x)
5578a2f92eSAlexandru Ardelean #define AXI_DMAC_DMA_DST_TYPE_MSK GENMASK(5, 4)
5678a2f92eSAlexandru Ardelean #define AXI_DMAC_DMA_DST_TYPE_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_TYPE_MSK, x)
5778a2f92eSAlexandru Ardelean #define AXI_DMAC_DMA_DST_WIDTH_MSK GENMASK(3, 0)
5878a2f92eSAlexandru Ardelean #define AXI_DMAC_DMA_DST_WIDTH_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_WIDTH_MSK, x)
599327c7e7SMathias Tausen #define AXI_DMAC_REG_COHERENCY_DESC 0x14
609327c7e7SMathias Tausen #define AXI_DMAC_DST_COHERENT_MSK BIT(0)
619327c7e7SMathias Tausen #define AXI_DMAC_DST_COHERENT_GET(x) FIELD_GET(AXI_DMAC_DST_COHERENT_MSK, x)
6278a2f92eSAlexandru Ardelean
630e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_IRQ_MASK 0x80
640e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_IRQ_PENDING 0x84
650e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_IRQ_SOURCE 0x88
660e3b67b3SLars-Peter Clausen
670e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_CTRL 0x400
680e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_TRANSFER_ID 0x404
690e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_START_TRANSFER 0x408
700e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_FLAGS 0x40c
710e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_DEST_ADDRESS 0x410
720e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_SRC_ADDRESS 0x414
730e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_X_LENGTH 0x418
740e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_Y_LENGTH 0x41c
750e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_DEST_STRIDE 0x420
760e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_SRC_STRIDE 0x424
770e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_TRANSFER_DONE 0x428
780e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_ACTIVE_TRANSFER_ID 0x42c
790e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_STATUS 0x430
800e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_CURRENT_SRC_ADDR 0x434
810e3b67b3SLars-Peter Clausen #define AXI_DMAC_REG_CURRENT_DEST_ADDR 0x438
82e3923592SAlexandru Ardelean #define AXI_DMAC_REG_PARTIAL_XFER_LEN 0x44c
83e3923592SAlexandru Ardelean #define AXI_DMAC_REG_PARTIAL_XFER_ID 0x450
840e3b67b3SLars-Peter Clausen
850e3b67b3SLars-Peter Clausen #define AXI_DMAC_CTRL_ENABLE BIT(0)
860e3b67b3SLars-Peter Clausen #define AXI_DMAC_CTRL_PAUSE BIT(1)
870e3b67b3SLars-Peter Clausen
880e3b67b3SLars-Peter Clausen #define AXI_DMAC_IRQ_SOT BIT(0)
890e3b67b3SLars-Peter Clausen #define AXI_DMAC_IRQ_EOT BIT(1)
900e3b67b3SLars-Peter Clausen
910e3b67b3SLars-Peter Clausen #define AXI_DMAC_FLAG_CYCLIC BIT(0)
92a3ee0bf2SMichael Hennerich #define AXI_DMAC_FLAG_LAST BIT(1)
93e3923592SAlexandru Ardelean #define AXI_DMAC_FLAG_PARTIAL_REPORT BIT(2)
94e3923592SAlexandru Ardelean
95e3923592SAlexandru Ardelean #define AXI_DMAC_FLAG_PARTIAL_XFER_DONE BIT(31)
960e3b67b3SLars-Peter Clausen
97008913dbSLars-Peter Clausen /* The maximum ID allocated by the hardware is 31 */
98008913dbSLars-Peter Clausen #define AXI_DMAC_SG_UNUSED 32U
99008913dbSLars-Peter Clausen
1000e3b67b3SLars-Peter Clausen struct axi_dmac_sg {
1010e3b67b3SLars-Peter Clausen dma_addr_t src_addr;
1020e3b67b3SLars-Peter Clausen dma_addr_t dest_addr;
1030e3b67b3SLars-Peter Clausen unsigned int x_len;
1040e3b67b3SLars-Peter Clausen unsigned int y_len;
1050e3b67b3SLars-Peter Clausen unsigned int dest_stride;
1060e3b67b3SLars-Peter Clausen unsigned int src_stride;
1070e3b67b3SLars-Peter Clausen unsigned int id;
108e3923592SAlexandru Ardelean unsigned int partial_len;
109008913dbSLars-Peter Clausen bool schedule_when_free;
1100e3b67b3SLars-Peter Clausen };
1110e3b67b3SLars-Peter Clausen
1120e3b67b3SLars-Peter Clausen struct axi_dmac_desc {
1130e3b67b3SLars-Peter Clausen struct virt_dma_desc vdesc;
1140e3b67b3SLars-Peter Clausen bool cyclic;
115e28d9155SAlexandru Ardelean bool have_partial_xfer;
1160e3b67b3SLars-Peter Clausen
1170e3b67b3SLars-Peter Clausen unsigned int num_submitted;
1180e3b67b3SLars-Peter Clausen unsigned int num_completed;
1190e3b67b3SLars-Peter Clausen unsigned int num_sgs;
1200e3b67b3SLars-Peter Clausen struct axi_dmac_sg sg[];
1210e3b67b3SLars-Peter Clausen };
1220e3b67b3SLars-Peter Clausen
1230e3b67b3SLars-Peter Clausen struct axi_dmac_chan {
1240e3b67b3SLars-Peter Clausen struct virt_dma_chan vchan;
1250e3b67b3SLars-Peter Clausen
1260e3b67b3SLars-Peter Clausen struct axi_dmac_desc *next_desc;
1270e3b67b3SLars-Peter Clausen struct list_head active_descs;
1280e3b67b3SLars-Peter Clausen enum dma_transfer_direction direction;
1290e3b67b3SLars-Peter Clausen
1300e3b67b3SLars-Peter Clausen unsigned int src_width;
1310e3b67b3SLars-Peter Clausen unsigned int dest_width;
1320e3b67b3SLars-Peter Clausen unsigned int src_type;
1330e3b67b3SLars-Peter Clausen unsigned int dest_type;
1340e3b67b3SLars-Peter Clausen
1350e3b67b3SLars-Peter Clausen unsigned int max_length;
136a5b20600SLars-Peter Clausen unsigned int address_align_mask;
137a5b20600SLars-Peter Clausen unsigned int length_align_mask;
1380e3b67b3SLars-Peter Clausen
139e3923592SAlexandru Ardelean bool hw_partial_xfer;
1400e3b67b3SLars-Peter Clausen bool hw_cyclic;
1410e3b67b3SLars-Peter Clausen bool hw_2d;
1420e3b67b3SLars-Peter Clausen };
1430e3b67b3SLars-Peter Clausen
1440e3b67b3SLars-Peter Clausen struct axi_dmac {
1450e3b67b3SLars-Peter Clausen void __iomem *base;
1460e3b67b3SLars-Peter Clausen int irq;
1470e3b67b3SLars-Peter Clausen
1480e3b67b3SLars-Peter Clausen struct clk *clk;
1490e3b67b3SLars-Peter Clausen
1500e3b67b3SLars-Peter Clausen struct dma_device dma_dev;
1510e3b67b3SLars-Peter Clausen struct axi_dmac_chan chan;
1520e3b67b3SLars-Peter Clausen };
1530e3b67b3SLars-Peter Clausen
chan_to_axi_dmac(struct axi_dmac_chan * chan)1540e3b67b3SLars-Peter Clausen static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
1550e3b67b3SLars-Peter Clausen {
1560e3b67b3SLars-Peter Clausen return container_of(chan->vchan.chan.device, struct axi_dmac,
1570e3b67b3SLars-Peter Clausen dma_dev);
1580e3b67b3SLars-Peter Clausen }
1590e3b67b3SLars-Peter Clausen
to_axi_dmac_chan(struct dma_chan * c)1600e3b67b3SLars-Peter Clausen static struct axi_dmac_chan *to_axi_dmac_chan(struct dma_chan *c)
1610e3b67b3SLars-Peter Clausen {
1620e3b67b3SLars-Peter Clausen return container_of(c, struct axi_dmac_chan, vchan.chan);
1630e3b67b3SLars-Peter Clausen }
1640e3b67b3SLars-Peter Clausen
to_axi_dmac_desc(struct virt_dma_desc * vdesc)1650e3b67b3SLars-Peter Clausen static struct axi_dmac_desc *to_axi_dmac_desc(struct virt_dma_desc *vdesc)
1660e3b67b3SLars-Peter Clausen {
1670e3b67b3SLars-Peter Clausen return container_of(vdesc, struct axi_dmac_desc, vdesc);
1680e3b67b3SLars-Peter Clausen }
1690e3b67b3SLars-Peter Clausen
axi_dmac_write(struct axi_dmac * axi_dmac,unsigned int reg,unsigned int val)1700e3b67b3SLars-Peter Clausen static void axi_dmac_write(struct axi_dmac *axi_dmac, unsigned int reg,
1710e3b67b3SLars-Peter Clausen unsigned int val)
1720e3b67b3SLars-Peter Clausen {
1730e3b67b3SLars-Peter Clausen writel(val, axi_dmac->base + reg);
1740e3b67b3SLars-Peter Clausen }
1750e3b67b3SLars-Peter Clausen
axi_dmac_read(struct axi_dmac * axi_dmac,unsigned int reg)1760e3b67b3SLars-Peter Clausen static int axi_dmac_read(struct axi_dmac *axi_dmac, unsigned int reg)
1770e3b67b3SLars-Peter Clausen {
1780e3b67b3SLars-Peter Clausen return readl(axi_dmac->base + reg);
1790e3b67b3SLars-Peter Clausen }
1800e3b67b3SLars-Peter Clausen
axi_dmac_src_is_mem(struct axi_dmac_chan * chan)1810e3b67b3SLars-Peter Clausen static int axi_dmac_src_is_mem(struct axi_dmac_chan *chan)
1820e3b67b3SLars-Peter Clausen {
1830e3b67b3SLars-Peter Clausen return chan->src_type == AXI_DMAC_BUS_TYPE_AXI_MM;
1840e3b67b3SLars-Peter Clausen }
1850e3b67b3SLars-Peter Clausen
axi_dmac_dest_is_mem(struct axi_dmac_chan * chan)1860e3b67b3SLars-Peter Clausen static int axi_dmac_dest_is_mem(struct axi_dmac_chan *chan)
1870e3b67b3SLars-Peter Clausen {
1880e3b67b3SLars-Peter Clausen return chan->dest_type == AXI_DMAC_BUS_TYPE_AXI_MM;
1890e3b67b3SLars-Peter Clausen }
1900e3b67b3SLars-Peter Clausen
axi_dmac_check_len(struct axi_dmac_chan * chan,unsigned int len)1910e3b67b3SLars-Peter Clausen static bool axi_dmac_check_len(struct axi_dmac_chan *chan, unsigned int len)
1920e3b67b3SLars-Peter Clausen {
193921234e0SLars-Peter Clausen if (len == 0)
1940e3b67b3SLars-Peter Clausen return false;
195a5b20600SLars-Peter Clausen if ((len & chan->length_align_mask) != 0) /* Not aligned */
1960e3b67b3SLars-Peter Clausen return false;
1970e3b67b3SLars-Peter Clausen return true;
1980e3b67b3SLars-Peter Clausen }
1990e3b67b3SLars-Peter Clausen
axi_dmac_check_addr(struct axi_dmac_chan * chan,dma_addr_t addr)2000e3b67b3SLars-Peter Clausen static bool axi_dmac_check_addr(struct axi_dmac_chan *chan, dma_addr_t addr)
2010e3b67b3SLars-Peter Clausen {
202a5b20600SLars-Peter Clausen if ((addr & chan->address_align_mask) != 0) /* Not aligned */
2030e3b67b3SLars-Peter Clausen return false;
2040e3b67b3SLars-Peter Clausen return true;
2050e3b67b3SLars-Peter Clausen }
2060e3b67b3SLars-Peter Clausen
axi_dmac_start_transfer(struct axi_dmac_chan * chan)2070e3b67b3SLars-Peter Clausen static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
2080e3b67b3SLars-Peter Clausen {
2090e3b67b3SLars-Peter Clausen struct axi_dmac *dmac = chan_to_axi_dmac(chan);
2100e3b67b3SLars-Peter Clausen struct virt_dma_desc *vdesc;
2110e3b67b3SLars-Peter Clausen struct axi_dmac_desc *desc;
2120e3b67b3SLars-Peter Clausen struct axi_dmac_sg *sg;
2130e3b67b3SLars-Peter Clausen unsigned int flags = 0;
2140e3b67b3SLars-Peter Clausen unsigned int val;
2150e3b67b3SLars-Peter Clausen
2160e3b67b3SLars-Peter Clausen val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER);
2170e3b67b3SLars-Peter Clausen if (val) /* Queue is full, wait for the next SOT IRQ */
2180e3b67b3SLars-Peter Clausen return;
2190e3b67b3SLars-Peter Clausen
2200e3b67b3SLars-Peter Clausen desc = chan->next_desc;
2210e3b67b3SLars-Peter Clausen
2220e3b67b3SLars-Peter Clausen if (!desc) {
2230e3b67b3SLars-Peter Clausen vdesc = vchan_next_desc(&chan->vchan);
2240e3b67b3SLars-Peter Clausen if (!vdesc)
2250e3b67b3SLars-Peter Clausen return;
2260e3b67b3SLars-Peter Clausen list_move_tail(&vdesc->node, &chan->active_descs);
2270e3b67b3SLars-Peter Clausen desc = to_axi_dmac_desc(vdesc);
2280e3b67b3SLars-Peter Clausen }
2290e3b67b3SLars-Peter Clausen sg = &desc->sg[desc->num_submitted];
2300e3b67b3SLars-Peter Clausen
231008913dbSLars-Peter Clausen /* Already queued in cyclic mode. Wait for it to finish */
232008913dbSLars-Peter Clausen if (sg->id != AXI_DMAC_SG_UNUSED) {
233008913dbSLars-Peter Clausen sg->schedule_when_free = true;
234008913dbSLars-Peter Clausen return;
235008913dbSLars-Peter Clausen }
236008913dbSLars-Peter Clausen
2370e3b67b3SLars-Peter Clausen desc->num_submitted++;
238e28d9155SAlexandru Ardelean if (desc->num_submitted == desc->num_sgs ||
239e28d9155SAlexandru Ardelean desc->have_partial_xfer) {
240008913dbSLars-Peter Clausen if (desc->cyclic)
241008913dbSLars-Peter Clausen desc->num_submitted = 0; /* Start again */
2420e3b67b3SLars-Peter Clausen else
243008913dbSLars-Peter Clausen chan->next_desc = NULL;
244a3ee0bf2SMichael Hennerich flags |= AXI_DMAC_FLAG_LAST;
245008913dbSLars-Peter Clausen } else {
2460e3b67b3SLars-Peter Clausen chan->next_desc = desc;
247008913dbSLars-Peter Clausen }
2480e3b67b3SLars-Peter Clausen
2490e3b67b3SLars-Peter Clausen sg->id = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_ID);
2500e3b67b3SLars-Peter Clausen
2510e3b67b3SLars-Peter Clausen if (axi_dmac_dest_is_mem(chan)) {
2520e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, sg->dest_addr);
2530e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, sg->dest_stride);
2540e3b67b3SLars-Peter Clausen }
2550e3b67b3SLars-Peter Clausen
2560e3b67b3SLars-Peter Clausen if (axi_dmac_src_is_mem(chan)) {
2570e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, sg->src_addr);
2580e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, sg->src_stride);
2590e3b67b3SLars-Peter Clausen }
2600e3b67b3SLars-Peter Clausen
2610e3b67b3SLars-Peter Clausen /*
2620e3b67b3SLars-Peter Clausen * If the hardware supports cyclic transfers and there is no callback to
26363ab76dbSLars-Peter Clausen * call and only a single segment, enable hw cyclic mode to avoid
26463ab76dbSLars-Peter Clausen * unnecessary interrupts.
2650e3b67b3SLars-Peter Clausen */
26663ab76dbSLars-Peter Clausen if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback &&
26763ab76dbSLars-Peter Clausen desc->num_sgs == 1)
2680e3b67b3SLars-Peter Clausen flags |= AXI_DMAC_FLAG_CYCLIC;
2690e3b67b3SLars-Peter Clausen
270e3923592SAlexandru Ardelean if (chan->hw_partial_xfer)
271e3923592SAlexandru Ardelean flags |= AXI_DMAC_FLAG_PARTIAL_REPORT;
272e3923592SAlexandru Ardelean
2730e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, sg->x_len - 1);
2740e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, sg->y_len - 1);
2750e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, flags);
2760e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_START_TRANSFER, 1);
2770e3b67b3SLars-Peter Clausen }
2780e3b67b3SLars-Peter Clausen
axi_dmac_active_desc(struct axi_dmac_chan * chan)2790e3b67b3SLars-Peter Clausen static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan)
2800e3b67b3SLars-Peter Clausen {
2810e3b67b3SLars-Peter Clausen return list_first_entry_or_null(&chan->active_descs,
2820e3b67b3SLars-Peter Clausen struct axi_dmac_desc, vdesc.node);
2830e3b67b3SLars-Peter Clausen }
2840e3b67b3SLars-Peter Clausen
axi_dmac_total_sg_bytes(struct axi_dmac_chan * chan,struct axi_dmac_sg * sg)285e3923592SAlexandru Ardelean static inline unsigned int axi_dmac_total_sg_bytes(struct axi_dmac_chan *chan,
286e3923592SAlexandru Ardelean struct axi_dmac_sg *sg)
287e3923592SAlexandru Ardelean {
288e3923592SAlexandru Ardelean if (chan->hw_2d)
289e3923592SAlexandru Ardelean return sg->x_len * sg->y_len;
290e3923592SAlexandru Ardelean else
291e3923592SAlexandru Ardelean return sg->x_len;
292e3923592SAlexandru Ardelean }
293e3923592SAlexandru Ardelean
axi_dmac_dequeue_partial_xfers(struct axi_dmac_chan * chan)294e3923592SAlexandru Ardelean static void axi_dmac_dequeue_partial_xfers(struct axi_dmac_chan *chan)
295e3923592SAlexandru Ardelean {
296e3923592SAlexandru Ardelean struct axi_dmac *dmac = chan_to_axi_dmac(chan);
297e3923592SAlexandru Ardelean struct axi_dmac_desc *desc;
298e3923592SAlexandru Ardelean struct axi_dmac_sg *sg;
299e3923592SAlexandru Ardelean u32 xfer_done, len, id, i;
300e3923592SAlexandru Ardelean bool found_sg;
301e3923592SAlexandru Ardelean
302e3923592SAlexandru Ardelean do {
303e3923592SAlexandru Ardelean len = axi_dmac_read(dmac, AXI_DMAC_REG_PARTIAL_XFER_LEN);
304e3923592SAlexandru Ardelean id = axi_dmac_read(dmac, AXI_DMAC_REG_PARTIAL_XFER_ID);
305e3923592SAlexandru Ardelean
306e3923592SAlexandru Ardelean found_sg = false;
307e3923592SAlexandru Ardelean list_for_each_entry(desc, &chan->active_descs, vdesc.node) {
308e3923592SAlexandru Ardelean for (i = 0; i < desc->num_sgs; i++) {
309e3923592SAlexandru Ardelean sg = &desc->sg[i];
310e3923592SAlexandru Ardelean if (sg->id == AXI_DMAC_SG_UNUSED)
311e3923592SAlexandru Ardelean continue;
312e3923592SAlexandru Ardelean if (sg->id == id) {
313e28d9155SAlexandru Ardelean desc->have_partial_xfer = true;
314e3923592SAlexandru Ardelean sg->partial_len = len;
315e3923592SAlexandru Ardelean found_sg = true;
316e3923592SAlexandru Ardelean break;
317e3923592SAlexandru Ardelean }
318e3923592SAlexandru Ardelean }
319e3923592SAlexandru Ardelean if (found_sg)
320e3923592SAlexandru Ardelean break;
321e3923592SAlexandru Ardelean }
322e3923592SAlexandru Ardelean
323e3923592SAlexandru Ardelean if (found_sg) {
324e3923592SAlexandru Ardelean dev_dbg(dmac->dma_dev.dev,
325e3923592SAlexandru Ardelean "Found partial segment id=%u, len=%u\n",
326e3923592SAlexandru Ardelean id, len);
327e3923592SAlexandru Ardelean } else {
328e3923592SAlexandru Ardelean dev_warn(dmac->dma_dev.dev,
329e3923592SAlexandru Ardelean "Not found partial segment id=%u, len=%u\n",
330e3923592SAlexandru Ardelean id, len);
331e3923592SAlexandru Ardelean }
332e3923592SAlexandru Ardelean
333e3923592SAlexandru Ardelean /* Check if we have any more partial transfers */
334e3923592SAlexandru Ardelean xfer_done = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_DONE);
335e3923592SAlexandru Ardelean xfer_done = !(xfer_done & AXI_DMAC_FLAG_PARTIAL_XFER_DONE);
336e3923592SAlexandru Ardelean
337e3923592SAlexandru Ardelean } while (!xfer_done);
338e3923592SAlexandru Ardelean }
339e3923592SAlexandru Ardelean
axi_dmac_compute_residue(struct axi_dmac_chan * chan,struct axi_dmac_desc * active)340e3923592SAlexandru Ardelean static void axi_dmac_compute_residue(struct axi_dmac_chan *chan,
341e3923592SAlexandru Ardelean struct axi_dmac_desc *active)
342e3923592SAlexandru Ardelean {
343e3923592SAlexandru Ardelean struct dmaengine_result *rslt = &active->vdesc.tx_result;
344e3923592SAlexandru Ardelean unsigned int start = active->num_completed - 1;
345e3923592SAlexandru Ardelean struct axi_dmac_sg *sg;
346e3923592SAlexandru Ardelean unsigned int i, total;
347e3923592SAlexandru Ardelean
348e3923592SAlexandru Ardelean rslt->result = DMA_TRANS_NOERROR;
349e3923592SAlexandru Ardelean rslt->residue = 0;
350e3923592SAlexandru Ardelean
351e3923592SAlexandru Ardelean /*
352e3923592SAlexandru Ardelean * We get here if the last completed segment is partial, which
353e3923592SAlexandru Ardelean * means we can compute the residue from that segment onwards
354e3923592SAlexandru Ardelean */
355e3923592SAlexandru Ardelean for (i = start; i < active->num_sgs; i++) {
356e3923592SAlexandru Ardelean sg = &active->sg[i];
357e3923592SAlexandru Ardelean total = axi_dmac_total_sg_bytes(chan, sg);
358e3923592SAlexandru Ardelean rslt->residue += (total - sg->partial_len);
359e3923592SAlexandru Ardelean }
360e3923592SAlexandru Ardelean }
361e3923592SAlexandru Ardelean
axi_dmac_transfer_done(struct axi_dmac_chan * chan,unsigned int completed_transfers)362008913dbSLars-Peter Clausen static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan,
3630e3b67b3SLars-Peter Clausen unsigned int completed_transfers)
3640e3b67b3SLars-Peter Clausen {
3650e3b67b3SLars-Peter Clausen struct axi_dmac_desc *active;
3660e3b67b3SLars-Peter Clausen struct axi_dmac_sg *sg;
367008913dbSLars-Peter Clausen bool start_next = false;
3680e3b67b3SLars-Peter Clausen
3690e3b67b3SLars-Peter Clausen active = axi_dmac_active_desc(chan);
3700e3b67b3SLars-Peter Clausen if (!active)
371008913dbSLars-Peter Clausen return false;
3720e3b67b3SLars-Peter Clausen
373e3923592SAlexandru Ardelean if (chan->hw_partial_xfer &&
374e3923592SAlexandru Ardelean (completed_transfers & AXI_DMAC_FLAG_PARTIAL_XFER_DONE))
375e3923592SAlexandru Ardelean axi_dmac_dequeue_partial_xfers(chan);
376e3923592SAlexandru Ardelean
3770e3b67b3SLars-Peter Clausen do {
3780e3b67b3SLars-Peter Clausen sg = &active->sg[active->num_completed];
379008913dbSLars-Peter Clausen if (sg->id == AXI_DMAC_SG_UNUSED) /* Not yet submitted */
380008913dbSLars-Peter Clausen break;
3810e3b67b3SLars-Peter Clausen if (!(BIT(sg->id) & completed_transfers))
3820e3b67b3SLars-Peter Clausen break;
3830e3b67b3SLars-Peter Clausen active->num_completed++;
384008913dbSLars-Peter Clausen sg->id = AXI_DMAC_SG_UNUSED;
385008913dbSLars-Peter Clausen if (sg->schedule_when_free) {
386008913dbSLars-Peter Clausen sg->schedule_when_free = false;
387008913dbSLars-Peter Clausen start_next = true;
388008913dbSLars-Peter Clausen }
389008913dbSLars-Peter Clausen
390e3923592SAlexandru Ardelean if (sg->partial_len)
391e3923592SAlexandru Ardelean axi_dmac_compute_residue(chan, active);
392e3923592SAlexandru Ardelean
393008913dbSLars-Peter Clausen if (active->cyclic)
394008913dbSLars-Peter Clausen vchan_cyclic_callback(&active->vdesc);
395008913dbSLars-Peter Clausen
396e3923592SAlexandru Ardelean if (active->num_completed == active->num_sgs ||
397e3923592SAlexandru Ardelean sg->partial_len) {
398008913dbSLars-Peter Clausen if (active->cyclic) {
399008913dbSLars-Peter Clausen active->num_completed = 0; /* wrap around */
400008913dbSLars-Peter Clausen } else {
4010e3b67b3SLars-Peter Clausen list_del(&active->vdesc.node);
4020e3b67b3SLars-Peter Clausen vchan_cookie_complete(&active->vdesc);
4030e3b67b3SLars-Peter Clausen active = axi_dmac_active_desc(chan);
4040e3b67b3SLars-Peter Clausen }
4050e3b67b3SLars-Peter Clausen }
406008913dbSLars-Peter Clausen } while (active);
407008913dbSLars-Peter Clausen
408008913dbSLars-Peter Clausen return start_next;
4090e3b67b3SLars-Peter Clausen }
4100e3b67b3SLars-Peter Clausen
axi_dmac_interrupt_handler(int irq,void * devid)4110e3b67b3SLars-Peter Clausen static irqreturn_t axi_dmac_interrupt_handler(int irq, void *devid)
4120e3b67b3SLars-Peter Clausen {
4130e3b67b3SLars-Peter Clausen struct axi_dmac *dmac = devid;
4140e3b67b3SLars-Peter Clausen unsigned int pending;
415008913dbSLars-Peter Clausen bool start_next = false;
4160e3b67b3SLars-Peter Clausen
4170e3b67b3SLars-Peter Clausen pending = axi_dmac_read(dmac, AXI_DMAC_REG_IRQ_PENDING);
41871831f65SLars-Peter Clausen if (!pending)
41971831f65SLars-Peter Clausen return IRQ_NONE;
42071831f65SLars-Peter Clausen
4210e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_PENDING, pending);
4220e3b67b3SLars-Peter Clausen
4230e3b67b3SLars-Peter Clausen spin_lock(&dmac->chan.vchan.lock);
4240e3b67b3SLars-Peter Clausen /* One or more transfers have finished */
4250e3b67b3SLars-Peter Clausen if (pending & AXI_DMAC_IRQ_EOT) {
4260e3b67b3SLars-Peter Clausen unsigned int completed;
4270e3b67b3SLars-Peter Clausen
4280e3b67b3SLars-Peter Clausen completed = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_DONE);
429008913dbSLars-Peter Clausen start_next = axi_dmac_transfer_done(&dmac->chan, completed);
4300e3b67b3SLars-Peter Clausen }
4310e3b67b3SLars-Peter Clausen /* Space has become available in the descriptor queue */
432008913dbSLars-Peter Clausen if ((pending & AXI_DMAC_IRQ_SOT) || start_next)
4330e3b67b3SLars-Peter Clausen axi_dmac_start_transfer(&dmac->chan);
4340e3b67b3SLars-Peter Clausen spin_unlock(&dmac->chan.vchan.lock);
4350e3b67b3SLars-Peter Clausen
4360e3b67b3SLars-Peter Clausen return IRQ_HANDLED;
4370e3b67b3SLars-Peter Clausen }
4380e3b67b3SLars-Peter Clausen
axi_dmac_terminate_all(struct dma_chan * c)4390e3b67b3SLars-Peter Clausen static int axi_dmac_terminate_all(struct dma_chan *c)
4400e3b67b3SLars-Peter Clausen {
4410e3b67b3SLars-Peter Clausen struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
4420e3b67b3SLars-Peter Clausen struct axi_dmac *dmac = chan_to_axi_dmac(chan);
4430e3b67b3SLars-Peter Clausen unsigned long flags;
4440e3b67b3SLars-Peter Clausen LIST_HEAD(head);
4450e3b67b3SLars-Peter Clausen
4460e3b67b3SLars-Peter Clausen spin_lock_irqsave(&chan->vchan.lock, flags);
4470e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
4480e3b67b3SLars-Peter Clausen chan->next_desc = NULL;
4490e3b67b3SLars-Peter Clausen vchan_get_all_descriptors(&chan->vchan, &head);
4500e3b67b3SLars-Peter Clausen list_splice_tail_init(&chan->active_descs, &head);
4510e3b67b3SLars-Peter Clausen spin_unlock_irqrestore(&chan->vchan.lock, flags);
4520e3b67b3SLars-Peter Clausen
4530e3b67b3SLars-Peter Clausen vchan_dma_desc_free_list(&chan->vchan, &head);
4540e3b67b3SLars-Peter Clausen
4550e3b67b3SLars-Peter Clausen return 0;
4560e3b67b3SLars-Peter Clausen }
4570e3b67b3SLars-Peter Clausen
axi_dmac_synchronize(struct dma_chan * c)458860dd64cSLars-Peter Clausen static void axi_dmac_synchronize(struct dma_chan *c)
459860dd64cSLars-Peter Clausen {
460860dd64cSLars-Peter Clausen struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
461860dd64cSLars-Peter Clausen
462860dd64cSLars-Peter Clausen vchan_synchronize(&chan->vchan);
463860dd64cSLars-Peter Clausen }
464860dd64cSLars-Peter Clausen
axi_dmac_issue_pending(struct dma_chan * c)4650e3b67b3SLars-Peter Clausen static void axi_dmac_issue_pending(struct dma_chan *c)
4660e3b67b3SLars-Peter Clausen {
4670e3b67b3SLars-Peter Clausen struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
4680e3b67b3SLars-Peter Clausen struct axi_dmac *dmac = chan_to_axi_dmac(chan);
4690e3b67b3SLars-Peter Clausen unsigned long flags;
4700e3b67b3SLars-Peter Clausen
4710e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE);
4720e3b67b3SLars-Peter Clausen
4730e3b67b3SLars-Peter Clausen spin_lock_irqsave(&chan->vchan.lock, flags);
4740e3b67b3SLars-Peter Clausen if (vchan_issue_pending(&chan->vchan))
4750e3b67b3SLars-Peter Clausen axi_dmac_start_transfer(chan);
4760e3b67b3SLars-Peter Clausen spin_unlock_irqrestore(&chan->vchan.lock, flags);
4770e3b67b3SLars-Peter Clausen }
4780e3b67b3SLars-Peter Clausen
axi_dmac_alloc_desc(unsigned int num_sgs)4790e3b67b3SLars-Peter Clausen static struct axi_dmac_desc *axi_dmac_alloc_desc(unsigned int num_sgs)
4800e3b67b3SLars-Peter Clausen {
4810e3b67b3SLars-Peter Clausen struct axi_dmac_desc *desc;
482008913dbSLars-Peter Clausen unsigned int i;
4830e3b67b3SLars-Peter Clausen
48448b02a85SGustavo A. R. Silva desc = kzalloc(struct_size(desc, sg, num_sgs), GFP_NOWAIT);
4850e3b67b3SLars-Peter Clausen if (!desc)
4860e3b67b3SLars-Peter Clausen return NULL;
4870e3b67b3SLars-Peter Clausen
488008913dbSLars-Peter Clausen for (i = 0; i < num_sgs; i++)
489008913dbSLars-Peter Clausen desc->sg[i].id = AXI_DMAC_SG_UNUSED;
490008913dbSLars-Peter Clausen
4910e3b67b3SLars-Peter Clausen desc->num_sgs = num_sgs;
4920e3b67b3SLars-Peter Clausen
4930e3b67b3SLars-Peter Clausen return desc;
4940e3b67b3SLars-Peter Clausen }
4950e3b67b3SLars-Peter Clausen
axi_dmac_fill_linear_sg(struct axi_dmac_chan * chan,enum dma_transfer_direction direction,dma_addr_t addr,unsigned int num_periods,unsigned int period_len,struct axi_dmac_sg * sg)496921234e0SLars-Peter Clausen static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan,
497921234e0SLars-Peter Clausen enum dma_transfer_direction direction, dma_addr_t addr,
498921234e0SLars-Peter Clausen unsigned int num_periods, unsigned int period_len,
499921234e0SLars-Peter Clausen struct axi_dmac_sg *sg)
500921234e0SLars-Peter Clausen {
501921234e0SLars-Peter Clausen unsigned int num_segments, i;
502921234e0SLars-Peter Clausen unsigned int segment_size;
503921234e0SLars-Peter Clausen unsigned int len;
504921234e0SLars-Peter Clausen
505921234e0SLars-Peter Clausen /* Split into multiple equally sized segments if necessary */
506921234e0SLars-Peter Clausen num_segments = DIV_ROUND_UP(period_len, chan->max_length);
507921234e0SLars-Peter Clausen segment_size = DIV_ROUND_UP(period_len, num_segments);
508921234e0SLars-Peter Clausen /* Take care of alignment */
509a5b20600SLars-Peter Clausen segment_size = ((segment_size - 1) | chan->length_align_mask) + 1;
510921234e0SLars-Peter Clausen
511921234e0SLars-Peter Clausen for (i = 0; i < num_periods; i++) {
512921234e0SLars-Peter Clausen len = period_len;
513921234e0SLars-Peter Clausen
514921234e0SLars-Peter Clausen while (len > segment_size) {
515921234e0SLars-Peter Clausen if (direction == DMA_DEV_TO_MEM)
516921234e0SLars-Peter Clausen sg->dest_addr = addr;
517921234e0SLars-Peter Clausen else
518921234e0SLars-Peter Clausen sg->src_addr = addr;
519921234e0SLars-Peter Clausen sg->x_len = segment_size;
520921234e0SLars-Peter Clausen sg->y_len = 1;
521921234e0SLars-Peter Clausen sg++;
522921234e0SLars-Peter Clausen addr += segment_size;
523921234e0SLars-Peter Clausen len -= segment_size;
524921234e0SLars-Peter Clausen }
525921234e0SLars-Peter Clausen
526921234e0SLars-Peter Clausen if (direction == DMA_DEV_TO_MEM)
527921234e0SLars-Peter Clausen sg->dest_addr = addr;
528921234e0SLars-Peter Clausen else
529921234e0SLars-Peter Clausen sg->src_addr = addr;
530921234e0SLars-Peter Clausen sg->x_len = len;
531921234e0SLars-Peter Clausen sg->y_len = 1;
532921234e0SLars-Peter Clausen sg++;
533921234e0SLars-Peter Clausen addr += len;
534921234e0SLars-Peter Clausen }
535921234e0SLars-Peter Clausen
536921234e0SLars-Peter Clausen return sg;
537921234e0SLars-Peter Clausen }
538921234e0SLars-Peter Clausen
axi_dmac_prep_slave_sg(struct dma_chan * c,struct scatterlist * sgl,unsigned int sg_len,enum dma_transfer_direction direction,unsigned long flags,void * context)5390e3b67b3SLars-Peter Clausen static struct dma_async_tx_descriptor *axi_dmac_prep_slave_sg(
5400e3b67b3SLars-Peter Clausen struct dma_chan *c, struct scatterlist *sgl,
5410e3b67b3SLars-Peter Clausen unsigned int sg_len, enum dma_transfer_direction direction,
5420e3b67b3SLars-Peter Clausen unsigned long flags, void *context)
5430e3b67b3SLars-Peter Clausen {
5440e3b67b3SLars-Peter Clausen struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
5450e3b67b3SLars-Peter Clausen struct axi_dmac_desc *desc;
546921234e0SLars-Peter Clausen struct axi_dmac_sg *dsg;
5470e3b67b3SLars-Peter Clausen struct scatterlist *sg;
548921234e0SLars-Peter Clausen unsigned int num_sgs;
5490e3b67b3SLars-Peter Clausen unsigned int i;
5500e3b67b3SLars-Peter Clausen
5510e3b67b3SLars-Peter Clausen if (direction != chan->direction)
5520e3b67b3SLars-Peter Clausen return NULL;
5530e3b67b3SLars-Peter Clausen
554921234e0SLars-Peter Clausen num_sgs = 0;
555921234e0SLars-Peter Clausen for_each_sg(sgl, sg, sg_len, i)
556921234e0SLars-Peter Clausen num_sgs += DIV_ROUND_UP(sg_dma_len(sg), chan->max_length);
557921234e0SLars-Peter Clausen
558921234e0SLars-Peter Clausen desc = axi_dmac_alloc_desc(num_sgs);
5590e3b67b3SLars-Peter Clausen if (!desc)
5600e3b67b3SLars-Peter Clausen return NULL;
5610e3b67b3SLars-Peter Clausen
562921234e0SLars-Peter Clausen dsg = desc->sg;
563921234e0SLars-Peter Clausen
5640e3b67b3SLars-Peter Clausen for_each_sg(sgl, sg, sg_len, i) {
5650e3b67b3SLars-Peter Clausen if (!axi_dmac_check_addr(chan, sg_dma_address(sg)) ||
5660e3b67b3SLars-Peter Clausen !axi_dmac_check_len(chan, sg_dma_len(sg))) {
5670e3b67b3SLars-Peter Clausen kfree(desc);
5680e3b67b3SLars-Peter Clausen return NULL;
5690e3b67b3SLars-Peter Clausen }
5700e3b67b3SLars-Peter Clausen
571921234e0SLars-Peter Clausen dsg = axi_dmac_fill_linear_sg(chan, direction, sg_dma_address(sg), 1,
572921234e0SLars-Peter Clausen sg_dma_len(sg), dsg);
5730e3b67b3SLars-Peter Clausen }
5740e3b67b3SLars-Peter Clausen
5750e3b67b3SLars-Peter Clausen desc->cyclic = false;
5760e3b67b3SLars-Peter Clausen
5770e3b67b3SLars-Peter Clausen return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
5780e3b67b3SLars-Peter Clausen }
5790e3b67b3SLars-Peter Clausen
axi_dmac_prep_dma_cyclic(struct dma_chan * c,dma_addr_t buf_addr,size_t buf_len,size_t period_len,enum dma_transfer_direction direction,unsigned long flags)5800e3b67b3SLars-Peter Clausen static struct dma_async_tx_descriptor *axi_dmac_prep_dma_cyclic(
5810e3b67b3SLars-Peter Clausen struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
5820e3b67b3SLars-Peter Clausen size_t period_len, enum dma_transfer_direction direction,
5830e3b67b3SLars-Peter Clausen unsigned long flags)
5840e3b67b3SLars-Peter Clausen {
5850e3b67b3SLars-Peter Clausen struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
5860e3b67b3SLars-Peter Clausen struct axi_dmac_desc *desc;
587921234e0SLars-Peter Clausen unsigned int num_periods, num_segments;
5880e3b67b3SLars-Peter Clausen
5890e3b67b3SLars-Peter Clausen if (direction != chan->direction)
5900e3b67b3SLars-Peter Clausen return NULL;
5910e3b67b3SLars-Peter Clausen
5920e3b67b3SLars-Peter Clausen if (!axi_dmac_check_len(chan, buf_len) ||
5930e3b67b3SLars-Peter Clausen !axi_dmac_check_addr(chan, buf_addr))
5940e3b67b3SLars-Peter Clausen return NULL;
5950e3b67b3SLars-Peter Clausen
5960e3b67b3SLars-Peter Clausen if (period_len == 0 || buf_len % period_len)
5970e3b67b3SLars-Peter Clausen return NULL;
5980e3b67b3SLars-Peter Clausen
5990e3b67b3SLars-Peter Clausen num_periods = buf_len / period_len;
600921234e0SLars-Peter Clausen num_segments = DIV_ROUND_UP(period_len, chan->max_length);
6010e3b67b3SLars-Peter Clausen
602921234e0SLars-Peter Clausen desc = axi_dmac_alloc_desc(num_periods * num_segments);
6030e3b67b3SLars-Peter Clausen if (!desc)
6040e3b67b3SLars-Peter Clausen return NULL;
6050e3b67b3SLars-Peter Clausen
606921234e0SLars-Peter Clausen axi_dmac_fill_linear_sg(chan, direction, buf_addr, num_periods,
607921234e0SLars-Peter Clausen period_len, desc->sg);
6080e3b67b3SLars-Peter Clausen
6090e3b67b3SLars-Peter Clausen desc->cyclic = true;
6100e3b67b3SLars-Peter Clausen
6110e3b67b3SLars-Peter Clausen return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
6120e3b67b3SLars-Peter Clausen }
6130e3b67b3SLars-Peter Clausen
axi_dmac_prep_interleaved(struct dma_chan * c,struct dma_interleaved_template * xt,unsigned long flags)6140e3b67b3SLars-Peter Clausen static struct dma_async_tx_descriptor *axi_dmac_prep_interleaved(
6150e3b67b3SLars-Peter Clausen struct dma_chan *c, struct dma_interleaved_template *xt,
6160e3b67b3SLars-Peter Clausen unsigned long flags)
6170e3b67b3SLars-Peter Clausen {
6180e3b67b3SLars-Peter Clausen struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
6190e3b67b3SLars-Peter Clausen struct axi_dmac_desc *desc;
6200e3b67b3SLars-Peter Clausen size_t dst_icg, src_icg;
6210e3b67b3SLars-Peter Clausen
6220e3b67b3SLars-Peter Clausen if (xt->frame_size != 1)
6230e3b67b3SLars-Peter Clausen return NULL;
6240e3b67b3SLars-Peter Clausen
6250e3b67b3SLars-Peter Clausen if (xt->dir != chan->direction)
6260e3b67b3SLars-Peter Clausen return NULL;
6270e3b67b3SLars-Peter Clausen
6280e3b67b3SLars-Peter Clausen if (axi_dmac_src_is_mem(chan)) {
6290e3b67b3SLars-Peter Clausen if (!xt->src_inc || !axi_dmac_check_addr(chan, xt->src_start))
6300e3b67b3SLars-Peter Clausen return NULL;
6310e3b67b3SLars-Peter Clausen }
6320e3b67b3SLars-Peter Clausen
6330e3b67b3SLars-Peter Clausen if (axi_dmac_dest_is_mem(chan)) {
6340e3b67b3SLars-Peter Clausen if (!xt->dst_inc || !axi_dmac_check_addr(chan, xt->dst_start))
6350e3b67b3SLars-Peter Clausen return NULL;
6360e3b67b3SLars-Peter Clausen }
6370e3b67b3SLars-Peter Clausen
6380e3b67b3SLars-Peter Clausen dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]);
6390e3b67b3SLars-Peter Clausen src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]);
6400e3b67b3SLars-Peter Clausen
6410e3b67b3SLars-Peter Clausen if (chan->hw_2d) {
6420e3b67b3SLars-Peter Clausen if (!axi_dmac_check_len(chan, xt->sgl[0].size) ||
643648865a7SAlexandru Ardelean xt->numf == 0)
6440e3b67b3SLars-Peter Clausen return NULL;
6450e3b67b3SLars-Peter Clausen if (xt->sgl[0].size + dst_icg > chan->max_length ||
6460e3b67b3SLars-Peter Clausen xt->sgl[0].size + src_icg > chan->max_length)
6470e3b67b3SLars-Peter Clausen return NULL;
6480e3b67b3SLars-Peter Clausen } else {
6490e3b67b3SLars-Peter Clausen if (dst_icg != 0 || src_icg != 0)
6500e3b67b3SLars-Peter Clausen return NULL;
6510e3b67b3SLars-Peter Clausen if (chan->max_length / xt->sgl[0].size < xt->numf)
6520e3b67b3SLars-Peter Clausen return NULL;
6530e3b67b3SLars-Peter Clausen if (!axi_dmac_check_len(chan, xt->sgl[0].size * xt->numf))
6540e3b67b3SLars-Peter Clausen return NULL;
6550e3b67b3SLars-Peter Clausen }
6560e3b67b3SLars-Peter Clausen
6570e3b67b3SLars-Peter Clausen desc = axi_dmac_alloc_desc(1);
6580e3b67b3SLars-Peter Clausen if (!desc)
6590e3b67b3SLars-Peter Clausen return NULL;
6600e3b67b3SLars-Peter Clausen
6610e3b67b3SLars-Peter Clausen if (axi_dmac_src_is_mem(chan)) {
6620e3b67b3SLars-Peter Clausen desc->sg[0].src_addr = xt->src_start;
6630e3b67b3SLars-Peter Clausen desc->sg[0].src_stride = xt->sgl[0].size + src_icg;
6640e3b67b3SLars-Peter Clausen }
6650e3b67b3SLars-Peter Clausen
6660e3b67b3SLars-Peter Clausen if (axi_dmac_dest_is_mem(chan)) {
6670e3b67b3SLars-Peter Clausen desc->sg[0].dest_addr = xt->dst_start;
6680e3b67b3SLars-Peter Clausen desc->sg[0].dest_stride = xt->sgl[0].size + dst_icg;
6690e3b67b3SLars-Peter Clausen }
6700e3b67b3SLars-Peter Clausen
6710e3b67b3SLars-Peter Clausen if (chan->hw_2d) {
6720e3b67b3SLars-Peter Clausen desc->sg[0].x_len = xt->sgl[0].size;
6730e3b67b3SLars-Peter Clausen desc->sg[0].y_len = xt->numf;
6740e3b67b3SLars-Peter Clausen } else {
6750e3b67b3SLars-Peter Clausen desc->sg[0].x_len = xt->sgl[0].size * xt->numf;
6760e3b67b3SLars-Peter Clausen desc->sg[0].y_len = 1;
6770e3b67b3SLars-Peter Clausen }
6780e3b67b3SLars-Peter Clausen
6798add6cceSDragos Bogdan if (flags & DMA_CYCLIC)
6808add6cceSDragos Bogdan desc->cyclic = true;
6818add6cceSDragos Bogdan
6820e3b67b3SLars-Peter Clausen return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
6830e3b67b3SLars-Peter Clausen }
6840e3b67b3SLars-Peter Clausen
axi_dmac_free_chan_resources(struct dma_chan * c)6850e3b67b3SLars-Peter Clausen static void axi_dmac_free_chan_resources(struct dma_chan *c)
6860e3b67b3SLars-Peter Clausen {
6870e3b67b3SLars-Peter Clausen vchan_free_chan_resources(to_virt_chan(c));
6880e3b67b3SLars-Peter Clausen }
6890e3b67b3SLars-Peter Clausen
axi_dmac_desc_free(struct virt_dma_desc * vdesc)6900e3b67b3SLars-Peter Clausen static void axi_dmac_desc_free(struct virt_dma_desc *vdesc)
6910e3b67b3SLars-Peter Clausen {
6920e3b67b3SLars-Peter Clausen kfree(container_of(vdesc, struct axi_dmac_desc, vdesc));
6930e3b67b3SLars-Peter Clausen }
6940e3b67b3SLars-Peter Clausen
axi_dmac_regmap_rdwr(struct device * dev,unsigned int reg)695fc15be39SAlexandru Ardelean static bool axi_dmac_regmap_rdwr(struct device *dev, unsigned int reg)
696fc15be39SAlexandru Ardelean {
697fc15be39SAlexandru Ardelean switch (reg) {
698fc15be39SAlexandru Ardelean case AXI_DMAC_REG_IRQ_MASK:
699fc15be39SAlexandru Ardelean case AXI_DMAC_REG_IRQ_SOURCE:
700fc15be39SAlexandru Ardelean case AXI_DMAC_REG_IRQ_PENDING:
701fc15be39SAlexandru Ardelean case AXI_DMAC_REG_CTRL:
702fc15be39SAlexandru Ardelean case AXI_DMAC_REG_TRANSFER_ID:
703fc15be39SAlexandru Ardelean case AXI_DMAC_REG_START_TRANSFER:
704fc15be39SAlexandru Ardelean case AXI_DMAC_REG_FLAGS:
705fc15be39SAlexandru Ardelean case AXI_DMAC_REG_DEST_ADDRESS:
706fc15be39SAlexandru Ardelean case AXI_DMAC_REG_SRC_ADDRESS:
707fc15be39SAlexandru Ardelean case AXI_DMAC_REG_X_LENGTH:
708fc15be39SAlexandru Ardelean case AXI_DMAC_REG_Y_LENGTH:
709fc15be39SAlexandru Ardelean case AXI_DMAC_REG_DEST_STRIDE:
710fc15be39SAlexandru Ardelean case AXI_DMAC_REG_SRC_STRIDE:
711fc15be39SAlexandru Ardelean case AXI_DMAC_REG_TRANSFER_DONE:
712fc15be39SAlexandru Ardelean case AXI_DMAC_REG_ACTIVE_TRANSFER_ID:
713fc15be39SAlexandru Ardelean case AXI_DMAC_REG_STATUS:
714fc15be39SAlexandru Ardelean case AXI_DMAC_REG_CURRENT_SRC_ADDR:
715fc15be39SAlexandru Ardelean case AXI_DMAC_REG_CURRENT_DEST_ADDR:
716fc15be39SAlexandru Ardelean case AXI_DMAC_REG_PARTIAL_XFER_LEN:
717fc15be39SAlexandru Ardelean case AXI_DMAC_REG_PARTIAL_XFER_ID:
718fc15be39SAlexandru Ardelean return true;
719fc15be39SAlexandru Ardelean default:
720fc15be39SAlexandru Ardelean return false;
721fc15be39SAlexandru Ardelean }
722fc15be39SAlexandru Ardelean }
723fc15be39SAlexandru Ardelean
724fc15be39SAlexandru Ardelean static const struct regmap_config axi_dmac_regmap_config = {
725fc15be39SAlexandru Ardelean .reg_bits = 32,
726fc15be39SAlexandru Ardelean .val_bits = 32,
727fc15be39SAlexandru Ardelean .reg_stride = 4,
728fc15be39SAlexandru Ardelean .max_register = AXI_DMAC_REG_PARTIAL_XFER_ID,
729fc15be39SAlexandru Ardelean .readable_reg = axi_dmac_regmap_rdwr,
730fc15be39SAlexandru Ardelean .writeable_reg = axi_dmac_regmap_rdwr,
731fc15be39SAlexandru Ardelean };
732fc15be39SAlexandru Ardelean
axi_dmac_adjust_chan_params(struct axi_dmac_chan * chan)7333061a65cSAlexandru Ardelean static void axi_dmac_adjust_chan_params(struct axi_dmac_chan *chan)
7343061a65cSAlexandru Ardelean {
7353061a65cSAlexandru Ardelean chan->address_align_mask = max(chan->dest_width, chan->src_width) - 1;
7363061a65cSAlexandru Ardelean
7373061a65cSAlexandru Ardelean if (axi_dmac_dest_is_mem(chan) && axi_dmac_src_is_mem(chan))
7383061a65cSAlexandru Ardelean chan->direction = DMA_MEM_TO_MEM;
7393061a65cSAlexandru Ardelean else if (!axi_dmac_dest_is_mem(chan) && axi_dmac_src_is_mem(chan))
7403061a65cSAlexandru Ardelean chan->direction = DMA_MEM_TO_DEV;
7413061a65cSAlexandru Ardelean else if (axi_dmac_dest_is_mem(chan) && !axi_dmac_src_is_mem(chan))
7423061a65cSAlexandru Ardelean chan->direction = DMA_DEV_TO_MEM;
7433061a65cSAlexandru Ardelean else
7443061a65cSAlexandru Ardelean chan->direction = DMA_DEV_TO_DEV;
7453061a65cSAlexandru Ardelean }
7463061a65cSAlexandru Ardelean
7470e3b67b3SLars-Peter Clausen /*
7480e3b67b3SLars-Peter Clausen * The configuration stored in the devicetree matches the configuration
7490e3b67b3SLars-Peter Clausen * parameters of the peripheral instance and allows the driver to know which
7500e3b67b3SLars-Peter Clausen * features are implemented and how it should behave.
7510e3b67b3SLars-Peter Clausen */
axi_dmac_parse_chan_dt(struct device_node * of_chan,struct axi_dmac_chan * chan)7520e3b67b3SLars-Peter Clausen static int axi_dmac_parse_chan_dt(struct device_node *of_chan,
7530e3b67b3SLars-Peter Clausen struct axi_dmac_chan *chan)
7540e3b67b3SLars-Peter Clausen {
7550e3b67b3SLars-Peter Clausen u32 val;
7560e3b67b3SLars-Peter Clausen int ret;
7570e3b67b3SLars-Peter Clausen
7580e3b67b3SLars-Peter Clausen ret = of_property_read_u32(of_chan, "reg", &val);
7590e3b67b3SLars-Peter Clausen if (ret)
7600e3b67b3SLars-Peter Clausen return ret;
7610e3b67b3SLars-Peter Clausen
7620e3b67b3SLars-Peter Clausen /* We only support 1 channel for now */
7630e3b67b3SLars-Peter Clausen if (val != 0)
7640e3b67b3SLars-Peter Clausen return -EINVAL;
7650e3b67b3SLars-Peter Clausen
7660e3b67b3SLars-Peter Clausen ret = of_property_read_u32(of_chan, "adi,source-bus-type", &val);
7670e3b67b3SLars-Peter Clausen if (ret)
7680e3b67b3SLars-Peter Clausen return ret;
7690e3b67b3SLars-Peter Clausen if (val > AXI_DMAC_BUS_TYPE_FIFO)
7700e3b67b3SLars-Peter Clausen return -EINVAL;
7710e3b67b3SLars-Peter Clausen chan->src_type = val;
7720e3b67b3SLars-Peter Clausen
7730e3b67b3SLars-Peter Clausen ret = of_property_read_u32(of_chan, "adi,destination-bus-type", &val);
7740e3b67b3SLars-Peter Clausen if (ret)
7750e3b67b3SLars-Peter Clausen return ret;
7760e3b67b3SLars-Peter Clausen if (val > AXI_DMAC_BUS_TYPE_FIFO)
7770e3b67b3SLars-Peter Clausen return -EINVAL;
7780e3b67b3SLars-Peter Clausen chan->dest_type = val;
7790e3b67b3SLars-Peter Clausen
7800e3b67b3SLars-Peter Clausen ret = of_property_read_u32(of_chan, "adi,source-bus-width", &val);
7810e3b67b3SLars-Peter Clausen if (ret)
7820e3b67b3SLars-Peter Clausen return ret;
7830e3b67b3SLars-Peter Clausen chan->src_width = val / 8;
7840e3b67b3SLars-Peter Clausen
7850e3b67b3SLars-Peter Clausen ret = of_property_read_u32(of_chan, "adi,destination-bus-width", &val);
7860e3b67b3SLars-Peter Clausen if (ret)
7870e3b67b3SLars-Peter Clausen return ret;
7880e3b67b3SLars-Peter Clausen chan->dest_width = val / 8;
7890e3b67b3SLars-Peter Clausen
7903061a65cSAlexandru Ardelean axi_dmac_adjust_chan_params(chan);
7910e3b67b3SLars-Peter Clausen
7920e3b67b3SLars-Peter Clausen return 0;
7930e3b67b3SLars-Peter Clausen }
7940e3b67b3SLars-Peter Clausen
axi_dmac_parse_dt(struct device * dev,struct axi_dmac * dmac)79506b6e88cSAlexandru Ardelean static int axi_dmac_parse_dt(struct device *dev, struct axi_dmac *dmac)
79606b6e88cSAlexandru Ardelean {
79706b6e88cSAlexandru Ardelean struct device_node *of_channels, *of_chan;
79806b6e88cSAlexandru Ardelean int ret;
79906b6e88cSAlexandru Ardelean
80006b6e88cSAlexandru Ardelean of_channels = of_get_child_by_name(dev->of_node, "adi,channels");
80106b6e88cSAlexandru Ardelean if (of_channels == NULL)
80206b6e88cSAlexandru Ardelean return -ENODEV;
80306b6e88cSAlexandru Ardelean
80406b6e88cSAlexandru Ardelean for_each_child_of_node(of_channels, of_chan) {
80506b6e88cSAlexandru Ardelean ret = axi_dmac_parse_chan_dt(of_chan, &dmac->chan);
80606b6e88cSAlexandru Ardelean if (ret) {
80706b6e88cSAlexandru Ardelean of_node_put(of_chan);
80806b6e88cSAlexandru Ardelean of_node_put(of_channels);
80906b6e88cSAlexandru Ardelean return -EINVAL;
81006b6e88cSAlexandru Ardelean }
81106b6e88cSAlexandru Ardelean }
81206b6e88cSAlexandru Ardelean of_node_put(of_channels);
81306b6e88cSAlexandru Ardelean
81406b6e88cSAlexandru Ardelean return 0;
81506b6e88cSAlexandru Ardelean }
81606b6e88cSAlexandru Ardelean
axi_dmac_read_chan_config(struct device * dev,struct axi_dmac * dmac)81778a2f92eSAlexandru Ardelean static int axi_dmac_read_chan_config(struct device *dev, struct axi_dmac *dmac)
81878a2f92eSAlexandru Ardelean {
81978a2f92eSAlexandru Ardelean struct axi_dmac_chan *chan = &dmac->chan;
82078a2f92eSAlexandru Ardelean unsigned int val, desc;
82178a2f92eSAlexandru Ardelean
82278a2f92eSAlexandru Ardelean desc = axi_dmac_read(dmac, AXI_DMAC_REG_INTERFACE_DESC);
82378a2f92eSAlexandru Ardelean if (desc == 0) {
82478a2f92eSAlexandru Ardelean dev_err(dev, "DMA interface register reads zero\n");
82578a2f92eSAlexandru Ardelean return -EFAULT;
82678a2f92eSAlexandru Ardelean }
82778a2f92eSAlexandru Ardelean
82878a2f92eSAlexandru Ardelean val = AXI_DMAC_DMA_SRC_TYPE_GET(desc);
82978a2f92eSAlexandru Ardelean if (val > AXI_DMAC_BUS_TYPE_FIFO) {
83078a2f92eSAlexandru Ardelean dev_err(dev, "Invalid source bus type read: %d\n", val);
83178a2f92eSAlexandru Ardelean return -EINVAL;
83278a2f92eSAlexandru Ardelean }
83378a2f92eSAlexandru Ardelean chan->src_type = val;
83478a2f92eSAlexandru Ardelean
83578a2f92eSAlexandru Ardelean val = AXI_DMAC_DMA_DST_TYPE_GET(desc);
83678a2f92eSAlexandru Ardelean if (val > AXI_DMAC_BUS_TYPE_FIFO) {
83778a2f92eSAlexandru Ardelean dev_err(dev, "Invalid destination bus type read: %d\n", val);
83878a2f92eSAlexandru Ardelean return -EINVAL;
83978a2f92eSAlexandru Ardelean }
84078a2f92eSAlexandru Ardelean chan->dest_type = val;
84178a2f92eSAlexandru Ardelean
84278a2f92eSAlexandru Ardelean val = AXI_DMAC_DMA_SRC_WIDTH_GET(desc);
84378a2f92eSAlexandru Ardelean if (val == 0) {
84478a2f92eSAlexandru Ardelean dev_err(dev, "Source bus width is zero\n");
84578a2f92eSAlexandru Ardelean return -EINVAL;
84678a2f92eSAlexandru Ardelean }
84778a2f92eSAlexandru Ardelean /* widths are stored in log2 */
84878a2f92eSAlexandru Ardelean chan->src_width = 1 << val;
84978a2f92eSAlexandru Ardelean
85078a2f92eSAlexandru Ardelean val = AXI_DMAC_DMA_DST_WIDTH_GET(desc);
85178a2f92eSAlexandru Ardelean if (val == 0) {
85278a2f92eSAlexandru Ardelean dev_err(dev, "Destination bus width is zero\n");
85378a2f92eSAlexandru Ardelean return -EINVAL;
85478a2f92eSAlexandru Ardelean }
85578a2f92eSAlexandru Ardelean chan->dest_width = 1 << val;
85678a2f92eSAlexandru Ardelean
85778a2f92eSAlexandru Ardelean axi_dmac_adjust_chan_params(chan);
85878a2f92eSAlexandru Ardelean
85978a2f92eSAlexandru Ardelean return 0;
86078a2f92eSAlexandru Ardelean }
86178a2f92eSAlexandru Ardelean
axi_dmac_detect_caps(struct axi_dmac * dmac,unsigned int version)862b377e670SAlexandru Ardelean static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
86356009f0dSLars-Peter Clausen {
86456009f0dSLars-Peter Clausen struct axi_dmac_chan *chan = &dmac->chan;
86556009f0dSLars-Peter Clausen
86656009f0dSLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, AXI_DMAC_FLAG_CYCLIC);
86756009f0dSLars-Peter Clausen if (axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS) == AXI_DMAC_FLAG_CYCLIC)
86856009f0dSLars-Peter Clausen chan->hw_cyclic = true;
86956009f0dSLars-Peter Clausen
87056009f0dSLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, 1);
87156009f0dSLars-Peter Clausen if (axi_dmac_read(dmac, AXI_DMAC_REG_Y_LENGTH) == 1)
87256009f0dSLars-Peter Clausen chan->hw_2d = true;
87356009f0dSLars-Peter Clausen
87456009f0dSLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, 0xffffffff);
87556009f0dSLars-Peter Clausen chan->max_length = axi_dmac_read(dmac, AXI_DMAC_REG_X_LENGTH);
87656009f0dSLars-Peter Clausen if (chan->max_length != UINT_MAX)
87756009f0dSLars-Peter Clausen chan->max_length++;
878b5d89905SLars-Peter Clausen
879b5d89905SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, 0xffffffff);
880b5d89905SLars-Peter Clausen if (axi_dmac_read(dmac, AXI_DMAC_REG_DEST_ADDRESS) == 0 &&
881b5d89905SLars-Peter Clausen chan->dest_type == AXI_DMAC_BUS_TYPE_AXI_MM) {
882b5d89905SLars-Peter Clausen dev_err(dmac->dma_dev.dev,
883b5d89905SLars-Peter Clausen "Destination memory-mapped interface not supported.");
884b5d89905SLars-Peter Clausen return -ENODEV;
885b5d89905SLars-Peter Clausen }
886b5d89905SLars-Peter Clausen
887b5d89905SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, 0xffffffff);
888b5d89905SLars-Peter Clausen if (axi_dmac_read(dmac, AXI_DMAC_REG_SRC_ADDRESS) == 0 &&
889b5d89905SLars-Peter Clausen chan->src_type == AXI_DMAC_BUS_TYPE_AXI_MM) {
890b5d89905SLars-Peter Clausen dev_err(dmac->dma_dev.dev,
891b5d89905SLars-Peter Clausen "Source memory-mapped interface not supported.");
892b5d89905SLars-Peter Clausen return -ENODEV;
893b5d89905SLars-Peter Clausen }
894b5d89905SLars-Peter Clausen
895e3923592SAlexandru Ardelean if (version >= ADI_AXI_PCORE_VER(4, 2, 'a'))
896e3923592SAlexandru Ardelean chan->hw_partial_xfer = true;
897e3923592SAlexandru Ardelean
898a5b20600SLars-Peter Clausen if (version >= ADI_AXI_PCORE_VER(4, 1, 'a')) {
899a5b20600SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, 0x00);
900a5b20600SLars-Peter Clausen chan->length_align_mask =
901a5b20600SLars-Peter Clausen axi_dmac_read(dmac, AXI_DMAC_REG_X_LENGTH);
902a5b20600SLars-Peter Clausen } else {
903a5b20600SLars-Peter Clausen chan->length_align_mask = chan->address_align_mask;
904a5b20600SLars-Peter Clausen }
905a5b20600SLars-Peter Clausen
906b5d89905SLars-Peter Clausen return 0;
90756009f0dSLars-Peter Clausen }
90856009f0dSLars-Peter Clausen
axi_dmac_probe(struct platform_device * pdev)9090e3b67b3SLars-Peter Clausen static int axi_dmac_probe(struct platform_device *pdev)
9100e3b67b3SLars-Peter Clausen {
9110e3b67b3SLars-Peter Clausen struct dma_device *dma_dev;
9120e3b67b3SLars-Peter Clausen struct axi_dmac *dmac;
913a5b982afSChuhong Yuan struct regmap *regmap;
914b377e670SAlexandru Ardelean unsigned int version;
9150e3b67b3SLars-Peter Clausen int ret;
9160e3b67b3SLars-Peter Clausen
9170e3b67b3SLars-Peter Clausen dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL);
9180e3b67b3SLars-Peter Clausen if (!dmac)
9190e3b67b3SLars-Peter Clausen return -ENOMEM;
9200e3b67b3SLars-Peter Clausen
9210e3b67b3SLars-Peter Clausen dmac->irq = platform_get_irq(pdev, 0);
92250dc60a2SLars-Peter Clausen if (dmac->irq < 0)
92350dc60a2SLars-Peter Clausen return dmac->irq;
92450dc60a2SLars-Peter Clausen if (dmac->irq == 0)
9250e3b67b3SLars-Peter Clausen return -EINVAL;
9260e3b67b3SLars-Peter Clausen
9274b23603aSTudor Ambarus dmac->base = devm_platform_ioremap_resource(pdev, 0);
9280e3b67b3SLars-Peter Clausen if (IS_ERR(dmac->base))
9290e3b67b3SLars-Peter Clausen return PTR_ERR(dmac->base);
9300e3b67b3SLars-Peter Clausen
9310e3b67b3SLars-Peter Clausen dmac->clk = devm_clk_get(&pdev->dev, NULL);
9320e3b67b3SLars-Peter Clausen if (IS_ERR(dmac->clk))
9330e3b67b3SLars-Peter Clausen return PTR_ERR(dmac->clk);
9340e3b67b3SLars-Peter Clausen
93508b36dbaSAlexandru Ardelean ret = clk_prepare_enable(dmac->clk);
93608b36dbaSAlexandru Ardelean if (ret < 0)
93708b36dbaSAlexandru Ardelean return ret;
93808b36dbaSAlexandru Ardelean
93978a2f92eSAlexandru Ardelean version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION);
94078a2f92eSAlexandru Ardelean
94178a2f92eSAlexandru Ardelean if (version >= ADI_AXI_PCORE_VER(4, 3, 'a'))
94278a2f92eSAlexandru Ardelean ret = axi_dmac_read_chan_config(&pdev->dev, dmac);
94378a2f92eSAlexandru Ardelean else
94406b6e88cSAlexandru Ardelean ret = axi_dmac_parse_dt(&pdev->dev, dmac);
94578a2f92eSAlexandru Ardelean
94606b6e88cSAlexandru Ardelean if (ret < 0)
94708b36dbaSAlexandru Ardelean goto err_clk_disable;
9480e3b67b3SLars-Peter Clausen
949a88fdeceSAlexandru Ardelean INIT_LIST_HEAD(&dmac->chan.active_descs);
950a88fdeceSAlexandru Ardelean
951921234e0SLars-Peter Clausen dma_set_max_seg_size(&pdev->dev, UINT_MAX);
9520e3b67b3SLars-Peter Clausen
9530e3b67b3SLars-Peter Clausen dma_dev = &dmac->dma_dev;
9540e3b67b3SLars-Peter Clausen dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
9550e3b67b3SLars-Peter Clausen dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask);
9569a05045dSDragos Bogdan dma_cap_set(DMA_INTERLEAVE, dma_dev->cap_mask);
9570e3b67b3SLars-Peter Clausen dma_dev->device_free_chan_resources = axi_dmac_free_chan_resources;
9580e3b67b3SLars-Peter Clausen dma_dev->device_tx_status = dma_cookie_status;
9590e3b67b3SLars-Peter Clausen dma_dev->device_issue_pending = axi_dmac_issue_pending;
9600e3b67b3SLars-Peter Clausen dma_dev->device_prep_slave_sg = axi_dmac_prep_slave_sg;
9610e3b67b3SLars-Peter Clausen dma_dev->device_prep_dma_cyclic = axi_dmac_prep_dma_cyclic;
9620e3b67b3SLars-Peter Clausen dma_dev->device_prep_interleaved_dma = axi_dmac_prep_interleaved;
9630e3b67b3SLars-Peter Clausen dma_dev->device_terminate_all = axi_dmac_terminate_all;
964860dd64cSLars-Peter Clausen dma_dev->device_synchronize = axi_dmac_synchronize;
9650e3b67b3SLars-Peter Clausen dma_dev->dev = &pdev->dev;
9660e3b67b3SLars-Peter Clausen dma_dev->src_addr_widths = BIT(dmac->chan.src_width);
9670e3b67b3SLars-Peter Clausen dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width);
9680e3b67b3SLars-Peter Clausen dma_dev->directions = BIT(dmac->chan.direction);
9690e3b67b3SLars-Peter Clausen dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
9700e3b67b3SLars-Peter Clausen INIT_LIST_HEAD(&dma_dev->channels);
9710e3b67b3SLars-Peter Clausen
9720e3b67b3SLars-Peter Clausen dmac->chan.vchan.desc_free = axi_dmac_desc_free;
9730e3b67b3SLars-Peter Clausen vchan_init(&dmac->chan.vchan, dma_dev);
9740e3b67b3SLars-Peter Clausen
975b377e670SAlexandru Ardelean ret = axi_dmac_detect_caps(dmac, version);
976b5d89905SLars-Peter Clausen if (ret)
977b5d89905SLars-Peter Clausen goto err_clk_disable;
97856009f0dSLars-Peter Clausen
9795b969bd1SAlexandru Ardelean dma_dev->copy_align = (dmac->chan.address_align_mask + 1);
9800e3b67b3SLars-Peter Clausen
9810e3b67b3SLars-Peter Clausen axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, 0x00);
9820e3b67b3SLars-Peter Clausen
9839327c7e7SMathias Tausen if (of_dma_is_coherent(pdev->dev.of_node)) {
9849327c7e7SMathias Tausen ret = axi_dmac_read(dmac, AXI_DMAC_REG_COHERENCY_DESC);
9859327c7e7SMathias Tausen
9869327c7e7SMathias Tausen if (version < ADI_AXI_PCORE_VER(4, 4, 'a') ||
9879327c7e7SMathias Tausen !AXI_DMAC_DST_COHERENT_GET(ret)) {
9889327c7e7SMathias Tausen dev_err(dmac->dma_dev.dev,
9899327c7e7SMathias Tausen "Coherent DMA not supported in hardware");
9909327c7e7SMathias Tausen ret = -EINVAL;
9919327c7e7SMathias Tausen goto err_clk_disable;
9929327c7e7SMathias Tausen }
9939327c7e7SMathias Tausen }
9949327c7e7SMathias Tausen
9950e3b67b3SLars-Peter Clausen ret = dma_async_device_register(dma_dev);
9960e3b67b3SLars-Peter Clausen if (ret)
9970e3b67b3SLars-Peter Clausen goto err_clk_disable;
9980e3b67b3SLars-Peter Clausen
9990e3b67b3SLars-Peter Clausen ret = of_dma_controller_register(pdev->dev.of_node,
10000e3b67b3SLars-Peter Clausen of_dma_xlate_by_chan_id, dma_dev);
10010e3b67b3SLars-Peter Clausen if (ret)
10020e3b67b3SLars-Peter Clausen goto err_unregister_device;
10030e3b67b3SLars-Peter Clausen
10049c87572eSMoritz Fischer ret = request_irq(dmac->irq, axi_dmac_interrupt_handler, IRQF_SHARED,
10050e3b67b3SLars-Peter Clausen dev_name(&pdev->dev), dmac);
10060e3b67b3SLars-Peter Clausen if (ret)
10070e3b67b3SLars-Peter Clausen goto err_unregister_of;
10080e3b67b3SLars-Peter Clausen
10090e3b67b3SLars-Peter Clausen platform_set_drvdata(pdev, dmac);
10100e3b67b3SLars-Peter Clausen
1011a5b982afSChuhong Yuan regmap = devm_regmap_init_mmio(&pdev->dev, dmac->base,
1012a5b982afSChuhong Yuan &axi_dmac_regmap_config);
1013a5b982afSChuhong Yuan if (IS_ERR(regmap)) {
1014a5b982afSChuhong Yuan ret = PTR_ERR(regmap);
1015a5b982afSChuhong Yuan goto err_free_irq;
1016a5b982afSChuhong Yuan }
1017fc15be39SAlexandru Ardelean
10180e3b67b3SLars-Peter Clausen return 0;
10190e3b67b3SLars-Peter Clausen
1020a5b982afSChuhong Yuan err_free_irq:
1021a5b982afSChuhong Yuan free_irq(dmac->irq, dmac);
10220e3b67b3SLars-Peter Clausen err_unregister_of:
10230e3b67b3SLars-Peter Clausen of_dma_controller_free(pdev->dev.of_node);
10240e3b67b3SLars-Peter Clausen err_unregister_device:
10250e3b67b3SLars-Peter Clausen dma_async_device_unregister(&dmac->dma_dev);
10260e3b67b3SLars-Peter Clausen err_clk_disable:
10270e3b67b3SLars-Peter Clausen clk_disable_unprepare(dmac->clk);
10280e3b67b3SLars-Peter Clausen
10290e3b67b3SLars-Peter Clausen return ret;
10300e3b67b3SLars-Peter Clausen }
10310e3b67b3SLars-Peter Clausen
axi_dmac_remove(struct platform_device * pdev)10320e3b67b3SLars-Peter Clausen static int axi_dmac_remove(struct platform_device *pdev)
10330e3b67b3SLars-Peter Clausen {
10340e3b67b3SLars-Peter Clausen struct axi_dmac *dmac = platform_get_drvdata(pdev);
10350e3b67b3SLars-Peter Clausen
10360e3b67b3SLars-Peter Clausen free_irq(dmac->irq, dmac);
1037*aa81c7b0SNuno Sa of_dma_controller_free(pdev->dev.of_node);
10380e3b67b3SLars-Peter Clausen tasklet_kill(&dmac->chan.vchan.task);
10390e3b67b3SLars-Peter Clausen dma_async_device_unregister(&dmac->dma_dev);
10400e3b67b3SLars-Peter Clausen clk_disable_unprepare(dmac->clk);
10410e3b67b3SLars-Peter Clausen
10420e3b67b3SLars-Peter Clausen return 0;
10430e3b67b3SLars-Peter Clausen }
10440e3b67b3SLars-Peter Clausen
10450e3b67b3SLars-Peter Clausen static const struct of_device_id axi_dmac_of_match_table[] = {
10460e3b67b3SLars-Peter Clausen { .compatible = "adi,axi-dmac-1.00.a" },
10470e3b67b3SLars-Peter Clausen { },
10480e3b67b3SLars-Peter Clausen };
10499bcfe38fSLars-Peter Clausen MODULE_DEVICE_TABLE(of, axi_dmac_of_match_table);
10500e3b67b3SLars-Peter Clausen
10510e3b67b3SLars-Peter Clausen static struct platform_driver axi_dmac_driver = {
10520e3b67b3SLars-Peter Clausen .driver = {
10530e3b67b3SLars-Peter Clausen .name = "dma-axi-dmac",
10540e3b67b3SLars-Peter Clausen .of_match_table = axi_dmac_of_match_table,
10550e3b67b3SLars-Peter Clausen },
10560e3b67b3SLars-Peter Clausen .probe = axi_dmac_probe,
10570e3b67b3SLars-Peter Clausen .remove = axi_dmac_remove,
10580e3b67b3SLars-Peter Clausen };
10590e3b67b3SLars-Peter Clausen module_platform_driver(axi_dmac_driver);
10600e3b67b3SLars-Peter Clausen
10610e3b67b3SLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
10620e3b67b3SLars-Peter Clausen MODULE_DESCRIPTION("DMA controller driver for the AXI-DMAC controller");
10630e3b67b3SLars-Peter Clausen MODULE_LICENSE("GPL v2");
1064