69ee386d94e6d1b420c70ee1663ad87028c17034
[linux-flexiantxendom0-3.2.10.git] / arch / arm / mach-pxa / dma.c
1 /*
2  *  linux/arch/arm/mach-pxa/dma.c
3  *
4  *  PXA DMA registration and IRQ dispatching
5  *
6  *  Author:     Nicolas Pitre
7  *  Created:    Nov 15, 2001
8  *  Copyright:  MontaVista Software Inc.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License version 2 as
12  *  published by the Free Software Foundation.
13  */
14
15 #include <linux/module.h>
16 #include <linux/init.h>
17 #include <linux/kernel.h>
18 #include <linux/errno.h>
19
20 #include <asm/system.h>
21 #include <asm/irq.h>
22 #include <asm/hardware.h>
23 #include <asm/dma.h>
24
25
26 static struct dma_channel {
27         char *name;
28         void (*irq_handler)(int, void *, struct pt_regs *);
29         void *data;
30 } dma_channels[16];
31
32
33 int pxa_request_dma (char *name, pxa_dma_prio prio,
34                          void (*irq_handler)(int, void *, struct pt_regs *),
35                          void *data)
36 {
37         unsigned long flags;
38         int i, found = 0;
39
40         /* basic sanity checks */
41         if (!name || !irq_handler)
42                 return -EINVAL;
43
44         local_irq_save(flags);
45
46         /* try grabbing a DMA channel with the requested priority */
47         for (i = prio; i < prio + (prio == DMA_PRIO_LOW) ? 8 : 4; i++) {
48                 if (!dma_channels[i].name) {
49                         found = 1;
50                         break;
51                 }
52         }
53
54         if (!found) {
55                 /* requested prio group is full, try hier priorities */
56                 for (i = prio-1; i >= 0; i--) {
57                         if (!dma_channels[i].name) {
58                                 found = 1;
59                                 break;
60                         }
61                 }
62         }
63
64         if (found) {
65                 DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
66                 dma_channels[i].name = name;
67                 dma_channels[i].irq_handler = irq_handler;
68                 dma_channels[i].data = data;
69         } else {
70                 printk (KERN_WARNING "No more available DMA channels for %s\n", name);
71                 i = -ENODEV;
72         }
73
74         local_irq_restore(flags);
75         return i;
76 }
77
78 void pxa_free_dma (int dma_ch)
79 {
80         unsigned long flags;
81
82         if (!dma_channels[dma_ch].name) {
83                 printk (KERN_CRIT
84                         "%s: trying to free channel %d which is already freed\n",
85                         __FUNCTION__, dma_ch);
86                 return;
87         }
88
89         local_irq_save(flags);
90         DCSR(dma_ch) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
91         dma_channels[dma_ch].name = NULL;
92         local_irq_restore(flags);
93 }
94
95 static void dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
96 {
97         int i, dint = DINT;
98
99         for (i = 0; i < 16; i++) {
100                 if (dint & (1 << i)) {
101                         struct dma_channel *channel = &dma_channels[i];
102                         if (channel->name && channel->irq_handler) {
103                                 channel->irq_handler(i, channel->data, regs);
104                         } else {
105                                 /*
106                                  * IRQ for an unregistered DMA channel:
107                                  * let's clear the interrupts and disable it.
108                                  */
109                                 printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i);
110                                 DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
111                         }
112                 }
113         }
114 }
115
116 static int __init pxa_dma_init (void)
117 {
118         int ret;
119
120         ret = request_irq (IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
121         if (ret)
122                 printk (KERN_CRIT "Wow!  Can't register IRQ for DMA\n");
123         return ret;
124 }
125
126 arch_initcall(pxa_dma_init);
127
128 EXPORT_SYMBOL(pxa_request_dma);
129 EXPORT_SYMBOL(pxa_free_dma);
130