xref: /plan9/sys/src/9/pc/dma.c (revision 425afbab47e573297e4abb9165d4d95c66cf3758)
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
_i8237alloc(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
dmainit(int chan,int maxtransfer)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
dmasetup(int chan,void * va,long len,int isread)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
dmadone(int chan)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
dmaend(int chan)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  */
265