Blame view
arch/arm/kernel/dma.c
5.66 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * linux/arch/arm/kernel/dma.c * * Copyright (C) 1995-2000 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Front-end to the DMA handling. This handles the allocation/freeing * of DMA channels, and provides a unified interface to the machines * DMA facilities. */ #include <linux/module.h> |
1da177e4c Linux-2.6.12-rc2 |
15 16 17 |
#include <linux/init.h> #include <linux/spinlock.h> #include <linux/errno.h> |
d667522fd [ARM] dma: ensure... |
18 |
#include <linux/scatterlist.h> |
e193ba290 ARM: dma: add /pr... |
19 20 |
#include <linux/seq_file.h> #include <linux/proc_fs.h> |
1da177e4c Linux-2.6.12-rc2 |
21 22 23 24 |
#include <asm/dma.h> #include <asm/mach/dma.h> |
bd31b8596 locking, ARM: Ann... |
25 |
DEFINE_RAW_SPINLOCK(dma_spin_lock); |
d7b4a7567 [ARM] Move DMA ex... |
26 |
EXPORT_SYMBOL(dma_spin_lock); |
1da177e4c Linux-2.6.12-rc2 |
27 |
|
2f757f2ab [ARM] dma: rejig ... |
28 |
static dma_t *dma_chan[MAX_DMA_CHANNELS]; |
1da177e4c Linux-2.6.12-rc2 |
29 |
|
3afb6e9c6 [ARM] dma: factor... |
30 31 |
static inline dma_t *dma_channel(unsigned int chan) { |
2f757f2ab [ARM] dma: rejig ... |
32 |
if (chan >= MAX_DMA_CHANNELS) |
3afb6e9c6 [ARM] dma: factor... |
33 |
return NULL; |
2f757f2ab [ARM] dma: rejig ... |
34 35 36 37 38 39 40 |
return dma_chan[chan]; } int __init isa_dma_add(unsigned int chan, dma_t *dma) { if (!dma->d_ops) return -EINVAL; |
d667522fd [ARM] dma: ensure... |
41 42 |
sg_init_table(&dma->buf, 1); |
2f757f2ab [ARM] dma: rejig ... |
43 44 45 46 |
if (dma_chan[chan]) return -EBUSY; dma_chan[chan] = dma; return 0; |
3afb6e9c6 [ARM] dma: factor... |
47 |
} |
1da177e4c Linux-2.6.12-rc2 |
48 |
/* |
1da177e4c Linux-2.6.12-rc2 |
49 50 51 52 |
* Request DMA channel * * On certain platforms, we have to allocate an interrupt as well... */ |
1df813027 [ARM] dma: remove... |
53 |
int request_dma(unsigned int chan, const char *device_id) |
1da177e4c Linux-2.6.12-rc2 |
54 |
{ |
3afb6e9c6 [ARM] dma: factor... |
55 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
56 |
int ret; |
3afb6e9c6 [ARM] dma: factor... |
57 |
if (!dma) |
1da177e4c Linux-2.6.12-rc2 |
58 59 60 61 62 63 64 65 66 67 68 |
goto bad_dma; if (xchg(&dma->lock, 1) != 0) goto busy; dma->device_id = device_id; dma->active = 0; dma->invalid = 1; ret = 0; if (dma->d_ops->request) |
1df813027 [ARM] dma: remove... |
69 |
ret = dma->d_ops->request(chan, dma); |
1da177e4c Linux-2.6.12-rc2 |
70 71 72 73 74 75 76 |
if (ret) xchg(&dma->lock, 0); return ret; bad_dma: |
1df813027 [ARM] dma: remove... |
77 78 |
printk(KERN_ERR "dma: trying to allocate DMA%d ", chan); |
1da177e4c Linux-2.6.12-rc2 |
79 80 81 82 83 |
return -EINVAL; busy: return -EBUSY; } |
d7b4a7567 [ARM] Move DMA ex... |
84 |
EXPORT_SYMBOL(request_dma); |
1da177e4c Linux-2.6.12-rc2 |
85 86 87 88 89 90 |
/* * Free DMA channel * * On certain platforms, we have to free interrupt as well... */ |
1df813027 [ARM] dma: remove... |
91 |
void free_dma(unsigned int chan) |
1da177e4c Linux-2.6.12-rc2 |
92 |
{ |
3afb6e9c6 [ARM] dma: factor... |
93 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
94 |
|
3afb6e9c6 [ARM] dma: factor... |
95 |
if (!dma) |
1da177e4c Linux-2.6.12-rc2 |
96 97 98 |
goto bad_dma; if (dma->active) { |
1df813027 [ARM] dma: remove... |
99 100 101 |
printk(KERN_ERR "dma%d: freeing active DMA ", chan); dma->d_ops->disable(chan, dma); |
1da177e4c Linux-2.6.12-rc2 |
102 103 104 105 106 |
dma->active = 0; } if (xchg(&dma->lock, 0) != 0) { if (dma->d_ops->free) |
1df813027 [ARM] dma: remove... |
107 |
dma->d_ops->free(chan, dma); |
1da177e4c Linux-2.6.12-rc2 |
108 109 |
return; } |
1df813027 [ARM] dma: remove... |
110 111 |
printk(KERN_ERR "dma%d: trying to free free DMA ", chan); |
1da177e4c Linux-2.6.12-rc2 |
112 113 114 |
return; bad_dma: |
1df813027 [ARM] dma: remove... |
115 116 |
printk(KERN_ERR "dma: trying to free DMA%d ", chan); |
1da177e4c Linux-2.6.12-rc2 |
117 |
} |
d7b4a7567 [ARM] Move DMA ex... |
118 |
EXPORT_SYMBOL(free_dma); |
1da177e4c Linux-2.6.12-rc2 |
119 120 121 |
/* Set DMA Scatter-Gather list */ |
1df813027 [ARM] dma: remove... |
122 |
void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg) |
1da177e4c Linux-2.6.12-rc2 |
123 |
{ |
3afb6e9c6 [ARM] dma: factor... |
124 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
125 126 127 |
if (dma->active) printk(KERN_ERR "dma%d: altering DMA SG while " |
1df813027 [ARM] dma: remove... |
128 129 |
"DMA active ", chan); |
1da177e4c Linux-2.6.12-rc2 |
130 131 132 |
dma->sg = sg; dma->sgcount = nr_sg; |
1da177e4c Linux-2.6.12-rc2 |
133 134 |
dma->invalid = 1; } |
d7b4a7567 [ARM] Move DMA ex... |
135 |
EXPORT_SYMBOL(set_dma_sg); |
1da177e4c Linux-2.6.12-rc2 |
136 137 138 139 140 |
/* Set DMA address * * Copy address to the structure, and set the invalid bit */ |
1df813027 [ARM] dma: remove... |
141 |
void __set_dma_addr (unsigned int chan, void *addr) |
1da177e4c Linux-2.6.12-rc2 |
142 |
{ |
3afb6e9c6 [ARM] dma: factor... |
143 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
144 145 146 |
if (dma->active) printk(KERN_ERR "dma%d: altering DMA address while " |
1df813027 [ARM] dma: remove... |
147 148 |
"DMA active ", chan); |
1da177e4c Linux-2.6.12-rc2 |
149 |
|
7cdad4829 [ARM] Remove '__a... |
150 151 |
dma->sg = NULL; dma->addr = addr; |
1da177e4c Linux-2.6.12-rc2 |
152 153 |
dma->invalid = 1; } |
d7b4a7567 [ARM] Move DMA ex... |
154 |
EXPORT_SYMBOL(__set_dma_addr); |
1da177e4c Linux-2.6.12-rc2 |
155 156 157 158 159 |
/* Set DMA byte count * * Copy address to the structure, and set the invalid bit */ |
1df813027 [ARM] dma: remove... |
160 |
void set_dma_count (unsigned int chan, unsigned long count) |
1da177e4c Linux-2.6.12-rc2 |
161 |
{ |
3afb6e9c6 [ARM] dma: factor... |
162 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
163 164 165 |
if (dma->active) printk(KERN_ERR "dma%d: altering DMA count while " |
1df813027 [ARM] dma: remove... |
166 167 |
"DMA active ", chan); |
1da177e4c Linux-2.6.12-rc2 |
168 |
|
7cdad4829 [ARM] Remove '__a... |
169 170 |
dma->sg = NULL; dma->count = count; |
1da177e4c Linux-2.6.12-rc2 |
171 172 |
dma->invalid = 1; } |
d7b4a7567 [ARM] Move DMA ex... |
173 |
EXPORT_SYMBOL(set_dma_count); |
1da177e4c Linux-2.6.12-rc2 |
174 175 176 |
/* Set DMA direction mode */ |
f0ffc8162 [ARM] dma: remove... |
177 |
void set_dma_mode (unsigned int chan, unsigned int mode) |
1da177e4c Linux-2.6.12-rc2 |
178 |
{ |
3afb6e9c6 [ARM] dma: factor... |
179 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
180 181 182 |
if (dma->active) printk(KERN_ERR "dma%d: altering DMA mode while " |
1df813027 [ARM] dma: remove... |
183 184 |
"DMA active ", chan); |
1da177e4c Linux-2.6.12-rc2 |
185 186 187 188 |
dma->dma_mode = mode; dma->invalid = 1; } |
d7b4a7567 [ARM] Move DMA ex... |
189 |
EXPORT_SYMBOL(set_dma_mode); |
1da177e4c Linux-2.6.12-rc2 |
190 191 192 |
/* Enable DMA channel */ |
1df813027 [ARM] dma: remove... |
193 |
void enable_dma (unsigned int chan) |
1da177e4c Linux-2.6.12-rc2 |
194 |
{ |
3afb6e9c6 [ARM] dma: factor... |
195 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
196 197 198 199 200 201 |
if (!dma->lock) goto free_dma; if (dma->active == 0) { dma->active = 1; |
1df813027 [ARM] dma: remove... |
202 |
dma->d_ops->enable(chan, dma); |
1da177e4c Linux-2.6.12-rc2 |
203 204 205 206 |
} return; free_dma: |
1df813027 [ARM] dma: remove... |
207 208 |
printk(KERN_ERR "dma%d: trying to enable free DMA ", chan); |
1da177e4c Linux-2.6.12-rc2 |
209 210 |
BUG(); } |
d7b4a7567 [ARM] Move DMA ex... |
211 |
EXPORT_SYMBOL(enable_dma); |
1da177e4c Linux-2.6.12-rc2 |
212 213 214 |
/* Disable DMA channel */ |
1df813027 [ARM] dma: remove... |
215 |
void disable_dma (unsigned int chan) |
1da177e4c Linux-2.6.12-rc2 |
216 |
{ |
3afb6e9c6 [ARM] dma: factor... |
217 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
218 219 220 221 222 223 |
if (!dma->lock) goto free_dma; if (dma->active == 1) { dma->active = 0; |
1df813027 [ARM] dma: remove... |
224 |
dma->d_ops->disable(chan, dma); |
1da177e4c Linux-2.6.12-rc2 |
225 226 227 228 |
} return; free_dma: |
1df813027 [ARM] dma: remove... |
229 230 |
printk(KERN_ERR "dma%d: trying to disable free DMA ", chan); |
1da177e4c Linux-2.6.12-rc2 |
231 232 |
BUG(); } |
d7b4a7567 [ARM] Move DMA ex... |
233 |
EXPORT_SYMBOL(disable_dma); |
1da177e4c Linux-2.6.12-rc2 |
234 235 236 237 |
/* * Is the specified DMA channel active? */ |
1df813027 [ARM] dma: remove... |
238 |
int dma_channel_active(unsigned int chan) |
1da177e4c Linux-2.6.12-rc2 |
239 |
{ |
3afb6e9c6 [ARM] dma: factor... |
240 241 |
dma_t *dma = dma_channel(chan); return dma->active; |
1da177e4c Linux-2.6.12-rc2 |
242 |
} |
ec14d7964 [ARM] Export dma_... |
243 |
EXPORT_SYMBOL(dma_channel_active); |
1da177e4c Linux-2.6.12-rc2 |
244 |
|
1df813027 [ARM] dma: remove... |
245 |
void set_dma_page(unsigned int chan, char pagenr) |
1da177e4c Linux-2.6.12-rc2 |
246 |
{ |
1df813027 [ARM] dma: remove... |
247 248 |
printk(KERN_ERR "dma%d: trying to set_dma_page ", chan); |
1da177e4c Linux-2.6.12-rc2 |
249 |
} |
d7b4a7567 [ARM] Move DMA ex... |
250 |
EXPORT_SYMBOL(set_dma_page); |
1da177e4c Linux-2.6.12-rc2 |
251 |
|
1df813027 [ARM] dma: remove... |
252 |
void set_dma_speed(unsigned int chan, int cycle_ns) |
1da177e4c Linux-2.6.12-rc2 |
253 |
{ |
3afb6e9c6 [ARM] dma: factor... |
254 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
255 256 257 |
int ret = 0; if (dma->d_ops->setspeed) |
1df813027 [ARM] dma: remove... |
258 |
ret = dma->d_ops->setspeed(chan, dma, cycle_ns); |
1da177e4c Linux-2.6.12-rc2 |
259 260 |
dma->speed = ret; } |
d7b4a7567 [ARM] Move DMA ex... |
261 |
EXPORT_SYMBOL(set_dma_speed); |
1da177e4c Linux-2.6.12-rc2 |
262 |
|
1df813027 [ARM] dma: remove... |
263 |
int get_dma_residue(unsigned int chan) |
1da177e4c Linux-2.6.12-rc2 |
264 |
{ |
3afb6e9c6 [ARM] dma: factor... |
265 |
dma_t *dma = dma_channel(chan); |
1da177e4c Linux-2.6.12-rc2 |
266 267 268 |
int ret = 0; if (dma->d_ops->residue) |
1df813027 [ARM] dma: remove... |
269 |
ret = dma->d_ops->residue(chan, dma); |
1da177e4c Linux-2.6.12-rc2 |
270 271 272 |
return ret; } |
d7b4a7567 [ARM] Move DMA ex... |
273 |
EXPORT_SYMBOL(get_dma_residue); |
e193ba290 ARM: dma: add /pr... |
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
#ifdef CONFIG_PROC_FS static int proc_dma_show(struct seq_file *m, void *v) { int i; for (i = 0 ; i < MAX_DMA_CHANNELS ; i++) { dma_t *dma = dma_channel(i); if (dma && dma->lock) seq_printf(m, "%2d: %s ", i, dma->device_id); } return 0; } static int proc_dma_open(struct inode *inode, struct file *file) { return single_open(file, proc_dma_show, NULL); } static const struct file_operations proc_dma_operations = { .open = proc_dma_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int __init proc_dma_init(void) { proc_create("dma", 0, NULL, &proc_dma_operations); return 0; } __initcall(proc_dma_init); #endif |