Commit 6ccd692b authored by Radhey Shyam Pandey's avatar Radhey Shyam Pandey Committed by Vinod Koul

dmaengine: xilinx_dma: Add Xilinx AXI MCDMA Engine driver support

Add support for AXI Multichannel Direct Memory Access (AXI MCDMA)
core, which is a soft Xilinx IP core that provides high-bandwidth
direct memory access between memory and AXI4-Stream target peripherals.
The AXI MCDMA core provides scatter-gather interface with multiple
independent transmit and receive channels. The driver supports
device_prep_slave_sg slave transfer mode.
Signed-off-by: default avatarRadhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
Link: https://lore.kernel.org/r/1571763622-29281-7-git-send-email-radhey.shyam.pandey@xilinx.comSigned-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent c2f6b67d
...@@ -655,6 +655,10 @@ config XILINX_DMA ...@@ -655,6 +655,10 @@ config XILINX_DMA
destination address. destination address.
AXI DMA engine provides high-bandwidth one dimensional direct AXI DMA engine provides high-bandwidth one dimensional direct
memory access between memory and AXI4-Stream target peripherals. memory access between memory and AXI4-Stream target peripherals.
AXI MCDMA engine provides high-bandwidth direct memory access
between memory and AXI4-Stream target peripherals. It provides
the scatter gather interface with multiple channels independent
configuration support.
config XILINX_ZYNQMP_DMA config XILINX_ZYNQMP_DMA
tristate "Xilinx ZynqMP DMA Engine" tristate "Xilinx ZynqMP DMA Engine"
......
...@@ -25,6 +25,12 @@ ...@@ -25,6 +25,12 @@
* The AXI CDMA, is a soft IP, which provides high-bandwidth Direct Memory * The AXI CDMA, is a soft IP, which provides high-bandwidth Direct Memory
* Access (DMA) between a memory-mapped source address and a memory-mapped * Access (DMA) between a memory-mapped source address and a memory-mapped
* destination address. * destination address.
*
* The AXI Multichannel Direct Memory Access (AXI MCDMA) core is a soft
* Xilinx IP that provides high-bandwidth direct memory access between
* memory and AXI4-Stream target peripherals. It provides scatter gather
* (SG) interface with multiple channels independent configuration support.
*
*/ */
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -116,7 +122,7 @@ ...@@ -116,7 +122,7 @@
#define XILINX_VDMA_ENABLE_VERTICAL_FLIP BIT(0) #define XILINX_VDMA_ENABLE_VERTICAL_FLIP BIT(0)
/* HW specific definitions */ /* HW specific definitions */
#define XILINX_DMA_MAX_CHANS_PER_DEVICE 0x2 #define XILINX_DMA_MAX_CHANS_PER_DEVICE 0x20
#define XILINX_DMA_DMAXR_ALL_IRQ_MASK \ #define XILINX_DMA_DMAXR_ALL_IRQ_MASK \
(XILINX_DMA_DMASR_FRM_CNT_IRQ | \ (XILINX_DMA_DMASR_FRM_CNT_IRQ | \
...@@ -179,6 +185,31 @@ ...@@ -179,6 +185,31 @@
#define xilinx_prep_dma_addr_t(addr) \ #define xilinx_prep_dma_addr_t(addr) \
((dma_addr_t)((u64)addr##_##msb << 32 | (addr))) ((dma_addr_t)((u64)addr##_##msb << 32 | (addr)))
/* AXI MCDMA Specific Registers/Offsets */
#define XILINX_MCDMA_MM2S_CTRL_OFFSET 0x0000
#define XILINX_MCDMA_S2MM_CTRL_OFFSET 0x0500
#define XILINX_MCDMA_CHEN_OFFSET 0x0008
#define XILINX_MCDMA_CH_ERR_OFFSET 0x0010
#define XILINX_MCDMA_RXINT_SER_OFFSET 0x0020
#define XILINX_MCDMA_TXINT_SER_OFFSET 0x0028
#define XILINX_MCDMA_CHAN_CR_OFFSET(x) (0x40 + (x) * 0x40)
#define XILINX_MCDMA_CHAN_SR_OFFSET(x) (0x44 + (x) * 0x40)
#define XILINX_MCDMA_CHAN_CDESC_OFFSET(x) (0x48 + (x) * 0x40)
#define XILINX_MCDMA_CHAN_TDESC_OFFSET(x) (0x50 + (x) * 0x40)
/* AXI MCDMA Specific Masks/Shifts */
#define XILINX_MCDMA_COALESCE_SHIFT 16
#define XILINX_MCDMA_COALESCE_MAX 24
#define XILINX_MCDMA_IRQ_ALL_MASK GENMASK(7, 5)
#define XILINX_MCDMA_COALESCE_MASK GENMASK(23, 16)
#define XILINX_MCDMA_CR_RUNSTOP_MASK BIT(0)
#define XILINX_MCDMA_IRQ_IOC_MASK BIT(5)
#define XILINX_MCDMA_IRQ_DELAY_MASK BIT(6)
#define XILINX_MCDMA_IRQ_ERR_MASK BIT(7)
#define XILINX_MCDMA_BD_EOP BIT(30)
#define XILINX_MCDMA_BD_SOP BIT(31)
/** /**
* struct xilinx_vdma_desc_hw - Hardware Descriptor * struct xilinx_vdma_desc_hw - Hardware Descriptor
* @next_desc: Next Descriptor Pointer @0x00 * @next_desc: Next Descriptor Pointer @0x00
...@@ -224,6 +255,30 @@ struct xilinx_axidma_desc_hw { ...@@ -224,6 +255,30 @@ struct xilinx_axidma_desc_hw {
u32 app[XILINX_DMA_NUM_APP_WORDS]; u32 app[XILINX_DMA_NUM_APP_WORDS];
} __aligned(64); } __aligned(64);
/**
* struct xilinx_aximcdma_desc_hw - Hardware Descriptor for AXI MCDMA
* @next_desc: Next Descriptor Pointer @0x00
* @next_desc_msb: MSB of Next Descriptor Pointer @0x04
* @buf_addr: Buffer address @0x08
* @buf_addr_msb: MSB of Buffer address @0x0C
* @rsvd: Reserved field @0x10
* @control: Control Information field @0x14
* @status: Status field @0x18
* @sideband_status: Status of sideband signals @0x1C
* @app: APP Fields @0x20 - 0x30
*/
struct xilinx_aximcdma_desc_hw {
u32 next_desc;
u32 next_desc_msb;
u32 buf_addr;
u32 buf_addr_msb;
u32 rsvd;
u32 control;
u32 status;
u32 sideband_status;
u32 app[XILINX_DMA_NUM_APP_WORDS];
} __aligned(64);
/** /**
* struct xilinx_cdma_desc_hw - Hardware Descriptor * struct xilinx_cdma_desc_hw - Hardware Descriptor
* @next_desc: Next Descriptor Pointer @0x00 * @next_desc: Next Descriptor Pointer @0x00
...@@ -270,6 +325,18 @@ struct xilinx_axidma_tx_segment { ...@@ -270,6 +325,18 @@ struct xilinx_axidma_tx_segment {
dma_addr_t phys; dma_addr_t phys;
} __aligned(64); } __aligned(64);
/**
* struct xilinx_aximcdma_tx_segment - Descriptor segment
* @hw: Hardware descriptor
* @node: Node in the descriptor segments list
* @phys: Physical address of segment
*/
struct xilinx_aximcdma_tx_segment {
struct xilinx_aximcdma_desc_hw hw;
struct list_head node;
dma_addr_t phys;
} __aligned(64);
/** /**
* struct xilinx_cdma_tx_segment - Descriptor segment * struct xilinx_cdma_tx_segment - Descriptor segment
* @hw: Hardware descriptor * @hw: Hardware descriptor
...@@ -329,11 +396,13 @@ struct xilinx_dma_tx_descriptor { ...@@ -329,11 +396,13 @@ struct xilinx_dma_tx_descriptor {
* @ext_addr: Indicates 64 bit addressing is supported by dma channel * @ext_addr: Indicates 64 bit addressing is supported by dma channel
* @desc_submitcount: Descriptor h/w submitted count * @desc_submitcount: Descriptor h/w submitted count
* @seg_v: Statically allocated segments base * @seg_v: Statically allocated segments base
* @seg_mv: Statically allocated segments base for MCDMA
* @seg_p: Physical allocated segments base * @seg_p: Physical allocated segments base
* @cyclic_seg_v: Statically allocated segment base for cyclic transfers * @cyclic_seg_v: Statically allocated segment base for cyclic transfers
* @cyclic_seg_p: Physical allocated segments base for cyclic dma * @cyclic_seg_p: Physical allocated segments base for cyclic dma
* @start_transfer: Differentiate b/w DMA IP's transfer * @start_transfer: Differentiate b/w DMA IP's transfer
* @stop_transfer: Differentiate b/w DMA IP's quiesce * @stop_transfer: Differentiate b/w DMA IP's quiesce
* @tdest: TDEST value for mcdma
* @has_vflip: S2MM vertical flip * @has_vflip: S2MM vertical flip
*/ */
struct xilinx_dma_chan { struct xilinx_dma_chan {
...@@ -364,11 +433,13 @@ struct xilinx_dma_chan { ...@@ -364,11 +433,13 @@ struct xilinx_dma_chan {
bool ext_addr; bool ext_addr;
u32 desc_submitcount; u32 desc_submitcount;
struct xilinx_axidma_tx_segment *seg_v; struct xilinx_axidma_tx_segment *seg_v;
struct xilinx_aximcdma_tx_segment *seg_mv;
dma_addr_t seg_p; dma_addr_t seg_p;
struct xilinx_axidma_tx_segment *cyclic_seg_v; struct xilinx_axidma_tx_segment *cyclic_seg_v;
dma_addr_t cyclic_seg_p; dma_addr_t cyclic_seg_p;
void (*start_transfer)(struct xilinx_dma_chan *chan); void (*start_transfer)(struct xilinx_dma_chan *chan);
int (*stop_transfer)(struct xilinx_dma_chan *chan); int (*stop_transfer)(struct xilinx_dma_chan *chan);
u16 tdest;
bool has_vflip; bool has_vflip;
}; };
...@@ -378,12 +449,14 @@ struct xilinx_dma_chan { ...@@ -378,12 +449,14 @@ struct xilinx_dma_chan {
* @XDMA_TYPE_AXIDMA: Axi dma ip. * @XDMA_TYPE_AXIDMA: Axi dma ip.
* @XDMA_TYPE_CDMA: Axi cdma ip. * @XDMA_TYPE_CDMA: Axi cdma ip.
* @XDMA_TYPE_VDMA: Axi vdma ip. * @XDMA_TYPE_VDMA: Axi vdma ip.
* @XDMA_TYPE_AXIMCDMA: Axi MCDMA ip.
* *
*/ */
enum xdma_ip_type { enum xdma_ip_type {
XDMA_TYPE_AXIDMA = 0, XDMA_TYPE_AXIDMA = 0,
XDMA_TYPE_CDMA, XDMA_TYPE_CDMA,
XDMA_TYPE_VDMA, XDMA_TYPE_VDMA,
XDMA_TYPE_AXIMCDMA
}; };
struct xilinx_dma_config { struct xilinx_dma_config {
...@@ -412,6 +485,7 @@ struct xilinx_dma_config { ...@@ -412,6 +485,7 @@ struct xilinx_dma_config {
* @nr_channels: Number of channels DMA device supports * @nr_channels: Number of channels DMA device supports
* @chan_id: DMA channel identifier * @chan_id: DMA channel identifier
* @max_buffer_len: Max buffer length * @max_buffer_len: Max buffer length
* @s2mm_index: S2MM channel index
*/ */
struct xilinx_dma_device { struct xilinx_dma_device {
void __iomem *regs; void __iomem *regs;
...@@ -430,6 +504,7 @@ struct xilinx_dma_device { ...@@ -430,6 +504,7 @@ struct xilinx_dma_device {
u32 nr_channels; u32 nr_channels;
u32 chan_id; u32 chan_id;
u32 max_buffer_len; u32 max_buffer_len;
u32 s2mm_index;
}; };
/* Macros */ /* Macros */
...@@ -530,6 +605,18 @@ static inline void xilinx_axidma_buf(struct xilinx_dma_chan *chan, ...@@ -530,6 +605,18 @@ static inline void xilinx_axidma_buf(struct xilinx_dma_chan *chan,
} }
} }
static inline void xilinx_aximcdma_buf(struct xilinx_dma_chan *chan,
struct xilinx_aximcdma_desc_hw *hw,
dma_addr_t buf_addr, size_t sg_used)
{
if (chan->ext_addr) {
hw->buf_addr = lower_32_bits(buf_addr + sg_used);
hw->buf_addr_msb = upper_32_bits(buf_addr + sg_used);
} else {
hw->buf_addr = buf_addr + sg_used;
}
}
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* Descriptors and segments alloc and free * Descriptors and segments alloc and free
*/ */
...@@ -603,6 +690,30 @@ xilinx_axidma_alloc_tx_segment(struct xilinx_dma_chan *chan) ...@@ -603,6 +690,30 @@ xilinx_axidma_alloc_tx_segment(struct xilinx_dma_chan *chan)
return segment; return segment;
} }
/**
* xilinx_aximcdma_alloc_tx_segment - Allocate transaction segment
* @chan: Driver specific DMA channel
*
* Return: The allocated segment on success and NULL on failure.
*/
static struct xilinx_aximcdma_tx_segment *
xilinx_aximcdma_alloc_tx_segment(struct xilinx_dma_chan *chan)
{
struct xilinx_aximcdma_tx_segment *segment = NULL;
unsigned long flags;
spin_lock_irqsave(&chan->lock, flags);
if (!list_empty(&chan->free_seg_list)) {
segment = list_first_entry(&chan->free_seg_list,
struct xilinx_aximcdma_tx_segment,
node);
list_del(&segment->node);
}
spin_unlock_irqrestore(&chan->lock, flags);
return segment;
}
static void xilinx_dma_clean_hw_desc(struct xilinx_axidma_desc_hw *hw) static void xilinx_dma_clean_hw_desc(struct xilinx_axidma_desc_hw *hw)
{ {
u32 next_desc = hw->next_desc; u32 next_desc = hw->next_desc;
...@@ -614,6 +725,17 @@ static void xilinx_dma_clean_hw_desc(struct xilinx_axidma_desc_hw *hw) ...@@ -614,6 +725,17 @@ static void xilinx_dma_clean_hw_desc(struct xilinx_axidma_desc_hw *hw)
hw->next_desc_msb = next_desc_msb; hw->next_desc_msb = next_desc_msb;
} }
static void xilinx_mcdma_clean_hw_desc(struct xilinx_aximcdma_desc_hw *hw)
{
u32 next_desc = hw->next_desc;
u32 next_desc_msb = hw->next_desc_msb;
memset(hw, 0, sizeof(struct xilinx_aximcdma_desc_hw));
hw->next_desc = next_desc;
hw->next_desc_msb = next_desc_msb;
}
/** /**
* xilinx_dma_free_tx_segment - Free transaction segment * xilinx_dma_free_tx_segment - Free transaction segment
* @chan: Driver specific DMA channel * @chan: Driver specific DMA channel
...@@ -627,6 +749,20 @@ static void xilinx_dma_free_tx_segment(struct xilinx_dma_chan *chan, ...@@ -627,6 +749,20 @@ static void xilinx_dma_free_tx_segment(struct xilinx_dma_chan *chan,
list_add_tail(&segment->node, &chan->free_seg_list); list_add_tail(&segment->node, &chan->free_seg_list);
} }
/**
* xilinx_mcdma_free_tx_segment - Free transaction segment
* @chan: Driver specific DMA channel
* @segment: DMA transaction segment
*/
static void xilinx_mcdma_free_tx_segment(struct xilinx_dma_chan *chan,
struct xilinx_aximcdma_tx_segment *
segment)
{
xilinx_mcdma_clean_hw_desc(&segment->hw);
list_add_tail(&segment->node, &chan->free_seg_list);
}
/** /**
* xilinx_cdma_free_tx_segment - Free transaction segment * xilinx_cdma_free_tx_segment - Free transaction segment
* @chan: Driver specific DMA channel * @chan: Driver specific DMA channel
...@@ -681,6 +817,7 @@ xilinx_dma_free_tx_descriptor(struct xilinx_dma_chan *chan, ...@@ -681,6 +817,7 @@ xilinx_dma_free_tx_descriptor(struct xilinx_dma_chan *chan,
struct xilinx_vdma_tx_segment *segment, *next; struct xilinx_vdma_tx_segment *segment, *next;
struct xilinx_cdma_tx_segment *cdma_segment, *cdma_next; struct xilinx_cdma_tx_segment *cdma_segment, *cdma_next;
struct xilinx_axidma_tx_segment *axidma_segment, *axidma_next; struct xilinx_axidma_tx_segment *axidma_segment, *axidma_next;
struct xilinx_aximcdma_tx_segment *aximcdma_segment, *aximcdma_next;
if (!desc) if (!desc)
return; return;
...@@ -696,12 +833,18 @@ xilinx_dma_free_tx_descriptor(struct xilinx_dma_chan *chan, ...@@ -696,12 +833,18 @@ xilinx_dma_free_tx_descriptor(struct xilinx_dma_chan *chan,
list_del(&cdma_segment->node); list_del(&cdma_segment->node);
xilinx_cdma_free_tx_segment(chan, cdma_segment); xilinx_cdma_free_tx_segment(chan, cdma_segment);
} }
} else { } else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
list_for_each_entry_safe(axidma_segment, axidma_next, list_for_each_entry_safe(axidma_segment, axidma_next,
&desc->segments, node) { &desc->segments, node) {
list_del(&axidma_segment->node); list_del(&axidma_segment->node);
xilinx_dma_free_tx_segment(chan, axidma_segment); xilinx_dma_free_tx_segment(chan, axidma_segment);
} }
} else {
list_for_each_entry_safe(aximcdma_segment, aximcdma_next,
&desc->segments, node) {
list_del(&aximcdma_segment->node);
xilinx_mcdma_free_tx_segment(chan, aximcdma_segment);
}
} }
kfree(desc); kfree(desc);
...@@ -770,10 +913,23 @@ static void xilinx_dma_free_chan_resources(struct dma_chan *dchan) ...@@ -770,10 +913,23 @@ static void xilinx_dma_free_chan_resources(struct dma_chan *dchan)
chan->cyclic_seg_v, chan->cyclic_seg_p); chan->cyclic_seg_v, chan->cyclic_seg_p);
} }
if (chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA) { if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
spin_lock_irqsave(&chan->lock, flags);
INIT_LIST_HEAD(&chan->free_seg_list);
spin_unlock_irqrestore(&chan->lock, flags);
/* Free memory that is allocated for BD */
dma_free_coherent(chan->dev, sizeof(*chan->seg_mv) *
XILINX_DMA_NUM_DESCS, chan->seg_mv,
chan->seg_p);
}
if (chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA &&
chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIMCDMA) {
dma_pool_destroy(chan->desc_pool); dma_pool_destroy(chan->desc_pool);
chan->desc_pool = NULL; chan->desc_pool = NULL;
} }
} }
/** /**
...@@ -955,6 +1111,30 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan) ...@@ -955,6 +1111,30 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
list_add_tail(&chan->seg_v[i].node, list_add_tail(&chan->seg_v[i].node,
&chan->free_seg_list); &chan->free_seg_list);
} }
} else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
/* Allocate the buffer descriptors. */
chan->seg_mv = dma_alloc_coherent(chan->dev,
sizeof(*chan->seg_mv) *
XILINX_DMA_NUM_DESCS,
&chan->seg_p, GFP_KERNEL);
if (!chan->seg_mv) {
dev_err(chan->dev,
"unable to allocate channel %d descriptors\n",
chan->id);
return -ENOMEM;
}
for (i = 0; i < XILINX_DMA_NUM_DESCS; i++) {
chan->seg_mv[i].hw.next_desc =
lower_32_bits(chan->seg_p + sizeof(*chan->seg_mv) *
((i + 1) % XILINX_DMA_NUM_DESCS));
chan->seg_mv[i].hw.next_desc_msb =
upper_32_bits(chan->seg_p + sizeof(*chan->seg_mv) *
((i + 1) % XILINX_DMA_NUM_DESCS));
chan->seg_mv[i].phys = chan->seg_p +
sizeof(*chan->seg_v) * i;
list_add_tail(&chan->seg_mv[i].node,
&chan->free_seg_list);
}
} else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_CDMA) { } else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_CDMA) {
chan->desc_pool = dma_pool_create("xilinx_cdma_desc_pool", chan->desc_pool = dma_pool_create("xilinx_cdma_desc_pool",
chan->dev, chan->dev,
...@@ -970,7 +1150,8 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan) ...@@ -970,7 +1150,8 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
} }
if (!chan->desc_pool && if (!chan->desc_pool &&
(chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA)) { ((chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA) &&
chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIMCDMA)) {
dev_err(chan->dev, dev_err(chan->dev,
"unable to allocate channel %d descriptor pool\n", "unable to allocate channel %d descriptor pool\n",
chan->id); chan->id);
...@@ -1367,6 +1548,76 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) ...@@ -1367,6 +1548,76 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
chan->idle = false; chan->idle = false;
} }
/**
* xilinx_mcdma_start_transfer - Starts MCDMA transfer
* @chan: Driver specific channel struct pointer
*/
static void xilinx_mcdma_start_transfer(struct xilinx_dma_chan *chan)
{
struct xilinx_dma_tx_descriptor *head_desc, *tail_desc;
struct xilinx_axidma_tx_segment *tail_segment;
u32 reg;
/*
* lock has been held by calling functions, so we don't need it
* to take it here again.
*/
if (chan->err)
return;
if (!chan->idle)
return;
if (list_empty(&chan->pending_list))
return;
head_desc = list_first_entry(&chan->pending_list,
struct xilinx_dma_tx_descriptor, node);
tail_desc = list_last_entry(&chan->pending_list,
struct xilinx_dma_tx_descriptor, node);
tail_segment = list_last_entry(&tail_desc->segments,
struct xilinx_axidma_tx_segment, node);
reg = dma_ctrl_read(chan, XILINX_MCDMA_CHAN_CR_OFFSET(chan->tdest));
if (chan->desc_pendingcount <= XILINX_MCDMA_COALESCE_MAX) {
reg &= ~XILINX_MCDMA_COALESCE_MASK;
reg |= chan->desc_pendingcount <<
XILINX_MCDMA_COALESCE_SHIFT;
}
reg |= XILINX_MCDMA_IRQ_ALL_MASK;
dma_ctrl_write(chan, XILINX_MCDMA_CHAN_CR_OFFSET(chan->tdest), reg);
/* Program current descriptor */
xilinx_write(chan, XILINX_MCDMA_CHAN_CDESC_OFFSET(chan->tdest),
head_desc->async_tx.phys);
/* Program channel enable register */
reg = dma_ctrl_read(chan, XILINX_MCDMA_CHEN_OFFSET);
reg |= BIT(chan->tdest);
dma_ctrl_write(chan, XILINX_MCDMA_CHEN_OFFSET, reg);
/* Start the fetch of BDs for the channel */
reg = dma_ctrl_read(chan, XILINX_MCDMA_CHAN_CR_OFFSET(chan->tdest));
reg |= XILINX_MCDMA_CR_RUNSTOP_MASK;
dma_ctrl_write(chan, XILINX_MCDMA_CHAN_CR_OFFSET(chan->tdest), reg);
xilinx_dma_start(chan);
if (chan->err)
return;
/* Start the transfer */
xilinx_write(chan, XILINX_MCDMA_CHAN_TDESC_OFFSET(chan->tdest),
tail_segment->phys);
list_splice_tail_init(&chan->pending_list, &chan->active_list);
chan->desc_pendingcount = 0;
chan->idle = false;
}
/** /**
* xilinx_dma_issue_pending - Issue pending transactions * xilinx_dma_issue_pending - Issue pending transactions
* @dchan: DMA channel * @dchan: DMA channel
...@@ -1465,6 +1716,74 @@ static int xilinx_dma_chan_reset(struct xilinx_dma_chan *chan) ...@@ -1465,6 +1716,74 @@ static int xilinx_dma_chan_reset(struct xilinx_dma_chan *chan)
return 0; return 0;
} }
/**
* xilinx_mcdma_irq_handler - MCDMA Interrupt handler
* @irq: IRQ number
* @data: Pointer to the Xilinx MCDMA channel structure
*
* Return: IRQ_HANDLED/IRQ_NONE
*/
static irqreturn_t xilinx_mcdma_irq_handler(int irq, void *data)
{
struct xilinx_dma_chan *chan = data;
u32 status, ser_offset, chan_sermask, chan_offset = 0, chan_id;
if (chan->direction == DMA_DEV_TO_MEM)
ser_offset = XILINX_MCDMA_RXINT_SER_OFFSET;
else
ser_offset = XILINX_MCDMA_TXINT_SER_OFFSET;
/* Read the channel id raising the interrupt*/
chan_sermask = dma_ctrl_read(chan, ser_offset);
chan_id = ffs(chan_sermask);
if (!chan_id)
return IRQ_NONE;
if (chan->direction == DMA_DEV_TO_MEM)
chan_offset = chan->xdev->s2mm_index;
chan_offset = chan_offset + (chan_id - 1);
chan = chan->xdev->chan[chan_offset];
/* Read the status and ack the interrupts. */
status = dma_ctrl_read(chan, XILINX_MCDMA_CHAN_SR_OFFSET(chan->tdest));
if (!(status & XILINX_MCDMA_IRQ_ALL_MASK))
return IRQ_NONE;
dma_ctrl_write(chan, XILINX_MCDMA_CHAN_SR_OFFSET(chan->tdest),
status & XILINX_MCDMA_IRQ_ALL_MASK);
if (status & XILINX_MCDMA_IRQ_ERR_MASK) {
dev_err(chan->dev, "Channel %p has errors %x cdr %x tdr %x\n",
chan,
dma_ctrl_read(chan, XILINX_MCDMA_CH_ERR_OFFSET),
dma_ctrl_read(chan, XILINX_MCDMA_CHAN_CDESC_OFFSET
(chan->tdest)),
dma_ctrl_read(chan, XILINX_MCDMA_CHAN_TDESC_OFFSET
(chan->tdest)));
chan->err = true;
}
if (status & XILINX_MCDMA_IRQ_DELAY_MASK) {
/*
* Device takes too long to do the transfer when user requires
* responsiveness.
*/
dev_dbg(chan->dev, "Inter-packet latency too long\n");
}
if (status & XILINX_MCDMA_IRQ_IOC_MASK) {
spin_lock(&chan->lock);
xilinx_dma_complete_descriptor(chan);
chan->idle = true;
chan->start_transfer(chan);
spin_unlock(&chan->lock);
}
tasklet_schedule(&chan->tasklet);
return IRQ_HANDLED;
}
/** /**
* xilinx_dma_irq_handler - DMA Interrupt handler * xilinx_dma_irq_handler - DMA Interrupt handler
* @irq: IRQ number * @irq: IRQ number
...@@ -1971,6 +2290,104 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_dma_cyclic( ...@@ -1971,6 +2290,104 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_dma_cyclic(
return NULL; return NULL;
} }
/**
* xilinx_mcdma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction
* @dchan: DMA channel
* @sgl: scatterlist to transfer to/from
* @sg_len: number of entries in @scatterlist
* @direction: DMA direction
* @flags: transfer ack flags
* @context: APP words of the descriptor
*
* Return: Async transaction descriptor on success and NULL on failure
*/
static struct dma_async_tx_descriptor *
xilinx_mcdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
unsigned int sg_len,
enum dma_transfer_direction direction,
unsigned long flags, void *context)
{
struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
struct xilinx_dma_tx_descriptor *desc;
struct xilinx_aximcdma_tx_segment *segment = NULL;
u32 *app_w = (u32 *)context;
struct scatterlist *sg;
size_t copy;
size_t sg_used;
unsigned int i;
if (!is_slave_direction(direction))
return NULL;
/* Allocate a transaction descriptor. */
desc = xilinx_dma_alloc_tx_descriptor(chan);
if (!desc)
return NULL;
dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
desc->async_tx.tx_submit = xilinx_dma_tx_submit;
/* Build transactions using information in the scatter gather list */
for_each_sg(sgl, sg, sg_len, i) {
sg_used = 0;
/* Loop until the entire scatterlist entry is used */
while (sg_used < sg_dma_len(sg)) {
struct xilinx_aximcdma_desc_hw *hw;
/* Get a free segment */
segment = xilinx_aximcdma_alloc_tx_segment(chan);
if (!segment)
goto error;
/*
* Calculate the maximum number of bytes to transfer,
* making sure it is less than the hw limit
*/
copy = min_t(size_t, sg_dma_len(sg) - sg_used,
chan->xdev->max_buffer_len);
hw = &segment->hw;
/* Fill in the descriptor */
xilinx_aximcdma_buf(chan, hw, sg_dma_address(sg),
sg_used);
hw->control = copy;
if (chan->direction == DMA_MEM_TO_DEV && app_w) {
memcpy(hw->app, app_w, sizeof(u32) *
XILINX_DMA_NUM_APP_WORDS);
}
sg_used += copy;
/*
* Insert the segment into the descriptor segments
* list.
*/
list_add_tail(&segment->node, &desc->segments);
}
}
segment = list_first_entry(&desc->segments,
struct xilinx_aximcdma_tx_segment, node);
desc->async_tx.phys = segment->phys;
/* For the last DMA_MEM_TO_DEV transfer, set EOP */
if (chan->direction == DMA_MEM_TO_DEV) {
segment->hw.control |= XILINX_MCDMA_BD_SOP;
segment = list_last_entry(&desc->segments,
struct xilinx_aximcdma_tx_segment,
node);
segment->hw.control |= XILINX_MCDMA_BD_EOP;
}
return &desc->async_tx;
error:
xilinx_dma_free_tx_descriptor(chan, desc);
return NULL;
}
/** /**
* xilinx_dma_terminate_all - Halt the channel and free descriptors * xilinx_dma_terminate_all - Halt the channel and free descriptors
* @dchan: Driver specific DMA Channel pointer * @dchan: Driver specific DMA Channel pointer
...@@ -2363,6 +2780,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, ...@@ -2363,6 +2780,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
of_device_is_compatible(node, "xlnx,axi-cdma-channel")) { of_device_is_compatible(node, "xlnx,axi-cdma-channel")) {
chan->direction = DMA_MEM_TO_DEV; chan->direction = DMA_MEM_TO_DEV;
chan->id = chan_id; chan->id = chan_id;
chan->tdest = chan_id;
chan->ctrl_offset = XILINX_DMA_MM2S_CTRL_OFFSET; chan->ctrl_offset = XILINX_DMA_MM2S_CTRL_OFFSET;
if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) { if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
...@@ -2379,6 +2797,8 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, ...@@ -2379,6 +2797,8 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
"xlnx,axi-dma-s2mm-channel")) { "xlnx,axi-dma-s2mm-channel")) {
chan->direction = DMA_DEV_TO_MEM; chan->direction = DMA_DEV_TO_MEM;
chan->id = chan_id; chan->id = chan_id;
xdev->s2mm_index = xdev->nr_channels;
chan->tdest = chan_id - xdev->nr_channels;
chan->has_vflip = of_property_read_bool(node, chan->has_vflip = of_property_read_bool(node,
"xlnx,enable-vert-flip"); "xlnx,enable-vert-flip");
if (chan->has_vflip) { if (chan->has_vflip) {
...@@ -2387,7 +2807,11 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, ...@@ -2387,7 +2807,11 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
XILINX_VDMA_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; chan->ctrl_offset = XILINX_DMA_S2MM_CTRL_OFFSET;
if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) { if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
chan->desc_offset = XILINX_VDMA_S2MM_DESC_OFFSET; chan->desc_offset = XILINX_VDMA_S2MM_DESC_OFFSET;
chan->config.park = 1; chan->config.park = 1;
...@@ -2402,7 +2826,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, ...@@ -2402,7 +2826,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
} }
/* Request the interrupt */ /* Request the interrupt */
chan->irq = irq_of_parse_and_map(node, 0); chan->irq = irq_of_parse_and_map(node, chan->tdest);
err = request_irq(chan->irq, xdev->dma_config->irq_handler, err = request_irq(chan->irq, xdev->dma_config->irq_handler,
IRQF_SHARED, "xilinx-dma-controller", chan); IRQF_SHARED, "xilinx-dma-controller", chan);
if (err) { if (err) {
...@@ -2413,6 +2837,9 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, ...@@ -2413,6 +2837,9 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
chan->start_transfer = xilinx_dma_start_transfer; chan->start_transfer = xilinx_dma_start_transfer;
chan->stop_transfer = xilinx_dma_stop_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) { } else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA) {
chan->start_transfer = xilinx_cdma_start_transfer; chan->start_transfer = xilinx_cdma_start_transfer;
chan->stop_transfer = xilinx_cdma_stop_transfer; chan->stop_transfer = xilinx_cdma_stop_transfer;
...@@ -2466,7 +2893,11 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, ...@@ -2466,7 +2893,11 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
static int xilinx_dma_child_probe(struct xilinx_dma_device *xdev, static int xilinx_dma_child_probe(struct xilinx_dma_device *xdev,
struct device_node *node) struct device_node *node)
{ {
int i, nr_channels = 1; int ret, i, nr_channels = 1;
ret = of_property_read_u32(node, "dma-channels", &nr_channels);
if (xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA && ret < 0)
dev_warn(xdev->dev, "missing dma-channels property\n");
for (i = 0; i < nr_channels; i++) for (i = 0; i < nr_channels; i++)
xilinx_dma_chan_probe(xdev, node, xdev->chan_id++); xilinx_dma_chan_probe(xdev, node, xdev->chan_id++);
...@@ -2501,6 +2932,11 @@ static const struct xilinx_dma_config axidma_config = { ...@@ -2501,6 +2932,11 @@ static const struct xilinx_dma_config axidma_config = {
.irq_handler = xilinx_dma_irq_handler, .irq_handler = xilinx_dma_irq_handler,
}; };
static const struct xilinx_dma_config aximcdma_config = {
.dmatype = XDMA_TYPE_AXIMCDMA,
.clk_init = axidma_clk_init,
.irq_handler = xilinx_mcdma_irq_handler,
};
static const struct xilinx_dma_config axicdma_config = { static const struct xilinx_dma_config axicdma_config = {
.dmatype = XDMA_TYPE_CDMA, .dmatype = XDMA_TYPE_CDMA,
.clk_init = axicdma_clk_init, .clk_init = axicdma_clk_init,
...@@ -2517,6 +2953,7 @@ static const struct of_device_id xilinx_dma_of_ids[] = { ...@@ -2517,6 +2953,7 @@ static const struct of_device_id xilinx_dma_of_ids[] = {
{ .compatible = "xlnx,axi-dma-1.00.a", .data = &axidma_config }, { .compatible = "xlnx,axi-dma-1.00.a", .data = &axidma_config },
{ .compatible = "xlnx,axi-cdma-1.00.a", .data = &axicdma_config }, { .compatible = "xlnx,axi-cdma-1.00.a", .data = &axicdma_config },
{ .compatible = "xlnx,axi-vdma-1.00.a", .data = &axivdma_config }, { .compatible = "xlnx,axi-vdma-1.00.a", .data = &axivdma_config },
{ .compatible = "xlnx,axi-mcdma-1.00.a", .data = &aximcdma_config },
{} {}
}; };
MODULE_DEVICE_TABLE(of, xilinx_dma_of_ids); MODULE_DEVICE_TABLE(of, xilinx_dma_of_ids);
...@@ -2567,7 +3004,8 @@ static int xilinx_dma_probe(struct platform_device *pdev) ...@@ -2567,7 +3004,8 @@ static int xilinx_dma_probe(struct platform_device *pdev)
/* Retrieve the DMA engine properties from the device tree */ /* Retrieve the DMA engine properties from the device tree */
xdev->max_buffer_len = GENMASK(XILINX_DMA_MAX_TRANS_LEN_MAX - 1, 0); xdev->max_buffer_len = GENMASK(XILINX_DMA_MAX_TRANS_LEN_MAX - 1, 0);
if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA ||
xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
if (!of_property_read_u32(node, "xlnx,sg-length-width", if (!of_property_read_u32(node, "xlnx,sg-length-width",
&len_width)) { &len_width)) {
if (len_width < XILINX_DMA_MAX_TRANS_LEN_MIN || if (len_width < XILINX_DMA_MAX_TRANS_LEN_MIN ||
...@@ -2641,6 +3079,8 @@ static int xilinx_dma_probe(struct platform_device *pdev) ...@@ -2641,6 +3079,8 @@ static int xilinx_dma_probe(struct platform_device *pdev)
/* Residue calculation is supported by only AXI DMA and CDMA */ /* Residue calculation is supported by only AXI DMA and CDMA */
xdev->common.residue_granularity = xdev->common.residue_granularity =
DMA_RESIDUE_GRANULARITY_SEGMENT; DMA_RESIDUE_GRANULARITY_SEGMENT;
} else if (xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
xdev->common.device_prep_slave_sg = xilinx_mcdma_prep_slave_sg;
} else { } else {
xdev->common.device_prep_interleaved_dma = xdev->common.device_prep_interleaved_dma =
xilinx_vdma_dma_prep_interleaved; xilinx_vdma_dma_prep_interleaved;
...@@ -2676,6 +3116,8 @@ static int xilinx_dma_probe(struct platform_device *pdev) ...@@ -2676,6 +3116,8 @@ static int xilinx_dma_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "Xilinx AXI DMA Engine Driver Probed!!\n"); dev_info(&pdev->dev, "Xilinx AXI DMA Engine Driver Probed!!\n");
else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA) else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA)
dev_info(&pdev->dev, "Xilinx AXI CDMA Engine Driver Probed!!\n"); dev_info(&pdev->dev, "Xilinx AXI CDMA Engine Driver Probed!!\n");
else if (xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA)
dev_info(&pdev->dev, "Xilinx AXI MCDMA Engine Driver Probed!!\n");
else else
dev_info(&pdev->dev, "Xilinx AXI VDMA Engine Driver Probed!!\n"); dev_info(&pdev->dev, "Xilinx AXI VDMA Engine Driver Probed!!\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment