diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/frv/kernel/dma.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/frv/kernel/dma.c')
-rw-r--r-- | arch/frv/kernel/dma.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/arch/frv/kernel/dma.c b/arch/frv/kernel/dma.c new file mode 100644 index 000000000000..f5de6cf7df4e --- /dev/null +++ b/arch/frv/kernel/dma.c | |||
@@ -0,0 +1,464 @@ | |||
1 | /* dma.c: DMA controller management on FR401 and the like | ||
2 | * | ||
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <asm/dma.h> | ||
18 | #include <asm/gpio-regs.h> | ||
19 | #include <asm/irc-regs.h> | ||
20 | #include <asm/cpu-irqs.h> | ||
21 | |||
22 | struct frv_dma_channel { | ||
23 | uint8_t flags; | ||
24 | #define FRV_DMA_FLAGS_RESERVED 0x01 | ||
25 | #define FRV_DMA_FLAGS_INUSE 0x02 | ||
26 | #define FRV_DMA_FLAGS_PAUSED 0x04 | ||
27 | uint8_t cap; /* capabilities available */ | ||
28 | int irq; /* completion IRQ */ | ||
29 | uint32_t dreqbit; | ||
30 | uint32_t dackbit; | ||
31 | uint32_t donebit; | ||
32 | const unsigned long ioaddr; /* DMA controller regs addr */ | ||
33 | const char *devname; | ||
34 | dma_irq_handler_t handler; | ||
35 | void *data; | ||
36 | }; | ||
37 | |||
38 | |||
39 | #define __get_DMAC(IO,X) ({ *(volatile unsigned long *)((IO) + DMAC_##X##x); }) | ||
40 | |||
41 | #define __set_DMAC(IO,X,V) \ | ||
42 | do { \ | ||
43 | *(volatile unsigned long *)((IO) + DMAC_##X##x) = (V); \ | ||
44 | mb(); \ | ||
45 | } while(0) | ||
46 | |||
47 | #define ___set_DMAC(IO,X,V) \ | ||
48 | do { \ | ||
49 | *(volatile unsigned long *)((IO) + DMAC_##X##x) = (V); \ | ||
50 | } while(0) | ||
51 | |||
52 | |||
53 | static struct frv_dma_channel frv_dma_channels[FRV_DMA_NCHANS] = { | ||
54 | [0] = { | ||
55 | .cap = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK | FRV_DMA_CAP_DONE, | ||
56 | .irq = IRQ_CPU_DMA0, | ||
57 | .dreqbit = SIR_DREQ0_INPUT, | ||
58 | .dackbit = SOR_DACK0_OUTPUT, | ||
59 | .donebit = SOR_DONE0_OUTPUT, | ||
60 | .ioaddr = 0xfe000900, | ||
61 | }, | ||
62 | [1] = { | ||
63 | .cap = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK | FRV_DMA_CAP_DONE, | ||
64 | .irq = IRQ_CPU_DMA1, | ||
65 | .dreqbit = SIR_DREQ1_INPUT, | ||
66 | .dackbit = SOR_DACK1_OUTPUT, | ||
67 | .donebit = SOR_DONE1_OUTPUT, | ||
68 | .ioaddr = 0xfe000980, | ||
69 | }, | ||
70 | [2] = { | ||
71 | .cap = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK, | ||
72 | .irq = IRQ_CPU_DMA2, | ||
73 | .dreqbit = SIR_DREQ2_INPUT, | ||
74 | .dackbit = SOR_DACK2_OUTPUT, | ||
75 | .ioaddr = 0xfe000a00, | ||
76 | }, | ||
77 | [3] = { | ||
78 | .cap = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK, | ||
79 | .irq = IRQ_CPU_DMA3, | ||
80 | .dreqbit = SIR_DREQ3_INPUT, | ||
81 | .dackbit = SOR_DACK3_OUTPUT, | ||
82 | .ioaddr = 0xfe000a80, | ||
83 | }, | ||
84 | [4] = { | ||
85 | .cap = FRV_DMA_CAP_DREQ, | ||
86 | .irq = IRQ_CPU_DMA4, | ||
87 | .dreqbit = SIR_DREQ4_INPUT, | ||
88 | .ioaddr = 0xfe001000, | ||
89 | }, | ||
90 | [5] = { | ||
91 | .cap = FRV_DMA_CAP_DREQ, | ||
92 | .irq = IRQ_CPU_DMA5, | ||
93 | .dreqbit = SIR_DREQ5_INPUT, | ||
94 | .ioaddr = 0xfe001080, | ||
95 | }, | ||
96 | [6] = { | ||
97 | .cap = FRV_DMA_CAP_DREQ, | ||
98 | .irq = IRQ_CPU_DMA6, | ||
99 | .dreqbit = SIR_DREQ6_INPUT, | ||
100 | .ioaddr = 0xfe001100, | ||
101 | }, | ||
102 | [7] = { | ||
103 | .cap = FRV_DMA_CAP_DREQ, | ||
104 | .irq = IRQ_CPU_DMA7, | ||
105 | .dreqbit = SIR_DREQ7_INPUT, | ||
106 | .ioaddr = 0xfe001180, | ||
107 | }, | ||
108 | }; | ||
109 | |||
110 | static DEFINE_RWLOCK(frv_dma_channels_lock); | ||
111 | |||
112 | unsigned long frv_dma_inprogress; | ||
113 | |||
114 | #define frv_clear_dma_inprogress(channel) \ | ||
115 | atomic_clear_mask(1 << (channel), &frv_dma_inprogress); | ||
116 | |||
117 | #define frv_set_dma_inprogress(channel) \ | ||
118 | atomic_set_mask(1 << (channel), &frv_dma_inprogress); | ||
119 | |||
120 | /*****************************************************************************/ | ||
121 | /* | ||
122 | * DMA irq handler - determine channel involved, grab status and call real handler | ||
123 | */ | ||
124 | static irqreturn_t dma_irq_handler(int irq, void *_channel, struct pt_regs *regs) | ||
125 | { | ||
126 | struct frv_dma_channel *channel = _channel; | ||
127 | |||
128 | frv_clear_dma_inprogress(channel - frv_dma_channels); | ||
129 | return channel->handler(channel - frv_dma_channels, | ||
130 | __get_DMAC(channel->ioaddr, CSTR), | ||
131 | channel->data, | ||
132 | regs); | ||
133 | |||
134 | } /* end dma_irq_handler() */ | ||
135 | |||
136 | /*****************************************************************************/ | ||
137 | /* | ||
138 | * Determine which DMA controllers are present on this CPU | ||
139 | */ | ||
140 | void __init frv_dma_init(void) | ||
141 | { | ||
142 | unsigned long psr = __get_PSR(); | ||
143 | int num_dma, i; | ||
144 | |||
145 | /* First, determine how many DMA channels are available */ | ||
146 | switch (PSR_IMPLE(psr)) { | ||
147 | case PSR_IMPLE_FR405: | ||
148 | case PSR_IMPLE_FR451: | ||
149 | case PSR_IMPLE_FR501: | ||
150 | case PSR_IMPLE_FR551: | ||
151 | num_dma = FRV_DMA_8CHANS; | ||
152 | break; | ||
153 | |||
154 | case PSR_IMPLE_FR401: | ||
155 | default: | ||
156 | num_dma = FRV_DMA_4CHANS; | ||
157 | break; | ||
158 | } | ||
159 | |||
160 | /* Now mark all of the non-existent channels as reserved */ | ||
161 | for(i = num_dma; i < FRV_DMA_NCHANS; i++) | ||
162 | frv_dma_channels[i].flags = FRV_DMA_FLAGS_RESERVED; | ||
163 | |||
164 | } /* end frv_dma_init() */ | ||
165 | |||
166 | /*****************************************************************************/ | ||
167 | /* | ||
168 | * allocate a DMA controller channel and the IRQ associated with it | ||
169 | */ | ||
170 | int frv_dma_open(const char *devname, | ||
171 | unsigned long dmamask, | ||
172 | int dmacap, | ||
173 | dma_irq_handler_t handler, | ||
174 | unsigned long irq_flags, | ||
175 | void *data) | ||
176 | { | ||
177 | struct frv_dma_channel *channel; | ||
178 | int dma, ret; | ||
179 | uint32_t val; | ||
180 | |||
181 | write_lock(&frv_dma_channels_lock); | ||
182 | |||
183 | ret = -ENOSPC; | ||
184 | |||
185 | for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) { | ||
186 | channel = &frv_dma_channels[dma]; | ||
187 | |||
188 | if (!test_bit(dma, &dmamask)) | ||
189 | continue; | ||
190 | |||
191 | if ((channel->cap & dmacap) != dmacap) | ||
192 | continue; | ||
193 | |||
194 | if (!frv_dma_channels[dma].flags) | ||
195 | goto found; | ||
196 | } | ||
197 | |||
198 | goto out; | ||
199 | |||
200 | found: | ||
201 | ret = request_irq(channel->irq, dma_irq_handler, irq_flags, devname, channel); | ||
202 | if (ret < 0) | ||
203 | goto out; | ||
204 | |||
205 | /* okay, we've allocated all the resources */ | ||
206 | channel = &frv_dma_channels[dma]; | ||
207 | |||
208 | channel->flags |= FRV_DMA_FLAGS_INUSE; | ||
209 | channel->devname = devname; | ||
210 | channel->handler = handler; | ||
211 | channel->data = data; | ||
212 | |||
213 | /* Now make sure we are set up for DMA and not GPIO */ | ||
214 | /* SIR bit must be set for DMA to work */ | ||
215 | __set_SIR(channel->dreqbit | __get_SIR()); | ||
216 | /* SOR bits depend on what the caller requests */ | ||
217 | val = __get_SOR(); | ||
218 | if(dmacap & FRV_DMA_CAP_DACK) | ||
219 | val |= channel->dackbit; | ||
220 | else | ||
221 | val &= ~channel->dackbit; | ||
222 | if(dmacap & FRV_DMA_CAP_DONE) | ||
223 | val |= channel->donebit; | ||
224 | else | ||
225 | val &= ~channel->donebit; | ||
226 | __set_SOR(val); | ||
227 | |||
228 | ret = dma; | ||
229 | out: | ||
230 | write_unlock(&frv_dma_channels_lock); | ||
231 | return ret; | ||
232 | } /* end frv_dma_open() */ | ||
233 | |||
234 | EXPORT_SYMBOL(frv_dma_open); | ||
235 | |||
236 | /*****************************************************************************/ | ||
237 | /* | ||
238 | * close a DMA channel and its associated interrupt | ||
239 | */ | ||
240 | void frv_dma_close(int dma) | ||
241 | { | ||
242 | struct frv_dma_channel *channel = &frv_dma_channels[dma]; | ||
243 | unsigned long flags; | ||
244 | |||
245 | write_lock_irqsave(&frv_dma_channels_lock, flags); | ||
246 | |||
247 | free_irq(channel->irq, channel); | ||
248 | frv_dma_stop(dma); | ||
249 | |||
250 | channel->flags &= ~FRV_DMA_FLAGS_INUSE; | ||
251 | |||
252 | write_unlock_irqrestore(&frv_dma_channels_lock, flags); | ||
253 | } /* end frv_dma_close() */ | ||
254 | |||
255 | EXPORT_SYMBOL(frv_dma_close); | ||
256 | |||
257 | /*****************************************************************************/ | ||
258 | /* | ||
259 | * set static configuration on a DMA channel | ||
260 | */ | ||
261 | void frv_dma_config(int dma, unsigned long ccfr, unsigned long cctr, unsigned long apr) | ||
262 | { | ||
263 | unsigned long ioaddr = frv_dma_channels[dma].ioaddr; | ||
264 | |||
265 | ___set_DMAC(ioaddr, CCFR, ccfr); | ||
266 | ___set_DMAC(ioaddr, CCTR, cctr); | ||
267 | ___set_DMAC(ioaddr, APR, apr); | ||
268 | mb(); | ||
269 | |||
270 | } /* end frv_dma_config() */ | ||
271 | |||
272 | EXPORT_SYMBOL(frv_dma_config); | ||
273 | |||
274 | /*****************************************************************************/ | ||
275 | /* | ||
276 | * start a DMA channel | ||
277 | */ | ||
278 | void frv_dma_start(int dma, | ||
279 | unsigned long sba, unsigned long dba, | ||
280 | unsigned long pix, unsigned long six, unsigned long bcl) | ||
281 | { | ||
282 | unsigned long ioaddr = frv_dma_channels[dma].ioaddr; | ||
283 | |||
284 | ___set_DMAC(ioaddr, SBA, sba); | ||
285 | ___set_DMAC(ioaddr, DBA, dba); | ||
286 | ___set_DMAC(ioaddr, PIX, pix); | ||
287 | ___set_DMAC(ioaddr, SIX, six); | ||
288 | ___set_DMAC(ioaddr, BCL, bcl); | ||
289 | ___set_DMAC(ioaddr, CSTR, 0); | ||
290 | mb(); | ||
291 | |||
292 | __set_DMAC(ioaddr, CCTR, __get_DMAC(ioaddr, CCTR) | DMAC_CCTRx_ACT); | ||
293 | frv_set_dma_inprogress(dma); | ||
294 | |||
295 | } /* end frv_dma_start() */ | ||
296 | |||
297 | EXPORT_SYMBOL(frv_dma_start); | ||
298 | |||
299 | /*****************************************************************************/ | ||
300 | /* | ||
301 | * restart a DMA channel that's been stopped in circular addressing mode by comparison-end | ||
302 | */ | ||
303 | void frv_dma_restart_circular(int dma, unsigned long six) | ||
304 | { | ||
305 | unsigned long ioaddr = frv_dma_channels[dma].ioaddr; | ||
306 | |||
307 | ___set_DMAC(ioaddr, SIX, six); | ||
308 | ___set_DMAC(ioaddr, CSTR, __get_DMAC(ioaddr, CSTR) & ~DMAC_CSTRx_CE); | ||
309 | mb(); | ||
310 | |||
311 | __set_DMAC(ioaddr, CCTR, __get_DMAC(ioaddr, CCTR) | DMAC_CCTRx_ACT); | ||
312 | frv_set_dma_inprogress(dma); | ||
313 | |||
314 | } /* end frv_dma_restart_circular() */ | ||
315 | |||
316 | EXPORT_SYMBOL(frv_dma_restart_circular); | ||
317 | |||
318 | /*****************************************************************************/ | ||
319 | /* | ||
320 | * stop a DMA channel | ||
321 | */ | ||
322 | void frv_dma_stop(int dma) | ||
323 | { | ||
324 | unsigned long ioaddr = frv_dma_channels[dma].ioaddr; | ||
325 | uint32_t cctr; | ||
326 | |||
327 | ___set_DMAC(ioaddr, CSTR, 0); | ||
328 | cctr = __get_DMAC(ioaddr, CCTR); | ||
329 | cctr &= ~(DMAC_CCTRx_IE | DMAC_CCTRx_ACT); | ||
330 | cctr |= DMAC_CCTRx_FC; /* fifo clear */ | ||
331 | __set_DMAC(ioaddr, CCTR, cctr); | ||
332 | __set_DMAC(ioaddr, BCL, 0); | ||
333 | frv_clear_dma_inprogress(dma); | ||
334 | } /* end frv_dma_stop() */ | ||
335 | |||
336 | EXPORT_SYMBOL(frv_dma_stop); | ||
337 | |||
338 | /*****************************************************************************/ | ||
339 | /* | ||
340 | * test interrupt status of DMA channel | ||
341 | */ | ||
342 | int is_frv_dma_interrupting(int dma) | ||
343 | { | ||
344 | unsigned long ioaddr = frv_dma_channels[dma].ioaddr; | ||
345 | |||
346 | return __get_DMAC(ioaddr, CSTR) & (1 << 23); | ||
347 | |||
348 | } /* end is_frv_dma_interrupting() */ | ||
349 | |||
350 | EXPORT_SYMBOL(is_frv_dma_interrupting); | ||
351 | |||
352 | /*****************************************************************************/ | ||
353 | /* | ||
354 | * dump data about a DMA channel | ||
355 | */ | ||
356 | void frv_dma_dump(int dma) | ||
357 | { | ||
358 | unsigned long ioaddr = frv_dma_channels[dma].ioaddr; | ||
359 | unsigned long cstr, pix, six, bcl; | ||
360 | |||
361 | cstr = __get_DMAC(ioaddr, CSTR); | ||
362 | pix = __get_DMAC(ioaddr, PIX); | ||
363 | six = __get_DMAC(ioaddr, SIX); | ||
364 | bcl = __get_DMAC(ioaddr, BCL); | ||
365 | |||
366 | printk("DMA[%d] cstr=%lx pix=%lx six=%lx bcl=%lx\n", dma, cstr, pix, six, bcl); | ||
367 | |||
368 | } /* end frv_dma_dump() */ | ||
369 | |||
370 | EXPORT_SYMBOL(frv_dma_dump); | ||
371 | |||
372 | /*****************************************************************************/ | ||
373 | /* | ||
374 | * pause all DMA controllers | ||
375 | * - called by clock mangling routines | ||
376 | * - caller must be holding interrupts disabled | ||
377 | */ | ||
378 | void frv_dma_pause_all(void) | ||
379 | { | ||
380 | struct frv_dma_channel *channel; | ||
381 | unsigned long ioaddr; | ||
382 | unsigned long cstr, cctr; | ||
383 | int dma; | ||
384 | |||
385 | write_lock(&frv_dma_channels_lock); | ||
386 | |||
387 | for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) { | ||
388 | channel = &frv_dma_channels[dma]; | ||
389 | |||
390 | if (!(channel->flags & FRV_DMA_FLAGS_INUSE)) | ||
391 | continue; | ||
392 | |||
393 | ioaddr = channel->ioaddr; | ||
394 | cctr = __get_DMAC(ioaddr, CCTR); | ||
395 | if (cctr & DMAC_CCTRx_ACT) { | ||
396 | cctr &= ~DMAC_CCTRx_ACT; | ||
397 | __set_DMAC(ioaddr, CCTR, cctr); | ||
398 | |||
399 | do { | ||
400 | cstr = __get_DMAC(ioaddr, CSTR); | ||
401 | } while (cstr & DMAC_CSTRx_BUSY); | ||
402 | |||
403 | if (cstr & DMAC_CSTRx_FED) | ||
404 | channel->flags |= FRV_DMA_FLAGS_PAUSED; | ||
405 | frv_clear_dma_inprogress(dma); | ||
406 | } | ||
407 | } | ||
408 | |||
409 | } /* end frv_dma_pause_all() */ | ||
410 | |||
411 | EXPORT_SYMBOL(frv_dma_pause_all); | ||
412 | |||
413 | /*****************************************************************************/ | ||
414 | /* | ||
415 | * resume paused DMA controllers | ||
416 | * - called by clock mangling routines | ||
417 | * - caller must be holding interrupts disabled | ||
418 | */ | ||
419 | void frv_dma_resume_all(void) | ||
420 | { | ||
421 | struct frv_dma_channel *channel; | ||
422 | unsigned long ioaddr; | ||
423 | unsigned long cstr, cctr; | ||
424 | int dma; | ||
425 | |||
426 | for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) { | ||
427 | channel = &frv_dma_channels[dma]; | ||
428 | |||
429 | if (!(channel->flags & FRV_DMA_FLAGS_PAUSED)) | ||
430 | continue; | ||
431 | |||
432 | ioaddr = channel->ioaddr; | ||
433 | cstr = __get_DMAC(ioaddr, CSTR); | ||
434 | cstr &= ~(DMAC_CSTRx_FED | DMAC_CSTRx_INT); | ||
435 | __set_DMAC(ioaddr, CSTR, cstr); | ||
436 | |||
437 | cctr = __get_DMAC(ioaddr, CCTR); | ||
438 | cctr |= DMAC_CCTRx_ACT; | ||
439 | __set_DMAC(ioaddr, CCTR, cctr); | ||
440 | |||
441 | channel->flags &= ~FRV_DMA_FLAGS_PAUSED; | ||
442 | frv_set_dma_inprogress(dma); | ||
443 | } | ||
444 | |||
445 | write_unlock(&frv_dma_channels_lock); | ||
446 | |||
447 | } /* end frv_dma_resume_all() */ | ||
448 | |||
449 | EXPORT_SYMBOL(frv_dma_resume_all); | ||
450 | |||
451 | /*****************************************************************************/ | ||
452 | /* | ||
453 | * dma status clear | ||
454 | */ | ||
455 | void frv_dma_status_clear(int dma) | ||
456 | { | ||
457 | unsigned long ioaddr = frv_dma_channels[dma].ioaddr; | ||
458 | uint32_t cctr; | ||
459 | ___set_DMAC(ioaddr, CSTR, 0); | ||
460 | |||
461 | cctr = __get_DMAC(ioaddr, CCTR); | ||
462 | } /* end frv_dma_status_clear() */ | ||
463 | |||
464 | EXPORT_SYMBOL(frv_dma_status_clear); | ||