Root/target/linux/xburst/files-2.6.32/arch/mips/jz4740/dma.c

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/regs.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    uint32_t ctrl;
150
151    switch(config->transfer_size) {
152        case JZ4740_DMA_TRANSFER_SIZE_2BYTE:
153            dma->transfer_shift = 1;
154            break;
155        case JZ4740_DMA_TRANSFER_SIZE_4BYTE:
156            dma->transfer_shift = 2;
157            break;
158        case JZ4740_DMA_TRANSFER_SIZE_16BYTE:
159            dma->transfer_shift = 4;
160            break;
161        case JZ4740_DMA_TRANSFER_SIZE_32BYTE:
162            dma->transfer_shift = 5;
163            break;
164        default:
165            dma->transfer_shift = 0;
166            break;
167    }
168
169    cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET;
170    cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET;
171    cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET;
172    cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET;
173    cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET;
174    cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE;
175
176    ctrl = JZ_DMA_STATUS_CTRL_NO_DESC;
177    ctrl |= JZ_DMA_STATUS_CTRL_HALT;
178
179    jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd);
180    jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), ctrl);
181    jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type);
182}
183EXPORT_SYMBOL_GPL(jz4740_dma_configure);
184
185void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src)
186{
187    jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src);
188}
189EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr);
190
191void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst)
192{
193    jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst);
194}
195EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr);
196
197void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count)
198{
199    count >>= dma->transfer_shift;
200    jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count);
201}
202EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count);
203
204void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma,
205                                jz4740_dma_complete_callback_t cb)
206{
207    dma->complete_cb = cb;
208}
209EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb);
210
211void jz4740_dma_free(struct jz4740_dma_chan *dma)
212{
213    dma->dev = NULL;
214    dma->complete_cb = NULL;
215    dma->used = 0;
216}
217EXPORT_SYMBOL_GPL(jz4740_dma_free);
218
219void jz4740_dma_enable(struct jz4740_dma_chan *dma)
220{
221    jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id),
222            JZ_DMA_STATUS_CTRL_ENABLE,
223            JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_HALT);
224
225    jz4740_dma_write_mask(JZ_REG_DMA_CTRL,
226            JZ_DMA_CTRL_ENABLE,
227            JZ_DMA_CTRL_ENABLE | JZ_DMA_CTRL_HALT);
228}
229EXPORT_SYMBOL_GPL(jz4740_dma_enable);
230
231void jz4740_dma_disable(struct jz4740_dma_chan *dma)
232{
233    jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
234            JZ_DMA_STATUS_CTRL_ENABLE);
235}
236EXPORT_SYMBOL_GPL(jz4740_dma_disable);
237
238uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma)
239{
240    uint32_t residue;
241    residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id));
242    return residue << dma->transfer_shift;
243}
244EXPORT_SYMBOL_GPL(jz4740_dma_get_residue);
245
246static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma)
247{
248    uint32_t status;
249
250    status = jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id));
251
252    jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
253        JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE);
254
255    if (dma->complete_cb)
256        dma->complete_cb(dma, 0, dma->dev);
257}
258
259static irqreturn_t jz4740_dma_irq(int irq, void *dev_id)
260{
261    uint32_t irq_status;
262    unsigned int i;
263
264    irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ);
265
266    for (i = 0; i < 6; ++i) {
267        if (irq_status & (1 << i))
268            jz4740_dma_chan_irq(&jz4740_dma_channels[i]);
269    }
270
271    return IRQ_HANDLED;
272}
273
274#if 0
275static struct jz4740_dma_config dma_test_config = {
276    .src_width = JZ4740_DMA_WIDTH_32BIT,
277    .dst_width = JZ4740_DMA_WIDTH_32BIT,
278    .transfer_size = JZ4740_DMA_TRANSFER_SIZE_4BYTE,
279    .request_type = JZ4740_DMA_TYPE_AUTO_REQUEST,
280    .flags = JZ4740_DMA_SRC_AUTOINC | JZ4740_DMA_DST_AUTOINC,
281    .mode = JZ4740_DMA_MODE_BLOCK,
282};
283
284static void jz4740_dma_test(void)
285{
286    uint32_t *buf1, *buf2;
287    dma_addr_t addr1, addr2;
288    struct jz4740_dma_chan *dma = jz4740_dma_request(NULL, "dma test");
289    int i;
290
291    printk("STARTING DMA TEST\n");
292
293    buf1 = dma_alloc_coherent(NULL,
294                        0x1000,
295                        &addr1, GFP_KERNEL);
296    buf2 = dma_alloc_coherent(NULL,
297                        0x1000,
298                        &addr2, GFP_KERNEL);
299
300    for (i = 0; i < 0x400; ++i)
301        buf1[i] = i;
302
303
304    jz4740_dma_configure(dma, &dma_test_config);
305    jz4740_dma_set_src_addr(dma, addr1);
306    jz4740_dma_set_dst_addr(dma, addr2);
307    jz4740_dma_set_transfer_count(dma, 0x1000);
308
309    jz4740_dma_enable(dma);
310    mdelay(2000);
311
312    for (i = 0; i < 0x400; ++i) {
313        if (buf2[i] != i)
314            printk("OH MY GOD: %x %x\n", i, buf2[i]);
315    }
316
317    printk("DMA TEST DONE\n");
318}
319#endif
320
321static int jz4740_dma_init(void)
322{
323    unsigned int ret;
324
325    jz4740_dma_base = ioremap(CPHYSADDR(DMAC_BASE), 0x400);
326
327    if (!jz4740_dma_base)
328        return -EBUSY;
329
330    spin_lock_init(&jz4740_dma_lock);
331
332    ret = request_irq(JZ_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL);
333
334    if (ret)
335        printk("JZ4740 DMA: Failed to request irq: %d\n", ret);
336
337    return ret;
338}
339arch_initcall(jz4740_dma_init);
340

Archive Download this file



interactive