Blame view

arch/arm/kernel/dma.c 5.66 KB
1da177e4c   Linus Torvalds   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   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
  #include <linux/init.h>
  #include <linux/spinlock.h>
  #include <linux/errno.h>
d667522fd   Russell King   [ARM] dma: ensure...
18
  #include <linux/scatterlist.h>
e193ba290   Russell King   ARM: dma: add /pr...
19
20
  #include <linux/seq_file.h>
  #include <linux/proc_fs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
  
  #include <asm/dma.h>
  
  #include <asm/mach/dma.h>
bd31b8596   Thomas Gleixner   locking, ARM: Ann...
25
  DEFINE_RAW_SPINLOCK(dma_spin_lock);
d7b4a7567   Russell King   [ARM] Move DMA ex...
26
  EXPORT_SYMBOL(dma_spin_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27

2f757f2ab   Russell King   [ARM] dma: rejig ...
28
  static dma_t *dma_chan[MAX_DMA_CHANNELS];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29

3afb6e9c6   Russell King   [ARM] dma: factor...
30
31
  static inline dma_t *dma_channel(unsigned int chan)
  {
2f757f2ab   Russell King   [ARM] dma: rejig ...
32
  	if (chan >= MAX_DMA_CHANNELS)
3afb6e9c6   Russell King   [ARM] dma: factor...
33
  		return NULL;
2f757f2ab   Russell King   [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   Russell King   [ARM] dma: ensure...
41
42
  
  	sg_init_table(&dma->buf, 1);
2f757f2ab   Russell King   [ARM] dma: rejig ...
43
44
45
46
  	if (dma_chan[chan])
  		return -EBUSY;
  	dma_chan[chan] = dma;
  	return 0;
3afb6e9c6   Russell King   [ARM] dma: factor...
47
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
   * Request DMA channel
   *
   * On certain platforms, we have to allocate an interrupt as well...
   */
1df813027   Russell King   [ARM] dma: remove...
53
  int request_dma(unsigned int chan, const char *device_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
55
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
  	int ret;
3afb6e9c6   Russell King   [ARM] dma: factor...
57
  	if (!dma)
1da177e4c   Linus Torvalds   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   Russell King   [ARM] dma: remove...
69
  		ret = dma->d_ops->request(chan, dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
76
  
  	if (ret)
  		xchg(&dma->lock, 0);
  
  	return ret;
  
  bad_dma:
1df813027   Russell King   [ARM] dma: remove...
77
78
  	printk(KERN_ERR "dma: trying to allocate DMA%d
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
81
82
83
  	return -EINVAL;
  
  busy:
  	return -EBUSY;
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
84
  EXPORT_SYMBOL(request_dma);
1da177e4c   Linus Torvalds   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   Russell King   [ARM] dma: remove...
91
  void free_dma(unsigned int chan)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
93
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94

3afb6e9c6   Russell King   [ARM] dma: factor...
95
  	if (!dma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
  		goto bad_dma;
  
  	if (dma->active) {
1df813027   Russell King   [ARM] dma: remove...
99
100
101
  		printk(KERN_ERR "dma%d: freeing active DMA
  ", chan);
  		dma->d_ops->disable(chan, dma);
1da177e4c   Linus Torvalds   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   Russell King   [ARM] dma: remove...
107
  			dma->d_ops->free(chan, dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  		return;
  	}
1df813027   Russell King   [ARM] dma: remove...
110
111
  	printk(KERN_ERR "dma%d: trying to free free DMA
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
  	return;
  
  bad_dma:
1df813027   Russell King   [ARM] dma: remove...
115
116
  	printk(KERN_ERR "dma: trying to free DMA%d
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
118
  EXPORT_SYMBOL(free_dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
121
  
  /* Set DMA Scatter-Gather list
   */
1df813027   Russell King   [ARM] dma: remove...
122
  void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
124
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
  
  	if (dma->active)
  		printk(KERN_ERR "dma%d: altering DMA SG while "
1df813027   Russell King   [ARM] dma: remove...
128
129
  		       "DMA active
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
  
  	dma->sg = sg;
  	dma->sgcount = nr_sg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
  	dma->invalid = 1;
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
135
  EXPORT_SYMBOL(set_dma_sg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
  
  /* Set DMA address
   *
   * Copy address to the structure, and set the invalid bit
   */
1df813027   Russell King   [ARM] dma: remove...
141
  void __set_dma_addr (unsigned int chan, void *addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
143
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
  
  	if (dma->active)
  		printk(KERN_ERR "dma%d: altering DMA address while "
1df813027   Russell King   [ARM] dma: remove...
147
148
  		       "DMA active
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149

7cdad4829   Russell King   [ARM] Remove '__a...
150
151
  	dma->sg = NULL;
  	dma->addr = addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
  	dma->invalid = 1;
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
154
  EXPORT_SYMBOL(__set_dma_addr);
1da177e4c   Linus Torvalds   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   Russell King   [ARM] dma: remove...
160
  void set_dma_count (unsigned int chan, unsigned long count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
162
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
  
  	if (dma->active)
  		printk(KERN_ERR "dma%d: altering DMA count while "
1df813027   Russell King   [ARM] dma: remove...
166
167
  		       "DMA active
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168

7cdad4829   Russell King   [ARM] Remove '__a...
169
170
  	dma->sg = NULL;
  	dma->count = count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
  	dma->invalid = 1;
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
173
  EXPORT_SYMBOL(set_dma_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
  
  /* Set DMA direction mode
   */
f0ffc8162   Russell King   [ARM] dma: remove...
177
  void set_dma_mode (unsigned int chan, unsigned int mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
179
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
  
  	if (dma->active)
  		printk(KERN_ERR "dma%d: altering DMA mode while "
1df813027   Russell King   [ARM] dma: remove...
183
184
  		       "DMA active
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
  
  	dma->dma_mode = mode;
  	dma->invalid = 1;
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
189
  EXPORT_SYMBOL(set_dma_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
  
  /* Enable DMA channel
   */
1df813027   Russell King   [ARM] dma: remove...
193
  void enable_dma (unsigned int chan)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
195
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   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   Russell King   [ARM] dma: remove...
202
  		dma->d_ops->enable(chan, dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
  	}
  	return;
  
  free_dma:
1df813027   Russell King   [ARM] dma: remove...
207
208
  	printk(KERN_ERR "dma%d: trying to enable free DMA
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
  	BUG();
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
211
  EXPORT_SYMBOL(enable_dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
214
  
  /* Disable DMA channel
   */
1df813027   Russell King   [ARM] dma: remove...
215
  void disable_dma (unsigned int chan)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
217
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   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   Russell King   [ARM] dma: remove...
224
  		dma->d_ops->disable(chan, dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
  	}
  	return;
  
  free_dma:
1df813027   Russell King   [ARM] dma: remove...
229
230
  	printk(KERN_ERR "dma%d: trying to disable free DMA
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
  	BUG();
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
233
  EXPORT_SYMBOL(disable_dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
236
237
  
  /*
   * Is the specified DMA channel active?
   */
1df813027   Russell King   [ARM] dma: remove...
238
  int dma_channel_active(unsigned int chan)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
240
241
  	dma_t *dma = dma_channel(chan);
  	return dma->active;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
  }
ec14d7964   Russell King   [ARM] Export dma_...
243
  EXPORT_SYMBOL(dma_channel_active);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244

1df813027   Russell King   [ARM] dma: remove...
245
  void set_dma_page(unsigned int chan, char pagenr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
  {
1df813027   Russell King   [ARM] dma: remove...
247
248
  	printk(KERN_ERR "dma%d: trying to set_dma_page
  ", chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
250
  EXPORT_SYMBOL(set_dma_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251

1df813027   Russell King   [ARM] dma: remove...
252
  void set_dma_speed(unsigned int chan, int cycle_ns)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
254
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
  	int ret = 0;
  
  	if (dma->d_ops->setspeed)
1df813027   Russell King   [ARM] dma: remove...
258
  		ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
  	dma->speed = ret;
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
261
  EXPORT_SYMBOL(set_dma_speed);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262

1df813027   Russell King   [ARM] dma: remove...
263
  int get_dma_residue(unsigned int chan)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  {
3afb6e9c6   Russell King   [ARM] dma: factor...
265
  	dma_t *dma = dma_channel(chan);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
268
  	int ret = 0;
  
  	if (dma->d_ops->residue)
1df813027   Russell King   [ARM] dma: remove...
269
  		ret = dma->d_ops->residue(chan, dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
272
  
  	return ret;
  }
d7b4a7567   Russell King   [ARM] Move DMA ex...
273
  EXPORT_SYMBOL(get_dma_residue);
e193ba290   Russell King   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