xref: /netbsd-src/sys/dev/isa/isadma.c (revision 5f7096188587a2c7c95fa3c69b78e1ec9c7923d0)
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