1/* 2 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> 3 * JZ4740 SoC DMA support 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the 7 * Free Software Foundation; either version 2 of the License, or (at your 8 * option) any later version. 9 * 10 * You should have received a copy of the GNU General Public License along 11 * with this program; if not, write to the Free Software Foundation, Inc., 12 * 675 Mass Ave, Cambridge, MA 02139, USA. 13 * 14 */ 15 16#include <linux/kernel.h> 17#include <linux/module.h> 18#include <linux/spinlock.h> 19#include <linux/interrupt.h> 20 21#include <linux/dma-mapping.h> 22#include <asm/mach-jz4740/dma.h> 23#include <asm/mach-jz4740/base.h> 24 25#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20) 26#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20) 27#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20) 28#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20) 29#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20) 30#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20) 31#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20) 32 33#define JZ_REG_DMA_CTRL 0x300 34#define JZ_REG_DMA_IRQ 0x304 35#define JZ_REG_DMA_DOORBELL 0x308 36#define JZ_REG_DMA_DOORBELL_SET 0x30C 37 38#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31) 39#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6) 40#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4) 41#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3) 42#define JZ_DMA_STATUS_CTRL_HALT BIT(2) 43#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1) 44#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0) 45 46#define JZ_DMA_CMD_SRC_INC BIT(23) 47#define JZ_DMA_CMD_DST_INC BIT(22) 48#define JZ_DMA_CMD_RDIL_MASK (0xf << 16) 49#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) 50#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12) 51#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8) 52#define JZ_DMA_CMD_BLOCK_MODE BIT(7) 53#define JZ_DMA_CMD_DESC_VALID BIT(4) 54#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3) 55#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) 56#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) 57#define JZ_DMA_CMD_LINK_ENABLE BIT(0) 58 59#define JZ_DMA_CMD_FLAGS_OFFSET 22 60#define JZ_DMA_CMD_RDIL_OFFSET 16 61#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14 62#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12 63#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8 64#define JZ_DMA_CMD_MODE_OFFSET 7 65 66#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8) 67#define JZ_DMA_CTRL_HALT BIT(3) 68#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2) 69#define JZ_DMA_CTRL_ENABLE BIT(0) 70 71 72static void __iomem *jz4740_dma_base; 73static spinlock_t jz4740_dma_lock; 74 75static inline uint32_t jz4740_dma_read(size_t reg) 76{ 77 return readl(jz4740_dma_base + reg); 78} 79 80static inline void jz4740_dma_write(size_t reg, uint32_t val) 81{ 82 writel(val, jz4740_dma_base + reg); 83} 84 85static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask) 86{ 87 uint32_t val2; 88 val2 = jz4740_dma_read(reg); 89 val2 &= ~mask; 90 val2 |= val; 91 jz4740_dma_write(reg, val2); 92} 93 94struct jz4740_dma_chan { 95 unsigned int id; 96 void *dev; 97 const char *name; 98 99 enum jz4740_dma_flags flags; 100 uint32_t transfer_shift; 101 102 jz4740_dma_complete_callback_t complete_cb; 103 104 unsigned used:1; 105}; 106 107#define JZ4740_DMA_CHANNEL(_id) { .id = _id } 108 109struct jz4740_dma_chan jz4740_dma_channels[] = { 110 JZ4740_DMA_CHANNEL(0), 111 JZ4740_DMA_CHANNEL(1), 112 JZ4740_DMA_CHANNEL(2), 113 JZ4740_DMA_CHANNEL(3), 114 JZ4740_DMA_CHANNEL(4), 115 JZ4740_DMA_CHANNEL(5), 116}; 117 118struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name) 119{ 120 unsigned int i; 121 struct jz4740_dma_chan *dma = NULL; 122 123 spin_lock(&jz4740_dma_lock); 124 125 for (i = 0; i < ARRAY_SIZE(jz4740_dma_channels); ++i) { 126 if (!jz4740_dma_channels[i].used) { 127 dma = &jz4740_dma_channels[i]; 128 dma->used = 1; 129 break; 130 } 131 } 132 133 spin_unlock(&jz4740_dma_lock); 134 135 if (!dma) 136 return NULL; 137 138 dma->dev = dev; 139 dma->name = name; 140 141 return dma; 142} 143EXPORT_SYMBOL_GPL(jz4740_dma_request); 144 145void jz4740_dma_configure(struct jz4740_dma_chan *dma, 146 const struct jz4740_dma_config *config) 147{ 148 uint32_t cmd; 149 150 switch (config->transfer_size) { 151 case JZ4740_DMA_TRANSFER_SIZE_2BYTE: 152 dma->transfer_shift = 1; 153 break; 154 case JZ4740_DMA_TRANSFER_SIZE_4BYTE: 155 dma->transfer_shift = 2; 156 break; 157 case JZ4740_DMA_TRANSFER_SIZE_16BYTE: 158 dma->transfer_shift = 4; 159 break; 160 case JZ4740_DMA_TRANSFER_SIZE_32BYTE: 161 dma->transfer_shift = 5; 162 break; 163 default: 164 dma->transfer_shift = 0; 165 break; 166 } 167 168 cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET; 169 cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET; 170 cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET; 171 cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET; 172 cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET; 173 cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; 174 175 jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd); 176 jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), 0); 177 jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type); 178} 179EXPORT_SYMBOL_GPL(jz4740_dma_configure); 180 181void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src) 182{ 183 jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src); 184} 185EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr); 186 187void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst) 188{ 189 jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst); 190} 191EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr); 192 193void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count) 194{ 195 count >>= dma->transfer_shift; 196 jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count); 197} 198EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count); 199 200void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, 201 jz4740_dma_complete_callback_t cb) 202{ 203 dma->complete_cb = cb; 204} 205EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb); 206 207void jz4740_dma_free(struct jz4740_dma_chan *dma) 208{ 209 dma->dev = NULL; 210 dma->complete_cb = NULL; 211 dma->used = 0; 212} 213EXPORT_SYMBOL_GPL(jz4740_dma_free); 214 215void jz4740_dma_enable(struct jz4740_dma_chan *dma) 216{ 217 jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 218 JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE, 219 JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC | 220 JZ_DMA_STATUS_CTRL_ENABLE); 221 222 jz4740_dma_write_mask(JZ_REG_DMA_CTRL, 223 JZ_DMA_CTRL_ENABLE, 224 JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE); 225} 226EXPORT_SYMBOL_GPL(jz4740_dma_enable); 227 228void jz4740_dma_disable(struct jz4740_dma_chan *dma) 229{ 230 jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, 231 JZ_DMA_STATUS_CTRL_ENABLE); 232} 233EXPORT_SYMBOL_GPL(jz4740_dma_disable); 234 235uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma) 236{ 237 uint32_t residue; 238 residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id)); 239 return residue << dma->transfer_shift; 240} 241EXPORT_SYMBOL_GPL(jz4740_dma_get_residue); 242 243static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma) 244{ 245 (void) jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id)); 246 247 jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, 248 JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE); 249 250 if (dma->complete_cb) 251 dma->complete_cb(dma, 0, dma->dev); 252} 253 254static irqreturn_t jz4740_dma_irq(int irq, void *dev_id) 255{ 256 uint32_t irq_status; 257 unsigned int i; 258 259 irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ); 260 261 for (i = 0; i < 6; ++i) { 262 if (irq_status & (1 << i)) 263 jz4740_dma_chan_irq(&jz4740_dma_channels[i]); 264 } 265 266 return IRQ_HANDLED; 267} 268 269static int jz4740_dma_init(void) 270{ 271 unsigned int ret; 272 273 jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400); 274 275 if (!jz4740_dma_base) 276 return -EBUSY; 277 278 spin_lock_init(&jz4740_dma_lock); 279 280 ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); 281 282 if (ret) 283 printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret); 284 285 return ret; 286} 287arch_initcall(jz4740_dma_init); 288