http://downloads.netgear.com/files/GPL/GPL_Source_V361j_DM111PSP_series_consumer_rele...
[bcm963xx.git] / kernel / linux / 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/interrupt.h>
19 #include <linux/errno.h>
20
21 #include <asm/system.h>
22 #include <asm/irq.h>
23 #include <asm/hardware.h>
24 #include <asm/dma.h>
25
26
27 static struct dma_channel {
28         char *name;
29         void (*irq_handler)(int, void *, struct pt_regs *);
30         void *data;
31 } dma_channels[PXA_DMA_CHANNELS];
32
33
34 int pxa_request_dma (char *name, pxa_dma_prio prio,
35                          void (*irq_handler)(int, void *, struct pt_regs *),
36                          void *data)
37 {
38         unsigned long flags;
39         int i, found = 0;
40
41         /* basic sanity checks */
42         if (!name || !irq_handler)
43                 return -EINVAL;
44
45         local_irq_save(flags);
46
47         /* try grabbing a DMA channel with the requested priority */
48         for (i = prio; i < prio + PXA_DMA_NBCH(prio); i++) {
49                 if (!dma_channels[i].name) {
50                         found = 1;
51                         break;
52                 }
53         }
54
55         if (!found) {
56                 /* requested prio group is full, try hier priorities */
57                 for (i = prio-1; i >= 0; i--) {
58                         if (!dma_channels[i].name) {
59                                 found = 1;
60                                 break;
61                         }
62                 }
63         }
64
65         if (found) {
66                 DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
67                 dma_channels[i].name = name;
68                 dma_channels[i].irq_handler = irq_handler;
69                 dma_channels[i].data = data;
70         } else {
71                 printk (KERN_WARNING "No more available DMA channels for %s\n", name);
72                 i = -ENODEV;
73         }
74
75         local_irq_restore(flags);
76         return i;
77 }
78
79 void pxa_free_dma (int dma_ch)
80 {
81         unsigned long flags;
82
83         if (!dma_channels[dma_ch].name) {
84                 printk (KERN_CRIT
85                         "%s: trying to free channel %d which is already freed\n",
86                         __FUNCTION__, dma_ch);
87                 return;
88         }
89
90         local_irq_save(flags);
91         DCSR(dma_ch) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
92         dma_channels[dma_ch].name = NULL;
93         local_irq_restore(flags);
94 }
95
96 static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
97 {
98         int i, dint = DINT;
99
100         for (i = 0; i < PXA_DMA_CHANNELS; i++) {
101                 if (dint & (1 << i)) {
102                         struct dma_channel *channel = &dma_channels[i];
103                         if (channel->name && channel->irq_handler) {
104                                 channel->irq_handler(i, channel->data, regs);
105                         } else {
106                                 /*
107                                  * IRQ for an unregistered DMA channel:
108                                  * let's clear the interrupts and disable it.
109                                  */
110                                 printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i);
111                                 DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
112                         }
113                 }
114         }
115         return IRQ_HANDLED;
116 }
117
118 static int __init pxa_dma_init (void)
119 {
120         int ret;
121
122         ret = request_irq (IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
123         if (ret)
124                 printk (KERN_CRIT "Wow!  Can't register IRQ for DMA\n");
125         return ret;
126 }
127
128 arch_initcall(pxa_dma_init);
129
130 EXPORT_SYMBOL(pxa_request_dma);
131 EXPORT_SYMBOL(pxa_free_dma);
132