1c6da0ba8SZhangfei Gao /* 2c6da0ba8SZhangfei Gao * Driver For Marvell Two-channel DMA Engine 3c6da0ba8SZhangfei Gao * 4c6da0ba8SZhangfei Gao * Copyright: Marvell International Ltd. 5c6da0ba8SZhangfei Gao * 6c6da0ba8SZhangfei Gao * The code contained herein is licensed under the GNU General Public 7c6da0ba8SZhangfei Gao * License. You may obtain a copy of the GNU General Public License 8c6da0ba8SZhangfei Gao * Version 2 or later at the following locations: 9c6da0ba8SZhangfei Gao * 10c6da0ba8SZhangfei Gao */ 11c6da0ba8SZhangfei Gao 127331205aSThierry Reding #include <linux/err.h> 13c6da0ba8SZhangfei Gao #include <linux/module.h> 14c6da0ba8SZhangfei Gao #include <linux/init.h> 15c6da0ba8SZhangfei Gao #include <linux/types.h> 16c6da0ba8SZhangfei Gao #include <linux/interrupt.h> 17c6da0ba8SZhangfei Gao #include <linux/dma-mapping.h> 18c6da0ba8SZhangfei Gao #include <linux/slab.h> 19c6da0ba8SZhangfei Gao #include <linux/dmaengine.h> 20c6da0ba8SZhangfei Gao #include <linux/platform_device.h> 21c6da0ba8SZhangfei Gao #include <linux/device.h> 22c6da0ba8SZhangfei Gao #include <mach/regs-icu.h> 23293b2da1SArnd Bergmann #include <linux/platform_data/dma-mmp_tdma.h> 24f1a77570SZhangfei Gao #include <linux/of_device.h> 25c6da0ba8SZhangfei Gao 26c6da0ba8SZhangfei Gao #include "dmaengine.h" 27c6da0ba8SZhangfei Gao 28c6da0ba8SZhangfei Gao /* 29c6da0ba8SZhangfei Gao * Two-Channel DMA registers 30c6da0ba8SZhangfei Gao */ 31c6da0ba8SZhangfei Gao #define TDBCR 0x00 /* Byte Count */ 32c6da0ba8SZhangfei Gao #define TDSAR 0x10 /* Src Addr */ 33c6da0ba8SZhangfei Gao #define TDDAR 0x20 /* Dst Addr */ 34c6da0ba8SZhangfei Gao #define TDNDPR 0x30 /* Next Desc */ 35c6da0ba8SZhangfei Gao #define TDCR 0x40 /* Control */ 36c6da0ba8SZhangfei Gao #define TDCP 0x60 /* Priority*/ 37c6da0ba8SZhangfei Gao #define TDCDPR 0x70 /* Current Desc */ 38c6da0ba8SZhangfei Gao #define TDIMR 0x80 /* Int Mask */ 39c6da0ba8SZhangfei Gao #define TDISR 0xa0 /* Int Status */ 40c6da0ba8SZhangfei Gao 41c6da0ba8SZhangfei Gao /* Two-Channel DMA Control Register */ 42c6da0ba8SZhangfei Gao #define TDCR_SSZ_8_BITS (0x0 << 22) /* Sample Size */ 43c6da0ba8SZhangfei Gao #define TDCR_SSZ_12_BITS (0x1 << 22) 44c6da0ba8SZhangfei Gao #define TDCR_SSZ_16_BITS (0x2 << 22) 45c6da0ba8SZhangfei Gao #define TDCR_SSZ_20_BITS (0x3 << 22) 46c6da0ba8SZhangfei Gao #define TDCR_SSZ_24_BITS (0x4 << 22) 47c6da0ba8SZhangfei Gao #define TDCR_SSZ_32_BITS (0x5 << 22) 48c6da0ba8SZhangfei Gao #define TDCR_SSZ_SHIFT (0x1 << 22) 49c6da0ba8SZhangfei Gao #define TDCR_SSZ_MASK (0x7 << 22) 50c6da0ba8SZhangfei Gao #define TDCR_SSPMOD (0x1 << 21) /* SSP MOD */ 51c6da0ba8SZhangfei Gao #define TDCR_ABR (0x1 << 20) /* Channel Abort */ 52c6da0ba8SZhangfei Gao #define TDCR_CDE (0x1 << 17) /* Close Desc Enable */ 53c6da0ba8SZhangfei Gao #define TDCR_PACKMOD (0x1 << 16) /* Pack Mode (ADMA Only) */ 54c6da0ba8SZhangfei Gao #define TDCR_CHANACT (0x1 << 14) /* Channel Active */ 55c6da0ba8SZhangfei Gao #define TDCR_FETCHND (0x1 << 13) /* Fetch Next Desc */ 56c6da0ba8SZhangfei Gao #define TDCR_CHANEN (0x1 << 12) /* Channel Enable */ 57c6da0ba8SZhangfei Gao #define TDCR_INTMODE (0x1 << 10) /* Interrupt Mode */ 58c6da0ba8SZhangfei Gao #define TDCR_CHAINMOD (0x1 << 9) /* Chain Mode */ 59c6da0ba8SZhangfei Gao #define TDCR_BURSTSZ_MSK (0x7 << 6) /* Burst Size */ 60c6da0ba8SZhangfei Gao #define TDCR_BURSTSZ_4B (0x0 << 6) 61c6da0ba8SZhangfei Gao #define TDCR_BURSTSZ_8B (0x1 << 6) 62c6da0ba8SZhangfei Gao #define TDCR_BURSTSZ_16B (0x3 << 6) 63c6da0ba8SZhangfei Gao #define TDCR_BURSTSZ_32B (0x6 << 6) 64c6da0ba8SZhangfei Gao #define TDCR_BURSTSZ_64B (0x7 << 6) 6520a90b0eSQiao Zhou #define TDCR_BURSTSZ_SQU_1B (0x5 << 6) 6620a90b0eSQiao Zhou #define TDCR_BURSTSZ_SQU_2B (0x6 << 6) 6720a90b0eSQiao Zhou #define TDCR_BURSTSZ_SQU_4B (0x0 << 6) 6820a90b0eSQiao Zhou #define TDCR_BURSTSZ_SQU_8B (0x1 << 6) 6920a90b0eSQiao Zhou #define TDCR_BURSTSZ_SQU_16B (0x3 << 6) 70c6da0ba8SZhangfei Gao #define TDCR_BURSTSZ_SQU_32B (0x7 << 6) 71c6da0ba8SZhangfei Gao #define TDCR_BURSTSZ_128B (0x5 << 6) 72c6da0ba8SZhangfei Gao #define TDCR_DSTDIR_MSK (0x3 << 4) /* Dst Direction */ 73c6da0ba8SZhangfei Gao #define TDCR_DSTDIR_ADDR_HOLD (0x2 << 4) /* Dst Addr Hold */ 74c6da0ba8SZhangfei Gao #define TDCR_DSTDIR_ADDR_INC (0x0 << 4) /* Dst Addr Increment */ 75c6da0ba8SZhangfei Gao #define TDCR_SRCDIR_MSK (0x3 << 2) /* Src Direction */ 76c6da0ba8SZhangfei Gao #define TDCR_SRCDIR_ADDR_HOLD (0x2 << 2) /* Src Addr Hold */ 77c6da0ba8SZhangfei Gao #define TDCR_SRCDIR_ADDR_INC (0x0 << 2) /* Src Addr Increment */ 78c6da0ba8SZhangfei Gao #define TDCR_DSTDESCCONT (0x1 << 1) 79c6da0ba8SZhangfei Gao #define TDCR_SRCDESTCONT (0x1 << 0) 80c6da0ba8SZhangfei Gao 81c6da0ba8SZhangfei Gao /* Two-Channel DMA Int Mask Register */ 82c6da0ba8SZhangfei Gao #define TDIMR_COMP (0x1 << 0) 83c6da0ba8SZhangfei Gao 84c6da0ba8SZhangfei Gao /* Two-Channel DMA Int Status Register */ 85c6da0ba8SZhangfei Gao #define TDISR_COMP (0x1 << 0) 86c6da0ba8SZhangfei Gao 87c6da0ba8SZhangfei Gao /* 88c6da0ba8SZhangfei Gao * Two-Channel DMA Descriptor Struct 89c6da0ba8SZhangfei Gao * NOTE: desc's buf must be aligned to 16 bytes. 90c6da0ba8SZhangfei Gao */ 91c6da0ba8SZhangfei Gao struct mmp_tdma_desc { 92c6da0ba8SZhangfei Gao u32 byte_cnt; 93c6da0ba8SZhangfei Gao u32 src_addr; 94c6da0ba8SZhangfei Gao u32 dst_addr; 95c6da0ba8SZhangfei Gao u32 nxt_desc; 96c6da0ba8SZhangfei Gao }; 97c6da0ba8SZhangfei Gao 98c6da0ba8SZhangfei Gao enum mmp_tdma_type { 99c6da0ba8SZhangfei Gao MMP_AUD_TDMA = 0, 100c6da0ba8SZhangfei Gao PXA910_SQU, 101c6da0ba8SZhangfei Gao }; 102c6da0ba8SZhangfei Gao 103c6da0ba8SZhangfei Gao #define TDMA_ALIGNMENT 3 104c6da0ba8SZhangfei Gao #define TDMA_MAX_XFER_BYTES SZ_64K 105c6da0ba8SZhangfei Gao 106c6da0ba8SZhangfei Gao struct mmp_tdma_chan { 107c6da0ba8SZhangfei Gao struct device *dev; 108c6da0ba8SZhangfei Gao struct dma_chan chan; 109c6da0ba8SZhangfei Gao struct dma_async_tx_descriptor desc; 110c6da0ba8SZhangfei Gao struct tasklet_struct tasklet; 111c6da0ba8SZhangfei Gao 112c6da0ba8SZhangfei Gao struct mmp_tdma_desc *desc_arr; 113c6da0ba8SZhangfei Gao phys_addr_t desc_arr_phys; 114c6da0ba8SZhangfei Gao int desc_num; 115c6da0ba8SZhangfei Gao enum dma_transfer_direction dir; 116c6da0ba8SZhangfei Gao dma_addr_t dev_addr; 117c6da0ba8SZhangfei Gao u32 burst_sz; 118c6da0ba8SZhangfei Gao enum dma_slave_buswidth buswidth; 119c6da0ba8SZhangfei Gao enum dma_status status; 120c6da0ba8SZhangfei Gao 121c6da0ba8SZhangfei Gao int idx; 122c6da0ba8SZhangfei Gao enum mmp_tdma_type type; 123c6da0ba8SZhangfei Gao int irq; 124c6da0ba8SZhangfei Gao unsigned long reg_base; 125c6da0ba8SZhangfei Gao 126c6da0ba8SZhangfei Gao size_t buf_len; 127c6da0ba8SZhangfei Gao size_t period_len; 128c6da0ba8SZhangfei Gao size_t pos; 129c6da0ba8SZhangfei Gao }; 130c6da0ba8SZhangfei Gao 131c6da0ba8SZhangfei Gao #define TDMA_CHANNEL_NUM 2 132c6da0ba8SZhangfei Gao struct mmp_tdma_device { 133c6da0ba8SZhangfei Gao struct device *dev; 134c6da0ba8SZhangfei Gao void __iomem *base; 135c6da0ba8SZhangfei Gao struct dma_device device; 136c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac[TDMA_CHANNEL_NUM]; 137c6da0ba8SZhangfei Gao }; 138c6da0ba8SZhangfei Gao 139c6da0ba8SZhangfei Gao #define to_mmp_tdma_chan(dchan) container_of(dchan, struct mmp_tdma_chan, chan) 140c6da0ba8SZhangfei Gao 141c6da0ba8SZhangfei Gao static void mmp_tdma_chan_set_desc(struct mmp_tdma_chan *tdmac, dma_addr_t phys) 142c6da0ba8SZhangfei Gao { 143c6da0ba8SZhangfei Gao writel(phys, tdmac->reg_base + TDNDPR); 144c6da0ba8SZhangfei Gao writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND, 145c6da0ba8SZhangfei Gao tdmac->reg_base + TDCR); 146c6da0ba8SZhangfei Gao } 147c6da0ba8SZhangfei Gao 148c6da0ba8SZhangfei Gao static void mmp_tdma_enable_chan(struct mmp_tdma_chan *tdmac) 149c6da0ba8SZhangfei Gao { 150c6da0ba8SZhangfei Gao /* enable irq */ 151c6da0ba8SZhangfei Gao writel(TDIMR_COMP, tdmac->reg_base + TDIMR); 152c6da0ba8SZhangfei Gao /* enable dma chan */ 153c6da0ba8SZhangfei Gao writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN, 154c6da0ba8SZhangfei Gao tdmac->reg_base + TDCR); 155c6da0ba8SZhangfei Gao tdmac->status = DMA_IN_PROGRESS; 156c6da0ba8SZhangfei Gao } 157c6da0ba8SZhangfei Gao 158c6da0ba8SZhangfei Gao static void mmp_tdma_disable_chan(struct mmp_tdma_chan *tdmac) 159c6da0ba8SZhangfei Gao { 160c6da0ba8SZhangfei Gao writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, 161c6da0ba8SZhangfei Gao tdmac->reg_base + TDCR); 1628e3c518fSQiao Zhou 1638e3c518fSQiao Zhou /* disable irq */ 1648e3c518fSQiao Zhou writel(0, tdmac->reg_base + TDIMR); 1658e3c518fSQiao Zhou 166c6da0ba8SZhangfei Gao tdmac->status = DMA_SUCCESS; 167c6da0ba8SZhangfei Gao } 168c6da0ba8SZhangfei Gao 169c6da0ba8SZhangfei Gao static void mmp_tdma_resume_chan(struct mmp_tdma_chan *tdmac) 170c6da0ba8SZhangfei Gao { 171c6da0ba8SZhangfei Gao writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN, 172c6da0ba8SZhangfei Gao tdmac->reg_base + TDCR); 173c6da0ba8SZhangfei Gao tdmac->status = DMA_IN_PROGRESS; 174c6da0ba8SZhangfei Gao } 175c6da0ba8SZhangfei Gao 176c6da0ba8SZhangfei Gao static void mmp_tdma_pause_chan(struct mmp_tdma_chan *tdmac) 177c6da0ba8SZhangfei Gao { 178c6da0ba8SZhangfei Gao writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, 179c6da0ba8SZhangfei Gao tdmac->reg_base + TDCR); 180c6da0ba8SZhangfei Gao tdmac->status = DMA_PAUSED; 181c6da0ba8SZhangfei Gao } 182c6da0ba8SZhangfei Gao 183c6da0ba8SZhangfei Gao static int mmp_tdma_config_chan(struct mmp_tdma_chan *tdmac) 184c6da0ba8SZhangfei Gao { 185c6da0ba8SZhangfei Gao unsigned int tdcr; 186c6da0ba8SZhangfei Gao 187c6da0ba8SZhangfei Gao mmp_tdma_disable_chan(tdmac); 188c6da0ba8SZhangfei Gao 189c6da0ba8SZhangfei Gao if (tdmac->dir == DMA_MEM_TO_DEV) 190c6da0ba8SZhangfei Gao tdcr = TDCR_DSTDIR_ADDR_HOLD | TDCR_SRCDIR_ADDR_INC; 191c6da0ba8SZhangfei Gao else if (tdmac->dir == DMA_DEV_TO_MEM) 192c6da0ba8SZhangfei Gao tdcr = TDCR_SRCDIR_ADDR_HOLD | TDCR_DSTDIR_ADDR_INC; 193c6da0ba8SZhangfei Gao 194c6da0ba8SZhangfei Gao if (tdmac->type == MMP_AUD_TDMA) { 195c6da0ba8SZhangfei Gao tdcr |= TDCR_PACKMOD; 196c6da0ba8SZhangfei Gao 197c6da0ba8SZhangfei Gao switch (tdmac->burst_sz) { 198c6da0ba8SZhangfei Gao case 4: 199c6da0ba8SZhangfei Gao tdcr |= TDCR_BURSTSZ_4B; 200c6da0ba8SZhangfei Gao break; 201c6da0ba8SZhangfei Gao case 8: 202c6da0ba8SZhangfei Gao tdcr |= TDCR_BURSTSZ_8B; 203c6da0ba8SZhangfei Gao break; 204c6da0ba8SZhangfei Gao case 16: 205c6da0ba8SZhangfei Gao tdcr |= TDCR_BURSTSZ_16B; 206c6da0ba8SZhangfei Gao break; 207c6da0ba8SZhangfei Gao case 32: 208c6da0ba8SZhangfei Gao tdcr |= TDCR_BURSTSZ_32B; 209c6da0ba8SZhangfei Gao break; 210c6da0ba8SZhangfei Gao case 64: 211c6da0ba8SZhangfei Gao tdcr |= TDCR_BURSTSZ_64B; 212c6da0ba8SZhangfei Gao break; 213c6da0ba8SZhangfei Gao case 128: 214c6da0ba8SZhangfei Gao tdcr |= TDCR_BURSTSZ_128B; 215c6da0ba8SZhangfei Gao break; 216c6da0ba8SZhangfei Gao default: 217c6da0ba8SZhangfei Gao dev_err(tdmac->dev, "mmp_tdma: unknown burst size.\n"); 218c6da0ba8SZhangfei Gao return -EINVAL; 219c6da0ba8SZhangfei Gao } 220c6da0ba8SZhangfei Gao 221c6da0ba8SZhangfei Gao switch (tdmac->buswidth) { 222c6da0ba8SZhangfei Gao case DMA_SLAVE_BUSWIDTH_1_BYTE: 223c6da0ba8SZhangfei Gao tdcr |= TDCR_SSZ_8_BITS; 224c6da0ba8SZhangfei Gao break; 225c6da0ba8SZhangfei Gao case DMA_SLAVE_BUSWIDTH_2_BYTES: 226c6da0ba8SZhangfei Gao tdcr |= TDCR_SSZ_16_BITS; 227c6da0ba8SZhangfei Gao break; 228c6da0ba8SZhangfei Gao case DMA_SLAVE_BUSWIDTH_4_BYTES: 229c6da0ba8SZhangfei Gao tdcr |= TDCR_SSZ_32_BITS; 230c6da0ba8SZhangfei Gao break; 231c6da0ba8SZhangfei Gao default: 232c6da0ba8SZhangfei Gao dev_err(tdmac->dev, "mmp_tdma: unknown bus size.\n"); 233c6da0ba8SZhangfei Gao return -EINVAL; 234c6da0ba8SZhangfei Gao } 235c6da0ba8SZhangfei Gao } else if (tdmac->type == PXA910_SQU) { 236c6da0ba8SZhangfei Gao tdcr |= TDCR_SSPMOD; 23720a90b0eSQiao Zhou 23820a90b0eSQiao Zhou switch (tdmac->burst_sz) { 23920a90b0eSQiao Zhou case 1: 24020a90b0eSQiao Zhou tdcr |= TDCR_BURSTSZ_SQU_1B; 24120a90b0eSQiao Zhou break; 24220a90b0eSQiao Zhou case 2: 24320a90b0eSQiao Zhou tdcr |= TDCR_BURSTSZ_SQU_2B; 24420a90b0eSQiao Zhou break; 24520a90b0eSQiao Zhou case 4: 24620a90b0eSQiao Zhou tdcr |= TDCR_BURSTSZ_SQU_4B; 24720a90b0eSQiao Zhou break; 24820a90b0eSQiao Zhou case 8: 24920a90b0eSQiao Zhou tdcr |= TDCR_BURSTSZ_SQU_8B; 25020a90b0eSQiao Zhou break; 25120a90b0eSQiao Zhou case 16: 25220a90b0eSQiao Zhou tdcr |= TDCR_BURSTSZ_SQU_16B; 25320a90b0eSQiao Zhou break; 25420a90b0eSQiao Zhou case 32: 25520a90b0eSQiao Zhou tdcr |= TDCR_BURSTSZ_SQU_32B; 25620a90b0eSQiao Zhou break; 25720a90b0eSQiao Zhou default: 25820a90b0eSQiao Zhou dev_err(tdmac->dev, "mmp_tdma: unknown burst size.\n"); 25920a90b0eSQiao Zhou return -EINVAL; 26020a90b0eSQiao Zhou } 261c6da0ba8SZhangfei Gao } 262c6da0ba8SZhangfei Gao 263c6da0ba8SZhangfei Gao writel(tdcr, tdmac->reg_base + TDCR); 264c6da0ba8SZhangfei Gao return 0; 265c6da0ba8SZhangfei Gao } 266c6da0ba8SZhangfei Gao 267c6da0ba8SZhangfei Gao static int mmp_tdma_clear_chan_irq(struct mmp_tdma_chan *tdmac) 268c6da0ba8SZhangfei Gao { 269c6da0ba8SZhangfei Gao u32 reg = readl(tdmac->reg_base + TDISR); 270c6da0ba8SZhangfei Gao 271c6da0ba8SZhangfei Gao if (reg & TDISR_COMP) { 272c6da0ba8SZhangfei Gao /* clear irq */ 273c6da0ba8SZhangfei Gao reg &= ~TDISR_COMP; 274c6da0ba8SZhangfei Gao writel(reg, tdmac->reg_base + TDISR); 275c6da0ba8SZhangfei Gao 276c6da0ba8SZhangfei Gao return 0; 277c6da0ba8SZhangfei Gao } 278c6da0ba8SZhangfei Gao return -EAGAIN; 279c6da0ba8SZhangfei Gao } 280c6da0ba8SZhangfei Gao 281c6da0ba8SZhangfei Gao static irqreturn_t mmp_tdma_chan_handler(int irq, void *dev_id) 282c6da0ba8SZhangfei Gao { 283c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = dev_id; 284c6da0ba8SZhangfei Gao 285c6da0ba8SZhangfei Gao if (mmp_tdma_clear_chan_irq(tdmac) == 0) { 286c6da0ba8SZhangfei Gao tdmac->pos = (tdmac->pos + tdmac->period_len) % tdmac->buf_len; 287c6da0ba8SZhangfei Gao tasklet_schedule(&tdmac->tasklet); 288c6da0ba8SZhangfei Gao return IRQ_HANDLED; 289c6da0ba8SZhangfei Gao } else 290c6da0ba8SZhangfei Gao return IRQ_NONE; 291c6da0ba8SZhangfei Gao } 292c6da0ba8SZhangfei Gao 293c6da0ba8SZhangfei Gao static irqreturn_t mmp_tdma_int_handler(int irq, void *dev_id) 294c6da0ba8SZhangfei Gao { 295c6da0ba8SZhangfei Gao struct mmp_tdma_device *tdev = dev_id; 296c6da0ba8SZhangfei Gao int i, ret; 297c6da0ba8SZhangfei Gao int irq_num = 0; 298c6da0ba8SZhangfei Gao 299c6da0ba8SZhangfei Gao for (i = 0; i < TDMA_CHANNEL_NUM; i++) { 300c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = tdev->tdmac[i]; 301c6da0ba8SZhangfei Gao 302c6da0ba8SZhangfei Gao ret = mmp_tdma_chan_handler(irq, tdmac); 303c6da0ba8SZhangfei Gao if (ret == IRQ_HANDLED) 304c6da0ba8SZhangfei Gao irq_num++; 305c6da0ba8SZhangfei Gao } 306c6da0ba8SZhangfei Gao 307c6da0ba8SZhangfei Gao if (irq_num) 308c6da0ba8SZhangfei Gao return IRQ_HANDLED; 309c6da0ba8SZhangfei Gao else 310c6da0ba8SZhangfei Gao return IRQ_NONE; 311c6da0ba8SZhangfei Gao } 312c6da0ba8SZhangfei Gao 313c6da0ba8SZhangfei Gao static void dma_do_tasklet(unsigned long data) 314c6da0ba8SZhangfei Gao { 315c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = (struct mmp_tdma_chan *)data; 316c6da0ba8SZhangfei Gao 317c6da0ba8SZhangfei Gao if (tdmac->desc.callback) 318c6da0ba8SZhangfei Gao tdmac->desc.callback(tdmac->desc.callback_param); 319c6da0ba8SZhangfei Gao 320c6da0ba8SZhangfei Gao } 321c6da0ba8SZhangfei Gao 322c6da0ba8SZhangfei Gao static void mmp_tdma_free_descriptor(struct mmp_tdma_chan *tdmac) 323c6da0ba8SZhangfei Gao { 324c6da0ba8SZhangfei Gao struct gen_pool *gpool; 325c6da0ba8SZhangfei Gao int size = tdmac->desc_num * sizeof(struct mmp_tdma_desc); 326c6da0ba8SZhangfei Gao 327c6da0ba8SZhangfei Gao gpool = sram_get_gpool("asram"); 328c6da0ba8SZhangfei Gao if (tdmac->desc_arr) 329c6da0ba8SZhangfei Gao gen_pool_free(gpool, (unsigned long)tdmac->desc_arr, 330c6da0ba8SZhangfei Gao size); 331c6da0ba8SZhangfei Gao tdmac->desc_arr = NULL; 332c6da0ba8SZhangfei Gao 333c6da0ba8SZhangfei Gao return; 334c6da0ba8SZhangfei Gao } 335c6da0ba8SZhangfei Gao 336c6da0ba8SZhangfei Gao static dma_cookie_t mmp_tdma_tx_submit(struct dma_async_tx_descriptor *tx) 337c6da0ba8SZhangfei Gao { 338c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(tx->chan); 339c6da0ba8SZhangfei Gao 340c6da0ba8SZhangfei Gao mmp_tdma_chan_set_desc(tdmac, tdmac->desc_arr_phys); 341c6da0ba8SZhangfei Gao 342c6da0ba8SZhangfei Gao return 0; 343c6da0ba8SZhangfei Gao } 344c6da0ba8SZhangfei Gao 345c6da0ba8SZhangfei Gao static int mmp_tdma_alloc_chan_resources(struct dma_chan *chan) 346c6da0ba8SZhangfei Gao { 347c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); 348c6da0ba8SZhangfei Gao int ret; 349c6da0ba8SZhangfei Gao 350c6da0ba8SZhangfei Gao dma_async_tx_descriptor_init(&tdmac->desc, chan); 351c6da0ba8SZhangfei Gao tdmac->desc.tx_submit = mmp_tdma_tx_submit; 352c6da0ba8SZhangfei Gao 353c6da0ba8SZhangfei Gao if (tdmac->irq) { 354c6da0ba8SZhangfei Gao ret = devm_request_irq(tdmac->dev, tdmac->irq, 355174b537aSMichael Opdenacker mmp_tdma_chan_handler, 0, "tdma", tdmac); 356c6da0ba8SZhangfei Gao if (ret) 357c6da0ba8SZhangfei Gao return ret; 358c6da0ba8SZhangfei Gao } 359c6da0ba8SZhangfei Gao return 1; 360c6da0ba8SZhangfei Gao } 361c6da0ba8SZhangfei Gao 362c6da0ba8SZhangfei Gao static void mmp_tdma_free_chan_resources(struct dma_chan *chan) 363c6da0ba8SZhangfei Gao { 364c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); 365c6da0ba8SZhangfei Gao 366c6da0ba8SZhangfei Gao if (tdmac->irq) 367c6da0ba8SZhangfei Gao devm_free_irq(tdmac->dev, tdmac->irq, tdmac); 368c6da0ba8SZhangfei Gao mmp_tdma_free_descriptor(tdmac); 369c6da0ba8SZhangfei Gao return; 370c6da0ba8SZhangfei Gao } 371c6da0ba8SZhangfei Gao 372c6da0ba8SZhangfei Gao struct mmp_tdma_desc *mmp_tdma_alloc_descriptor(struct mmp_tdma_chan *tdmac) 373c6da0ba8SZhangfei Gao { 374c6da0ba8SZhangfei Gao struct gen_pool *gpool; 375c6da0ba8SZhangfei Gao int size = tdmac->desc_num * sizeof(struct mmp_tdma_desc); 376c6da0ba8SZhangfei Gao 377c6da0ba8SZhangfei Gao gpool = sram_get_gpool("asram"); 378c6da0ba8SZhangfei Gao if (!gpool) 379c6da0ba8SZhangfei Gao return NULL; 380c6da0ba8SZhangfei Gao 381c6da0ba8SZhangfei Gao tdmac->desc_arr = (void *)gen_pool_alloc(gpool, size); 382c6da0ba8SZhangfei Gao if (!tdmac->desc_arr) 383c6da0ba8SZhangfei Gao return NULL; 384c6da0ba8SZhangfei Gao 385c6da0ba8SZhangfei Gao tdmac->desc_arr_phys = gen_pool_virt_to_phys(gpool, 386c6da0ba8SZhangfei Gao (unsigned long)tdmac->desc_arr); 387c6da0ba8SZhangfei Gao 388c6da0ba8SZhangfei Gao return tdmac->desc_arr; 389c6da0ba8SZhangfei Gao } 390c6da0ba8SZhangfei Gao 391c6da0ba8SZhangfei Gao static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic( 392c6da0ba8SZhangfei Gao struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, 393c6da0ba8SZhangfei Gao size_t period_len, enum dma_transfer_direction direction, 394ec8b5e48SPeter Ujfalusi unsigned long flags, void *context) 395c6da0ba8SZhangfei Gao { 396c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); 397c6da0ba8SZhangfei Gao struct mmp_tdma_desc *desc; 398c6da0ba8SZhangfei Gao int num_periods = buf_len / period_len; 399c6da0ba8SZhangfei Gao int i = 0, buf = 0; 400c6da0ba8SZhangfei Gao 401c6da0ba8SZhangfei Gao if (tdmac->status != DMA_SUCCESS) 402c6da0ba8SZhangfei Gao return NULL; 403c6da0ba8SZhangfei Gao 404c6da0ba8SZhangfei Gao if (period_len > TDMA_MAX_XFER_BYTES) { 405c6da0ba8SZhangfei Gao dev_err(tdmac->dev, 406c6da0ba8SZhangfei Gao "maximum period size exceeded: %d > %d\n", 407c6da0ba8SZhangfei Gao period_len, TDMA_MAX_XFER_BYTES); 408c6da0ba8SZhangfei Gao goto err_out; 409c6da0ba8SZhangfei Gao } 410c6da0ba8SZhangfei Gao 411c6da0ba8SZhangfei Gao tdmac->status = DMA_IN_PROGRESS; 412c6da0ba8SZhangfei Gao tdmac->desc_num = num_periods; 413c6da0ba8SZhangfei Gao desc = mmp_tdma_alloc_descriptor(tdmac); 414c6da0ba8SZhangfei Gao if (!desc) 415c6da0ba8SZhangfei Gao goto err_out; 416c6da0ba8SZhangfei Gao 417c6da0ba8SZhangfei Gao while (buf < buf_len) { 418c6da0ba8SZhangfei Gao desc = &tdmac->desc_arr[i]; 419c6da0ba8SZhangfei Gao 420c6da0ba8SZhangfei Gao if (i + 1 == num_periods) 421c6da0ba8SZhangfei Gao desc->nxt_desc = tdmac->desc_arr_phys; 422c6da0ba8SZhangfei Gao else 423c6da0ba8SZhangfei Gao desc->nxt_desc = tdmac->desc_arr_phys + 424c6da0ba8SZhangfei Gao sizeof(*desc) * (i + 1); 425c6da0ba8SZhangfei Gao 426c6da0ba8SZhangfei Gao if (direction == DMA_MEM_TO_DEV) { 427c6da0ba8SZhangfei Gao desc->src_addr = dma_addr; 428c6da0ba8SZhangfei Gao desc->dst_addr = tdmac->dev_addr; 429c6da0ba8SZhangfei Gao } else { 430c6da0ba8SZhangfei Gao desc->src_addr = tdmac->dev_addr; 431c6da0ba8SZhangfei Gao desc->dst_addr = dma_addr; 432c6da0ba8SZhangfei Gao } 433c6da0ba8SZhangfei Gao desc->byte_cnt = period_len; 434c6da0ba8SZhangfei Gao dma_addr += period_len; 435c6da0ba8SZhangfei Gao buf += period_len; 436c6da0ba8SZhangfei Gao i++; 437c6da0ba8SZhangfei Gao } 438c6da0ba8SZhangfei Gao 439c6da0ba8SZhangfei Gao tdmac->buf_len = buf_len; 440c6da0ba8SZhangfei Gao tdmac->period_len = period_len; 441c6da0ba8SZhangfei Gao tdmac->pos = 0; 442c6da0ba8SZhangfei Gao 443c6da0ba8SZhangfei Gao return &tdmac->desc; 444c6da0ba8SZhangfei Gao 445c6da0ba8SZhangfei Gao err_out: 446c6da0ba8SZhangfei Gao tdmac->status = DMA_ERROR; 447c6da0ba8SZhangfei Gao return NULL; 448c6da0ba8SZhangfei Gao } 449c6da0ba8SZhangfei Gao 450c6da0ba8SZhangfei Gao static int mmp_tdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, 451c6da0ba8SZhangfei Gao unsigned long arg) 452c6da0ba8SZhangfei Gao { 453c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); 454c6da0ba8SZhangfei Gao struct dma_slave_config *dmaengine_cfg = (void *)arg; 455c6da0ba8SZhangfei Gao int ret = 0; 456c6da0ba8SZhangfei Gao 457c6da0ba8SZhangfei Gao switch (cmd) { 458c6da0ba8SZhangfei Gao case DMA_TERMINATE_ALL: 459c6da0ba8SZhangfei Gao mmp_tdma_disable_chan(tdmac); 460c6da0ba8SZhangfei Gao break; 461c6da0ba8SZhangfei Gao case DMA_PAUSE: 462c6da0ba8SZhangfei Gao mmp_tdma_pause_chan(tdmac); 463c6da0ba8SZhangfei Gao break; 464c6da0ba8SZhangfei Gao case DMA_RESUME: 465c6da0ba8SZhangfei Gao mmp_tdma_resume_chan(tdmac); 466c6da0ba8SZhangfei Gao break; 467c6da0ba8SZhangfei Gao case DMA_SLAVE_CONFIG: 468c6da0ba8SZhangfei Gao if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { 469c6da0ba8SZhangfei Gao tdmac->dev_addr = dmaengine_cfg->src_addr; 470c6da0ba8SZhangfei Gao tdmac->burst_sz = dmaengine_cfg->src_maxburst; 471c6da0ba8SZhangfei Gao tdmac->buswidth = dmaengine_cfg->src_addr_width; 472c6da0ba8SZhangfei Gao } else { 473c6da0ba8SZhangfei Gao tdmac->dev_addr = dmaengine_cfg->dst_addr; 474c6da0ba8SZhangfei Gao tdmac->burst_sz = dmaengine_cfg->dst_maxburst; 475c6da0ba8SZhangfei Gao tdmac->buswidth = dmaengine_cfg->dst_addr_width; 476c6da0ba8SZhangfei Gao } 477c6da0ba8SZhangfei Gao tdmac->dir = dmaengine_cfg->direction; 478c6da0ba8SZhangfei Gao return mmp_tdma_config_chan(tdmac); 479c6da0ba8SZhangfei Gao default: 480c6da0ba8SZhangfei Gao ret = -ENOSYS; 481c6da0ba8SZhangfei Gao } 482c6da0ba8SZhangfei Gao 483c6da0ba8SZhangfei Gao return ret; 484c6da0ba8SZhangfei Gao } 485c6da0ba8SZhangfei Gao 486c6da0ba8SZhangfei Gao static enum dma_status mmp_tdma_tx_status(struct dma_chan *chan, 487c6da0ba8SZhangfei Gao dma_cookie_t cookie, struct dma_tx_state *txstate) 488c6da0ba8SZhangfei Gao { 489c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); 490c6da0ba8SZhangfei Gao 491c14d2bc4SAndy Shevchenko dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, 492c14d2bc4SAndy Shevchenko tdmac->buf_len - tdmac->pos); 493c6da0ba8SZhangfei Gao 494c6da0ba8SZhangfei Gao return tdmac->status; 495c6da0ba8SZhangfei Gao } 496c6da0ba8SZhangfei Gao 497c6da0ba8SZhangfei Gao static void mmp_tdma_issue_pending(struct dma_chan *chan) 498c6da0ba8SZhangfei Gao { 499c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); 500c6da0ba8SZhangfei Gao 501c6da0ba8SZhangfei Gao mmp_tdma_enable_chan(tdmac); 502c6da0ba8SZhangfei Gao } 503c6da0ba8SZhangfei Gao 5044bf27b8bSGreg Kroah-Hartman static int mmp_tdma_remove(struct platform_device *pdev) 505c6da0ba8SZhangfei Gao { 506c6da0ba8SZhangfei Gao struct mmp_tdma_device *tdev = platform_get_drvdata(pdev); 507c6da0ba8SZhangfei Gao 508c6da0ba8SZhangfei Gao dma_async_device_unregister(&tdev->device); 509c6da0ba8SZhangfei Gao return 0; 510c6da0ba8SZhangfei Gao } 511c6da0ba8SZhangfei Gao 512463a1f8bSBill Pemberton static int mmp_tdma_chan_init(struct mmp_tdma_device *tdev, 513c6da0ba8SZhangfei Gao int idx, int irq, int type) 514c6da0ba8SZhangfei Gao { 515c6da0ba8SZhangfei Gao struct mmp_tdma_chan *tdmac; 516c6da0ba8SZhangfei Gao 517c6da0ba8SZhangfei Gao if (idx >= TDMA_CHANNEL_NUM) { 518c6da0ba8SZhangfei Gao dev_err(tdev->dev, "too many channels for device!\n"); 519c6da0ba8SZhangfei Gao return -EINVAL; 520c6da0ba8SZhangfei Gao } 521c6da0ba8SZhangfei Gao 522c6da0ba8SZhangfei Gao /* alloc channel */ 523c6da0ba8SZhangfei Gao tdmac = devm_kzalloc(tdev->dev, sizeof(*tdmac), GFP_KERNEL); 524c6da0ba8SZhangfei Gao if (!tdmac) { 525c6da0ba8SZhangfei Gao dev_err(tdev->dev, "no free memory for DMA channels!\n"); 526c6da0ba8SZhangfei Gao return -ENOMEM; 527c6da0ba8SZhangfei Gao } 528c6da0ba8SZhangfei Gao if (irq) 529f1a77570SZhangfei Gao tdmac->irq = irq; 530c6da0ba8SZhangfei Gao tdmac->dev = tdev->dev; 531c6da0ba8SZhangfei Gao tdmac->chan.device = &tdev->device; 532c6da0ba8SZhangfei Gao tdmac->idx = idx; 533c6da0ba8SZhangfei Gao tdmac->type = type; 534c6da0ba8SZhangfei Gao tdmac->reg_base = (unsigned long)tdev->base + idx * 4; 535c6da0ba8SZhangfei Gao tdmac->status = DMA_SUCCESS; 536c6da0ba8SZhangfei Gao tdev->tdmac[tdmac->idx] = tdmac; 537c6da0ba8SZhangfei Gao tasklet_init(&tdmac->tasklet, dma_do_tasklet, (unsigned long)tdmac); 538c6da0ba8SZhangfei Gao 539c6da0ba8SZhangfei Gao /* add the channel to tdma_chan list */ 540c6da0ba8SZhangfei Gao list_add_tail(&tdmac->chan.device_node, 541c6da0ba8SZhangfei Gao &tdev->device.channels); 542c6da0ba8SZhangfei Gao return 0; 543c6da0ba8SZhangfei Gao } 544c6da0ba8SZhangfei Gao 545f1a77570SZhangfei Gao static struct of_device_id mmp_tdma_dt_ids[] = { 546f1a77570SZhangfei Gao { .compatible = "marvell,adma-1.0", .data = (void *)MMP_AUD_TDMA}, 547f1a77570SZhangfei Gao { .compatible = "marvell,pxa910-squ", .data = (void *)PXA910_SQU}, 548f1a77570SZhangfei Gao {} 549f1a77570SZhangfei Gao }; 550f1a77570SZhangfei Gao MODULE_DEVICE_TABLE(of, mmp_tdma_dt_ids); 551f1a77570SZhangfei Gao 552463a1f8bSBill Pemberton static int mmp_tdma_probe(struct platform_device *pdev) 553c6da0ba8SZhangfei Gao { 554f1a77570SZhangfei Gao enum mmp_tdma_type type; 555f1a77570SZhangfei Gao const struct of_device_id *of_id; 556c6da0ba8SZhangfei Gao struct mmp_tdma_device *tdev; 557c6da0ba8SZhangfei Gao struct resource *iores; 558c6da0ba8SZhangfei Gao int i, ret; 559f1a77570SZhangfei Gao int irq = 0, irq_num = 0; 560c6da0ba8SZhangfei Gao int chan_num = TDMA_CHANNEL_NUM; 561c6da0ba8SZhangfei Gao 562f1a77570SZhangfei Gao of_id = of_match_device(mmp_tdma_dt_ids, &pdev->dev); 563f1a77570SZhangfei Gao if (of_id) 564f1a77570SZhangfei Gao type = (enum mmp_tdma_type) of_id->data; 565f1a77570SZhangfei Gao else 566f1a77570SZhangfei Gao type = platform_get_device_id(pdev)->driver_data; 567f1a77570SZhangfei Gao 568c6da0ba8SZhangfei Gao /* always have couple channels */ 569c6da0ba8SZhangfei Gao tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL); 570c6da0ba8SZhangfei Gao if (!tdev) 571c6da0ba8SZhangfei Gao return -ENOMEM; 572c6da0ba8SZhangfei Gao 573c6da0ba8SZhangfei Gao tdev->dev = &pdev->dev; 574c6da0ba8SZhangfei Gao 575f1a77570SZhangfei Gao for (i = 0; i < chan_num; i++) { 576f1a77570SZhangfei Gao if (platform_get_irq(pdev, i) > 0) 577f1a77570SZhangfei Gao irq_num++; 578f1a77570SZhangfei Gao } 579c6da0ba8SZhangfei Gao 580c6da0ba8SZhangfei Gao iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5817331205aSThierry Reding tdev->base = devm_ioremap_resource(&pdev->dev, iores); 5827331205aSThierry Reding if (IS_ERR(tdev->base)) 5837331205aSThierry Reding return PTR_ERR(tdev->base); 584c6da0ba8SZhangfei Gao 585f1a77570SZhangfei Gao INIT_LIST_HEAD(&tdev->device.channels); 586f1a77570SZhangfei Gao 587f1a77570SZhangfei Gao if (irq_num != chan_num) { 588f1a77570SZhangfei Gao irq = platform_get_irq(pdev, 0); 589f1a77570SZhangfei Gao ret = devm_request_irq(&pdev->dev, irq, 590174b537aSMichael Opdenacker mmp_tdma_int_handler, 0, "tdma", tdev); 591c6da0ba8SZhangfei Gao if (ret) 592c6da0ba8SZhangfei Gao return ret; 593c6da0ba8SZhangfei Gao } 594c6da0ba8SZhangfei Gao 595f1a77570SZhangfei Gao /* initialize channel parameters */ 596f1a77570SZhangfei Gao for (i = 0; i < chan_num; i++) { 597f1a77570SZhangfei Gao irq = (irq_num != chan_num) ? 0 : platform_get_irq(pdev, i); 598f1a77570SZhangfei Gao ret = mmp_tdma_chan_init(tdev, i, irq, type); 599f1a77570SZhangfei Gao if (ret) 600f1a77570SZhangfei Gao return ret; 601f1a77570SZhangfei Gao } 602f1a77570SZhangfei Gao 603c6da0ba8SZhangfei Gao dma_cap_set(DMA_SLAVE, tdev->device.cap_mask); 604c6da0ba8SZhangfei Gao dma_cap_set(DMA_CYCLIC, tdev->device.cap_mask); 605c6da0ba8SZhangfei Gao tdev->device.dev = &pdev->dev; 606c6da0ba8SZhangfei Gao tdev->device.device_alloc_chan_resources = 607c6da0ba8SZhangfei Gao mmp_tdma_alloc_chan_resources; 608c6da0ba8SZhangfei Gao tdev->device.device_free_chan_resources = 609c6da0ba8SZhangfei Gao mmp_tdma_free_chan_resources; 610c6da0ba8SZhangfei Gao tdev->device.device_prep_dma_cyclic = mmp_tdma_prep_dma_cyclic; 611c6da0ba8SZhangfei Gao tdev->device.device_tx_status = mmp_tdma_tx_status; 612c6da0ba8SZhangfei Gao tdev->device.device_issue_pending = mmp_tdma_issue_pending; 613c6da0ba8SZhangfei Gao tdev->device.device_control = mmp_tdma_control; 614c6da0ba8SZhangfei Gao tdev->device.copy_align = TDMA_ALIGNMENT; 615c6da0ba8SZhangfei Gao 616c6da0ba8SZhangfei Gao dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); 617c6da0ba8SZhangfei Gao platform_set_drvdata(pdev, tdev); 618c6da0ba8SZhangfei Gao 619c6da0ba8SZhangfei Gao ret = dma_async_device_register(&tdev->device); 620c6da0ba8SZhangfei Gao if (ret) { 621c6da0ba8SZhangfei Gao dev_err(tdev->device.dev, "unable to register\n"); 622c6da0ba8SZhangfei Gao return ret; 623c6da0ba8SZhangfei Gao } 624c6da0ba8SZhangfei Gao 625c6da0ba8SZhangfei Gao dev_info(tdev->device.dev, "initialized\n"); 626c6da0ba8SZhangfei Gao return 0; 627c6da0ba8SZhangfei Gao } 628c6da0ba8SZhangfei Gao 629c6da0ba8SZhangfei Gao static const struct platform_device_id mmp_tdma_id_table[] = { 630c6da0ba8SZhangfei Gao { "mmp-adma", MMP_AUD_TDMA }, 631c6da0ba8SZhangfei Gao { "pxa910-squ", PXA910_SQU }, 632c6da0ba8SZhangfei Gao { }, 633c6da0ba8SZhangfei Gao }; 634c6da0ba8SZhangfei Gao 635c6da0ba8SZhangfei Gao static struct platform_driver mmp_tdma_driver = { 636c6da0ba8SZhangfei Gao .driver = { 637c6da0ba8SZhangfei Gao .name = "mmp-tdma", 638c6da0ba8SZhangfei Gao .owner = THIS_MODULE, 639f1a77570SZhangfei Gao .of_match_table = mmp_tdma_dt_ids, 640c6da0ba8SZhangfei Gao }, 641c6da0ba8SZhangfei Gao .id_table = mmp_tdma_id_table, 642c6da0ba8SZhangfei Gao .probe = mmp_tdma_probe, 643a7d6e3ecSBill Pemberton .remove = mmp_tdma_remove, 644c6da0ba8SZhangfei Gao }; 645c6da0ba8SZhangfei Gao 646c6da0ba8SZhangfei Gao module_platform_driver(mmp_tdma_driver); 647c6da0ba8SZhangfei Gao 648c6da0ba8SZhangfei Gao MODULE_LICENSE("GPL"); 649c6da0ba8SZhangfei Gao MODULE_DESCRIPTION("MMP Two-Channel DMA Driver"); 650c6da0ba8SZhangfei Gao MODULE_ALIAS("platform:mmp-tdma"); 651c6da0ba8SZhangfei Gao MODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); 652c6da0ba8SZhangfei Gao MODULE_AUTHOR("Zhangfei Gao <zhangfei.gao@marvell.com>"); 653