]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/alphapc/dma.c
merge
[plan9front.git] / sys / src / 9 / 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  */