isadma.c revision 1.3 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.3 1993/10/17 05:34:23 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_dma_cascade(): program 8237 DMA controller channel to accept
90 * external dma control by a board.
91 */
92 void
93 at_dma_cascade(chan)
94 unsigned chan;
95 {
96 #ifdef DIAGNOSTIC
97 if (chan > 7)
98 panic("at_dma_cascade: impossible request");
99 #endif
100
101 /* set dma channel mode, and set dma channel mode */
102 if ((chan & 4) == 0) {
103 outb(DMA1_MODE, DMA37MD_CASCADE | chan);
104 outb(DMA1_SMSK, chan);
105 } else {
106 outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
107 outb(DMA2_SMSK, chan & 3);
108 }
109 }
110
111 /*
112 * at_dma(): program 8237 DMA controller channel, avoid page alignment
113 * problems by using a bounce buffer.
114 */
115 void
116 at_dma(read, addr, nbytes, chan)
117 int read;
118 caddr_t addr;
119 vm_size_t nbytes;
120 unsigned chan;
121 {
122 vm_offset_t phys;
123 int waport;
124 caddr_t newaddr;
125
126 if (chan > 7 ||
127 (chan < 4 && nbytes > (1<<16)) ||
128 (chan >= 4 && (nbytes > (1<<17) || nbytes & 1 || (u_int)addr & 1)))
129 panic("at_dma: impossible request");
130
131 if (at_dma_rangecheck((vm_offset_t)addr, nbytes, chan)) {
132 /* XXX totally braindead; NBPG is not enough */
133 if (bouncebuf[chan] == 0)
134 bouncebuf[chan] =
135 (caddr_t) isaphysmem + NBPG*chan;
136 bouncesize[chan] = nbytes;
137 newaddr = bouncebuf[chan];
138 /* copy bounce buffer on write */
139 if (!read) {
140 bcopy(addr, newaddr, nbytes);
141 bounced[chan] = 0;
142 } else
143 bounced[chan] = addr;
144 addr = newaddr;
145 }
146
147 /* translate to physical */
148 phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
149
150 if ((chan & 4) == 0) {
151 /*
152 * Program one of DMA channels 0..3. These are
153 * byte mode channels.
154 */
155 /* set dma channel mode, and reset address ff */
156 if (read)
157 outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
158 else
159 outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
160 outb(DMA1_FFC, 0);
161
162 /* send start address */
163 waport = DMA1_CHN(chan);
164 outb(waport, phys);
165 outb(waport, phys>>8);
166 outb(dmapageport[chan], phys>>16);
167
168 /* send count */
169 outb(waport + 1, --nbytes);
170 outb(waport + 1, nbytes>>8);
171
172 /* unmask channel */
173 outb(DMA1_SMSK, DMA37SM_CLEAR | chan);
174 } else {
175 /*
176 * Program one of DMA channels 4..7. These are
177 * word mode channels.
178 */
179 /* set dma channel mode, and reset address ff */
180 if (read)
181 outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
182 else
183 outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
184 outb(DMA2_FFC, 0);
185
186 /* send start address */
187 waport = DMA2_CHN(chan & 3);
188 outb(waport, phys>>1);
189 outb(waport, phys>>9);
190 outb(dmapageport[chan], phys>>16);
191
192 /* send count */
193 nbytes >>= 1;
194 outb(waport + 2, --nbytes);
195 outb(waport + 2, nbytes>>8);
196
197 /* unmask channel */
198 outb(DMA2_SMSK, DMA37SM_CLEAR | (chan & 3));
199 }
200 }
201
202 /*
203 * Abort a DMA request, clearing the bounce buffer, if any.
204 */
205 void
206 at_dma_abort(chan)
207 unsigned chan;
208 {
209
210 #ifdef DIAGNOSTIC
211 if (chan > 7)
212 panic("at_dma_abort: impossible request");
213 #endif
214
215 bounced[chan] = 0;
216
217 /* mask channel */
218 if ((chan & 4) == 0)
219 outb(DMA1_SMSK, DMA37SM_SET | chan);
220 else
221 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
222 }
223
224 /*
225 * End a DMA request, copying data from the bounce buffer, if any,
226 * when reading.
227 */
228 void
229 at_dma_terminate(chan)
230 unsigned chan;
231 {
232 u_char tc;
233
234 #ifdef DIAGNOSTIC
235 if (chan > 7)
236 panic("at_dma_terminate: impossible request");
237 #endif
238
239 /* check that the terminal count was reached */
240 if ((chan & 4) == 0)
241 tc = inb(DMA1_SR) & (1 << chan);
242 else
243 tc = inb(DMA2_SR) & (1 << (chan & 3));
244 if (tc == 0)
245 /* XXX probably should panic or something */
246 log(LOG_ERR, "dma channel %d not finished\n", chan);
247
248 /* copy bounce buffer on read */
249 if (bounced[chan]) {
250 bcopy(bouncebuf[chan], bounced[chan], bouncesize[chan]);
251 bounced[chan] = 0;
252 }
253
254 /* mask channel */
255 if ((chan & 4) == 0)
256 outb(DMA1_SMSK, DMA37SM_SET | chan);
257 else
258 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
259 }
260
261 /*
262 * Check for problems with the address range of a DMA transfer
263 * (non-contiguous physical pages, outside of bus address space,
264 * crossing DMA page boundaries).
265 * Return true if special handling needed.
266 */
267 int
268 at_dma_rangecheck(va, length, chan)
269 vm_offset_t va;
270 unsigned length;
271 unsigned chan;
272 {
273 vm_offset_t phys, priorpage = 0, endva;
274 u_int dma_pgmsk = (chan&4) ? ~(128*1024-1) : ~(64*1024-1);
275
276 endva = round_page(va + length);
277 for (; va < endva ; va += NBPG) {
278 phys = trunc_page(pmap_extract(pmap_kernel(), va));
279 if (phys == 0)
280 panic("isa_dmacheck: no physical page present");
281 if (phys >= (1<<24))
282 return 1;
283 if (priorpage) {
284 if (priorpage + NBPG != phys)
285 return 1;
286 /* check if crossing a DMA page boundary */
287 if ((priorpage ^ phys) & dma_pgmsk)
288 return 1;
289 }
290 priorpage = phys;
291 }
292 return 0;
293 }
294