]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/dma.c
Import sources from 2011-03-30 iso image - lib
[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     isread;
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, BY2PG, 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->isread = 0;
141
142         return 0;
143 }
144
145 /*
146  *  setup a dma transfer.  if the destination is not in kernel
147  *  memory, allocate a page for the transfer.
148  *
149  *  we assume BIOS has set up the command register before we
150  *  are booted.
151  *
152  *  return the updated transfer length (we can't transfer across 64k
153  *  boundaries)
154  */
155 long
156 dmasetup(int chan, void *va, long len, int isread)
157 {
158         DMA *dp;
159         ulong pa;
160         uchar mode;
161         DMAxfer *xp;
162
163         dp = &dma[(chan>>2)&1];
164         chan = chan & 3;
165         xp = &dp->x[chan];
166
167         /*
168          *  if this isn't kernel memory or crossing 64k boundary or above 16 meg
169          *  use the bounce buffer.
170          */
171         if((ulong)va < KZERO 
172         || ((pa=PADDR(va))&0xFFFF0000) != ((pa+len)&0xFFFF0000)
173         || pa >= 16*MB){
174                 if(xp->bva == nil)
175                         return -1;
176                 if(len > xp->blen)
177                         len = xp->blen;
178                 if(!isread)
179                         memmove(xp->bva, va, len);
180                 xp->va = va;
181                 xp->len = len;
182                 xp->isread = isread;
183                 pa = xp->bpa;
184         }
185         else
186                 xp->len = 0;
187
188         /*
189          * this setup must be atomic
190          */
191         ilock(dp);
192         mode = (isread ? 0x44 : 0x48) | chan;
193         outb(dp->mode, mode);   /* single mode dma (give CPU a chance at mem) */
194         outb(dp->page[chan], pa>>16);
195         outb(dp->cbp, 0);               /* set count & address to their first byte */
196         outb(dp->addr[chan], pa>>dp->shift);            /* set address */
197         outb(dp->addr[chan], pa>>(8+dp->shift));
198         outb(dp->count[chan], (len>>dp->shift)-1);              /* set count */
199         outb(dp->count[chan], ((len>>dp->shift)-1)>>8);
200         outb(dp->sbm, chan);            /* enable the channel */
201         iunlock(dp);
202
203         return len;
204 }
205
206 int
207 dmadone(int chan)
208 {
209         DMA *dp;
210
211         dp = &dma[(chan>>2)&1];
212         chan = chan & 3;
213
214         return inb(dp->cmd) & (1<<chan);
215 }
216
217 /*
218  *  this must be called after a dma has been completed.
219  *
220  *  if a page has been allocated for the dma,
221  *  copy the data into the actual destination
222  *  and free the page.
223  */
224 void
225 dmaend(int chan)
226 {
227         DMA *dp;
228         DMAxfer *xp;
229
230         dp = &dma[(chan>>2)&1];
231         chan = chan & 3;
232
233         /*
234          *  disable the channel
235          */
236         ilock(dp);
237         outb(dp->sbm, 4|chan);
238         iunlock(dp);
239
240         xp = &dp->x[chan];
241         if(xp->len == 0 || !xp->isread)
242                 return;
243
244         /*
245          *  copy out of temporary page
246          */
247         memmove(xp->va, xp->bva, xp->len);
248         xp->len = 0;
249 }
250
251 /*
252 int
253 dmacount(int chan)
254 {
255         int     retval;
256         DMA     *dp;
257  
258         dp = &dma[(chan>>2)&1];
259         outb(dp->cbp, 0);
260         retval = inb(dp->count[chan]);
261         retval |= inb(dp->count[chan]) << 8;
262         return((retval<<dp->shift)+1);
263 }
264  */