Home | History | Annotate | Line # | Download | only in isa
isadma.c revision 1.6
      1 #include <sys/param.h>
      2 #include <sys/systm.h>
      3 #include <sys/file.h>
      4 #include <sys/buf.h>
      5 #include <sys/syslog.h>
      6 #include <sys/malloc.h>
      7 #include <sys/uio.h>
      8 
      9 #include <vm/vm.h>
     10 
     11 #include <machine/pio.h>
     12 
     13 #include <i386/isa/isareg.h>
     14 #include <i386/isa/dmavar.h>
     15 #include <i386/isa/dmareg.h>
     16 
     17 /* region of physical memory known to be contiguous */
     18 vm_offset_t isaphysmem;
     19 static caddr_t dma_bounce[8];		/* XXX */
     20 static char bounced[8];		/* XXX */
     21 #define MAXDMASZ 512		/* XXX */
     22 
     23 /* high byte of address is stored in this port for i-th dma channel */
     24 static short dmapageport[8] =
     25 	{ 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
     26 
     27 /*
     28  * isa_dmacascade(): program 8237 DMA controller channel to accept
     29  * external dma control by a board.
     30  */
     31 void
     32 isa_dmacascade(chan)
     33 	int chan;
     34 {
     35 
     36 #ifdef DIAGNOSTIC
     37 	if (chan < 0 || chan > 7)
     38 		panic("isa_dmacascade: impossible request");
     39 #endif
     40 
     41 	/* set dma channel mode, and set dma channel mode */
     42 	if ((chan & 4) == 0) {
     43 		outb(DMA1_MODE, DMA37MD_CASCADE | chan);
     44 		outb(DMA1_SMSK, chan);
     45 	} else {
     46 		outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
     47 		outb(DMA2_SMSK, chan & 3);
     48 	}
     49 }
     50 
     51 /*
     52  * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
     53  * problems by using a bounce buffer.
     54  */
     55 void
     56 isa_dmastart(flags, addr, nbytes, chan)
     57 	int flags;
     58 	caddr_t addr;
     59 	vm_size_t nbytes;
     60 	int chan;
     61 {
     62 	vm_offset_t phys;
     63 	int waport;
     64 	caddr_t newaddr;
     65 
     66 #ifdef DIAGNOSTIC
     67 	if (chan < 0 || chan > 7 ||
     68 	    ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) :
     69 	    (nbytes >= (1<<16))))
     70 		panic("isa_dmastart: impossible request");
     71 #endif
     72 
     73 	if (isa_dmarangecheck(addr, nbytes, chan)) {
     74 		if (dma_bounce[chan] == 0)
     75 			dma_bounce[chan] =
     76 			    /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
     77 			    (caddr_t) isaphysmem + NBPG*chan;
     78 		bounced[chan] = 1;
     79 		newaddr = dma_bounce[chan];
     80 		*(int *) newaddr = 0;	/* XXX */
     81 		/* copy bounce buffer on write */
     82 		if ((flags & B_READ) == 0)
     83 			bcopy(addr, newaddr, nbytes);
     84 		addr = newaddr;
     85 	}
     86 
     87 	/* translate to physical */
     88 	phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
     89 
     90 	if ((chan & 4) == 0) {
     91 		/*
     92 		 * Program one of DMA channels 0..3.  These are
     93 		 * byte mode channels.
     94 		 */
     95 		/* set dma channel mode, and reset address ff */
     96 		if (flags & B_READ)
     97 			outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_WRITE);
     98 		else
     99 			outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_READ);
    100 		outb(DMA1_FFC, 0);
    101 
    102 		/* send start address */
    103 		waport =  DMA1_CHN(chan);
    104 		outb(waport, phys);
    105 		outb(waport, phys>>8);
    106 		outb(dmapageport[chan], phys>>16);
    107 
    108 		/* send count */
    109 		outb(waport + 1, --nbytes);
    110 		outb(waport + 1, nbytes>>8);
    111 
    112 		/* unmask channel */
    113 		outb(DMA1_SMSK, chan | DMA37SM_CLEAR);
    114 	} else {
    115 		/*
    116 		 * Program one of DMA channels 4..7.  These are
    117 		 * word mode channels.
    118 		 */
    119 		/* set dma channel mode, and reset address ff */
    120 		if (flags & B_READ)
    121 			outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_WRITE);
    122 		else
    123 			outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_READ);
    124 		outb(DMA2_FFC, 0);
    125 
    126 		/* send start address */
    127 		waport = DMA2_CHN(chan & 3);
    128 		outb(waport, phys>>1);
    129 		outb(waport, phys>>9);
    130 		outb(dmapageport[chan], phys>>16);
    131 
    132 		/* send count */
    133 		nbytes >>= 1;
    134 		outb(waport + 2, --nbytes);
    135 		outb(waport + 2, nbytes>>8);
    136 
    137 		/* unmask channel */
    138 		outb(DMA2_SMSK, (chan & 3) | DMA37SM_CLEAR);
    139 	}
    140 }
    141 
    142 void
    143 isa_dmaabort(chan)
    144 	int chan;
    145 {
    146 
    147 #ifdef DIAGNOSTIC
    148 	if (chan < 0 || chan > 7)
    149 		panic("isa_dmaabort: impossible request");
    150 #endif
    151 
    152 	bounced[chan] = 0;
    153 
    154 	/* mask channel */
    155 	if ((chan & 4) == 0)
    156 		outb(DMA1_SMSK, DMA37SM_SET | chan);
    157 	else
    158 		outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
    159 }
    160 
    161 void
    162 isa_dmadone(flags, addr, nbytes, chan)
    163 	int flags;
    164 	caddr_t addr;
    165 	vm_size_t nbytes;
    166 	int chan;
    167 {
    168 	u_char tc;
    169 
    170 #ifdef DIAGNOSTIC
    171 	if (chan < 0 || chan > 7)
    172 		panic("isa_dmadone: impossible request");
    173 #endif
    174 
    175 	/* check that the terminal count was reached */
    176 	if ((chan & 4) == 0)
    177 		tc = inb(DMA1_SR) & (1 << chan);
    178 	else
    179 		tc = inb(DMA2_SR) & (1 << (chan & 3));
    180 	if (tc == 0)
    181 		/* XXX probably should panic or something */
    182 		log(LOG_ERR, "dma channel %d not finished\n", chan);
    183 
    184 	/* copy bounce buffer on read */
    185 	if (bounced[chan]) {
    186 		bcopy(dma_bounce[chan], addr, nbytes);
    187 		bounced[chan] = 0;
    188 	}
    189 
    190 	/* mask channel */
    191 	if ((chan & 4) == 0)
    192 		outb(DMA1_SMSK, DMA37SM_SET | chan);
    193 	else
    194 		outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
    195 }
    196 
    197 /*
    198  * Check for problems with the address range of a DMA transfer
    199  * (non-contiguous physical pages, outside of bus address space,
    200  * crossing DMA page boundaries).
    201  * Return true if special handling needed.
    202  */
    203 int
    204 isa_dmarangecheck(va, length, chan)
    205 	vm_offset_t va;
    206 	u_long length;
    207 	int chan;
    208 {
    209 	vm_offset_t phys, priorpage = 0, endva;
    210 	u_int dma_pgmsk = (chan & 4) ?  ~(128*1024-1) : ~(64*1024-1);
    211 
    212 	endva = round_page(va + length);
    213 	for (; va < endva ; va += NBPG) {
    214 		phys = trunc_page(pmap_extract(pmap_kernel(), va));
    215 		if (phys == 0)
    216 			panic("isa_dmacheck: no physical page present");
    217 		if (phys >= (1<<24))
    218 			return 1;
    219 		if (priorpage) {
    220 			if (priorpage + NBPG != phys)
    221 				return 1;
    222 			/* check if crossing a DMA page boundary */
    223 			if ((priorpage ^ phys) & dma_pgmsk)
    224 				return 1;
    225 		}
    226 		priorpage = phys;
    227 	}
    228 	return 0;
    229 }
    230 
    231 /* head of queue waiting for physmem to become available */
    232 struct buf isa_physmemq;
    233 
    234 /* blocked waiting for resource to become free for exclusive use */
    235 static isaphysmemflag;
    236 /* if waited for and call requested when free (B_CALL) */
    237 static void (*isaphysmemunblock)(); /* needs to be a list */
    238 
    239 /*
    240  * Allocate contiguous physical memory for transfer, returning
    241  * a *virtual* address to region. May block waiting for resource.
    242  * (assumed to be called at splbio())
    243  */
    244 caddr_t
    245 isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) {
    246 
    247 	isaphysmemunblock = func;
    248 	while (isaphysmemflag & B_BUSY) {
    249 		isaphysmemflag |= B_WANTED;
    250 		sleep((caddr_t)&isaphysmemflag, PRIBIO);
    251 	}
    252 	isaphysmemflag |= B_BUSY;
    253 
    254 	return((caddr_t)isaphysmem);
    255 }
    256 
    257 /*
    258  * Free contiguous physical memory used for transfer.
    259  * (assumed to be called at splbio())
    260  */
    261 void
    262 isa_freephysmem(caddr_t va, unsigned length) {
    263 
    264 	isaphysmemflag &= ~B_BUSY;
    265 	if (isaphysmemflag & B_WANTED) {
    266 		isaphysmemflag &= B_WANTED;
    267 		wakeup((caddr_t)&isaphysmemflag);
    268 		if (isaphysmemunblock)
    269 			(*isaphysmemunblock)();
    270 	}
    271 }
    272