FreeBSD/Linux Kernel Cross Reference
sys/alphapc/dma.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6
7 #include "io.h"
8
9 typedef struct DMAport DMAport;
10 typedef struct DMA DMA;
11 typedef struct DMAxfer DMAxfer;
12
13 /*
14 * state of a dma transfer
15 */
16 struct DMAxfer
17 {
18 ulong bpa; /* bounce buffer physical address */
19 void* bva; /* bounce buffer virtual address */
20 int blen; /* bounce buffer length */
21 void* va; /* virtual address destination/src */
22 long len; /* bytes to be transferred */
23 int isread;
24 };
25
26 /*
27 * the dma controllers. the first half of this structure specifies
28 * the I/O ports used by the DMA controllers.
29 */
30 struct DMAport
31 {
32 uchar addr[4]; /* current address (4 channels) */
33 uchar count[4]; /* current count (4 channels) */
34 uchar page[4]; /* page registers (4 channels) */
35 uchar cmd; /* command status register */
36 uchar req; /* request registers */
37 uchar sbm; /* single bit mask register */
38 uchar mode; /* mode register */
39 uchar cbp; /* clear byte pointer */
40 uchar mc; /* master clear */
41 uchar cmask; /* clear mask register */
42 uchar wam; /* write all mask register bit */
43 };
44
45 struct DMA
46 {
47 DMAport;
48 int shift;
49 Lock;
50 DMAxfer x[4];
51 };
52
53 DMA dma[2] = {
54 { 0x00, 0x02, 0x04, 0x06,
55 0x01, 0x03, 0x05, 0x07,
56 0x87, 0x83, 0x81, 0x82,
57 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
58 0 },
59
60 { 0xc0, 0xc4, 0xc8, 0xcc,
61 0xc2, 0xc6, 0xca, 0xce,
62 0x8f, 0x8b, 0x89, 0x8a,
63 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
64 1 },
65 };
66
67 extern int i8237dma;
68 static void* i8237bva[2];
69 static int i8237used;
70
71 /*
72 * DMA must be in the first 16MB. This gets called early by the
73 * initialisation routines of any devices which require DMA to ensure
74 * the allocated bounce buffers are below the 16MB limit.
75 */
76 void
77 _i8237alloc(void)
78 {
79 void* bva;
80
81 if(i8237dma <= 0)
82 return;
83 if(i8237dma > 2)
84 i8237dma = 2;
85
86 bva = xspanalloc(64*1024*i8237dma, BY2PG, 64*1024);
87 if(bva == nil || PADDR(bva)+64*1024*i8237dma > 16*MB){
88 /*
89 * This will panic with the current
90 * implementation of xspanalloc().
91 if(bva != nil)
92 xfree(bva);
93 */
94 return;
95 }
96
97 i8237bva[0] = bva;
98 if(i8237dma == 2)
99 i8237bva[1] = ((uchar*)i8237bva[0])+64*1024;
100 }
101
102 static void
103 dmastatus(DMA *dp, int chan, char c)
104 {
105 int a, l, s;
106
107 ilock(dp);
108 outb(dp->cbp, 0);
109 a = inb(dp->addr[chan]);
110 a |= inb(dp->addr[chan])<<8;
111 a |= inb(dp->page[chan])<<16;
112 a |= inb(0x400|dp->page[chan])<<24;
113 outb(dp->cbp, 0);
114 l = inb(dp->count[chan]);
115 l |= inb(dp->count[chan])<<8;
116 s = inb(dp->cmd);
117 iunlock(dp);
118 print("%c: addr %uX len %uX stat %uX\n", c, a, l, s);
119 }
120
121 int
122 dmainit(int chan, int maxtransfer)
123 {
124 DMA *dp;
125 DMAxfer *xp;
126 static int once;
127
128 if(once == 0){
129 if(ioalloc(0x00, 0x10, 0, "dma") < 0
130 || ioalloc(0x80, 0x10, 0, "dma") < 0
131 || ioalloc(0xd0, 0x10, 0, "dma") < 0)
132 panic("dmainit");
133 outb(dma[0].mc, 0);
134 outb(dma[1].mc, 0);
135 outb(dma[0].cmask, 0);
136 outb(dma[1].cmask, 0);
137 outb(dma[1].mode, 0xC0);
138 once = 1;
139 }
140
141 if(maxtransfer > 64*1024)
142 maxtransfer = 64*1024;
143
144 dp = &dma[(chan>>2)&1];
145 chan = chan & 3;
146 xp = &dp->x[chan];
147 if(xp->bva != nil){
148 if(xp->blen < maxtransfer)
149 return 1;
150 return 0;
151 }
152 //dmastatus(dp, chan, 'I');
153
154 if(i8237used >= i8237dma || i8237bva[i8237used] == nil){
155 print("no i8237 DMA bounce buffer < 16MB\n");
156 return 1;
157 }
158 xp->bva = i8237bva[i8237used++];
159 xp->bpa = PADDR(xp->bva);
160 xp->blen = maxtransfer;
161 xp->len = 0;
162 xp->isread = 0;
163
164 return 0;
165 }
166
167 void
168 xdmastatus(int chan)
169 {
170 DMA *dp;
171
172 dp = &dma[(chan>>2)&1];
173 chan = chan & 3;
174
175 dmastatus(dp, chan, 'X');
176 }
177
178 /*
179 * setup a dma transfer. if the destination is not in kernel
180 * memory, allocate a page for the transfer.
181 *
182 * we assume BIOS has set up the command register before we
183 * are booted.
184 *
185 * return the updated transfer length (we can't transfer across 64k
186 * boundaries)
187 */
188 long
189 dmasetup(int chan, void *va, long len, int isread)
190 {
191 DMA *dp;
192 ulong pa;
193 uchar mode;
194 DMAxfer *xp;
195
196 dp = &dma[(chan>>2)&1];
197 chan = chan & 3;
198 xp = &dp->x[chan];
199 //print("va%lux+", va);
200 #define tryPCI
201 #ifndef PCIWADDR
202 #define PCIWADDR(va) PADDR(va)
203 #endif /* PCIWADDR */
204 #ifdef notdef
205
206 /*
207 * if this isn't kernel memory or crossing 64k boundary or above 16 meg
208 * use the bounce buffer.
209 */
210 pa = PADDR(va);
211 if((((ulong)va)&0xF0000000) != KZERO
212 || (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000)
213 || pa >= 16*MB) {
214 if(xp->bva == nil)
215 return -1;
216 if(len > xp->blen)
217 len = xp->blen;
218 if(!isread)
219 memmove(xp->bva, va, len);
220 xp->va = va;
221 xp->len = len;
222 xp->isread = isread;
223 pa = xp->bpa;
224 }
225 else
226 xp->len = 0;
227 #endif /* notdef */
228 #ifdef tryISA
229 pa = ISAWADDR(va);
230 #endif /* tryISA */
231 #ifdef tryPCI
232 pa = PCIWADDR(va);
233 if((((ulong)va)&0xF0000000) != KZERO){
234 if(xp->bva == nil)
235 return -1;
236 if(len > xp->blen)
237 len = xp->blen;
238 if(!isread)
239 memmove(xp->bva, va, len);
240 xp->va = va;
241 xp->len = len;
242 xp->isread = isread;
243 pa = PCIWADDR(xp->bva);
244 }
245 else
246 xp->len = 0;
247 #endif /* tryPCI */
248
249 /*
250 * this setup must be atomic
251 */
252 mode = (isread ? 0x44 : 0x48) | chan;
253 ilock(dp);
254 outb(dp->cbp, 0); /* set count & address to their first byte */
255 outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */
256 outb(dp->addr[chan], pa>>dp->shift); /* set address */
257 outb(dp->addr[chan], pa>>(8+dp->shift));
258 outb(dp->page[chan], pa>>16);
259 #ifdef tryPCI
260 outb(0x400|dp->page[chan], pa>>24);
261 #endif /* tryPCI */
262 outb(dp->cbp, 0); /* set count & address to their first byte */
263 outb(dp->count[chan], (len>>dp->shift)-1); /* set count */
264 outb(dp->count[chan], ((len>>dp->shift)-1)>>8);
265 outb(dp->sbm, chan); /* enable the channel */
266 iunlock(dp);
267 //dmastatus(dp, chan, 'S');
268
269 return len;
270 }
271
272 int
273 dmadone(int chan)
274 {
275 DMA *dp;
276
277 dp = &dma[(chan>>2)&1];
278 chan = chan & 3;
279
280 return inb(dp->cmd) & (1<<chan);
281 }
282
283 /*
284 * this must be called after a dma has been completed.
285 *
286 * if a page has been allocated for the dma,
287 * copy the data into the actual destination
288 * and free the page.
289 */
290 void
291 dmaend(int chan)
292 {
293 DMA *dp;
294 DMAxfer *xp;
295
296 dp = &dma[(chan>>2)&1];
297 chan = chan & 3;
298
299 //dmastatus(dp, chan, 'E');
300 /*
301 * disable the channel
302 */
303 ilock(dp);
304 outb(dp->sbm, 4|chan);
305 iunlock(dp);
306
307 xp = &dp->x[chan];
308 if(xp->len == 0 || !xp->isread)
309 return;
310
311 /*
312 * copy out of temporary page
313 */
314 memmove(xp->va, xp->bva, xp->len);
315 xp->len = 0;
316 }
317
318 /*
319 int
320 dmacount(int chan)
321 {
322 int retval;
323 DMA *dp;
324
325 dp = &dma[(chan>>2)&1];
326 outb(dp->cbp, 0);
327 retval = inb(dp->count[chan]);
328 retval |= inb(dp->count[chan]) << 8;
329 return((retval<<dp->shift)+1);
330 }
331 */
Cache object: dfa1ca0b864021f5ac4635ac6bf7f1af
|