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