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