Linux-libre 3.0.53-gnu1
[librecmc/linux-libre.git] / arch / unicore32 / kernel / dma.c
1 /*
2  * linux/arch/unicore32/kernel/dma.c
3  *
4  * Code specific to PKUnity SoC and UniCore ISA
5  *
6  *      Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7  *      Copyright (C) 2001-2010 Guan Xuetao
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13
14 #include <linux/module.h>
15 #include <linux/init.h>
16 #include <linux/kernel.h>
17 #include <linux/interrupt.h>
18 #include <linux/errno.h>
19 #include <linux/io.h>
20
21 #include <asm/system.h>
22 #include <asm/irq.h>
23 #include <mach/hardware.h>
24 #include <mach/dma.h>
25
26 struct dma_channel {
27         char *name;
28         puv3_dma_prio prio;
29         void (*irq_handler)(int, void *);
30         void (*err_handler)(int, void *);
31         void *data;
32 };
33
34 static struct dma_channel dma_channels[MAX_DMA_CHANNELS];
35
36 int puv3_request_dma(char *name, puv3_dma_prio prio,
37                          void (*irq_handler)(int, void *),
38                          void (*err_handler)(int, void *),
39                          void *data)
40 {
41         unsigned long flags;
42         int i, found = 0;
43
44         /* basic sanity checks */
45         if (!name)
46                 return -EINVAL;
47
48         local_irq_save(flags);
49
50         do {
51                 /* try grabbing a DMA channel with the requested priority */
52                 for (i = 0; i < MAX_DMA_CHANNELS; i++) {
53                         if ((dma_channels[i].prio == prio) &&
54                             !dma_channels[i].name) {
55                                 found = 1;
56                                 break;
57                         }
58                 }
59                 /* if requested prio group is full, try a hier priority */
60         } while (!found && prio--);
61
62         if (found) {
63                 dma_channels[i].name = name;
64                 dma_channels[i].irq_handler = irq_handler;
65                 dma_channels[i].err_handler = err_handler;
66                 dma_channels[i].data = data;
67         } else {
68                 printk(KERN_WARNING "No more available DMA channels for %s\n",
69                                 name);
70                 i = -ENODEV;
71         }
72
73         local_irq_restore(flags);
74         return i;
75 }
76 EXPORT_SYMBOL(puv3_request_dma);
77
78 void puv3_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                         __func__, dma_ch);
86                 return;
87         }
88
89         local_irq_save(flags);
90         dma_channels[dma_ch].name = NULL;
91         dma_channels[dma_ch].err_handler = NULL;
92         local_irq_restore(flags);
93 }
94 EXPORT_SYMBOL(puv3_free_dma);
95
96 static irqreturn_t dma_irq_handler(int irq, void *dev_id)
97 {
98         int i, dint;
99
100         dint = readl(DMAC_ITCSR);
101         for (i = 0; i < MAX_DMA_CHANNELS; i++) {
102                 if (dint & DMAC_CHANNEL(i)) {
103                         struct dma_channel *channel = &dma_channels[i];
104
105                         /* Clear TC interrupt of channel i */
106                         writel(DMAC_CHANNEL(i), DMAC_ITCCR);
107                         writel(0, DMAC_ITCCR);
108
109                         if (channel->name && channel->irq_handler) {
110                                 channel->irq_handler(i, channel->data);
111                         } else {
112                                 /*
113                                  * IRQ for an unregistered DMA channel:
114                                  * let's clear the interrupts and disable it.
115                                  */
116                                 printk(KERN_WARNING "spurious IRQ for"
117                                                 " DMA channel %d\n", i);
118                         }
119                 }
120         }
121         return IRQ_HANDLED;
122 }
123
124 static irqreturn_t dma_err_handler(int irq, void *dev_id)
125 {
126         int i, dint;
127
128         dint = readl(DMAC_IESR);
129         for (i = 0; i < MAX_DMA_CHANNELS; i++) {
130                 if (dint & DMAC_CHANNEL(i)) {
131                         struct dma_channel *channel = &dma_channels[i];
132
133                         /* Clear Err interrupt of channel i */
134                         writel(DMAC_CHANNEL(i), DMAC_IECR);
135                         writel(0, DMAC_IECR);
136
137                         if (channel->name && channel->err_handler) {
138                                 channel->err_handler(i, channel->data);
139                         } else {
140                                 /*
141                                  * IRQ for an unregistered DMA channel:
142                                  * let's clear the interrupts and disable it.
143                                  */
144                                 printk(KERN_WARNING "spurious IRQ for"
145                                                 " DMA channel %d\n", i);
146                         }
147                 }
148         }
149         return IRQ_HANDLED;
150 }
151
152 int __init puv3_init_dma(void)
153 {
154         int i, ret;
155
156         /* dma channel priorities on v8 processors:
157          * ch 0 - 1  <--> (0) DMA_PRIO_HIGH
158          * ch 2 - 3  <--> (1) DMA_PRIO_MEDIUM
159          * ch 4 - 5  <--> (2) DMA_PRIO_LOW
160          */
161         for (i = 0; i < MAX_DMA_CHANNELS; i++) {
162                 puv3_stop_dma(i);
163                 dma_channels[i].name = NULL;
164                 dma_channels[i].prio = min((i & 0x7) >> 1, DMA_PRIO_LOW);
165         }
166
167         ret = request_irq(IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
168         if (ret) {
169                 printk(KERN_CRIT "Can't register IRQ for DMA\n");
170                 return ret;
171         }
172
173         ret = request_irq(IRQ_DMAERR, dma_err_handler, 0, "DMAERR", NULL);
174         if (ret) {
175                 printk(KERN_CRIT "Can't register IRQ for DMAERR\n");
176                 free_irq(IRQ_DMA, "DMA");
177                 return ret;
178         }
179
180         return 0;
181 }
182
183 postcore_initcall(puv3_init_dma);