isadma.c revision 1.4 1 /*-
2 * Copyright (c) 1993 Charles Hannum.
3 * Copyright (c) 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * William Jolitz.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the University of
20 * California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 * from: @(#)isa.c 7.2 (Berkeley) 5/13/91
38 * $Id: isadma.c,v 1.4 1993/10/22 20:24:14 mycroft Exp $
39 */
40
41 /*
42 * code to deal with ISA DMA
43 */
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/conf.h>
48 #include <sys/file.h>
49 #include <sys/syslog.h>
50 #include <sys/device.h>
51 #include <vm/vm.h>
52
53 #include <machine/cpu.h>
54 #include <machine/pio.h>
55
56 #include <i386/isa/isa.h>
57 #include <i386/isa/isavar.h>
58 #include <i386/isa/ic/i8237.h>
59
60 /*
61 ** Register definitions for DMA controller 1 (channels 0..3):
62 */
63 #define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */
64 #define DMA1_SR (IO_DMA1 + 1*8) /* status register */
65 #define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */
66 #define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */
67 #define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */
68
69 /*
70 ** Register definitions for DMA controller 2 (channels 4..7):
71 */
72 #define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */
73 #define DMA2_SR (IO_DMA2 + 2*8) /* status register */
74 #define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */
75 #define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */
76 #define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */
77
78 /* region of physical memory known to be contiguous */
79 caddr_t isaphysmem;
80 static caddr_t bouncebuf[8]; /* XXX */
81 static caddr_t bounced[8]; /* XXX */
82 static vm_size_t bouncesize[8]; /* XXX */
83
84 /* high byte of address is stored in this port for i-th dma channel */
85 static u_short dmapageport[8] =
86 { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
87
88 /*
89 * at_setup_dmachan(): allocate bounce buffer and check for conflicts.
90 *
91 * XXX
92 * This sucks. We should be able to bounce more than NBPG bytes, but I
93 * don't feel like writing the code to do contiguous allocation right now.
94 */
95 void
96 at_setup_dmachan(chan, max)
97 int chan;
98 u_long max;
99 {
100
101 #ifdef DIAGNOSTIC
102 if (chan > 7 || chan < 0)
103 panic("at_setup_dmachan: impossible request");
104 if (max > NBPG)
105 panic("at_setup_dmachan: what a lose");
106 #endif
107
108 /* XXX check for drq conflict? */
109
110 bouncebuf[chan] = (caddr_t) isaphysmem + NBPG*chan;
111 }
112
113 /*
114 * at_dma_cascade(): program 8237 DMA controller channel to accept
115 * external dma control by a board.
116 */
117 void
118 at_dma_cascade(chan)
119 int chan;
120 {
121
122 #ifdef DIAGNOSTIC
123 if (chan > 7 || chan < 0)
124 panic("at_dma_cascade: impossible request");
125 #endif
126
127 /* XXX check for drq conflict? */
128
129 /* set dma channel mode, and set dma channel mode */
130 if ((chan & 4) == 0) {
131 outb(DMA1_MODE, DMA37MD_CASCADE | chan);
132 outb(DMA1_SMSK, chan);
133 } else {
134 outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
135 outb(DMA2_SMSK, chan & 3);
136 }
137 }
138
139 /*
140 * at_dma(): program 8237 DMA controller channel, avoid page alignment
141 * problems by using a bounce buffer.
142 */
143 void
144 at_dma(read, addr, nbytes, chan)
145 int read;
146 caddr_t addr;
147 vm_size_t nbytes;
148 int chan;
149 {
150 vm_offset_t phys;
151 int waport;
152 caddr_t newaddr;
153
154 #ifdef DIAGNOSTIC
155 if (chan > 7 || chan < 0 ||
156 (chan < 4 && nbytes > (1<<16)) ||
157 (chan >= 4 && (nbytes > (1<<17) || nbytes & 1 || (u_int)addr & 1)))
158 panic("at_dma: impossible request");
159 #endif
160
161 if (at_dma_rangecheck((vm_offset_t)addr, nbytes, chan)) {
162 if (bouncebuf[chan] == 0)
163 /* some twit forgot to call at_setup_dmachan() */
164 panic("at_dma: no bounce buffer");
165 /* XXX totally braindead; NBPG is not enough */
166 if (nbytes > NBPG)
167 panic("at_dma: transfer too large");
168 bouncesize[chan] = nbytes;
169 newaddr = bouncebuf[chan];
170 /* copy bounce buffer on write */
171 if (!read) {
172 bcopy(addr, newaddr, nbytes);
173 bounced[chan] = 0;
174 } else
175 bounced[chan] = addr;
176 addr = newaddr;
177 }
178
179 /* translate to physical */
180 phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
181
182 if ((chan & 4) == 0) {
183 /*
184 * Program one of DMA channels 0..3. These are
185 * byte mode channels.
186 */
187 /* set dma channel mode, and reset address ff */
188 if (read)
189 outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
190 else
191 outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
192 outb(DMA1_FFC, 0);
193
194 /* send start address */
195 waport = DMA1_CHN(chan);
196 outb(waport, phys);
197 outb(waport, phys>>8);
198 outb(dmapageport[chan], phys>>16);
199
200 /* send count */
201 outb(waport + 1, --nbytes);
202 outb(waport + 1, nbytes>>8);
203
204 /* unmask channel */
205 outb(DMA1_SMSK, DMA37SM_CLEAR | chan);
206 } else {
207 /*
208 * Program one of DMA channels 4..7. These are
209 * word mode channels.
210 */
211 /* set dma channel mode, and reset address ff */
212 if (read)
213 outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
214 else
215 outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
216 outb(DMA2_FFC, 0);
217
218 /* send start address */
219 waport = DMA2_CHN(chan & 3);
220 outb(waport, phys>>1);
221 outb(waport, phys>>9);
222 outb(dmapageport[chan], phys>>16);
223
224 /* send count */
225 nbytes >>= 1;
226 outb(waport + 2, --nbytes);
227 outb(waport + 2, nbytes>>8);
228
229 /* unmask channel */
230 outb(DMA2_SMSK, DMA37SM_CLEAR | (chan & 3));
231 }
232 }
233
234 /*
235 * Abort a DMA request, clearing the bounce buffer, if any.
236 */
237 void
238 at_dma_abort(chan)
239 int chan;
240 {
241
242 #ifdef DIAGNOSTIC
243 if (chan > 7 || chan < 0)
244 panic("at_dma_abort: impossible request");
245 #endif
246
247 bounced[chan] = 0;
248
249 /* mask channel */
250 if ((chan & 4) == 0)
251 outb(DMA1_SMSK, DMA37SM_SET | chan);
252 else
253 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
254 }
255
256 /*
257 * End a DMA request, copying data from the bounce buffer, if any,
258 * when reading.
259 */
260 void
261 at_dma_terminate(chan)
262 unsigned chan;
263 {
264 u_char tc;
265
266 #ifdef DIAGNOSTIC
267 if (chan > 7 || chan < 0)
268 panic("at_dma_terminate: impossible request");
269 #endif
270
271 /* check that the terminal count was reached */
272 if ((chan & 4) == 0)
273 tc = inb(DMA1_SR) & (1 << chan);
274 else
275 tc = inb(DMA2_SR) & (1 << (chan & 3));
276 if (tc == 0)
277 /* XXX probably should panic or something */
278 log(LOG_ERR, "dma channel %d not finished\n", chan);
279
280 /* copy bounce buffer on read */
281 if (bounced[chan]) {
282 bcopy(bouncebuf[chan], bounced[chan], bouncesize[chan]);
283 bounced[chan] = 0;
284 }
285
286 /* mask channel */
287 if ((chan & 4) == 0)
288 outb(DMA1_SMSK, DMA37SM_SET | chan);
289 else
290 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
291 }
292
293 /*
294 * Check for problems with the address range of a DMA transfer
295 * (non-contiguous physical pages, outside of bus address space,
296 * crossing DMA page boundaries).
297 * Return true if special handling needed.
298 */
299 int
300 at_dma_rangecheck(va, length, chan)
301 vm_offset_t va;
302 u_long length;
303 int chan;
304 {
305 vm_offset_t phys, priorpage = 0, endva;
306 u_int dma_pgmsk = (chan&4) ? ~(128*1024-1) : ~(64*1024-1);
307
308 endva = round_page(va + length);
309 for (; va < endva ; va += NBPG) {
310 phys = trunc_page(pmap_extract(pmap_kernel(), va));
311 if (phys == 0)
312 panic("at_dma_rangecheck: no physical page present");
313 if (phys >= (1<<24))
314 return 1;
315 if (priorpage) {
316 if (priorpage + NBPG != phys)
317 return 1;
318 /* check if crossing a DMA page boundary */
319 if ((priorpage ^ phys) & dma_pgmsk)
320 return 1;
321 }
322 priorpage = phys;
323 }
324 return 0;
325 }
326