diff options
Diffstat (limited to 'arch/mips/au1000/common/dma.c')
-rw-r--r-- | arch/mips/au1000/common/dma.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/arch/mips/au1000/common/dma.c b/arch/mips/au1000/common/dma.c new file mode 100644 index 000000000000..372c33f1353d --- /dev/null +++ b/arch/mips/au1000/common/dma.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * A DMA channel allocator for Au1000. API is modeled loosely off of | ||
5 | * linux/kernel/dma.c. | ||
6 | * | ||
7 | * Copyright 2000 MontaVista Software Inc. | ||
8 | * Author: MontaVista Software, Inc. | ||
9 | * stevel@mvista.com or source@mvista.com | ||
10 | * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the | ||
14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
15 | * option) any later version. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
20 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
23 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
24 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
27 | * | ||
28 | * You should have received a copy of the GNU General Public License along | ||
29 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
30 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
31 | * | ||
32 | */ | ||
33 | #include <linux/config.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/sched.h> | ||
38 | #include <linux/spinlock.h> | ||
39 | #include <linux/string.h> | ||
40 | #include <linux/delay.h> | ||
41 | #include <linux/interrupt.h> | ||
42 | #include <linux/module.h> | ||
43 | #include <asm/system.h> | ||
44 | #include <asm/mach-au1x00/au1000.h> | ||
45 | #include <asm/mach-au1x00/au1000_dma.h> | ||
46 | |||
47 | #if defined(CONFIG_SOC_AU1000) || defined(CONFIG_SOC_AU1500) || defined(CONFIG_SOC_AU1100) | ||
48 | /* | ||
49 | * A note on resource allocation: | ||
50 | * | ||
51 | * All drivers needing DMA channels, should allocate and release them | ||
52 | * through the public routines `request_dma()' and `free_dma()'. | ||
53 | * | ||
54 | * In order to avoid problems, all processes should allocate resources in | ||
55 | * the same sequence and release them in the reverse order. | ||
56 | * | ||
57 | * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. | ||
58 | * When releasing them, first release the IRQ, then release the DMA. The | ||
59 | * main reason for this order is that, if you are requesting the DMA buffer | ||
60 | * done interrupt, you won't know the irq number until the DMA channel is | ||
61 | * returned from request_dma. | ||
62 | */ | ||
63 | |||
64 | |||
65 | DEFINE_SPINLOCK(au1000_dma_spin_lock); | ||
66 | |||
67 | struct dma_chan au1000_dma_table[NUM_AU1000_DMA_CHANNELS] = { | ||
68 | {.dev_id = -1,}, | ||
69 | {.dev_id = -1,}, | ||
70 | {.dev_id = -1,}, | ||
71 | {.dev_id = -1,}, | ||
72 | {.dev_id = -1,}, | ||
73 | {.dev_id = -1,}, | ||
74 | {.dev_id = -1,}, | ||
75 | {.dev_id = -1,} | ||
76 | }; | ||
77 | EXPORT_SYMBOL(au1000_dma_table); | ||
78 | |||
79 | // Device FIFO addresses and default DMA modes | ||
80 | static const struct dma_dev { | ||
81 | unsigned int fifo_addr; | ||
82 | unsigned int dma_mode; | ||
83 | } dma_dev_table[DMA_NUM_DEV] = { | ||
84 | {UART0_ADDR + UART_TX, 0}, | ||
85 | {UART0_ADDR + UART_RX, 0}, | ||
86 | {0, 0}, | ||
87 | {0, 0}, | ||
88 | {AC97C_DATA, DMA_DW16 }, // coherent | ||
89 | {AC97C_DATA, DMA_DR | DMA_DW16 }, // coherent | ||
90 | {UART3_ADDR + UART_TX, DMA_DW8 | DMA_NC}, | ||
91 | {UART3_ADDR + UART_RX, DMA_DR | DMA_DW8 | DMA_NC}, | ||
92 | {USBD_EP0RD, DMA_DR | DMA_DW8 | DMA_NC}, | ||
93 | {USBD_EP0WR, DMA_DW8 | DMA_NC}, | ||
94 | {USBD_EP2WR, DMA_DW8 | DMA_NC}, | ||
95 | {USBD_EP3WR, DMA_DW8 | DMA_NC}, | ||
96 | {USBD_EP4RD, DMA_DR | DMA_DW8 | DMA_NC}, | ||
97 | {USBD_EP5RD, DMA_DR | DMA_DW8 | DMA_NC}, | ||
98 | {I2S_DATA, DMA_DW32 | DMA_NC}, | ||
99 | {I2S_DATA, DMA_DR | DMA_DW32 | DMA_NC} | ||
100 | }; | ||
101 | |||
102 | int au1000_dma_read_proc(char *buf, char **start, off_t fpos, | ||
103 | int length, int *eof, void *data) | ||
104 | { | ||
105 | int i, len = 0; | ||
106 | struct dma_chan *chan; | ||
107 | |||
108 | for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) { | ||
109 | if ((chan = get_dma_chan(i)) != NULL) { | ||
110 | len += sprintf(buf + len, "%2d: %s\n", | ||
111 | i, chan->dev_str); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | if (fpos >= len) { | ||
116 | *start = buf; | ||
117 | *eof = 1; | ||
118 | return 0; | ||
119 | } | ||
120 | *start = buf + fpos; | ||
121 | if ((len -= fpos) > length) | ||
122 | return length; | ||
123 | *eof = 1; | ||
124 | return len; | ||
125 | } | ||
126 | |||
127 | // Device FIFO addresses and default DMA modes - 2nd bank | ||
128 | static const struct dma_dev dma_dev_table_bank2[DMA_NUM_DEV_BANK2] = { | ||
129 | {SD0_XMIT_FIFO, DMA_DS | DMA_DW8}, // coherent | ||
130 | {SD0_RECV_FIFO, DMA_DS | DMA_DR | DMA_DW8}, // coherent | ||
131 | {SD1_XMIT_FIFO, DMA_DS | DMA_DW8}, // coherent | ||
132 | {SD1_RECV_FIFO, DMA_DS | DMA_DR | DMA_DW8} // coherent | ||
133 | }; | ||
134 | |||
135 | void dump_au1000_dma_channel(unsigned int dmanr) | ||
136 | { | ||
137 | struct dma_chan *chan; | ||
138 | |||
139 | if (dmanr >= NUM_AU1000_DMA_CHANNELS) | ||
140 | return; | ||
141 | chan = &au1000_dma_table[dmanr]; | ||
142 | |||
143 | printk(KERN_INFO "Au1000 DMA%d Register Dump:\n", dmanr); | ||
144 | printk(KERN_INFO " mode = 0x%08x\n", | ||
145 | au_readl(chan->io + DMA_MODE_SET)); | ||
146 | printk(KERN_INFO " addr = 0x%08x\n", | ||
147 | au_readl(chan->io + DMA_PERIPHERAL_ADDR)); | ||
148 | printk(KERN_INFO " start0 = 0x%08x\n", | ||
149 | au_readl(chan->io + DMA_BUFFER0_START)); | ||
150 | printk(KERN_INFO " start1 = 0x%08x\n", | ||
151 | au_readl(chan->io + DMA_BUFFER1_START)); | ||
152 | printk(KERN_INFO " count0 = 0x%08x\n", | ||
153 | au_readl(chan->io + DMA_BUFFER0_COUNT)); | ||
154 | printk(KERN_INFO " count1 = 0x%08x\n", | ||
155 | au_readl(chan->io + DMA_BUFFER1_COUNT)); | ||
156 | } | ||
157 | |||
158 | |||
159 | /* | ||
160 | * Finds a free channel, and binds the requested device to it. | ||
161 | * Returns the allocated channel number, or negative on error. | ||
162 | * Requests the DMA done IRQ if irqhandler != NULL. | ||
163 | */ | ||
164 | int request_au1000_dma(int dev_id, const char *dev_str, | ||
165 | irqreturn_t (*irqhandler)(int, void *, struct pt_regs *), | ||
166 | unsigned long irqflags, | ||
167 | void *irq_dev_id) | ||
168 | { | ||
169 | struct dma_chan *chan; | ||
170 | const struct dma_dev *dev; | ||
171 | int i, ret; | ||
172 | |||
173 | #if defined(CONFIG_SOC_AU1100) | ||
174 | if (dev_id < 0 || dev_id >= (DMA_NUM_DEV + DMA_NUM_DEV_BANK2)) | ||
175 | return -EINVAL; | ||
176 | #else | ||
177 | if (dev_id < 0 || dev_id >= DMA_NUM_DEV) | ||
178 | return -EINVAL; | ||
179 | #endif | ||
180 | |||
181 | for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) { | ||
182 | if (au1000_dma_table[i].dev_id < 0) | ||
183 | break; | ||
184 | } | ||
185 | if (i == NUM_AU1000_DMA_CHANNELS) | ||
186 | return -ENODEV; | ||
187 | |||
188 | chan = &au1000_dma_table[i]; | ||
189 | |||
190 | if (dev_id >= DMA_NUM_DEV) { | ||
191 | dev_id -= DMA_NUM_DEV; | ||
192 | dev = &dma_dev_table_bank2[dev_id]; | ||
193 | } else { | ||
194 | dev = &dma_dev_table[dev_id]; | ||
195 | } | ||
196 | |||
197 | if (irqhandler) { | ||
198 | chan->irq = AU1000_DMA_INT_BASE + i; | ||
199 | chan->irq_dev = irq_dev_id; | ||
200 | if ((ret = request_irq(chan->irq, irqhandler, irqflags, | ||
201 | dev_str, chan->irq_dev))) { | ||
202 | chan->irq = 0; | ||
203 | chan->irq_dev = NULL; | ||
204 | return ret; | ||
205 | } | ||
206 | } else { | ||
207 | chan->irq = 0; | ||
208 | chan->irq_dev = NULL; | ||
209 | } | ||
210 | |||
211 | // fill it in | ||
212 | chan->io = DMA_CHANNEL_BASE + i * DMA_CHANNEL_LEN; | ||
213 | chan->dev_id = dev_id; | ||
214 | chan->dev_str = dev_str; | ||
215 | chan->fifo_addr = dev->fifo_addr; | ||
216 | chan->mode = dev->dma_mode; | ||
217 | |||
218 | /* initialize the channel before returning */ | ||
219 | init_dma(i); | ||
220 | |||
221 | return i; | ||
222 | } | ||
223 | EXPORT_SYMBOL(request_au1000_dma); | ||
224 | |||
225 | void free_au1000_dma(unsigned int dmanr) | ||
226 | { | ||
227 | struct dma_chan *chan = get_dma_chan(dmanr); | ||
228 | if (!chan) { | ||
229 | printk("Trying to free DMA%d\n", dmanr); | ||
230 | return; | ||
231 | } | ||
232 | |||
233 | disable_dma(dmanr); | ||
234 | if (chan->irq) | ||
235 | free_irq(chan->irq, chan->irq_dev); | ||
236 | |||
237 | chan->irq = 0; | ||
238 | chan->irq_dev = NULL; | ||
239 | chan->dev_id = -1; | ||
240 | } | ||
241 | EXPORT_SYMBOL(free_au1000_dma); | ||
242 | |||
243 | #endif // AU1000 AU1500 AU1100 | ||