xref: /openbmc/linux/arch/arm/kernel/dma.c (revision b6dcefde)
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);
267