xref: /netbsd-src/sys/dev/isa/isadma.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: isadma.c,v 1.21 1996/10/13 01:37:54 christos Exp $	*/
2 
3 #include <sys/param.h>
4 #include <sys/systm.h>
5 #include <sys/file.h>
6 #include <sys/buf.h>
7 #include <sys/syslog.h>
8 #include <sys/malloc.h>
9 #include <sys/proc.h>
10 #include <sys/uio.h>
11 
12 #include <vm/vm.h>
13 
14 #include <machine/pio.h>
15 
16 #include <dev/isa/isareg.h>
17 #include <dev/isa/isadmavar.h>
18 #include <dev/isa/isadmareg.h>
19 
20 /* region of physical memory known to be contiguous */
21 vm_offset_t isaphysmem;
22 static caddr_t dma_bounce[8];		/* XXX */
23 static char bounced[8];		/* XXX */
24 #define MAXDMASZ 512		/* XXX */
25 static u_int8_t dma_finished;
26 
27 /* high byte of address is stored in this port for i-th dma channel */
28 static int dmapageport[2][4] = {
29 	{0x87, 0x83, 0x81, 0x82},
30 	{0x8f, 0x8b, 0x89, 0x8a}
31 };
32 
33 static u_int8_t dmamode[4] = {
34 	DMA37MD_READ | DMA37MD_SINGLE,
35 	DMA37MD_WRITE | DMA37MD_SINGLE,
36 	DMA37MD_READ | DMA37MD_LOOP,
37 	DMA37MD_WRITE | DMA37MD_LOOP
38 };
39 
40 int	isa_dmarangecheck	__P((vm_offset_t, u_long, int));
41 caddr_t	isa_allocphysmem	__P((caddr_t, unsigned, void (*)(void)));
42 void	isa_freephysmem		__P((caddr_t, unsigned));
43 
44 /*
45  * isa_dmacascade(): program 8237 DMA controller channel to accept
46  * external dma control by a board.
47  */
48 void
49 isa_dmacascade(chan)
50 	int chan;
51 {
52 
53 #ifdef ISADMA_DEBUG
54 	if (chan < 0 || chan > 7)
55 		panic("isa_dmacascade: impossible request");
56 #endif
57 
58 	/* set dma channel mode, and set dma channel mode */
59 	if ((chan & 4) == 0) {
60 		outb(DMA1_MODE, chan | DMA37MD_CASCADE);
61 		outb(DMA1_SMSK, chan);
62 	} else {
63 		chan &= 3;
64 
65 		outb(DMA2_MODE, chan | DMA37MD_CASCADE);
66 		outb(DMA2_SMSK, chan);
67 	}
68 }
69 
70 /*
71  * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
72  * problems by using a bounce buffer.
73  */
74 void
75 isa_dmastart(flags, addr, nbytes, chan)
76 	int flags;
77 	caddr_t addr;
78 	vm_size_t nbytes;
79 	int chan;
80 {
81 	vm_offset_t phys;
82 	int waport;
83 	caddr_t newaddr;
84 
85 #ifdef ISADMA_DEBUG
86 	if (chan < 0 || chan > 7 ||
87 	    ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) :
88 	    (nbytes >= (1<<16))))
89 		panic("isa_dmastart: impossible request");
90 #endif
91 
92 	if (isa_dmarangecheck((vm_offset_t) addr, nbytes, chan)) {
93 		if (dma_bounce[chan] == 0)
94 			dma_bounce[chan] =
95 			    /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
96 			    (caddr_t) isaphysmem + NBPG*chan;
97 		bounced[chan] = 1;
98 		newaddr = dma_bounce[chan];
99 		*(int *) newaddr = 0;	/* XXX */
100 		/* copy bounce buffer on write */
101 		if ((flags & DMAMODE_READ) == 0)
102 			bcopy(addr, newaddr, nbytes);
103 		addr = newaddr;
104 	}
105 
106 	/* translate to physical */
107 	phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
108 
109 	dma_finished &= ~(1 << chan);
110 
111 	if ((chan & 4) == 0) {
112 		/*
113 		 * Program one of DMA channels 0..3.  These are
114 		 * byte mode channels.
115 		 */
116 		/* set dma channel mode, and reset address ff */
117 		outb(DMA1_MODE, chan | dmamode[flags]);
118 		outb(DMA1_FFC, 0);
119 
120 		/* send start address */
121 		waport = DMA1_CHN(chan);
122 		outb(dmapageport[0][chan], phys>>16);
123 		outb(waport, phys);
124 		outb(waport, phys>>8);
125 
126 		/* send count */
127 		outb(waport + 1, --nbytes);
128 		outb(waport + 1, nbytes>>8);
129 
130 		/* unmask channel */
131 		outb(DMA1_SMSK, chan | DMA37SM_CLEAR);
132 	} else {
133 		chan &= 3;
134 
135 		/*
136 		 * Program one of DMA channels 4..7.  These are
137 		 * word mode channels.
138 		 */
139 		/* set dma channel mode, and reset address ff */
140 		outb(DMA2_MODE, chan | dmamode[flags]);
141 		outb(DMA2_FFC, 0);
142 
143 		/* send start address */
144 		waport = DMA2_CHN(chan);
145 		outb(dmapageport[1][chan], phys>>16);
146 		phys >>= 1;
147 		outb(waport, phys);
148 		outb(waport, phys>>8);
149 
150 		/* send count */
151 		nbytes >>= 1;
152 		outb(waport + 2, --nbytes);
153 		outb(waport + 2, nbytes>>8);
154 
155 		/* unmask channel */
156 		outb(DMA2_SMSK, chan | DMA37SM_CLEAR);
157 	}
158 }
159 
160 void
161 isa_dmaabort(chan)
162 	int chan;
163 {
164 
165 #ifdef ISADMA_DEBUG
166 	if (chan < 0 || chan > 7)
167 		panic("isa_dmaabort: impossible request");
168 #endif
169 
170 	bounced[chan] = 0;
171 
172 	/* mask channel */
173 	if ((chan & 4) == 0)
174 		outb(DMA1_SMSK, DMA37SM_SET | chan);
175 	else
176 		outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
177 }
178 
179 int
180 isa_dmafinished(chan)
181 	int chan;
182 {
183 
184 #ifdef ISADMA_DEBUG
185 	if (chan < 0 || chan > 7)
186 		panic("isa_dmafinished: impossible request");
187 #endif
188 
189 	/* check that the terminal count was reached */
190 	if ((chan & 4) == 0)
191 		dma_finished |= inb(DMA1_SR) & 0x0f;
192 	else
193 		dma_finished |= (inb(DMA2_SR) & 0x0f) << 4;
194 
195 	return ((dma_finished & (1 << chan)) != 0);
196 }
197 
198 void
199 isa_dmadone(flags, addr, nbytes, chan)
200 	int flags;
201 	caddr_t addr;
202 	vm_size_t nbytes;
203 	int chan;
204 {
205 
206 #ifdef ISADMA_DEBUG
207 	if (chan < 0 || chan > 7)
208 		panic("isa_dmadone: impossible request");
209 #endif
210 
211 	if (!isa_dmafinished(chan))
212 		printf("isa_dmadone: channel %d not finished\n", chan);
213 
214 	/* mask channel */
215 	if ((chan & 4) == 0)
216 		outb(DMA1_SMSK, DMA37SM_SET | chan);
217 	else
218 		outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
219 
220 	/* copy bounce buffer on read */
221 	if (bounced[chan]) {
222 		bcopy(dma_bounce[chan], addr, nbytes);
223 		bounced[chan] = 0;
224 	}
225 }
226 
227 /*
228  * Check for problems with the address range of a DMA transfer
229  * (non-contiguous physical pages, outside of bus address space,
230  * crossing DMA page boundaries).
231  * Return true if special handling needed.
232  */
233 int
234 isa_dmarangecheck(va, length, chan)
235 	vm_offset_t va;
236 	u_long length;
237 	int chan;
238 {
239 	vm_offset_t phys, priorpage = 0, endva;
240 	u_int dma_pgmsk = (chan & 4) ?  ~(128*1024-1) : ~(64*1024-1);
241 
242 	endva = round_page(va + length);
243 	for (; va < endva ; va += NBPG) {
244 		phys = trunc_page(pmap_extract(pmap_kernel(), va));
245 		if (phys == 0)
246 			panic("isa_dmacheck: no physical page present");
247 		if (phys >= (1<<24))
248 			return 1;
249 		if (priorpage) {
250 			if (priorpage + NBPG != phys)
251 				return 1;
252 			/* check if crossing a DMA page boundary */
253 			if ((priorpage ^ phys) & dma_pgmsk)
254 				return 1;
255 		}
256 		priorpage = phys;
257 	}
258 	return 0;
259 }
260 
261 /* head of queue waiting for physmem to become available */
262 struct buf isa_physmemq;
263 
264 /* blocked waiting for resource to become free for exclusive use */
265 static isaphysmemflag;
266 /* if waited for and call requested when free (B_CALL) */
267 static void (*isaphysmemunblock) __P((void)); /* needs to be a list */
268 
269 /*
270  * Allocate contiguous physical memory for transfer, returning
271  * a *virtual* address to region. May block waiting for resource.
272  * (assumed to be called at splbio())
273  */
274 caddr_t
275 isa_allocphysmem(ca, length, func)
276 	caddr_t ca;
277 	unsigned length;
278 	void (*func) __P((void));
279 {
280 
281 	isaphysmemunblock = func;
282 	while (isaphysmemflag & B_BUSY) {
283 		isaphysmemflag |= B_WANTED;
284 		sleep((caddr_t)&isaphysmemflag, PRIBIO);
285 	}
286 	isaphysmemflag |= B_BUSY;
287 
288 	return((caddr_t)isaphysmem);
289 }
290 
291 /*
292  * Free contiguous physical memory used for transfer.
293  * (assumed to be called at splbio())
294  */
295 void
296 isa_freephysmem(va, length)
297 	caddr_t va;
298 	unsigned length;
299 {
300 
301 	isaphysmemflag &= ~B_BUSY;
302 	if (isaphysmemflag & B_WANTED) {
303 		isaphysmemflag &= B_WANTED;
304 		wakeup((caddr_t)&isaphysmemflag);
305 		if (isaphysmemunblock)
306 			(*isaphysmemunblock)();
307 	}
308 }
309