Root/arch/mips/jz4740/dma.c

Source at commit 4bbb1eea9c3c814b96eb9987571274d3c9e1f686 created 8 years 7 months ago.
By Maarten ter Huurne, MIPS: JZ4740: Use round robin DMA channel priority mode.
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_012345 (0x0 << 8)
67#define JZ_DMA_CTRL_PRIORITY_023145 (0x1 << 8)
68#define JZ_DMA_CTRL_PRIORITY_201345 (0x2 << 8)
69#define JZ_DMA_CTRL_PRIORITY_ROUND_ROBIN (0x2 << 8)
70#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8)
71#define JZ_DMA_CTRL_HALT BIT(3)
72#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2)
73#define JZ_DMA_CTRL_ENABLE BIT(0)
74
75
76static void __iomem *jz4740_dma_base;
77static spinlock_t jz4740_dma_lock;
78
79static inline uint32_t jz4740_dma_read(size_t reg)
80{
81    return readl(jz4740_dma_base + reg);
82}
83
84static inline void jz4740_dma_write(size_t reg, uint32_t val)
85{
86    writel(val, jz4740_dma_base + reg);
87}
88
89static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask)
90{
91    uint32_t val2;
92    val2 = jz4740_dma_read(reg);
93    val2 &= ~mask;
94    val2 |= val;
95    jz4740_dma_write(reg, val2);
96}
97
98struct jz4740_dma_chan {
99    unsigned int id;
100    void *dev;
101    const char *name;
102
103    enum jz4740_dma_flags flags;
104    uint32_t transfer_shift;
105
106    jz4740_dma_complete_callback_t complete_cb;
107
108    unsigned used:1;
109};
110
111#define JZ4740_DMA_CHANNEL(_id) { .id = _id }
112
113struct jz4740_dma_chan jz4740_dma_channels[] = {
114    JZ4740_DMA_CHANNEL(0),
115    JZ4740_DMA_CHANNEL(1),
116    JZ4740_DMA_CHANNEL(2),
117    JZ4740_DMA_CHANNEL(3),
118    JZ4740_DMA_CHANNEL(4),
119    JZ4740_DMA_CHANNEL(5),
120};
121
122struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name,
123                       int prio)
124{
125    unsigned int i;
126    struct jz4740_dma_chan *dma = NULL;
127
128    if (prio < 0 || prio > 1)
129        return NULL;
130
131    spin_lock(&jz4740_dma_lock);
132
133    for (i = prio * 3; i < prio * 3 + 3; ++i) {
134        if (!jz4740_dma_channels[i].used) {
135            dma = &jz4740_dma_channels[i];
136            dma->used = 1;
137            break;
138        }
139    }
140
141    spin_unlock(&jz4740_dma_lock);
142
143    if (!dma)
144        return NULL;
145
146    dma->dev = dev;
147    dma->name = name;
148
149    return dma;
150}
151EXPORT_SYMBOL_GPL(jz4740_dma_request);
152
153void jz4740_dma_configure(struct jz4740_dma_chan *dma,
154    const struct jz4740_dma_config *config)
155{
156    uint32_t cmd;
157
158    switch (config->transfer_size) {
159    case JZ4740_DMA_TRANSFER_SIZE_2BYTE:
160        dma->transfer_shift = 1;
161        break;
162    case JZ4740_DMA_TRANSFER_SIZE_4BYTE:
163        dma->transfer_shift = 2;
164        break;
165    case JZ4740_DMA_TRANSFER_SIZE_16BYTE:
166        dma->transfer_shift = 4;
167        break;
168    case JZ4740_DMA_TRANSFER_SIZE_32BYTE:
169        dma->transfer_shift = 5;
170        break;
171    default:
172        dma->transfer_shift = 0;
173        break;
174    }
175
176    cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET;
177    cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET;
178    cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET;
179    cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET;
180    cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET;
181    cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE;
182
183    jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd);
184    jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), 0);
185    jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type);
186}
187EXPORT_SYMBOL_GPL(jz4740_dma_configure);
188
189void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src)
190{
191    jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src);
192}
193EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr);
194
195void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst)
196{
197    jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst);
198}
199EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr);
200
201void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count)
202{
203    count >>= dma->transfer_shift;
204    jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count);
205}
206EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count);
207
208void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma,
209    jz4740_dma_complete_callback_t cb)
210{
211    dma->complete_cb = cb;
212}
213EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb);
214
215void jz4740_dma_free(struct jz4740_dma_chan *dma)
216{
217    dma->dev = NULL;
218    dma->complete_cb = NULL;
219    dma->used = 0;
220}
221EXPORT_SYMBOL_GPL(jz4740_dma_free);
222
223void jz4740_dma_enable(struct jz4740_dma_chan *dma)
224{
225    jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id),
226            JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE,
227            JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC |
228            JZ_DMA_STATUS_CTRL_ENABLE);
229
230    jz4740_dma_write_mask(JZ_REG_DMA_CTRL,
231            JZ_DMA_CTRL_ENABLE,
232            JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE);
233}
234EXPORT_SYMBOL_GPL(jz4740_dma_enable);
235
236void jz4740_dma_disable(struct jz4740_dma_chan *dma)
237{
238    jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
239            JZ_DMA_STATUS_CTRL_ENABLE);
240}
241EXPORT_SYMBOL_GPL(jz4740_dma_disable);
242
243uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma)
244{
245    uint32_t residue;
246    residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id));
247    return residue << dma->transfer_shift;
248}
249EXPORT_SYMBOL_GPL(jz4740_dma_get_residue);
250
251static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma)
252{
253    (void) jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id));
254
255    jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
256        JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE);
257
258    if (dma->complete_cb)
259        dma->complete_cb(dma, 0, dma->dev);
260}
261
262static irqreturn_t jz4740_dma_irq(int irq, void *dev_id)
263{
264    uint32_t irq_status;
265    unsigned int i;
266
267    irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ);
268
269    for (i = 0; i < 6; ++i) {
270        if (irq_status & (1 << i))
271            jz4740_dma_chan_irq(&jz4740_dma_channels[i]);
272    }
273
274    return IRQ_HANDLED;
275}
276
277static int jz4740_dma_init(void)
278{
279    unsigned int ret;
280
281    jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400);
282    if (!jz4740_dma_base)
283        return -EBUSY;
284
285    spin_lock_init(&jz4740_dma_lock);
286
287    ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL);
288    if (ret) {
289        printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret);
290        goto err_iounmap;
291    }
292
293    jz4740_dma_write_mask(JZ_REG_DMA_CTRL,
294                  JZ_DMA_CTRL_PRIORITY_MASK,
295                  JZ_DMA_CTRL_PRIORITY_ROUND_ROBIN);
296
297    return 0;
298
299err_iounmap:
300    iounmap(jz4740_dma_base);
301    return ret;
302}
303arch_initcall(jz4740_dma_init);
304

Archive Download this file



interactive