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