diff options
author | Vinod Koul <vinod.koul@intel.com> | 2016-05-17 10:15:34 +0530 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2016-05-17 10:15:34 +0530 |
commit | f9114a54c1d828abbe87ac446a2da49d9720203f (patch) | |
tree | 3a455186b118c7ec19896f1327a806e602e2d1ff /drivers/dma | |
parent | 0f5c85f48a3d5cb4f551a7d68592085e75591df3 (diff) | |
parent | ba16db36b5dd6a0f053ba7d50745f29678b4ca8f (diff) |
Merge branch 'topic/xilinx' into for-linus
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/Kconfig | 2 | ||||
-rw-r--r-- | drivers/dma/xilinx/xilinx_vdma.c | 1663 |
2 files changed, 1297 insertions, 368 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 8d0e5ebe1dac..8c98779a12b1 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -521,7 +521,7 @@ config XGENE_DMA config XILINX_VDMA tristate "Xilinx AXI VDMA Engine" - depends on (ARCH_ZYNQ || MICROBLAZE) + depends on (ARCH_ZYNQ || MICROBLAZE || ARM64) select DMA_ENGINE help Enable support for Xilinx AXI VDMA Soft IP. diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c index 0ee0321868d3..fe6958100db0 100644 --- a/drivers/dma/xilinx/xilinx_vdma.c +++ b/drivers/dma/xilinx/xilinx_vdma.c @@ -16,6 +16,15 @@ * video device (S2MM). Initialization, status, interrupt and management * registers are accessed through an AXI4-Lite slave interface. * + * The AXI Direct Memory Access (AXI DMA) core is a soft Xilinx IP core that + * provides high-bandwidth one dimensional direct memory access between memory + * and AXI4-Stream target peripherals. It supports one receive and one + * transmit channel, both of them optional at synthesis time. + * + * 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 + * destination address. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or @@ -35,116 +44,138 @@ #include <linux/of_platform.h> #include <linux/of_irq.h> #include <linux/slab.h> +#include <linux/clk.h> #include "../dmaengine.h" /* Register/Descriptor Offsets */ -#define XILINX_VDMA_MM2S_CTRL_OFFSET 0x0000 -#define XILINX_VDMA_S2MM_CTRL_OFFSET 0x0030 +#define XILINX_DMA_MM2S_CTRL_OFFSET 0x0000 +#define XILINX_DMA_S2MM_CTRL_OFFSET 0x0030 #define XILINX_VDMA_MM2S_DESC_OFFSET 0x0050 #define XILINX_VDMA_S2MM_DESC_OFFSET 0x00a0 /* Control Registers */ -#define XILINX_VDMA_REG_DMACR 0x0000 -#define XILINX_VDMA_DMACR_DELAY_MAX 0xff -#define XILINX_VDMA_DMACR_DELAY_SHIFT 24 -#define XILINX_VDMA_DMACR_FRAME_COUNT_MAX 0xff -#define XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT 16 -#define XILINX_VDMA_DMACR_ERR_IRQ BIT(14) -#define XILINX_VDMA_DMACR_DLY_CNT_IRQ BIT(13) -#define XILINX_VDMA_DMACR_FRM_CNT_IRQ BIT(12) -#define XILINX_VDMA_DMACR_MASTER_SHIFT 8 -#define XILINX_VDMA_DMACR_FSYNCSRC_SHIFT 5 -#define XILINX_VDMA_DMACR_FRAMECNT_EN BIT(4) -#define XILINX_VDMA_DMACR_GENLOCK_EN BIT(3) -#define XILINX_VDMA_DMACR_RESET BIT(2) -#define XILINX_VDMA_DMACR_CIRC_EN BIT(1) -#define XILINX_VDMA_DMACR_RUNSTOP BIT(0) -#define XILINX_VDMA_DMACR_FSYNCSRC_MASK GENMASK(6, 5) - -#define XILINX_VDMA_REG_DMASR 0x0004 -#define XILINX_VDMA_DMASR_EOL_LATE_ERR BIT(15) -#define XILINX_VDMA_DMASR_ERR_IRQ BIT(14) -#define XILINX_VDMA_DMASR_DLY_CNT_IRQ BIT(13) -#define XILINX_VDMA_DMASR_FRM_CNT_IRQ BIT(12) -#define XILINX_VDMA_DMASR_SOF_LATE_ERR BIT(11) -#define XILINX_VDMA_DMASR_SG_DEC_ERR BIT(10) -#define XILINX_VDMA_DMASR_SG_SLV_ERR BIT(9) -#define XILINX_VDMA_DMASR_EOF_EARLY_ERR BIT(8) -#define XILINX_VDMA_DMASR_SOF_EARLY_ERR BIT(7) -#define XILINX_VDMA_DMASR_DMA_DEC_ERR BIT(6) -#define XILINX_VDMA_DMASR_DMA_SLAVE_ERR BIT(5) -#define XILINX_VDMA_DMASR_DMA_INT_ERR BIT(4) -#define XILINX_VDMA_DMASR_IDLE BIT(1) -#define XILINX_VDMA_DMASR_HALTED BIT(0) -#define XILINX_VDMA_DMASR_DELAY_MASK GENMASK(31, 24) -#define XILINX_VDMA_DMASR_FRAME_COUNT_MASK GENMASK(23, 16) - -#define XILINX_VDMA_REG_CURDESC 0x0008 -#define XILINX_VDMA_REG_TAILDESC 0x0010 -#define XILINX_VDMA_REG_REG_INDEX 0x0014 -#define XILINX_VDMA_REG_FRMSTORE 0x0018 -#define XILINX_VDMA_REG_THRESHOLD 0x001c -#define XILINX_VDMA_REG_FRMPTR_STS 0x0024 -#define XILINX_VDMA_REG_PARK_PTR 0x0028 -#define XILINX_VDMA_PARK_PTR_WR_REF_SHIFT 8 -#define XILINX_VDMA_PARK_PTR_RD_REF_SHIFT 0 -#define XILINX_VDMA_REG_VDMA_VERSION 0x002c +#define XILINX_DMA_REG_DMACR 0x0000 +#define XILINX_DMA_DMACR_DELAY_MAX 0xff +#define XILINX_DMA_DMACR_DELAY_SHIFT 24 +#define XILINX_DMA_DMACR_FRAME_COUNT_MAX 0xff +#define XILINX_DMA_DMACR_FRAME_COUNT_SHIFT 16 +#define XILINX_DMA_DMACR_ERR_IRQ BIT(14) +#define XILINX_DMA_DMACR_DLY_CNT_IRQ BIT(13) +#define XILINX_DMA_DMACR_FRM_CNT_IRQ BIT(12) +#define XILINX_DMA_DMACR_MASTER_SHIFT 8 +#define XILINX_DMA_DMACR_FSYNCSRC_SHIFT 5 +#define XILINX_DMA_DMACR_FRAMECNT_EN BIT(4) +#define XILINX_DMA_DMACR_GENLOCK_EN BIT(3) +#define XILINX_DMA_DMACR_RESET BIT(2) +#define XILINX_DMA_DMACR_CIRC_EN BIT(1) +#define XILINX_DMA_DMACR_RUNSTOP BIT(0) +#define XILINX_DMA_DMACR_FSYNCSRC_MASK GENMASK(6, 5) + +#define XILINX_DMA_REG_DMASR 0x0004 +#define XILINX_DMA_DMASR_EOL_LATE_ERR BIT(15) +#define XILINX_DMA_DMASR_ERR_IRQ BIT(14) +#define XILINX_DMA_DMASR_DLY_CNT_IRQ BIT(13) +#define XILINX_DMA_DMASR_FRM_CNT_IRQ BIT(12) +#define XILINX_DMA_DMASR_SOF_LATE_ERR BIT(11) +#define XILINX_DMA_DMASR_SG_DEC_ERR BIT(10) +#define XILINX_DMA_DMASR_SG_SLV_ERR BIT(9) +#define XILINX_DMA_DMASR_EOF_EARLY_ERR BIT(8) +#define XILINX_DMA_DMASR_SOF_EARLY_ERR BIT(7) +#define XILINX_DMA_DMASR_DMA_DEC_ERR BIT(6) +#define XILINX_DMA_DMASR_DMA_SLAVE_ERR BIT(5) +#define XILINX_DMA_DMASR_DMA_INT_ERR BIT(4) +#define XILINX_DMA_DMASR_IDLE BIT(1) +#define XILINX_DMA_DMASR_HALTED BIT(0) +#define XILINX_DMA_DMASR_DELAY_MASK GENMASK(31, 24) +#define XILINX_DMA_DMASR_FRAME_COUNT_MASK GENMASK(23, 16) + +#define XILINX_DMA_REG_CURDESC 0x0008 +#define XILINX_DMA_REG_TAILDESC 0x0010 +#define XILINX_DMA_REG_REG_INDEX 0x0014 +#define XILINX_DMA_REG_FRMSTORE 0x0018 +#define XILINX_DMA_REG_THRESHOLD 0x001c +#define XILINX_DMA_REG_FRMPTR_STS 0x0024 +#define XILINX_DMA_REG_PARK_PTR 0x0028 +#define XILINX_DMA_PARK_PTR_WR_REF_SHIFT 8 +#define XILINX_DMA_PARK_PTR_RD_REF_SHIFT 0 +#define XILINX_DMA_REG_VDMA_VERSION 0x002c /* Register Direct Mode Registers */ -#define XILINX_VDMA_REG_VSIZE 0x0000 -#define XILINX_VDMA_REG_HSIZE 0x0004 +#define XILINX_DMA_REG_VSIZE 0x0000 +#define XILINX_DMA_REG_HSIZE 0x0004 -#define XILINX_VDMA_REG_FRMDLY_STRIDE 0x0008 -#define XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_SHIFT 24 -#define XILINX_VDMA_FRMDLY_STRIDE_STRIDE_SHIFT 0 +#define XILINX_DMA_REG_FRMDLY_STRIDE 0x0008 +#define XILINX_DMA_FRMDLY_STRIDE_FRMDLY_SHIFT 24 +#define XILINX_DMA_FRMDLY_STRIDE_STRIDE_SHIFT 0 #define XILINX_VDMA_REG_START_ADDRESS(n) (0x000c + 4 * (n)) +#define XILINX_VDMA_REG_START_ADDRESS_64(n) (0x000c + 8 * (n)) /* HW specific definitions */ -#define XILINX_VDMA_MAX_CHANS_PER_DEVICE 0x2 - -#define XILINX_VDMA_DMAXR_ALL_IRQ_MASK \ - (XILINX_VDMA_DMASR_FRM_CNT_IRQ | \ - XILINX_VDMA_DMASR_DLY_CNT_IRQ | \ - XILINX_VDMA_DMASR_ERR_IRQ) - -#define XILINX_VDMA_DMASR_ALL_ERR_MASK \ - (XILINX_VDMA_DMASR_EOL_LATE_ERR | \ - XILINX_VDMA_DMASR_SOF_LATE_ERR | \ - XILINX_VDMA_DMASR_SG_DEC_ERR | \ - XILINX_VDMA_DMASR_SG_SLV_ERR | \ - XILINX_VDMA_DMASR_EOF_EARLY_ERR | \ - XILINX_VDMA_DMASR_SOF_EARLY_ERR | \ - XILINX_VDMA_DMASR_DMA_DEC_ERR | \ - XILINX_VDMA_DMASR_DMA_SLAVE_ERR | \ - XILINX_VDMA_DMASR_DMA_INT_ERR) +#define XILINX_DMA_MAX_CHANS_PER_DEVICE 0x2 + +#define XILINX_DMA_DMAXR_ALL_IRQ_MASK \ + (XILINX_DMA_DMASR_FRM_CNT_IRQ | \ + XILINX_DMA_DMASR_DLY_CNT_IRQ | \ + XILINX_DMA_DMASR_ERR_IRQ) + +#define XILINX_DMA_DMASR_ALL_ERR_MASK \ + (XILINX_DMA_DMASR_EOL_LATE_ERR | \ + XILINX_DMA_DMASR_SOF_LATE_ERR | \ + XILINX_DMA_DMASR_SG_DEC_ERR | \ + XILINX_DMA_DMASR_SG_SLV_ERR | \ + XILINX_DMA_DMASR_EOF_EARLY_ERR | \ + XILINX_DMA_DMASR_SOF_EARLY_ERR | \ + XILINX_DMA_DMASR_DMA_DEC_ERR | \ + XILINX_DMA_DMASR_DMA_SLAVE_ERR | \ + XILINX_DMA_DMASR_DMA_INT_ERR) /* * Recoverable errors are DMA Internal error, SOF Early, EOF Early * and SOF Late. They are only recoverable when C_FLUSH_ON_FSYNC * is enabled in the h/w system. */ -#define XILINX_VDMA_DMASR_ERR_RECOVER_MASK \ - (XILINX_VDMA_DMASR_SOF_LATE_ERR | \ - XILINX_VDMA_DMASR_EOF_EARLY_ERR | \ - XILINX_VDMA_DMASR_SOF_EARLY_ERR | \ - XILINX_VDMA_DMASR_DMA_INT_ERR) +#define XILINX_DMA_DMASR_ERR_RECOVER_MASK \ + (XILINX_DMA_DMASR_SOF_LATE_ERR | \ + XILINX_DMA_DMASR_EOF_EARLY_ERR | \ + XILINX_DMA_DMASR_SOF_EARLY_ERR | \ + XILINX_DMA_DMASR_DMA_INT_ERR) /* Axi VDMA Flush on Fsync bits */ -#define XILINX_VDMA_FLUSH_S2MM 3 -#define XILINX_VDMA_FLUSH_MM2S 2 -#define XILINX_VDMA_FLUSH_BOTH 1 +#define XILINX_DMA_FLUSH_S2MM 3 +#define XILINX_DMA_FLUSH_MM2S 2 +#define XILINX_DMA_FLUSH_BOTH 1 /* Delay loop counter to prevent hardware failure */ -#define XILINX_VDMA_LOOP_COUNT 1000000 +#define XILINX_DMA_LOOP_COUNT 1000000 + +/* AXI DMA Specific Registers/Offsets */ +#define XILINX_DMA_REG_SRCDSTADDR 0x18 +#define XILINX_DMA_REG_BTT 0x28 + +/* AXI DMA Specific Masks/Bit fields */ +#define XILINX_DMA_MAX_TRANS_LEN GENMASK(22, 0) +#define XILINX_DMA_CR_COALESCE_MAX GENMASK(23, 16) +#define XILINX_DMA_CR_COALESCE_SHIFT 16 +#define XILINX_DMA_BD_SOP BIT(27) +#define XILINX_DMA_BD_EOP BIT(26) +#define XILINX_DMA_COALESCE_MAX 255 +#define XILINX_DMA_NUM_APP_WORDS 5 + +/* AXI CDMA Specific Registers/Offsets */ +#define XILINX_CDMA_REG_SRCADDR 0x18 +#define XILINX_CDMA_REG_DSTADDR 0x20 + +/* AXI CDMA Specific Masks */ +#define XILINX_CDMA_CR_SGMODE BIT(3) /** * struct xilinx_vdma_desc_hw - Hardware Descriptor * @next_desc: Next Descriptor Pointer @0x00 * @pad1: Reserved @0x04 * @buf_addr: Buffer address @0x08 - * @pad2: Reserved @0x0C + * @buf_addr_msb: MSB of Buffer address @0x0C * @vsize: Vertical Size @0x10 * @hsize: Horizontal Size @0x14 * @stride: Number of bytes between the first @@ -154,13 +185,59 @@ struct xilinx_vdma_desc_hw { u32 next_desc; u32 pad1; u32 buf_addr; - u32 pad2; + u32 buf_addr_msb; u32 vsize; u32 hsize; u32 stride; } __aligned(64); /** + * struct xilinx_axidma_desc_hw - Hardware Descriptor for AXI DMA + * @next_desc: Next Descriptor Pointer @0x00 + * @pad1: Reserved @0x04 + * @buf_addr: Buffer address @0x08 + * @pad2: Reserved @0x0C + * @pad3: Reserved @0x10 + * @pad4: Reserved @0x14 + * @control: Control field @0x18 + * @status: Status field @0x1C + * @app: APP Fields @0x20 - 0x30 + */ +struct xilinx_axidma_desc_hw { + u32 next_desc; + u32 pad1; + u32 buf_addr; + u32 pad2; + u32 pad3; + u32 pad4; + u32 control; + u32 status; + u32 app[XILINX_DMA_NUM_APP_WORDS]; +} __aligned(64); + +/** + * struct xilinx_cdma_desc_hw - Hardware Descriptor + * @next_desc: Next Descriptor Pointer @0x00 + * @pad1: Reserved @0x04 + * @src_addr: Source address @0x08 + * @pad2: Reserved @0x0C + * @dest_addr: Destination address @0x10 + * @pad3: Reserved @0x14 + * @control: Control field @0x18 + * @status: Status field @0x1C + */ +struct xilinx_cdma_desc_hw { + u32 next_desc; + u32 pad1; + u32 src_addr; + u32 pad2; + u32 dest_addr; + u32 pad3; + u32 control; + u32 status; +} __aligned(64); + +/** * struct xilinx_vdma_tx_segment - Descriptor segment * @hw: Hardware descriptor * @node: Node in the descriptor segments list @@ -173,19 +250,43 @@ struct xilinx_vdma_tx_segment { } __aligned(64); /** - * struct xilinx_vdma_tx_descriptor - Per Transaction structure + * struct xilinx_axidma_tx_segment - Descriptor segment + * @hw: Hardware descriptor + * @node: Node in the descriptor segments list + * @phys: Physical address of segment + */ +struct xilinx_axidma_tx_segment { + struct xilinx_axidma_desc_hw hw; + struct list_head node; + dma_addr_t phys; +} __aligned(64); + +/** + * struct xilinx_cdma_tx_segment - Descriptor segment + * @hw: Hardware descriptor + * @node: Node in the descriptor segments list + * @phys: Physical address of segment + */ +struct xilinx_cdma_tx_segment { + struct xilinx_cdma_desc_hw hw; + struct list_head node; + dma_addr_t phys; +} __aligned(64); + +/** + * struct xilinx_dma_tx_descriptor - Per Transaction structure * @async_tx: Async transaction descriptor * @segments: TX segments list * @node: Node in the channel descriptors list */ -struct xilinx_vdma_tx_descriptor { +struct xilinx_dma_tx_descriptor { struct dma_async_tx_descriptor async_tx; struct list_head segments; struct list_head node; }; /** - * struct xilinx_vdma_chan - Driver specific VDMA channel structure + * struct xilinx_dma_chan - Driver specific DMA channel structure * @xdev: Driver specific device structure * @ctrl_offset: Control registers offset * @desc_offset: TX descriptor registers offset @@ -207,9 +308,14 @@ struct xilinx_vdma_tx_descriptor { * @config: Device configuration info * @flush_on_fsync: Flush on Frame sync * @desc_pendingcount: Descriptor pending count + * @ext_addr: Indicates 64 bit addressing is supported by dma channel + * @desc_submitcount: Descriptor h/w submitted count + * @residue: Residue for AXI DMA + * @seg_v: Statically allocated segments base + * @start_transfer: Differentiate b/w DMA IP's transfer */ -struct xilinx_vdma_chan { - struct xilinx_vdma_device *xdev; +struct xilinx_dma_chan { + struct xilinx_dma_device *xdev; u32 ctrl_offset; u32 desc_offset; spinlock_t lock; @@ -230,73 +336,122 @@ struct xilinx_vdma_chan { struct xilinx_vdma_config config; bool flush_on_fsync; u32 desc_pendingcount; + bool ext_addr; + u32 desc_submitcount; + u32 residue; + struct xilinx_axidma_tx_segment *seg_v; + void (*start_transfer)(struct xilinx_dma_chan *chan); +}; + +struct xilinx_dma_config { + enum xdma_ip_type dmatype; + int (*clk_init)(struct platform_device *pdev, struct clk **axi_clk, + struct clk **tx_clk, struct clk **txs_clk, + struct clk **rx_clk, struct clk **rxs_clk); }; /** - * struct xilinx_vdma_device - VDMA device structure + * struct xilinx_dma_device - DMA device structure * @regs: I/O mapped base address * @dev: Device Structure * @common: DMA device structure - * @chan: Driver specific VDMA channel + * @chan: Driver specific DMA channel * @has_sg: Specifies whether Scatter-Gather is present or not * @flush_on_fsync: Flush on frame sync + * @ext_addr: Indicates 64 bit addressing is supported by dma device + * @pdev: Platform device structure pointer + * @dma_config: DMA config structure + * @axi_clk: DMA Axi4-lite interace clock + * @tx_clk: DMA mm2s clock + * @txs_clk: DMA mm2s stream clock + * @rx_clk: DMA s2mm clock + * @rxs_clk: DMA s2mm stream clock */ -struct xilinx_vdma_device { +struct xilinx_dma_device { void __iomem *regs; struct device *dev; struct dma_device common; - struct xilinx_vdma_chan *chan[XILINX_VDMA_MAX_CHANS_PER_DEVICE]; + struct xilinx_dma_chan *chan[XILINX_DMA_MAX_CHANS_PER_DEVICE]; bool has_sg; u32 flush_on_fsync; + bool ext_addr; + struct platform_device *pdev; + const struct xilinx_dma_config *dma_config; + struct clk *axi_clk; + struct clk *tx_clk; + struct clk *txs_clk; + struct clk *rx_clk; + struct clk *rxs_clk; }; /* Macros */ #define to_xilinx_chan(chan) \ - container_of(chan, struct xilinx_vdma_chan, common) -#define to_vdma_tx_descriptor(tx) \ - container_of(tx, struct xilinx_vdma_tx_descriptor, async_tx) -#define xilinx_vdma_poll_timeout(chan, reg, val, cond, delay_us, timeout_us) \ + container_of(chan, struct xilinx_dma_chan, common) +#define to_dma_tx_descriptor(tx) \ + container_of(tx, struct xilinx_dma_tx_descriptor, async_tx) +#define xilinx_dma_poll_timeout(chan, reg, val, cond, delay_us, timeout_us) \ readl_poll_timeout(chan->xdev->regs + chan->ctrl_offset + reg, val, \ cond, delay_us, timeout_us) /* IO accessors */ -static inline u32 vdma_read(struct xilinx_vdma_chan *chan, u32 reg) +static inline u32 dma_read(struct xilinx_dma_chan *chan, u32 reg) { return ioread32(chan->xdev->regs + reg); } -static inline void vdma_write(struct xilinx_vdma_chan *chan, u32 reg, u32 value) +static inline void dma_write(struct xilinx_dma_chan *chan, u32 reg, u32 value) { iowrite32(value, chan->xdev->regs + reg); } -static inline void vdma_desc_write(struct xilinx_vdma_chan *chan, u32 reg, +static inline void vdma_desc_write(struct xilinx_dma_chan *chan, u32 reg, u32 value) { - vdma_write(chan, chan->desc_offset + reg, value); + dma_write(chan, chan->desc_offset + reg, value); } -static inline u32 vdma_ctrl_read(struct xilinx_vdma_chan *chan, u32 reg) +static inline u32 dma_ctrl_read(struct xilinx_dma_chan *chan, u32 reg) { - return vdma_read(chan, chan->ctrl_offset + reg); + return dma_read(chan, chan->ctrl_offset + reg); } -static inline void vdma_ctrl_write(struct xilinx_vdma_chan *chan, u32 reg, +static inline void dma_ctrl_write(struct xilinx_dma_chan *chan, u32 reg, u32 value) { - vdma_write(chan, chan->ctrl_offset + reg, value); + dma_write(chan, chan->ctrl_offset + reg, value); } -static inline void vdma_ctrl_clr(struct xilinx_vdma_chan *chan, u32 reg, +static inline void dma_ctrl_clr(struct xilinx_dma_chan *chan, u32 reg, u32 clr) { - vdma_ctrl_write(chan, reg, vdma_ctrl_read(chan, reg) & ~clr); + dma_ctrl_write(chan, reg, dma_ctrl_read(chan, reg) & ~clr); } -static inline void vdma_ctrl_set(struct xilinx_vdma_chan *chan, u32 reg, +static inline void dma_ctrl_set(struct xilinx_dma_chan *chan, u32 reg, u32 set) { - vdma_ctrl_write(chan, reg, vdma_ctrl_read(chan, reg) | set); + dma_ctrl_write(chan, reg, dma_ctrl_read(chan, reg) | set); +} + +/** + * vdma_desc_write_64 - 64-bit descriptor write + * @chan: Driver specific VDMA channel + * @reg: Register to write + * @value_lsb: lower address of the descriptor. + * @value_msb: upper address of the descriptor. + * + * Since vdma driver is trying to write to a register offset which is not a + * multiple of 64 bits(ex : 0x5c), we are writing as two separate 32 bits + * instead of a single 64 bit register write. + */ +static inline void vdma_desc_write_64(struct xilinx_dma_chan *chan, u32 reg, + u32 value_lsb, u32 value_msb) +{ + /* Write the lsb 32 bits*/ + writel(value_lsb, chan->xdev->regs + chan->desc_offset + reg); + + /* Write the msb 32 bits */ + writel(value_msb, chan->xdev->regs + chan->desc_offset + reg + 4); } /* ----------------------------------------------------------------------------- @@ -305,16 +460,59 @@ static inline void vdma_ctrl_set(struct xilinx_vdma_chan *chan, u32 reg, /** * xilinx_vdma_alloc_tx_segment - Allocate transaction segment - * @chan: Driver specific VDMA channel + * @chan: Driver specific DMA channel * * Return: The allocated segment on success and NULL on failure. */ static struct xilinx_vdma_tx_segment * -xilinx_vdma_alloc_tx_segment(struct xilinx_vdma_chan *chan) +xilinx_vdma_alloc_tx_segment(struct xilinx_dma_chan *chan) { struct xilinx_vdma_tx_segment *segment; dma_addr_t phys; + segment = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &phys); + if (!segment) + return NULL; + + segment->phys = phys; + + return segment; +} + +/** + * xilinx_cdma_alloc_tx_segment - Allocate transaction segment + * @chan: Driver specific DMA channel + * + * Return: The allocated segment on success and NULL on failure. + */ +static struct xilinx_cdma_tx_segment * +xilinx_cdma_alloc_tx_segment(struct xilinx_dma_chan *chan) +{ + struct xilinx_cdma_tx_segment *segment; + dma_addr_t phys; + + segment = dma_pool_alloc(chan->desc_pool, GFP_ATOMIC, &phys); + if (!segment) + return NULL; + + memset(segment, 0, sizeof(*segment)); + segment->phys = phys; + + return segment; +} + +/** + * xilinx_axidma_alloc_tx_segment - Allocate transaction segment + * @chan: Driver specific DMA channel + * + * Return: The allocated segment on success and NULL on failure. + */ +static struct xilinx_axidma_tx_segment * +xilinx_axidma_alloc_tx_segment(struct xilinx_dma_chan *chan) +{ + struct xilinx_axidma_tx_segment *segment; + dma_addr_t phys; + segment = dma_pool_alloc(chan->desc_pool, GFP_ATOMIC, &phys); if (!segment) return NULL; @@ -326,26 +524,48 @@ xilinx_vdma_alloc_tx_segment(struct xilinx_vdma_chan *chan) } /** + * xilinx_dma_free_tx_segment - Free transaction segment + * @chan: Driver specific DMA channel + * @segment: DMA transaction segment + */ +static void xilinx_dma_free_tx_segment(struct xilinx_dma_chan *chan, + struct xilinx_axidma_tx_segment *segment) +{ + dma_pool_free(chan->desc_pool, segment, segment->phys); +} + +/** + * xilinx_cdma_free_tx_segment - Free transaction segment + * @chan: Driver specific DMA channel + * @segment: DMA transaction segment + */ +static void xilinx_cdma_free_tx_segment(struct xilinx_dma_chan *chan, + struct xilinx_cdma_tx_segment *segment) +{ + dma_pool_free(chan->desc_pool, segment, segment->phys); +} + +/** * xilinx_vdma_free_tx_segment - Free transaction segment - * @chan: Driver specific VDMA channel - * @segment: VDMA transaction segment + * @chan: Driver specific DMA channel + * @segment: DMA transaction segment */ -static void xilinx_vdma_free_tx_segment(struct xilinx_vdma_chan *chan, +static void xilinx_vdma_free_tx_segment(struct xilinx_dma_chan *chan, struct xilinx_vdma_tx_segment *segment) { dma_pool_free(chan->desc_pool, segment, segment->phys); } /** - * xilinx_vdma_tx_descriptor - Allocate transaction descriptor - * @chan: Driver specific VDMA channel + * xilinx_dma_tx_descriptor - Allocate transaction descriptor + * @chan: Driver specific DMA channel * * Return: The allocated descriptor on success and NULL on failure. */ -static struct xilinx_vdma_tx_descriptor * -xilinx_vdma_alloc_tx_descriptor(struct xilinx_vdma_chan *chan) +static struct xilinx_dma_tx_descriptor * +xilinx_dma_alloc_tx_descriptor(struct xilinx_dma_chan *chan) { - struct xilinx_vdma_tx_descriptor *desc; + struct xilinx_dma_tx_descriptor *desc; desc = kzalloc(sizeof(*desc), GFP_KERNEL); if (!desc) @@ -357,22 +577,38 @@ xilinx_vdma_alloc_tx_descriptor(struct xilinx_vdma_chan *chan) } /** - * xilinx_vdma_free_tx_descriptor - Free transaction descriptor - * @chan: Driver specific VDMA channel - * @desc: VDMA transaction descriptor + * xilinx_dma_free_tx_descriptor - Free transaction descriptor + * @chan: Driver specific DMA channel + * @desc: DMA transaction descriptor */ static void -xilinx_vdma_free_tx_descriptor(struct xilinx_vdma_chan *chan, - struct xilinx_vdma_tx_descriptor *desc) +xilinx_dma_free_tx_descriptor(struct xilinx_dma_chan *chan, + struct xilinx_dma_tx_descriptor *desc) { struct xilinx_vdma_tx_segment *segment, *next; + struct xilinx_cdma_tx_segment *cdma_segment, *cdma_next; + struct xilinx_axidma_tx_segment *axidma_segment, *axidma_next; if (!desc) return; - list_for_each_entry_safe(segment, next, &desc->segments, node) { - list_del(&segment->node); - xilinx_vdma_free_tx_segment(chan, segment); + if (chan->xdev->dma_config->dmatype == XDMA_TYPE_VDMA) { + list_for_each_entry_safe(segment, next, &desc->segments, node) { + list_del(&segment->node); + xilinx_vdma_free_tx_segment(chan, segment); + } + } else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_CDMA) { + list_for_each_entry_safe(cdma_segment, cdma_next, + &desc->segments, node) { + list_del(&cdma_segment->node); + xilinx_cdma_free_tx_segment(chan, cdma_segment); + } + } else { + list_for_each_entry_safe(axidma_segment, axidma_next, + &desc->segments, node) { + list_del(&axidma_segment->node); + xilinx_dma_free_tx_segment(chan, axidma_segment); + } } kfree(desc); @@ -381,60 +617,62 @@ xilinx_vdma_free_tx_descriptor(struct xilinx_vdma_chan *chan, /* Required functions */ /** - * xilinx_vdma_free_desc_list - Free descriptors list - * @chan: Driver specific VDMA channel + * xilinx_dma_free_desc_list - Free descriptors list + * @chan: Driver specific DMA channel * @list: List to parse and delete the descriptor */ -static void xilinx_vdma_free_desc_list(struct xilinx_vdma_chan *chan, +static void xilinx_dma_free_desc_list(struct xilinx_dma_chan *chan, struct list_head *list) { - struct xilinx_vdma_tx_descriptor *desc, *next; + struct xilinx_dma_tx_descriptor *desc, *next; list_for_each_entry_safe(desc, next, list, node) { list_del(&desc->node); - xilinx_vdma_free_tx_descriptor(chan, desc); + xilinx_dma_free_tx_descriptor(chan, desc); } } /** - * xilinx_vdma_free_descriptors - Free channel descriptors - * @chan: Driver specific VDMA channel + * xilinx_dma_free_descriptors - Free channel descriptors + * @chan: Driver specific DMA channel */ -static void xilinx_vdma_free_descriptors(struct xilinx_vdma_chan *chan) +static void xilinx_dma_free_descriptors(struct xilinx_dma_chan *chan) { unsigned long flags; spin_lock_irqsave(&chan->lock, flags); - xilinx_vdma_free_desc_list(chan, &chan->pending_list); - xilinx_vdma_free_desc_list(chan, &chan->done_list); - xilinx_vdma_free_desc_list(chan, &chan->active_list); + xilinx_dma_free_desc_list(chan, &chan->pending_list); + xilinx_dma_free_desc_list(chan, &chan->done_list); + xilinx_dma_free_desc_list(chan, &chan->active_list); spin_unlock_irqrestore(&chan->lock, flags); } /** - * xilinx_vdma_free_chan_resources - Free channel resources + * xilinx_dma_free_chan_resources - Free channel resources * @dchan: DMA channel */ -static void xilinx_vdma_free_chan_resources(struct dma_chan *dchan) +static void xilinx_dma_free_chan_resources(struct dma_chan *dchan) { - struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_dma_chan *chan = to_xilinx_chan(dchan); dev_dbg(chan->dev, "Free all channel resources.\n"); - xilinx_vdma_free_descriptors(chan); + xilinx_dma_free_descriptors(chan); + if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) + xilinx_dma_free_tx_segment(chan, chan->seg_v); dma_pool_destroy(chan->desc_pool); chan->desc_pool = NULL; } /** - * xilinx_vdma_chan_desc_cleanup - Clean channel descriptors - * @chan: Driver specific VDMA channel + * xilinx_dma_chan_desc_cleanup - Clean channel descriptors + * @chan: Driver specific DMA channel */ -static void xilinx_vdma_chan_desc_cleanup(struct xilinx_vdma_chan *chan) +static void xilinx_dma_chan_desc_cleanup(struct xilinx_dma_chan *chan) { - struct xilinx_vdma_tx_descriptor *desc, *next; + struct xilinx_dma_tx_descriptor *desc, *next; unsigned long flags; spin_lock_irqsave(&chan->lock, flags); @@ -457,32 +695,32 @@ static void xilinx_vdma_chan_desc_cleanup(struct xilinx_vdma_chan *chan) /* Run any dependencies, then free the descriptor */ dma_run_dependencies(&desc->async_tx); - xilinx_vdma_free_tx_descriptor(chan, desc); + xilinx_dma_free_tx_descriptor(chan, desc); } spin_unlock_irqrestore(&chan->lock, flags); } /** - * xilinx_vdma_do_tasklet - Schedule completion tasklet - * @data: Pointer to the Xilinx VDMA channel structure + * xilinx_dma_do_tasklet - Schedule completion tasklet + * @data: Pointer to the Xilinx DMA channel structure */ -static void xilinx_vdma_do_tasklet(unsigned long data) +static void xilinx_dma_do_tasklet(unsigned long data) { - struct xilinx_vdma_chan *chan = (struct xilinx_vdma_chan *)data; + struct xilinx_dma_chan *chan = (struct xilinx_dma_chan *)data; - xilinx_vdma_chan_desc_cleanup(chan); + xilinx_dma_chan_desc_cleanup(chan); } /** - * xilinx_vdma_alloc_chan_resources - Allocate channel resources + * xilinx_dma_alloc_chan_resources - Allocate channel resources * @dchan: DMA channel * * Return: '0' on success and failure value on error */ -static int xilinx_vdma_alloc_chan_resources(struct dma_chan *dchan) +static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan) { - struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_dma_chan *chan = to_xilinx_chan(dchan); /* Has this channel already been allocated? */ if (chan->desc_pool) @@ -492,10 +730,26 @@ static int xilinx_vdma_alloc_chan_resources(struct dma_chan *dchan) * We need the descriptor to be aligned to 64bytes * for meeting Xilinx VDMA specification requirement. */ - chan->desc_pool = dma_pool_create("xilinx_vdma_desc_pool", - chan->dev, - sizeof(struct xilinx_vdma_tx_segment), - __alignof__(struct xilinx_vdma_tx_segment), 0); + if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { + chan->desc_pool = dma_pool_create("xilinx_dma_desc_pool", + chan->dev, + sizeof(struct xilinx_axidma_tx_segment), + __alignof__(struct xilinx_axidma_tx_segment), + 0); + } else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_CDMA) { + chan->desc_pool = dma_pool_create("xilinx_cdma_desc_pool", + chan->dev, + sizeof(struct xilinx_cdma_tx_segment), + __alignof__(struct xilinx_cdma_tx_segment), + 0); + } else { + chan->desc_pool = dma_pool_create("xilinx_vdma_desc_pool", + chan->dev, + sizeof(struct xilinx_vdma_tx_segment), + __alignof__(struct xilinx_vdma_tx_segment), + 0); + } + if (!chan->desc_pool) { dev_err(chan->dev, "unable to allocate channel %d descriptor pool\n", @@ -503,110 +757,160 @@ static int xilinx_vdma_alloc_chan_resources(struct dma_chan *dchan) return -ENOMEM; } + if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) + /* + * For AXI DMA case after submitting a pending_list, keep + * an extra segment allocated so that the "next descriptor" + * pointer on the tail descriptor always points to a + * valid descriptor, even when paused after reaching taildesc. + * This way, it is possible to issue additional + * transfers without halting and restarting the channel. + */ + chan->seg_v = xilinx_axidma_alloc_tx_segment(chan); + dma_cookie_init(dchan); + + if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { + /* For AXI DMA resetting once channel will reset the + * other channel as well so enable the interrupts here. + */ + dma_ctrl_set(chan, XILINX_DMA_REG_DMACR, + XILINX_DMA_DMAXR_ALL_IRQ_MASK); + } + + if ((chan->xdev->dma_config->dmatype == XDMA_TYPE_CDMA) && chan->has_sg) + dma_ctrl_set(chan, XILINX_DMA_REG_DMACR, + XILINX_CDMA_CR_SGMODE); + return 0; } /** - * xilinx_vdma_tx_status - Get VDMA transaction status + * xilinx_dma_tx_status - Get DMA transaction status * @dchan: DMA channel * @cookie: Transaction identifier * @txstate: Transaction state * * Return: DMA transaction status */ -static enum dma_status xilinx_vdma_tx_status(struct dma_chan *dchan, +static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - return dma_cookie_status(dchan, cookie, txstate); + struct xilinx_dma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_dma_tx_descriptor *desc; + struct xilinx_axidma_tx_segment *segment; + struct xilinx_axidma_desc_hw *hw; + enum dma_status ret; + unsigned long flags; + u32 residue = 0; + + ret = dma_cookie_status(dchan, cookie, txstate); + if (ret == DMA_COMPLETE || !txstate) + return ret; + + if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { + spin_lock_irqsave(&chan->lock, flags); + + desc = list_last_entry(&chan->active_list, + struct xilinx_dma_tx_descriptor, node); + if (chan->has_sg) { + list_for_each_entry(segment, &desc->segments, node) { + hw = &segment->hw; + residue += (hw->control - hw->status) & + XILINX_DMA_MAX_TRANS_LEN; + } + } + spin_unlock_irqrestore(&chan->lock, flags); + + chan->residue = residue; + dma_set_residue(txstate, chan->residue); + } + + return ret; } /** - * xilinx_vdma_is_running - Check if VDMA channel is running - * @chan: Driver specific VDMA channel + * xilinx_dma_is_running - Check if DMA channel is running + * @chan: Driver specific DMA channel * * Return: '1' if running, '0' if not. */ -static bool xilinx_vdma_is_running(struct xilinx_vdma_chan *chan) +static bool xilinx_dma_is_running(struct xilinx_dma_chan *chan) { - return !(vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) & - XILINX_VDMA_DMASR_HALTED) && - (vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) & - XILINX_VDMA_DMACR_RUNSTOP); + return !(dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) & + XILINX_DMA_DMASR_HALTED) && + (dma_ctrl_read(chan, XILINX_DMA_REG_DMACR) & + XILINX_DMA_DMACR_RUNSTOP); } /** - * xilinx_vdma_is_idle - Check if VDMA channel is idle - * @chan: Driver specific VDMA channel + * xilinx_dma_is_idle - Check if DMA channel is idle + * @chan: Driver specific DMA channel * * Return: '1' if idle, '0' if not. */ -static bool xilinx_vdma_is_idle(struct xilinx_vdma_chan *chan) +static bool xilinx_dma_is_idle(struct xilinx_dma_chan *chan) { - return vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) & - XILINX_VDMA_DMASR_IDLE; + return dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) & + XILINX_DMA_DMASR_IDLE; } /** - * xilinx_vdma_halt - Halt VDMA channel - * @chan: Driver specific VDMA channel + * xilinx_dma_halt - Halt DMA channel + * @chan: Driver specific DMA channel */ -static void xilinx_vdma_halt(struct xilinx_vdma_chan *chan) +static void xilinx_dma_halt(struct xilinx_dma_chan *chan) { int err; u32 val; - vdma_ctrl_clr(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RUNSTOP); + dma_ctrl_clr(chan, XILINX_DMA_REG_DMACR, XILINX_DMA_DMACR_RUNSTOP); /* Wait for the hardware to halt */ - err = xilinx_vdma_poll_timeout(chan, XILINX_VDMA_REG_DMASR, val, - (val & XILINX_VDMA_DMASR_HALTED), 0, - XILINX_VDMA_LOOP_COUNT); + err = xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, + (val & XILINX_DMA_DMASR_HALTED), 0, + XILINX_DMA_LOOP_COUNT); if (err) { dev_err(chan->dev, "Cannot stop channel %p: %x\n", - chan, vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR)); + chan, dma_ctrl_read(chan, XILINX_DMA_REG_DMASR)); chan->err = true; } - - return; } /** - * xilinx_vdma_start - Start VDMA channel - * @chan: Driver specific VDMA channel + * xilinx_dma_start - Start DMA channel + * @chan: Driver specific DMA channel */ -static void xilinx_vdma_start(struct xilinx_vdma_chan *chan) +static void xilinx_dma_start(struct xilinx_dma_chan *chan) { int err; u32 val; - vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RUNSTOP); + dma_ctrl_set(chan, XILINX_DMA_REG_DMACR, XILINX_DMA_DMACR_RUNSTOP); /* Wait for the hardware to start */ - err = xilinx_vdma_poll_timeout(chan, XILINX_VDMA_REG_DMASR, val, - !(val & XILINX_VDMA_DMASR_HALTED), 0, - XILINX_VDMA_LOOP_COUNT); + err = xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, + !(val & XILINX_DMA_DMASR_HALTED), 0, + XILINX_DMA_LOOP_COUNT); if (err) { dev_err(chan->dev, "Cannot start channel %p: %x\n", - chan, vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR)); + chan, dma_ctrl_read(chan, XILINX_DMA_REG_DMASR)); chan->err = true; } - - return; } /** * xilinx_vdma_start_transfer - Starts VDMA transfer * @chan: Driver specific channel struct pointer */ -static void xilinx_vdma_start_transfer(struct xilinx_vdma_chan *chan) +static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) { struct xilinx_vdma_config *config = &chan->config; - struct xilinx_vdma |