Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
f30c2269 | 2 | * arch/arm/mach-sa1100/dma.c |
1da177e4 LT |
3 | * |
4 | * Support functions for the SA11x0 internal DMA channels. | |
5 | * | |
6 | * Copyright (C) 2000, 2001 by Nicolas Pitre | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/spinlock.h> | |
17 | #include <linux/errno.h> | |
18 | ||
19 | #include <asm/system.h> | |
20 | #include <asm/irq.h> | |
be509729 | 21 | #include <asm/arch/hardware.h> |
1da177e4 LT |
22 | #include <asm/dma.h> |
23 | ||
24 | ||
25 | #undef DEBUG | |
26 | #ifdef DEBUG | |
27 | #define DPRINTK( s, arg... ) printk( "dma<%p>: " s, regs , ##arg ) | |
28 | #else | |
29 | #define DPRINTK( x... ) | |
30 | #endif | |
31 | ||
32 | ||
33 | typedef struct { | |
34 | const char *device_id; /* device name */ | |
35 | u_long device; /* this channel device, 0 if unused*/ | |
36 | dma_callback_t callback; /* to call when DMA completes */ | |
37 | void *data; /* ... with private data ptr */ | |
38 | } sa1100_dma_t; | |
39 | ||
40 | static sa1100_dma_t dma_chan[SA1100_DMA_CHANNELS]; | |
41 | ||
42 | static spinlock_t dma_list_lock; | |
43 | ||
44 | ||
0cd61b68 | 45 | static irqreturn_t dma_irq_handler(int irq, void *dev_id) |
1da177e4 LT |
46 | { |
47 | dma_regs_t *dma_regs = dev_id; | |
48 | sa1100_dma_t *dma = dma_chan + (((u_int)dma_regs >> 5) & 7); | |
49 | int status = dma_regs->RdDCSR; | |
50 | ||
51 | if (status & (DCSR_ERROR)) { | |
52 | printk(KERN_CRIT "DMA on \"%s\" caused an error\n", dma->device_id); | |
53 | dma_regs->ClrDCSR = DCSR_ERROR; | |
54 | } | |
55 | ||
56 | dma_regs->ClrDCSR = status & (DCSR_DONEA | DCSR_DONEB); | |
57 | if (dma->callback) { | |
58 | if (status & DCSR_DONEA) | |
59 | dma->callback(dma->data); | |
60 | if (status & DCSR_DONEB) | |
61 | dma->callback(dma->data); | |
62 | } | |
63 | return IRQ_HANDLED; | |
64 | } | |
65 | ||
66 | ||
67 | /** | |
68 | * sa1100_request_dma - allocate one of the SA11x0's DMA chanels | |
69 | * @device: The SA11x0 peripheral targeted by this request | |
70 | * @device_id: An ascii name for the claiming device | |
71 | * @callback: Function to be called when the DMA completes | |
72 | * @data: A cookie passed back to the callback function | |
73 | * @dma_regs: Pointer to the location of the allocated channel's identifier | |
74 | * | |
75 | * This function will search for a free DMA channel and returns the | |
76 | * address of the hardware registers for that channel as the channel | |
77 | * identifier. This identifier is written to the location pointed by | |
78 | * @dma_regs. The list of possible values for @device are listed into | |
79 | * linux/include/asm-arm/arch-sa1100/dma.h as a dma_device_t enum. | |
80 | * | |
81 | * Note that reading from a port and writing to the same port are | |
82 | * actually considered as two different streams requiring separate | |
83 | * DMA registrations. | |
84 | * | |
85 | * The @callback function is called from interrupt context when one | |
86 | * of the two possible DMA buffers in flight has terminated. That | |
87 | * function has to be small and efficient while posponing more complex | |
88 | * processing to a lower priority execution context. | |
89 | * | |
90 | * If no channels are available, or if the desired @device is already in | |
91 | * use by another DMA channel, then an error code is returned. This | |
92 | * function must be called before any other DMA calls. | |
93 | **/ | |
94 | ||
95 | int sa1100_request_dma (dma_device_t device, const char *device_id, | |
96 | dma_callback_t callback, void *data, | |
97 | dma_regs_t **dma_regs) | |
98 | { | |
99 | sa1100_dma_t *dma = NULL; | |
100 | dma_regs_t *regs; | |
101 | int i, err; | |
102 | ||
103 | *dma_regs = NULL; | |
104 | ||
105 | err = 0; | |
106 | spin_lock(&dma_list_lock); | |
107 | for (i = 0; i < SA1100_DMA_CHANNELS; i++) { | |
108 | if (dma_chan[i].device == device) { | |
109 | err = -EBUSY; | |
110 | break; | |
111 | } else if (!dma_chan[i].device && !dma) { | |
112 | dma = &dma_chan[i]; | |
113 | } | |
114 | } | |
115 | if (!err) { | |
116 | if (dma) | |
117 | dma->device = device; | |
118 | else | |
119 | err = -ENOSR; | |
120 | } | |
121 | spin_unlock(&dma_list_lock); | |
122 | if (err) | |
123 | return err; | |
124 | ||
125 | i = dma - dma_chan; | |
126 | regs = (dma_regs_t *)&DDAR(i); | |
52e405ea | 127 | err = request_irq(IRQ_DMA0 + i, dma_irq_handler, IRQF_DISABLED, |
1da177e4 LT |
128 | device_id, regs); |
129 | if (err) { | |
130 | printk(KERN_ERR | |
131 | "%s: unable to request IRQ %d for %s\n", | |
8e86f427 | 132 | __func__, IRQ_DMA0 + i, device_id); |
1da177e4 LT |
133 | dma->device = 0; |
134 | return err; | |
135 | } | |
136 | ||
137 | *dma_regs = regs; | |
138 | dma->device_id = device_id; | |
139 | dma->callback = callback; | |
140 | dma->data = data; | |
141 | ||
142 | regs->ClrDCSR = | |
143 | (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB | | |
144 | DCSR_IE | DCSR_ERROR | DCSR_RUN); | |
145 | regs->DDAR = device; | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
150 | ||
151 | /** | |
152 | * sa1100_free_dma - free a SA11x0 DMA channel | |
153 | * @regs: identifier for the channel to free | |
154 | * | |
155 | * This clears all activities on a given DMA channel and releases it | |
156 | * for future requests. The @regs identifier is provided by a | |
157 | * successful call to sa1100_request_dma(). | |
158 | **/ | |
159 | ||
160 | void sa1100_free_dma(dma_regs_t *regs) | |
161 | { | |
162 | int i; | |
163 | ||
164 | for (i = 0; i < SA1100_DMA_CHANNELS; i++) | |
165 | if (regs == (dma_regs_t *)&DDAR(i)) | |
166 | break; | |
167 | if (i >= SA1100_DMA_CHANNELS) { | |
8e86f427 | 168 | printk(KERN_ERR "%s: bad DMA identifier\n", __func__); |
1da177e4 LT |
169 | return; |
170 | } | |
171 | ||
172 | if (!dma_chan[i].device) { | |
8e86f427 | 173 | printk(KERN_ERR "%s: Trying to free free DMA\n", __func__); |
1da177e4 LT |
174 | return; |
175 | } | |
176 | ||
177 | regs->ClrDCSR = | |
178 | (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB | | |
179 | DCSR_IE | DCSR_ERROR | DCSR_RUN); | |
180 | free_irq(IRQ_DMA0 + i, regs); | |
181 | dma_chan[i].device = 0; | |
182 | } | |
183 | ||
184 | ||
185 | /** | |
186 | * sa1100_start_dma - submit a data buffer for DMA | |
187 | * @regs: identifier for the channel to use | |
188 | * @dma_ptr: buffer physical (or bus) start address | |
189 | * @size: buffer size | |
190 | * | |
191 | * This function hands the given data buffer to the hardware for DMA | |
192 | * access. If another buffer is already in flight then this buffer | |
193 | * will be queued so the DMA engine will switch to it automatically | |
194 | * when the previous one is done. The DMA engine is actually toggling | |
195 | * between two buffers so at most 2 successful calls can be made before | |
196 | * one of them terminates and the callback function is called. | |
197 | * | |
198 | * The @regs identifier is provided by a successful call to | |
199 | * sa1100_request_dma(). | |
200 | * | |
201 | * The @size must not be larger than %MAX_DMA_SIZE. If a given buffer | |
202 | * is larger than that then it's the caller's responsibility to split | |
203 | * it into smaller chunks and submit them separately. If this is the | |
204 | * case then a @size of %CUT_DMA_SIZE is recommended to avoid ending | |
205 | * up with too small chunks. The callback function can be used to chain | |
206 | * submissions of buffer chunks. | |
207 | * | |
208 | * Error return values: | |
209 | * %-EOVERFLOW: Given buffer size is too big. | |
210 | * %-EBUSY: Both DMA buffers are already in use. | |
211 | * %-EAGAIN: Both buffers were busy but one of them just completed | |
212 | * but the interrupt handler has to execute first. | |
213 | * | |
214 | * This function returs 0 on success. | |
215 | **/ | |
216 | ||
217 | int sa1100_start_dma(dma_regs_t *regs, dma_addr_t dma_ptr, u_int size) | |
218 | { | |
219 | unsigned long flags; | |
220 | u_long status; | |
221 | int ret; | |
222 | ||
223 | if (dma_ptr & 3) | |
224 | printk(KERN_WARNING "DMA: unaligned start address (0x%08lx)\n", | |
225 | (unsigned long)dma_ptr); | |
226 | ||
227 | if (size > MAX_DMA_SIZE) | |
228 | return -EOVERFLOW; | |
229 | ||
230 | local_irq_save(flags); | |
231 | status = regs->RdDCSR; | |
232 | ||
233 | /* If both DMA buffers are started, there's nothing else we can do. */ | |
234 | if ((status & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB)) { | |
235 | DPRINTK("start: st %#x busy\n", status); | |
236 | ret = -EBUSY; | |
237 | goto out; | |
238 | } | |
239 | ||
240 | if (((status & DCSR_BIU) && (status & DCSR_STRTB)) || | |
241 | (!(status & DCSR_BIU) && !(status & DCSR_STRTA))) { | |
242 | if (status & DCSR_DONEA) { | |
243 | /* give a chance for the interrupt to be processed */ | |
244 | ret = -EAGAIN; | |
245 | goto out; | |
246 | } | |
247 | regs->DBSA = dma_ptr; | |
248 | regs->DBTA = size; | |
249 | regs->SetDCSR = DCSR_STRTA | DCSR_IE | DCSR_RUN; | |
250 | DPRINTK("start a=%#x s=%d on A\n", dma_ptr, size); | |
251 | } else { | |
252 | if (status & DCSR_DONEB) { | |
253 | /* give a chance for the interrupt to be processed */ | |
254 | ret = -EAGAIN; | |
255 | goto out; | |
256 | } | |
257 | regs->DBSB = dma_ptr; | |
258 | regs->DBTB = size; | |
259 | regs->SetDCSR = DCSR_STRTB | DCSR_IE | DCSR_RUN; | |
260 | DPRINTK("start a=%#x s=%d on B\n", dma_ptr, size); | |
261 | } | |
262 | ret = 0; | |
263 | ||
264 | out: | |
265 | local_irq_restore(flags); | |
266 | return ret; | |
267 | } | |
268 | ||
269 | ||
270 | /** | |
271 | * sa1100_get_dma_pos - return current DMA position | |
272 | * @regs: identifier for the channel to use | |
273 | * | |
274 | * This function returns the current physical (or bus) address for the | |
275 | * given DMA channel. If the channel is running i.e. not in a stopped | |
276 | * state then the caller must disable interrupts prior calling this | |
277 | * function and process the returned value before re-enabling them to | |
278 | * prevent races with the completion interrupt handler and the callback | |
279 | * function. The validation of the returned value is the caller's | |
280 | * responsibility as well -- the hardware seems to return out of range | |
281 | * values when the DMA engine completes a buffer. | |
282 | * | |
283 | * The @regs identifier is provided by a successful call to | |
284 | * sa1100_request_dma(). | |
285 | **/ | |
286 | ||
287 | dma_addr_t sa1100_get_dma_pos(dma_regs_t *regs) | |
288 | { | |
289 | int status; | |
290 | ||
291 | /* | |
292 | * We must determine whether buffer A or B is active. | |
293 | * Two possibilities: either we are in the middle of | |
294 | * a buffer, or the DMA controller just switched to the | |
295 | * next toggle but the interrupt hasn't been serviced yet. | |
296 | * The former case is straight forward. In the later case, | |
297 | * we'll do like if DMA is just at the end of the previous | |
298 | * toggle since all registers haven't been reset yet. | |
299 | * This goes around the edge case and since we're always | |
300 | * a little behind anyways it shouldn't make a big difference. | |
301 | * If DMA has been stopped prior calling this then the | |
302 | * position is exact. | |
303 | */ | |
304 | status = regs->RdDCSR; | |
305 | if ((!(status & DCSR_BIU) && (status & DCSR_STRTA)) || | |
306 | ( (status & DCSR_BIU) && !(status & DCSR_STRTB))) | |
307 | return regs->DBSA; | |
308 | else | |
309 | return regs->DBSB; | |
310 | } | |
311 | ||
312 | ||
313 | /** | |
314 | * sa1100_reset_dma - reset a DMA channel | |
315 | * @regs: identifier for the channel to use | |
316 | * | |
317 | * This function resets and reconfigure the given DMA channel. This is | |
318 | * particularly useful after a sleep/wakeup event. | |
319 | * | |
320 | * The @regs identifier is provided by a successful call to | |
321 | * sa1100_request_dma(). | |
322 | **/ | |
323 | ||
324 | void sa1100_reset_dma(dma_regs_t *regs) | |
325 | { | |
326 | int i; | |
327 | ||
328 | for (i = 0; i < SA1100_DMA_CHANNELS; i++) | |
329 | if (regs == (dma_regs_t *)&DDAR(i)) | |
330 | break; | |
331 | if (i >= SA1100_DMA_CHANNELS) { | |
8e86f427 | 332 | printk(KERN_ERR "%s: bad DMA identifier\n", __func__); |
1da177e4 LT |
333 | return; |
334 | } | |
335 | ||
336 | regs->ClrDCSR = | |
337 | (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB | | |
338 | DCSR_IE | DCSR_ERROR | DCSR_RUN); | |
339 | regs->DDAR = dma_chan[i].device; | |
340 | } | |
341 | ||
342 | ||
343 | EXPORT_SYMBOL(sa1100_request_dma); | |
344 | EXPORT_SYMBOL(sa1100_free_dma); | |
345 | EXPORT_SYMBOL(sa1100_start_dma); | |
346 | EXPORT_SYMBOL(sa1100_get_dma_pos); | |
347 | EXPORT_SYMBOL(sa1100_reset_dma); | |
348 |