blob: 7d5b9fb01e71d9d5b9e8dab4e62d3302fd3fde79 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/arch/arm/kernel/dma.c
3 *
4 * Copyright (C) 1995-2000 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Front-end to the DMA handling. This handles the allocation/freeing
11 * of DMA channels, and provides a unified interface to the machines
12 * DMA facilities.
13 */
14#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/init.h>
16#include <linux/spinlock.h>
17#include <linux/errno.h>
Russell Kingd6675222008-12-08 17:50:25 +000018#include <linux/scatterlist.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
20#include <asm/dma.h>
21
22#include <asm/mach/dma.h>
23
24DEFINE_SPINLOCK(dma_spin_lock);
Russell Kingd7b4a752006-01-04 15:52:45 +000025EXPORT_SYMBOL(dma_spin_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
Russell King2f757f22008-12-08 16:33:30 +000027static dma_t *dma_chan[MAX_DMA_CHANNELS];
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Russell King3afb6e92008-12-08 16:08:48 +000029static inline dma_t *dma_channel(unsigned int chan)
30{
Russell King2f757f22008-12-08 16:33:30 +000031 if (chan >= MAX_DMA_CHANNELS)
Russell King3afb6e92008-12-08 16:08:48 +000032 return NULL;
33
Russell King2f757f22008-12-08 16:33:30 +000034 return dma_chan[chan];
35}
36
37int __init isa_dma_add(unsigned int chan, dma_t *dma)
38{
39 if (!dma->d_ops)
40 return -EINVAL;
Russell Kingd6675222008-12-08 17:50:25 +000041
42 sg_init_table(&dma->buf, 1);
43
Russell King2f757f22008-12-08 16:33:30 +000044 if (dma_chan[chan])
45 return -EBUSY;
46 dma_chan[chan] = dma;
47 return 0;
Russell King3afb6e92008-12-08 16:08:48 +000048}
49
Linus Torvalds1da177e2005-04-16 15:20:36 -070050/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 * Request DMA channel
52 *
53 * On certain platforms, we have to allocate an interrupt as well...
54 */
Russell King1df81302008-12-08 15:58:50 +000055int request_dma(unsigned int chan, const char *device_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070056{
Russell King3afb6e92008-12-08 16:08:48 +000057 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 int ret;
59
Russell King3afb6e92008-12-08 16:08:48 +000060 if (!dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 goto bad_dma;
62
63 if (xchg(&dma->lock, 1) != 0)
64 goto busy;
65
66 dma->device_id = device_id;
67 dma->active = 0;
68 dma->invalid = 1;
69
70 ret = 0;
71 if (dma->d_ops->request)
Russell King1df81302008-12-08 15:58:50 +000072 ret = dma->d_ops->request(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74 if (ret)
75 xchg(&dma->lock, 0);
76
77 return ret;
78
79bad_dma:
Russell King1df81302008-12-08 15:58:50 +000080 printk(KERN_ERR "dma: trying to allocate DMA%d\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 return -EINVAL;
82
83busy:
84 return -EBUSY;
85}
Russell Kingd7b4a752006-01-04 15:52:45 +000086EXPORT_SYMBOL(request_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
88/*
89 * Free DMA channel
90 *
91 * On certain platforms, we have to free interrupt as well...
92 */
Russell King1df81302008-12-08 15:58:50 +000093void free_dma(unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -070094{
Russell King3afb6e92008-12-08 16:08:48 +000095 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
Russell King3afb6e92008-12-08 16:08:48 +000097 if (!dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 goto bad_dma;
99
100 if (dma->active) {
Russell King1df81302008-12-08 15:58:50 +0000101 printk(KERN_ERR "dma%d: freeing active DMA\n", chan);
102 dma->d_ops->disable(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 dma->active = 0;
104 }
105
106 if (xchg(&dma->lock, 0) != 0) {
107 if (dma->d_ops->free)
Russell King1df81302008-12-08 15:58:50 +0000108 dma->d_ops->free(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 return;
110 }
111
Russell King1df81302008-12-08 15:58:50 +0000112 printk(KERN_ERR "dma%d: trying to free free DMA\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 return;
114
115bad_dma:
Russell King1df81302008-12-08 15:58:50 +0000116 printk(KERN_ERR "dma: trying to free DMA%d\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117}
Russell Kingd7b4a752006-01-04 15:52:45 +0000118EXPORT_SYMBOL(free_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
120/* Set DMA Scatter-Gather list
121 */
Russell King1df81302008-12-08 15:58:50 +0000122void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123{
Russell King3afb6e92008-12-08 16:08:48 +0000124 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 if (dma->active)
127 printk(KERN_ERR "dma%d: altering DMA SG while "
Russell King1df81302008-12-08 15:58:50 +0000128 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
130 dma->sg = sg;
131 dma->sgcount = nr_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 dma->invalid = 1;
133}
Russell Kingd7b4a752006-01-04 15:52:45 +0000134EXPORT_SYMBOL(set_dma_sg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
136/* Set DMA address
137 *
138 * Copy address to the structure, and set the invalid bit
139 */
Russell King1df81302008-12-08 15:58:50 +0000140void __set_dma_addr (unsigned int chan, void *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141{
Russell King3afb6e92008-12-08 16:08:48 +0000142 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 if (dma->active)
145 printk(KERN_ERR "dma%d: altering DMA address while "
Russell King1df81302008-12-08 15:58:50 +0000146 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
Russell King7cdad482006-01-04 15:08:30 +0000148 dma->sg = NULL;
149 dma->addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 dma->invalid = 1;
151}
Russell Kingd7b4a752006-01-04 15:52:45 +0000152EXPORT_SYMBOL(__set_dma_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
154/* Set DMA byte count
155 *
156 * Copy address to the structure, and set the invalid bit
157 */
Russell King1df81302008-12-08 15:58:50 +0000158void set_dma_count (unsigned int chan, unsigned long count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159{
Russell King3afb6e92008-12-08 16:08:48 +0000160 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
162 if (dma->active)
163 printk(KERN_ERR "dma%d: altering DMA count while "
Russell King1df81302008-12-08 15:58:50 +0000164 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165
Russell King7cdad482006-01-04 15:08:30 +0000166 dma->sg = NULL;
167 dma->count = count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 dma->invalid = 1;
169}
Russell Kingd7b4a752006-01-04 15:52:45 +0000170EXPORT_SYMBOL(set_dma_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
172/* Set DMA direction mode
173 */
Russell Kingf0ffc812009-01-02 12:34:55 +0000174void set_dma_mode (unsigned int chan, unsigned int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175{
Russell King3afb6e92008-12-08 16:08:48 +0000176 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
178 if (dma->active)
179 printk(KERN_ERR "dma%d: altering DMA mode while "
Russell King1df81302008-12-08 15:58:50 +0000180 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
182 dma->dma_mode = mode;
183 dma->invalid = 1;
184}
Russell Kingd7b4a752006-01-04 15:52:45 +0000185EXPORT_SYMBOL(set_dma_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
187/* Enable DMA channel
188 */
Russell King1df81302008-12-08 15:58:50 +0000189void enable_dma (unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190{
Russell King3afb6e92008-12-08 16:08:48 +0000191 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
193 if (!dma->lock)
194 goto free_dma;
195
196 if (dma->active == 0) {
197 dma->active = 1;
Russell King1df81302008-12-08 15:58:50 +0000198 dma->d_ops->enable(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 }
200 return;
201
202free_dma:
Russell King1df81302008-12-08 15:58:50 +0000203 printk(KERN_ERR "dma%d: trying to enable free DMA\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 BUG();
205}
Russell Kingd7b4a752006-01-04 15:52:45 +0000206EXPORT_SYMBOL(enable_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
208/* Disable DMA channel
209 */
Russell King1df81302008-12-08 15:58:50 +0000210void disable_dma (unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
Russell King3afb6e92008-12-08 16:08:48 +0000212 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
214 if (!dma->lock)
215 goto free_dma;
216
217 if (dma->active == 1) {
218 dma->active = 0;
Russell King1df81302008-12-08 15:58:50 +0000219 dma->d_ops->disable(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 }
221 return;
222
223free_dma:
Russell King1df81302008-12-08 15:58:50 +0000224 printk(KERN_ERR "dma%d: trying to disable free DMA\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 BUG();
226}
Russell Kingd7b4a752006-01-04 15:52:45 +0000227EXPORT_SYMBOL(disable_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
229/*
230 * Is the specified DMA channel active?
231 */
Russell King1df81302008-12-08 15:58:50 +0000232int dma_channel_active(unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
Russell King3afb6e92008-12-08 16:08:48 +0000234 dma_t *dma = dma_channel(chan);
235 return dma->active;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236}
Russell Kingec14d792007-03-31 21:36:53 +0100237EXPORT_SYMBOL(dma_channel_active);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
Russell King1df81302008-12-08 15:58:50 +0000239void set_dma_page(unsigned int chan, char pagenr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240{
Russell King1df81302008-12-08 15:58:50 +0000241 printk(KERN_ERR "dma%d: trying to set_dma_page\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242}
Russell Kingd7b4a752006-01-04 15:52:45 +0000243EXPORT_SYMBOL(set_dma_page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Russell King1df81302008-12-08 15:58:50 +0000245void set_dma_speed(unsigned int chan, int cycle_ns)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246{
Russell King3afb6e92008-12-08 16:08:48 +0000247 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 int ret = 0;
249
250 if (dma->d_ops->setspeed)
Russell King1df81302008-12-08 15:58:50 +0000251 ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 dma->speed = ret;
253}
Russell Kingd7b4a752006-01-04 15:52:45 +0000254EXPORT_SYMBOL(set_dma_speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Russell King1df81302008-12-08 15:58:50 +0000256int get_dma_residue(unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
Russell King3afb6e92008-12-08 16:08:48 +0000258 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 int ret = 0;
260
261 if (dma->d_ops->residue)
Russell King1df81302008-12-08 15:58:50 +0000262 ret = dma->d_ops->residue(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264 return ret;
265}
Russell Kingd7b4a752006-01-04 15:52:45 +0000266EXPORT_SYMBOL(get_dma_residue);