aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-pnx4008/dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-pnx4008/dma.c')
-rw-r--r--arch/arm/mach-pnx4008/dma.c1109
1 files changed, 1109 insertions, 0 deletions
diff --git a/arch/arm/mach-pnx4008/dma.c b/arch/arm/mach-pnx4008/dma.c
new file mode 100644
index 000000000000..981aa9dcdede
--- /dev/null
+++ b/arch/arm/mach-pnx4008/dma.c
@@ -0,0 +1,1109 @@
1/*
2 * linux/arch/arm/mach-pnx4008/dma.c
3 *
4 * PNX4008 DMA registration and IRQ dispatching
5 *
6 * Author: Vitaly Wool
7 * Copyright: MontaVista Software Inc. (c) 2005
8 *
9 * Based on the code from Nicolas Pitre
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/kernel.h>
19#include <linux/interrupt.h>
20#include <linux/errno.h>
21#include <linux/err.h>
22#include <linux/dma-mapping.h>
23#include <linux/clk.h>
24
25#include <asm/system.h>
26#include <asm/irq.h>
27#include <asm/hardware.h>
28#include <asm/dma.h>
29#include <asm/dma-mapping.h>
30#include <asm/io.h>
31#include <asm/mach/dma.h>
32#include <asm/arch/clock.h>
33
34static struct dma_channel {
35 char *name;
36 void (*irq_handler) (int, int, void *, struct pt_regs *);
37 void *data;
38 struct pnx4008_dma_ll *ll;
39 u32 ll_dma;
40 void *target_addr;
41 int target_id;
42} dma_channels[MAX_DMA_CHANNELS];
43
44static struct ll_pool {
45 void *vaddr;
46 void *cur;
47 dma_addr_t dma_addr;
48 int count;
49} ll_pool;
50
51static spinlock_t ll_lock = SPIN_LOCK_UNLOCKED;
52
53struct pnx4008_dma_ll *pnx4008_alloc_ll_entry(dma_addr_t * ll_dma)
54{
55 struct pnx4008_dma_ll *ll = NULL;
56 unsigned long flags;
57
58 spin_lock_irqsave(&ll_lock, flags);
59 if (ll_pool.count > 4) { /* can give one more */
60 ll = *(struct pnx4008_dma_ll **) ll_pool.cur;
61 *ll_dma = ll_pool.dma_addr + ((void *)ll - ll_pool.vaddr);
62 *(void **)ll_pool.cur = **(void ***)ll_pool.cur;
63 memset(ll, 0, sizeof(*ll));
64 ll_pool.count--;
65 }
66 spin_unlock_irqrestore(&ll_lock, flags);
67
68 return ll;
69}
70
71EXPORT_SYMBOL_GPL(pnx4008_alloc_ll_entry);
72
73void pnx4008_free_ll_entry(struct pnx4008_dma_ll * ll, dma_addr_t ll_dma)
74{
75 unsigned long flags;
76
77 if (ll) {
78 if ((unsigned long)((long)ll - (long)ll_pool.vaddr) > 0x4000) {
79 printk(KERN_ERR "Trying to free entry not allocated by DMA\n");
80 BUG();
81 }
82
83 if (ll->flags & DMA_BUFFER_ALLOCATED)
84 ll->free(ll->alloc_data);
85
86 spin_lock_irqsave(&ll_lock, flags);
87 *(long *)ll = *(long *)ll_pool.cur;
88 *(long *)ll_pool.cur = (long)ll;
89 ll_pool.count++;
90 spin_unlock_irqrestore(&ll_lock, flags);
91 }
92}
93
94EXPORT_SYMBOL_GPL(pnx4008_free_ll_entry);
95
96void pnx4008_free_ll(u32 ll_dma, struct pnx4008_dma_ll * ll)
97{
98 struct pnx4008_dma_ll *ptr;
99 u32 dma;
100
101 while (ll) {
102 dma = ll->next_dma;
103 ptr = ll->next;
104 pnx4008_free_ll_entry(ll, ll_dma);
105
106 ll_dma = dma;
107 ll = ptr;
108 }
109}
110
111EXPORT_SYMBOL_GPL(pnx4008_free_ll);
112
113static int dma_channels_requested = 0;
114
115static inline void dma_increment_usage(void)
116{
117 if (!dma_channels_requested++) {
118 struct clk *clk = clk_get(0, "dma_ck");
119 if (!IS_ERR(clk)) {
120 clk_set_rate(clk, 1);
121 clk_put(clk);
122 }
123 pnx4008_config_dma(-1, -1, 1);
124 }
125}
126static inline void dma_decrement_usage(void)
127{
128 if (!--dma_channels_requested) {
129 struct clk *clk = clk_get(0, "dma_ck");
130 if (!IS_ERR(clk)) {
131 clk_set_rate(clk, 0);
132 clk_put(clk);
133 }
134 pnx4008_config_dma(-1, -1, 0);
135
136 }
137}
138
139static spinlock_t dma_lock = SPIN_LOCK_UNLOCKED;
140
141static inline void pnx4008_dma_lock(void)
142{
143 spin_lock_irq(&dma_lock);
144}
145
146static inline void pnx4008_dma_unlock(void)
147{
148 spin_unlock_irq(&dma_lock);
149}
150
151#define VALID_CHANNEL(c) (((c) >= 0) && ((c) < MAX_DMA_CHANNELS))
152
153int pnx4008_request_channel(char *name, int ch,
154 void (*irq_handler) (int, int, void *,
155 struct pt_regs *), void *data)
156{
157 int i, found = 0;
158
159 /* basic sanity checks */
160 if (!name || (ch != -1 && !VALID_CHANNEL(ch)))
161 return -EINVAL;
162
163 pnx4008_dma_lock();
164
165 /* try grabbing a DMA channel with the requested priority */
166 for (i = MAX_DMA_CHANNELS - 1; i >= 0; i--) {
167 if (!dma_channels[i].name && (ch == -1 || ch == i)) {
168 found = 1;
169 break;
170 }
171 }
172
173 if (found) {
174 dma_increment_usage();
175 dma_channels[i].name = name;
176 dma_channels[i].irq_handler = irq_handler;
177 dma_channels[i].data = data;
178 dma_channels[i].ll = NULL;
179 dma_channels[i].ll_dma = 0;
180 } else {
181 printk(KERN_WARNING "No more available DMA channels for %s\n",
182 name);
183 i = -ENODEV;
184 }
185
186 pnx4008_dma_unlock();
187 return i;
188}
189
190EXPORT_SYMBOL_GPL(pnx4008_request_channel);
191
192void pnx4008_free_channel(int ch)
193{
194 if (!dma_channels[ch].name) {
195 printk(KERN_CRIT
196 "%s: trying to free channel %d which is already freed\n",
197 __FUNCTION__, ch);
198 return;
199 }
200
201 pnx4008_dma_lock();
202 pnx4008_free_ll(dma_channels[ch].ll_dma, dma_channels[ch].ll);
203 dma_channels[ch].ll = NULL;
204 dma_decrement_usage();
205
206 dma_channels[ch].name = NULL;
207 pnx4008_dma_unlock();
208}
209
210EXPORT_SYMBOL_GPL(pnx4008_free_channel);
211
212int pnx4008_config_dma(int ahb_m1_be, int ahb_m2_be, int enable)
213{
214 unsigned long dma_cfg = __raw_readl(DMAC_CONFIG);
215
216 switch (ahb_m1_be) {
217 case 0:
218 dma_cfg &= ~(1 << 1);
219 break;
220 case 1:
221 dma_cfg |= (1 << 1);
222 break;
223 default:
224 break;
225 }
226
227 switch (ahb_m2_be) {
228 case 0:
229 dma_cfg &= ~(1 << 2);
230 break;
231 case 1:
232 dma_cfg |= (1 << 2);
233 break;
234 default:
235 break;
236 }
237
238 switch (enable) {
239 case 0:
240 dma_cfg &= ~(1 << 0);
241 break;
242 case 1:
243 dma_cfg |= (1 << 0);
244 break;
245 default:
246 break;
247 }
248
249 pnx4008_dma_lock();
250 __raw_writel(dma_cfg, DMAC_CONFIG);
251 pnx4008_dma_unlock();
252
253 return 0;
254}
255
256EXPORT_SYMBOL_GPL(pnx4008_config_dma);
257
258int pnx4008_dma_pack_control(const struct pnx4008_dma_ch_ctrl * ch_ctrl,
259 unsigned long *ctrl)
260{
261 int i = 0, dbsize, sbsize, err = 0;
262
263 if (!ctrl || !ch_ctrl) {
264 err = -EINVAL;
265 goto out;
266 }
267
268 *ctrl = 0;
269
270 switch (ch_ctrl->tc_mask) {
271 case 0:
272 break;
273 case 1:
274 *ctrl |= (1 << 31);
275 break;
276
277 default:
278 err = -EINVAL;
279 goto out;
280 }
281
282 switch (ch_ctrl->cacheable) {
283 case 0:
284 break;
285 case 1:
286 *ctrl |= (1 << 30);
287 break;
288
289 default:
290 err = -EINVAL;
291 goto out;
292 }
293 switch (ch_ctrl->bufferable) {
294 case 0:
295 break;
296 case 1:
297 *ctrl |= (1 << 29);
298 break;
299
300 default:
301 err = -EINVAL;
302 goto out;
303 }
304 switch (ch_ctrl->priv_mode) {
305 case 0:
306 break;
307 case 1:
308 *ctrl |= (1 << 28);
309 break;
310
311 default:
312 err = -EINVAL;
313 goto out;
314 }
315 switch (ch_ctrl->di) {
316 case 0:
317 break;
318 case 1:
319 *ctrl |= (1 << 27);
320 break;
321
322 default:
323 err = -EINVAL;
324 goto out;
325 }
326 switch (ch_ctrl->si) {
327 case 0:
328 break;
329 case 1:
330 *ctrl |= (1 << 26);
331 break;
332
333 default:
334 err = -EINVAL;
335 goto out;
336 }
337 switch (ch_ctrl->dest_ahb1) {
338 case 0:
339 break;
340 case 1:
341 *ctrl |= (1 << 25);
342 break;
343
344 default:
345 err = -EINVAL;
346 goto out;
347 }
348 switch (ch_ctrl->src_ahb1) {
349 case 0:
350 break;
351 case 1:
352 *ctrl |= (1 << 24);
353 break;
354
355 default:
356 err = -EINVAL;
357 goto out;
358 }
359 switch (ch_ctrl->dwidth) {
360 case WIDTH_BYTE:
361 *ctrl &= ~(7 << 21);
362 break;
363 case WIDTH_HWORD:
364 *ctrl &= ~(7 << 21);
365 *ctrl |= (1 << 21);
366 break;
367 case WIDTH_WORD:
368 *ctrl &= ~(7 << 21);
369 *ctrl |= (2 << 21);
370 break;
371
372 default:
373 err = -EINVAL;
374 goto out;
375 }
376 switch (ch_ctrl->swidth) {
377 case WIDTH_BYTE:
378 *ctrl &= ~(7 << 18);
379 break;
380 case WIDTH_HWORD:
381 *ctrl &= ~(7 << 18);
382 *ctrl |= (1 << 18);
383 break;
384 case WIDTH_WORD:
385 *ctrl &= ~(7 << 18);
386 *ctrl |= (2 << 18);
387 break;
388
389 default:
390 err = -EINVAL;
391 goto out;
392 }
393 dbsize = ch_ctrl->dbsize;
394 while (!(dbsize & 1)) {
395 i++;
396 dbsize >>= 1;
397 }
398 if (ch_ctrl->dbsize != 1 || i > 8 || i == 1) {
399 err = -EINVAL;
400 goto out;
401 } else if (i > 1)
402 i--;
403 *ctrl &= ~(7 << 15);
404 *ctrl |= (i << 15);
405
406 sbsize = ch_ctrl->sbsize;
407 while (!(sbsize & 1)) {
408 i++;
409 sbsize >>= 1;
410 }
411 if (ch_ctrl->sbsize != 1 || i > 8 || i == 1) {
412 err = -EINVAL;
413 goto out;
414 } else if (i > 1)
415 i--;
416 *ctrl &= ~(7 << 12);
417 *ctrl |= (i << 12);
418
419 if (ch_ctrl->tr_size > 0x7ff) {
420 err = -E2BIG;
421 goto out;
422 }
423 *ctrl &= ~0x7ff;
424 *ctrl |= ch_ctrl->tr_size & 0x7ff;
425
426out:
427 return err;
428}
429
430EXPORT_SYMBOL_GPL(pnx4008_dma_pack_control);
431
432int pnx4008_dma_parse_control(unsigned long ctrl,
433 struct pnx4008_dma_ch_ctrl * ch_ctrl)
434{
435 int err = 0;
436
437 if (!ch_ctrl) {
438 err = -EINVAL;
439 goto out;
440 }
441
442 ch_ctrl->tr_size = ctrl & 0x7ff;
443 ctrl >>= 12;
444
445 ch_ctrl->sbsize = 1 << (ctrl & 7);
446 if (ch_ctrl->sbsize > 1)
447 ch_ctrl->sbsize <<= 1;
448 ctrl >>= 3;
449
450 ch_ctrl->dbsize = 1 << (ctrl & 7);
451 if (ch_ctrl->dbsize > 1)
452 ch_ctrl->dbsize <<= 1;
453 ctrl >>= 3;
454
455 switch (ctrl & 7) {
456 case 0:
457 ch_ctrl->swidth = WIDTH_BYTE;
458 break;
459 case 1:
460 ch_ctrl->swidth = WIDTH_HWORD;
461 break;
462 case 2:
463 ch_ctrl->swidth = WIDTH_WORD;
464 break;
465 default:
466 err = -EINVAL;
467 goto out;
468 }
469 ctrl >>= 3;
470
471 switch (ctrl & 7) {
472 case 0:
473 ch_ctrl->dwidth = WIDTH_BYTE;
474 break;
475 case 1:
476 ch_ctrl->dwidth = WIDTH_HWORD;
477 break;
478 case 2:
479 ch_ctrl->dwidth = WIDTH_WORD;
480 break;
481 default:
482 err = -EINVAL;
483 goto out;
484 }
485 ctrl >>= 3;
486
487 ch_ctrl->src_ahb1 = ctrl & 1;
488 ctrl >>= 1;
489
490 ch_ctrl->dest_ahb1 = ctrl & 1;
491 ctrl >>= 1;
492
493 ch_ctrl->si = ctrl & 1;
494 ctrl >>= 1;
495
496 ch_ctrl->di = ctrl & 1;
497 ctrl >>= 1;
498
499 ch_ctrl->priv_mode = ctrl & 1;
500 ctrl >>= 1;
501
502 ch_ctrl->bufferable = ctrl & 1;
503 ctrl >>= 1;
504
505 ch_ctrl->cacheable = ctrl & 1;
506 ctrl >>= 1;
507
508 ch_ctrl->tc_mask = ctrl & 1;
509
510out:
511 return err;
512}
513
514EXPORT_SYMBOL_GPL(pnx4008_dma_parse_control);
515
516int pnx4008_dma_pack_config(const struct pnx4008_dma_ch_config * ch_cfg,
517 unsigned long *cfg)
518{
519 int err = 0;
520
521 if (!cfg || !ch_cfg) {
522 err = -EINVAL;
523 goto out;
524 }
525
526 *cfg = 0;
527
528 switch (ch_cfg->halt) {
529 case 0:
530 break;
531 case 1:
532 *cfg |= (1 << 18);
533 break;
534
535 default:
536 err = -EINVAL;
537 goto out;
538 }
539 switch (ch_cfg->active) {
540 case 0:
541 break;
542 case 1:
543 *cfg |= (1 << 17);
544 break;
545
546 default:
547 err = -EINVAL;
548 goto out;
549 }
550 switch (ch_cfg->lock) {
551 case 0:
552 break;
553 case 1:
554 *cfg |= (1 << 16);
555 break;
556
557 default:
558 err = -EINVAL;
559 goto out;
560 }
561 switch (ch_cfg->itc) {
562 case 0:
563 break;
564 case 1:
565 *cfg |= (1 << 15);
566 break;
567
568 default:
569 err = -EINVAL;
570 goto out;
571 }
572 switch (ch_cfg->ie) {
573 case 0:
574 break;
575 case 1:
576 *cfg |= (1 << 14);
577 break;
578
579 default:
580 err = -EINVAL;
581 goto out;
582 }
583 switch (ch_cfg->flow_cntrl) {
584 case FC_MEM2MEM_DMA:
585 *cfg &= ~(7 << 11);
586 break;
587 case FC_MEM2PER_DMA:
588 *cfg &= ~(7 << 11);
589 *cfg |= (1 << 11);
590 break;
591 case FC_PER2MEM_DMA:
592 *cfg &= ~(7 << 11);
593 *cfg |= (2 << 11);
594 break;
595 case FC_PER2PER_DMA:
596 *cfg &= ~(7 << 11);
597 *cfg |= (3 << 11);
598 break;
599 case FC_PER2PER_DPER:
600 *cfg &= ~(7 << 11);
601 *cfg |= (4 << 11);
602 break;
603 case FC_MEM2PER_PER:
604 *cfg &= ~(7 << 11);
605 *cfg |= (5 << 11);
606 break;
607 case FC_PER2MEM_PER:
608 *cfg &= ~(7 << 11);
609 *cfg |= (6 << 11);
610 break;
611 case FC_PER2PER_SPER:
612 *cfg |= (7 << 11);
613 break;
614
615 default:
616 err = -EINVAL;
617 goto out;
618 }
619 *cfg &= ~(0x1f << 6);
620 *cfg |= ((ch_cfg->dest_per & 0x1f) << 6);
621
622 *cfg &= ~(0x1f << 1);
623 *cfg |= ((ch_cfg->src_per & 0x1f) << 1);
624
625out:
626 return err;
627}
628
629EXPORT_SYMBOL_GPL(pnx4008_dma_pack_config);
630
631int pnx4008_dma_parse_config(unsigned long cfg,
632 struct pnx4008_dma_ch_config * ch_cfg)
633{
634 int err = 0;
635
636 if (!ch_cfg) {
637 err = -EINVAL;
638 goto out;
639 }
640
641 cfg >>= 1;
642
643 ch_cfg->src_per = cfg & 0x1f;
644 cfg >>= 5;
645
646 ch_cfg->dest_per = cfg & 0x1f;
647 cfg >>= 5;
648
649 switch (cfg & 7) {
650 case 0:
651 ch_cfg->flow_cntrl = FC_MEM2MEM_DMA;
652 break;
653 case 1:
654 ch_cfg->flow_cntrl = FC_MEM2PER_DMA;
655 break;
656 case 2:
657 ch_cfg->flow_cntrl = FC_PER2MEM_DMA;
658 break;
659 case 3:
660 ch_cfg->flow_cntrl = FC_PER2PER_DMA;
661 break;
662 case 4:
663 ch_cfg->flow_cntrl = FC_PER2PER_DPER;
664 break;
665 case 5:
666 ch_cfg->flow_cntrl = FC_MEM2PER_PER;
667 break;
668 case 6:
669 ch_cfg->flow_cntrl = FC_PER2MEM_PER;
670 break;
671 case 7:
672 ch_cfg->flow_cntrl = FC_PER2PER_SPER;
673 }
674 cfg >>= 3;
675
676 ch_cfg->ie = cfg & 1;
677 cfg >>= 1;
678
679 ch_cfg->itc = cfg & 1;
680 cfg >>= 1;
681
682 ch_cfg->lock = cfg & 1;
683 cfg >>= 1;
684
685 ch_cfg->active = cfg & 1;
686 cfg >>= 1;
687
688 ch_cfg->halt = cfg & 1;
689
690out:
691 return err;
692}
693
694EXPORT_SYMBOL_GPL(pnx4008_dma_parse_config);
695
696void pnx4008_dma_split_head_entry(struct pnx4008_dma_config * config,
697 struct pnx4008_dma_ch_ctrl * ctrl)
698{
699 int new_len = ctrl->tr_size, num_entries = 0;
700 int old_len = new_len;
701 int src_width, dest_width, count = 1;
702
703 switch (ctrl->swidth) {
704 case WIDTH_BYTE:
705 src_width = 1;
706 break;
707 case WIDTH_HWORD:
708 src_width = 2;
709 break;
710 case WIDTH_WORD:
711 src_width = 4;
712 break;
713 default:
714 return;
715 }
716
717 switch (ctrl->dwidth) {
718 case WIDTH_BYTE:
719 dest_width = 1;
720 break;
721 case WIDTH_HWORD:
722 dest_width = 2;
723 break;
724 case WIDTH_WORD:
725 dest_width = 4;
726 break;
727 default:
728 return;
729 }
730
731 while (new_len > 0x7FF) {
732 num_entries++;
733 new_len = (ctrl->tr_size + num_entries) / (num_entries + 1);
734 }
735 if (num_entries != 0) {
736 struct pnx4008_dma_ll *ll = NULL;
737 config->ch_ctrl &= ~0x7ff;
738 config->ch_ctrl |= new_len;
739 if (!config->is_ll) {
740 config->is_ll = 1;
741 while (num_entries) {
742 if (!ll) {
743 config->ll =
744 pnx4008_alloc_ll_entry(&config->
745 ll_dma);
746 ll = config->ll;
747 } else {
748 ll->next =
749 pnx4008_alloc_ll_entry(&ll->
750 next_dma);
751 ll = ll->next;
752 }
753
754 if (ctrl->si)
755 ll->src_addr =
756 config->src_addr +
757 src_width * new_len * count;
758 else
759 ll->src_addr = config->src_addr;
760 if (ctrl->di)
761 ll->dest_addr =
762 config->dest_addr +
763 dest_width * new_len * count;
764 else
765 ll->dest_addr = config->dest_addr;
766 ll->ch_ctrl = config->ch_ctrl & 0x7fffffff;
767 ll->next_dma = 0;
768 ll->next = NULL;
769 num_entries--;
770 count++;
771 }
772 } else {
773 struct pnx4008_dma_ll *ll_old = config->ll;
774 unsigned long ll_dma_old = config->ll_dma;
775 while (num_entries) {
776 if (!ll) {
777 config->ll =
778 pnx4008_alloc_ll_entry(&config->
779 ll_dma);
780 ll = config->ll;
781 } else {
782 ll->next =
783 pnx4008_alloc_ll_entry(&ll->
784 next_dma);
785 ll = ll->next;
786 }
787
788 if (ctrl->si)
789 ll->src_addr =
790 config->src_addr +
791 src_width * new_len * count;
792 else
793 ll->src_addr = config->src_addr;
794 if (ctrl->di)
795 ll->dest_addr =
796 config->dest_addr +
797 dest_width * new_len * count;
798 else
799 ll->dest_addr = config->dest_addr;
800 ll->ch_ctrl = config->ch_ctrl & 0x7fffffff;
801 ll->next_dma = 0;
802 ll->next = NULL;
803 num_entries--;
804 count++;
805 }
806 ll->next_dma = ll_dma_old;
807 ll->next = ll_old;
808 }
809 /* adjust last length/tc */
810 ll->ch_ctrl = config->ch_ctrl & (~0x7ff);
811 ll->ch_ctrl |= old_len - new_len * (count - 1);
812 config->ch_ctrl &= 0x7fffffff;
813 }
814}
815
816EXPORT_SYMBOL_GPL(pnx4008_dma_split_head_entry);
817
818void pnx4008_dma_split_ll_entry(struct pnx4008_dma_ll * cur_ll,
819 struct pnx4008_dma_ch_ctrl * ctrl)
820{
821 int new_len = ctrl->tr_size, num_entries = 0;
822 int old_len = new_len;
823 int src_width, dest_width, count = 1;
824
825 switch (ctrl->swidth) {
826 case WIDTH_BYTE:
827 src_width = 1;
828 break;
829 case WIDTH_HWORD:
830 src_width = 2;
831 break;
832 case WIDTH_WORD:
833 src_width = 4;
834 break;
835 default:
836 return;
837 }
838
839 switch (ctrl->dwidth) {
840 case WIDTH_BYTE:
841 dest_width = 1;
842 break;
843 case WIDTH_HWORD:
844 dest_width = 2;
845 break;
846 case WIDTH_WORD:
847 dest_width = 4;
848 break;
849 default:
850 return;
851 }
852
853 while (new_len > 0x7FF) {
854 num_entries++;
855 new_len = (ctrl->tr_size + num_entries) / (num_entries + 1);
856 }
857 if (num_entries != 0) {
858 struct pnx4008_dma_ll *ll = NULL;
859 cur_ll->ch_ctrl &= ~0x7ff;
860 cur_ll->ch_ctrl |= new_len;
861 if (!cur_ll->next) {
862 while (num_entries) {
863 if (!ll) {
864 cur_ll->next =
865 pnx4008_alloc_ll_entry(&cur_ll->
866 next_dma);
867 ll = cur_ll->next;
868 } else {
869 ll->next =
870 pnx4008_alloc_ll_entry(&ll->
871 next_dma);
872 ll = ll->next;
873 }
874
875 if (ctrl->si)
876 ll->src_addr =
877 cur_ll->src_addr +
878 src_width * new_len * count;
879 else
880 ll->src_addr = cur_ll->src_addr;
881 if (ctrl->di)
882 ll->dest_addr =
883 cur_ll->dest_addr +
884 dest_width * new_len * count;
885 else
886 ll->dest_addr = cur_ll->dest_addr;
887 ll->ch_ctrl = cur_ll->ch_ctrl & 0x7fffffff;
888 ll->next_dma = 0;
889 ll->next = NULL;
890 num_entries--;
891 count++;
892 }
893 } else {
894 struct pnx4008_dma_ll *ll_old = cur_ll->next;
895 unsigned long ll_dma_old = cur_ll->next_dma;
896 while (num_entries) {
897 if (!ll) {
898 cur_ll->next =
899 pnx4008_alloc_ll_entry(&cur_ll->
900 next_dma);
901 ll = cur_ll->next;
902 } else {
903 ll->next =
904 pnx4008_alloc_ll_entry(&ll->
905 next_dma);
906 ll = ll->next;
907 }
908
909 if (ctrl->si)
910 ll->src_addr =
911 cur_ll->src_addr +
912 src_width * new_len * count;
913 else
914 ll->src_addr = cur_ll->src_addr;
915 if (ctrl->di)
916 ll->dest_addr =
917 cur_ll->dest_addr +
918 dest_width * new_len * count;
919 else
920 ll->dest_addr = cur_ll->dest_addr;
921 ll->ch_ctrl = cur_ll->ch_ctrl & 0x7fffffff;
922 ll->next_dma = 0;
923 ll->next = NULL;
924 num_entries--;
925 count++;
926 }
927
928 ll->next_dma = ll_dma_old;
929 ll->next = ll_old;
930 }
931 /* adjust last length/tc */
932 ll->ch_ctrl = cur_ll->ch_ctrl & (~0x7ff);
933 ll->ch_ctrl |= old_len - new_len * (count - 1);
934 cur_ll->ch_ctrl &= 0x7fffffff;
935 }
936}
937
938EXPORT_SYMBOL_GPL(pnx4008_dma_split_ll_entry);
939
940int pnx4008_config_channel(int ch, struct pnx4008_dma_config * config)
941{
942 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)
943 return -EINVAL;
944
945 pnx4008_dma_lock();
946 __raw_writel(config->src_addr, DMAC_Cx_SRC_ADDR(ch));
947 __raw_writel(config->dest_addr, DMAC_Cx_DEST_ADDR(ch));
948
949 if (config->is_ll)
950 __raw_writel(config->ll_dma, DMAC_Cx_LLI(ch));
951 else
952 __raw_writel(0, DMAC_Cx_LLI(ch));
953
954 __raw_writel(config->ch_ctrl, DMAC_Cx_CONTROL(ch));
955 __raw_writel(config->ch_cfg, DMAC_Cx_CONFIG(ch));
956 pnx4008_dma_unlock();
957
958 return 0;
959
960}
961
962EXPORT_SYMBOL_GPL(pnx4008_config_channel);
963
964int pnx4008_channel_get_config(int ch, struct pnx4008_dma_config * config)
965{
966 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name || !config)
967 return -EINVAL;
968
969 pnx4008_dma_lock();
970 config->ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));
971 config->ch_ctrl = __raw_readl(DMAC_Cx_CONTROL(ch));
972
973 config->ll_dma = __raw_readl(DMAC_Cx_LLI(ch));
974 config->is_ll = config->ll_dma ? 1 : 0;
975
976 config->src_addr = __raw_readl(DMAC_Cx_SRC_ADDR(ch));
977 config->dest_addr = __raw_readl(DMAC_Cx_DEST_ADDR(ch));
978 pnx4008_dma_unlock();
979
980 return 0;
981}
982
983EXPORT_SYMBOL_GPL(pnx4008_channel_get_config);
984
985int pnx4008_dma_ch_enable(int ch)
986{
987 unsigned long ch_cfg;
988
989 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)
990 return -EINVAL;
991
992 pnx4008_dma_lock();
993 ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));
994 ch_cfg |= 1;
995 __raw_writel(ch_cfg, DMAC_Cx_CONFIG(ch));
996 pnx4008_dma_unlock();
997
998 return 0;
999}
1000
1001EXPORT_SYMBOL_GPL(pnx4008_dma_ch_enable);
1002
1003int pnx4008_dma_ch_disable(int ch)
1004{
1005 unsigned long ch_cfg;
1006
1007 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)
1008 return -EINVAL;
1009
1010 pnx4008_dma_lock();
1011 ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));
1012 ch_cfg &= ~1;
1013 __raw_writel(ch_cfg, DMAC_Cx_CONFIG(ch));
1014 pnx4008_dma_unlock();
1015
1016 return 0;
1017}
1018
1019EXPORT_SYMBOL_GPL(pnx4008_dma_ch_disable);
1020
1021int pnx4008_dma_ch_enabled(int ch)
1022{
1023 unsigned long ch_cfg;
1024
1025 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)
1026 return -EINVAL;
1027
1028 pnx4008_dma_lock();
1029 ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));
1030 pnx4008_dma_unlock();
1031
1032 return ch_cfg & 1;
1033}
1034
1035EXPORT_SYMBOL_GPL(pnx4008_dma_ch_enabled);
1036
1037static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
1038{
1039 int i;
1040 unsigned long dint = __raw_readl(DMAC_INT_STAT);
1041 unsigned long tcint = __raw_readl(DMAC_INT_TC_STAT);
1042 unsigned long eint = __raw_readl(DMAC_INT_ERR_STAT);
1043 unsigned long i_bit;
1044
1045 for (i = MAX_DMA_CHANNELS - 1; i >= 0; i--) {
1046 i_bit = 1 << i;
1047 if (dint & i_bit) {
1048 struct dma_channel *channel = &dma_channels[i];
1049
1050 if (channel->name && channel->irq_handler) {
1051 int cause = 0;
1052
1053 if (eint & i_bit)
1054 cause |= DMA_ERR_INT;
1055 if (tcint & i_bit)
1056 cause |= DMA_TC_INT;
1057 channel->irq_handler(i, cause, channel->data,
1058 regs);
1059 } else {
1060 /*
1061 * IRQ for an unregistered DMA channel
1062 */
1063 printk(KERN_WARNING
1064 "spurious IRQ for DMA channel %d\n", i);
1065 }
1066 if (tcint & i_bit)
1067 __raw_writel(i_bit, DMAC_INT_TC_CLEAR);
1068 if (eint & i_bit)
1069 __raw_writel(i_bit, DMAC_INT_ERR_CLEAR);
1070 }
1071 }
1072 return IRQ_HANDLED;
1073}
1074
1075static int __init pnx4008_dma_init(void)
1076{
1077 int ret, i;
1078
1079 ret = request_irq(DMA_INT, dma_irq_handler, 0, "DMA", NULL);
1080 if (ret) {
1081 printk(KERN_CRIT "Wow! Can't register IRQ for DMA\n");
1082 goto out;
1083 }
1084
1085 ll_pool.count = 0x4000 / sizeof(struct pnx4008_dma_ll);
1086 ll_pool.cur = ll_pool.vaddr =
1087 dma_alloc_coherent(NULL, ll_pool.count * sizeof(struct pnx4008_dma_ll),
1088 &ll_pool.dma_addr, GFP_KERNEL);
1089
1090 if (!ll_pool.vaddr) {
1091 ret = -ENOMEM;
1092 free_irq(DMA_INT, NULL);
1093 goto out;
1094 }
1095
1096 for (i = 0; i < ll_pool.count - 1; i++) {
1097 void **addr = ll_pool.vaddr + i * sizeof(struct pnx4008_dma_ll);
1098 *addr = (void *)addr + sizeof(struct pnx4008_dma_ll);
1099 }
1100 *(long *)(ll_pool.vaddr +
1101 (ll_pool.count - 1) * sizeof(struct pnx4008_dma_ll)) =
1102 (long)ll_pool.vaddr;
1103
1104 __raw_writel(1, DMAC_CONFIG);
1105
1106out:
1107 return ret;
1108}
1109arch_initcall(pnx4008_dma_init);