Merge commit 'kumar/next' into merge
[linux-2.6] / arch / arm / kernel / dma.c
1 /*
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>
15 #include <linux/init.h>
16 #include <linux/spinlock.h>
17 #include <linux/errno.h>
18 #include <linux/scatterlist.h>
19
20 #include <asm/dma.h>
21
22 #include <asm/mach/dma.h>
23
24 DEFINE_SPINLOCK(dma_spin_lock);
25 EXPORT_SYMBOL(dma_spin_lock);
26
27 static dma_t *dma_chan[MAX_DMA_CHANNELS];
28
29 static inline dma_t *dma_channel(unsigned int chan)
30 {
31         if (chan >= MAX_DMA_CHANNELS)
32                 return NULL;
33
34         return dma_chan[chan];
35 }
36
37 int __init isa_dma_add(unsigned int chan, dma_t *dma)
38 {
39         if (!dma->d_ops)
40                 return -EINVAL;
41
42         sg_init_table(&dma->buf, 1);
43
44         if (dma_chan[chan])
45                 return -EBUSY;
46         dma_chan[chan] = dma;
47         return 0;
48 }
49
50 /*
51  * Request DMA channel
52  *
53  * On certain platforms, we have to allocate an interrupt as well...
54  */
55 int request_dma(unsigned int chan, const char *device_id)
56 {
57         dma_t *dma = dma_channel(chan);
58         int ret;
59
60         if (!dma)
61                 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)
72                 ret = dma->d_ops->request(chan, dma);
73
74         if (ret)
75                 xchg(&dma->lock, 0);
76
77         return ret;
78
79 bad_dma:
80         printk(KERN_ERR "dma: trying to allocate DMA%d\n", chan);
81         return -EINVAL;
82
83 busy:
84         return -EBUSY;
85 }
86 EXPORT_SYMBOL(request_dma);
87
88 /*
89  * Free DMA channel
90  *
91  * On certain platforms, we have to free interrupt as well...
92  */
93 void free_dma(unsigned int chan)
94 {
95         dma_t *dma = dma_channel(chan);
96
97         if (!dma)
98                 goto bad_dma;
99
100         if (dma->active) {
101                 printk(KERN_ERR "dma%d: freeing active DMA\n", chan);
102                 dma->d_ops->disable(chan, dma);
103                 dma->active = 0;
104         }
105
106         if (xchg(&dma->lock, 0) != 0) {
107                 if (dma->d_ops->free)
108                         dma->d_ops->free(chan, dma);
109                 return;
110         }
111
112         printk(KERN_ERR "dma%d: trying to free free DMA\n", chan);
113         return;
114
115 bad_dma:
116         printk(KERN_ERR "dma: trying to free DMA%d\n", chan);
117 }
118 EXPORT_SYMBOL(free_dma);
119
120 /* Set DMA Scatter-Gather list
121  */
122 void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
123 {
124         dma_t *dma = dma_channel(chan);
125
126         if (dma->active)
127                 printk(KERN_ERR "dma%d: altering DMA SG while "
128                        "DMA active\n", chan);
129
130         dma->sg = sg;
131         dma->sgcount = nr_sg;
132         dma->invalid = 1;
133 }
134 EXPORT_SYMBOL(set_dma_sg);
135
136 /* Set DMA address
137  *
138  * Copy address to the structure, and set the invalid bit
139  */
140 void __set_dma_addr (unsigned int chan, void *addr)
141 {
142         dma_t *dma = dma_channel(chan);
143
144         if (dma->active)
145                 printk(KERN_ERR "dma%d: altering DMA address while "
146                        "DMA active\n", chan);
147
148         dma->sg = NULL;
149         dma->addr = addr;
150         dma->invalid = 1;
151 }
152 EXPORT_SYMBOL(__set_dma_addr);
153
154 /* Set DMA byte count
155  *
156  * Copy address to the structure, and set the invalid bit
157  */
158 void set_dma_count (unsigned int chan, unsigned long count)
159 {
160         dma_t *dma = dma_channel(chan);
161
162         if (dma->active)
163                 printk(KERN_ERR "dma%d: altering DMA count while "
164                        "DMA active\n", chan);
165
166         dma->sg = NULL;
167         dma->count = count;
168         dma->invalid = 1;
169 }
170 EXPORT_SYMBOL(set_dma_count);
171
172 /* Set DMA direction mode
173  */
174 void set_dma_mode (unsigned int chan, unsigned int mode)
175 {
176         dma_t *dma = dma_channel(chan);
177
178         if (dma->active)
179                 printk(KERN_ERR "dma%d: altering DMA mode while "
180                        "DMA active\n", chan);
181
182         dma->dma_mode = mode;
183         dma->invalid = 1;
184 }
185 EXPORT_SYMBOL(set_dma_mode);
186
187 /* Enable DMA channel
188  */
189 void enable_dma (unsigned int chan)
190 {
191         dma_t *dma = dma_channel(chan);
192
193         if (!dma->lock)
194                 goto free_dma;
195
196         if (dma->active == 0) {
197                 dma->active = 1;
198                 dma->d_ops->enable(chan, dma);
199         }
200         return;
201
202 free_dma:
203         printk(KERN_ERR "dma%d: trying to enable free DMA\n", chan);
204         BUG();
205 }
206 EXPORT_SYMBOL(enable_dma);
207
208 /* Disable DMA channel
209  */
210 void disable_dma (unsigned int chan)
211 {
212         dma_t *dma = dma_channel(chan);
213
214         if (!dma->lock)
215                 goto free_dma;
216
217         if (dma->active == 1) {
218                 dma->active = 0;
219                 dma->d_ops->disable(chan, dma);
220         }
221         return;
222
223 free_dma:
224         printk(KERN_ERR "dma%d: trying to disable free DMA\n", chan);
225         BUG();
226 }
227 EXPORT_SYMBOL(disable_dma);
228
229 /*
230  * Is the specified DMA channel active?
231  */
232 int dma_channel_active(unsigned int chan)
233 {
234         dma_t *dma = dma_channel(chan);
235         return dma->active;
236 }
237 EXPORT_SYMBOL(dma_channel_active);
238
239 void set_dma_page(unsigned int chan, char pagenr)
240 {
241         printk(KERN_ERR "dma%d: trying to set_dma_page\n", chan);
242 }
243 EXPORT_SYMBOL(set_dma_page);
244
245 void set_dma_speed(unsigned int chan, int cycle_ns)
246 {
247         dma_t *dma = dma_channel(chan);
248         int ret = 0;
249
250         if (dma->d_ops->setspeed)
251                 ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
252         dma->speed = ret;
253 }
254 EXPORT_SYMBOL(set_dma_speed);
255
256 int get_dma_residue(unsigned int chan)
257 {
258         dma_t *dma = dma_channel(chan);
259         int ret = 0;
260
261         if (dma->d_ops->residue)
262                 ret = dma->d_ops->residue(chan, dma);
263
264         return ret;
265 }
266 EXPORT_SYMBOL(get_dma_residue);