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