Home | History | Annotate | Line # | Download | only in isa
isadma.c revision 1.23
      1  1.23   mycroft /*	$NetBSD: isadma.c,v 1.23 1997/03/21 02:17:11 mycroft Exp $	*/
      2   1.8       cgd 
      3   1.3   mycroft #include <sys/param.h>
      4   1.3   mycroft #include <sys/systm.h>
      5   1.3   mycroft #include <sys/file.h>
      6   1.5   mycroft #include <sys/buf.h>
      7   1.3   mycroft #include <sys/syslog.h>
      8   1.5   mycroft #include <sys/malloc.h>
      9  1.19  christos #include <sys/proc.h>
     10   1.5   mycroft #include <sys/uio.h>
     11   1.5   mycroft 
     12   1.3   mycroft #include <vm/vm.h>
     13   1.3   mycroft 
     14   1.3   mycroft #include <machine/pio.h>
     15   1.3   mycroft 
     16  1.12       cgd #include <dev/isa/isareg.h>
     17  1.12       cgd #include <dev/isa/isadmavar.h>
     18  1.12       cgd #include <dev/isa/isadmareg.h>
     19   1.1   mycroft 
     20   1.1   mycroft /* region of physical memory known to be contiguous */
     21   1.5   mycroft vm_offset_t isaphysmem;
     22   1.5   mycroft static caddr_t dma_bounce[8];		/* XXX */
     23   1.5   mycroft static char bounced[8];		/* XXX */
     24   1.5   mycroft #define MAXDMASZ 512		/* XXX */
     25  1.14   mycroft static u_int8_t dma_finished;
     26   1.1   mycroft 
     27   1.1   mycroft /* high byte of address is stored in this port for i-th dma channel */
     28  1.18   mycroft static int dmapageport[2][4] = {
     29  1.18   mycroft 	{0x87, 0x83, 0x81, 0x82},
     30  1.18   mycroft 	{0x8f, 0x8b, 0x89, 0x8a}
     31  1.15   mycroft };
     32  1.15   mycroft 
     33  1.15   mycroft static u_int8_t dmamode[4] = {
     34  1.17   mycroft 	DMA37MD_READ | DMA37MD_SINGLE,
     35  1.15   mycroft 	DMA37MD_WRITE | DMA37MD_SINGLE,
     36  1.17   mycroft 	DMA37MD_READ | DMA37MD_LOOP,
     37  1.17   mycroft 	DMA37MD_WRITE | DMA37MD_LOOP
     38  1.15   mycroft };
     39   1.1   mycroft 
     40  1.19  christos int	isa_dmarangecheck	__P((vm_offset_t, u_long, int));
     41  1.19  christos caddr_t	isa_allocphysmem	__P((caddr_t, unsigned, void (*)(void)));
     42  1.19  christos void	isa_freephysmem		__P((caddr_t, unsigned));
     43  1.19  christos 
     44  1.23   mycroft inline void
     45  1.23   mycroft isa_dmaunmask(chan)
     46  1.23   mycroft 	int chan;
     47  1.23   mycroft {
     48  1.23   mycroft 	int ochan = chan & 3;
     49  1.23   mycroft 
     50  1.23   mycroft #ifdef ISADMA_DEBUG
     51  1.23   mycroft 	if (chan < 0 || chan > 7)
     52  1.23   mycroft 		panic("isa_dmacascade: impossible request");
     53  1.23   mycroft #endif
     54  1.23   mycroft 
     55  1.23   mycroft 	/* set dma channel mode, and set dma channel mode */
     56  1.23   mycroft 	if ((chan & 4) == 0)
     57  1.23   mycroft 		outb(DMA1_SMSK, ochan | DMA37SM_CLEAR);
     58  1.23   mycroft 	else
     59  1.23   mycroft 		outb(DMA2_SMSK, ochan | DMA37SM_CLEAR);
     60  1.23   mycroft }
     61  1.23   mycroft 
     62  1.23   mycroft inline void
     63  1.23   mycroft isa_dmamask(chan)
     64  1.23   mycroft 	int chan;
     65  1.23   mycroft {
     66  1.23   mycroft 	int ochan = chan & 3;
     67  1.23   mycroft 
     68  1.23   mycroft #ifdef ISADMA_DEBUG
     69  1.23   mycroft 	if (chan < 0 || chan > 7)
     70  1.23   mycroft 		panic("isa_dmacascade: impossible request");
     71  1.23   mycroft #endif
     72  1.23   mycroft 
     73  1.23   mycroft 	/* set dma channel mode, and set dma channel mode */
     74  1.23   mycroft 	if ((chan & 4) == 0) {
     75  1.23   mycroft 		outb(DMA1_SMSK, ochan | DMA37SM_SET);
     76  1.23   mycroft 		outb(DMA1_FFC, 0);
     77  1.23   mycroft 	} else {
     78  1.23   mycroft 		outb(DMA2_SMSK, ochan | DMA37SM_SET);
     79  1.23   mycroft 		outb(DMA2_FFC, 0);
     80  1.23   mycroft 	}
     81  1.23   mycroft }
     82  1.23   mycroft 
     83   1.1   mycroft /*
     84   1.5   mycroft  * isa_dmacascade(): program 8237 DMA controller channel to accept
     85   1.1   mycroft  * external dma control by a board.
     86   1.1   mycroft  */
     87   1.1   mycroft void
     88   1.5   mycroft isa_dmacascade(chan)
     89   1.4   mycroft 	int chan;
     90   1.1   mycroft {
     91  1.23   mycroft 	int ochan = chan & 3;
     92   1.4   mycroft 
     93  1.13   mycroft #ifdef ISADMA_DEBUG
     94   1.5   mycroft 	if (chan < 0 || chan > 7)
     95   1.5   mycroft 		panic("isa_dmacascade: impossible request");
     96   1.1   mycroft #endif
     97   1.1   mycroft 
     98   1.1   mycroft 	/* set dma channel mode, and set dma channel mode */
     99  1.23   mycroft 	if ((chan & 4) == 0)
    100  1.23   mycroft 		outb(DMA1_MODE, ochan | DMA37MD_CASCADE);
    101  1.23   mycroft 	else
    102  1.23   mycroft 		outb(DMA2_MODE, ochan | DMA37MD_CASCADE);
    103  1.15   mycroft 
    104  1.23   mycroft 	isa_dmaunmask(chan);
    105   1.1   mycroft }
    106   1.1   mycroft 
    107   1.1   mycroft /*
    108   1.5   mycroft  * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
    109   1.1   mycroft  * problems by using a bounce buffer.
    110   1.1   mycroft  */
    111   1.1   mycroft void
    112   1.5   mycroft isa_dmastart(flags, addr, nbytes, chan)
    113   1.5   mycroft 	int flags;
    114   1.1   mycroft 	caddr_t addr;
    115   1.1   mycroft 	vm_size_t nbytes;
    116   1.4   mycroft 	int chan;
    117   1.1   mycroft {
    118   1.1   mycroft 	vm_offset_t phys;
    119   1.1   mycroft 	int waport;
    120   1.1   mycroft 	caddr_t newaddr;
    121  1.23   mycroft 	int ochan = chan & 3;
    122   1.1   mycroft 
    123  1.13   mycroft #ifdef ISADMA_DEBUG
    124   1.5   mycroft 	if (chan < 0 || chan > 7 ||
    125   1.5   mycroft 	    ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) :
    126   1.5   mycroft 	    (nbytes >= (1<<16))))
    127   1.5   mycroft 		panic("isa_dmastart: impossible request");
    128   1.4   mycroft #endif
    129   1.1   mycroft 
    130  1.19  christos 	if (isa_dmarangecheck((vm_offset_t) addr, nbytes, chan)) {
    131   1.5   mycroft 		if (dma_bounce[chan] == 0)
    132   1.5   mycroft 			dma_bounce[chan] =
    133   1.5   mycroft 			    /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
    134   1.5   mycroft 			    (caddr_t) isaphysmem + NBPG*chan;
    135   1.5   mycroft 		bounced[chan] = 1;
    136   1.5   mycroft 		newaddr = dma_bounce[chan];
    137   1.5   mycroft 		*(int *) newaddr = 0;	/* XXX */
    138   1.1   mycroft 		/* copy bounce buffer on write */
    139  1.15   mycroft 		if ((flags & DMAMODE_READ) == 0)
    140   1.1   mycroft 			bcopy(addr, newaddr, nbytes);
    141   1.1   mycroft 		addr = newaddr;
    142   1.1   mycroft 	}
    143   1.1   mycroft 
    144   1.1   mycroft 	/* translate to physical */
    145  1.11   mycroft 	phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
    146   1.1   mycroft 
    147  1.23   mycroft 	isa_dmamask(chan);
    148  1.14   mycroft 	dma_finished &= ~(1 << chan);
    149  1.14   mycroft 
    150   1.1   mycroft 	if ((chan & 4) == 0) {
    151  1.23   mycroft 		/* set dma channel mode */
    152  1.23   mycroft 		outb(DMA1_MODE, ochan | dmamode[flags]);
    153   1.1   mycroft 
    154   1.1   mycroft 		/* send start address */
    155  1.23   mycroft 		waport = DMA1_CHN(ochan);
    156  1.23   mycroft 		outb(dmapageport[0][ochan], phys>>16);
    157   1.1   mycroft 		outb(waport, phys);
    158   1.1   mycroft 		outb(waport, phys>>8);
    159   1.1   mycroft 
    160   1.1   mycroft 		/* send count */
    161   1.1   mycroft 		outb(waport + 1, --nbytes);
    162   1.1   mycroft 		outb(waport + 1, nbytes>>8);
    163   1.1   mycroft 	} else {
    164  1.23   mycroft 		/* set dma channel mode */
    165  1.23   mycroft 		outb(DMA2_MODE, ochan | dmamode[flags]);
    166   1.1   mycroft 
    167   1.1   mycroft 		/* send start address */
    168  1.23   mycroft 		waport = DMA2_CHN(ochan);
    169  1.23   mycroft 		outb(dmapageport[1][ochan], phys>>16);
    170  1.15   mycroft 		phys >>= 1;
    171  1.15   mycroft 		outb(waport, phys);
    172  1.15   mycroft 		outb(waport, phys>>8);
    173   1.1   mycroft 
    174   1.1   mycroft 		/* send count */
    175   1.1   mycroft 		nbytes >>= 1;
    176   1.1   mycroft 		outb(waport + 2, --nbytes);
    177   1.1   mycroft 		outb(waport + 2, nbytes>>8);
    178  1.23   mycroft 	}
    179   1.1   mycroft 
    180  1.23   mycroft 	isa_dmaunmask(chan);
    181   1.1   mycroft }
    182   1.1   mycroft 
    183   1.1   mycroft void
    184   1.5   mycroft isa_dmaabort(chan)
    185   1.4   mycroft 	int chan;
    186   1.1   mycroft {
    187   1.1   mycroft 
    188  1.13   mycroft #ifdef ISADMA_DEBUG
    189   1.5   mycroft 	if (chan < 0 || chan > 7)
    190   1.5   mycroft 		panic("isa_dmaabort: impossible request");
    191   1.1   mycroft #endif
    192   1.1   mycroft 
    193  1.23   mycroft 	isa_dmamask(chan);
    194  1.23   mycroft 
    195   1.1   mycroft 	bounced[chan] = 0;
    196  1.22   mycroft }
    197  1.22   mycroft 
    198  1.22   mycroft vm_size_t
    199  1.22   mycroft isa_dmacount(chan)
    200  1.22   mycroft 	int chan;
    201  1.22   mycroft {
    202  1.22   mycroft 	int waport;
    203  1.22   mycroft 	vm_size_t nbytes;
    204  1.23   mycroft 	int ochan = chan & 3;
    205  1.22   mycroft 
    206  1.22   mycroft #ifdef ISADMA_DEBUG
    207  1.22   mycroft 	if (chan < 0 || chan > 7)
    208  1.22   mycroft 		panic("isa_dmafinished: impossible request");
    209  1.22   mycroft #endif
    210  1.22   mycroft 
    211  1.23   mycroft 	isa_dmamask(chan);
    212  1.23   mycroft 
    213  1.22   mycroft 	/* check that the terminal count was reached */
    214  1.23   mycroft 	if (!isa_dmafinished(chan)) {
    215  1.23   mycroft 		/* read count */
    216  1.23   mycroft 		if ((chan & 4) == 0) {
    217  1.23   mycroft 			waport = DMA1_CHN(ochan);
    218  1.22   mycroft 			nbytes = inb(waport + 1) + 1;
    219  1.22   mycroft 			nbytes += inb(waport + 1) << 8;
    220  1.23   mycroft 		} else {
    221  1.23   mycroft 			waport = DMA2_CHN(ochan);
    222  1.22   mycroft 			nbytes = inb(waport + 2) + 1;
    223  1.22   mycroft 			nbytes += inb(waport + 2) << 8;
    224  1.22   mycroft 			nbytes <<= 1;
    225  1.23   mycroft 		}
    226  1.23   mycroft 	} else
    227  1.23   mycroft 		nbytes = 0;
    228  1.22   mycroft 
    229  1.23   mycroft 	isa_dmaunmask(chan);
    230  1.22   mycroft 	return (nbytes);
    231   1.1   mycroft }
    232   1.1   mycroft 
    233  1.13   mycroft int
    234  1.13   mycroft isa_dmafinished(chan)
    235   1.5   mycroft 	int chan;
    236   1.1   mycroft {
    237   1.1   mycroft 
    238  1.13   mycroft #ifdef ISADMA_DEBUG
    239   1.5   mycroft 	if (chan < 0 || chan > 7)
    240  1.13   mycroft 		panic("isa_dmafinished: impossible request");
    241   1.1   mycroft #endif
    242   1.1   mycroft 
    243   1.1   mycroft 	/* check that the terminal count was reached */
    244   1.1   mycroft 	if ((chan & 4) == 0)
    245  1.14   mycroft 		dma_finished |= inb(DMA1_SR) & 0x0f;
    246   1.1   mycroft 	else
    247  1.14   mycroft 		dma_finished |= (inb(DMA2_SR) & 0x0f) << 4;
    248  1.14   mycroft 
    249  1.14   mycroft 	return ((dma_finished & (1 << chan)) != 0);
    250  1.13   mycroft }
    251  1.13   mycroft 
    252  1.13   mycroft void
    253  1.13   mycroft isa_dmadone(flags, addr, nbytes, chan)
    254  1.13   mycroft 	int flags;
    255  1.13   mycroft 	caddr_t addr;
    256  1.13   mycroft 	vm_size_t nbytes;
    257  1.13   mycroft 	int chan;
    258  1.13   mycroft {
    259  1.13   mycroft 
    260  1.13   mycroft #ifdef ISADMA_DEBUG
    261  1.13   mycroft 	if (chan < 0 || chan > 7)
    262  1.13   mycroft 		panic("isa_dmadone: impossible request");
    263  1.13   mycroft #endif
    264  1.14   mycroft 
    265  1.23   mycroft 	isa_dmamask(chan);
    266  1.23   mycroft 
    267  1.14   mycroft 	if (!isa_dmafinished(chan))
    268  1.21  christos 		printf("isa_dmadone: channel %d not finished\n", chan);
    269   1.9   mycroft 
    270   1.1   mycroft 	/* copy bounce buffer on read */
    271   1.1   mycroft 	if (bounced[chan]) {
    272   1.5   mycroft 		bcopy(dma_bounce[chan], addr, nbytes);
    273   1.1   mycroft 		bounced[chan] = 0;
    274   1.1   mycroft 	}
    275   1.1   mycroft }
    276   1.1   mycroft 
    277   1.1   mycroft /*
    278   1.1   mycroft  * Check for problems with the address range of a DMA transfer
    279   1.1   mycroft  * (non-contiguous physical pages, outside of bus address space,
    280   1.1   mycroft  * crossing DMA page boundaries).
    281   1.1   mycroft  * Return true if special handling needed.
    282   1.1   mycroft  */
    283   1.1   mycroft int
    284   1.5   mycroft isa_dmarangecheck(va, length, chan)
    285   1.1   mycroft 	vm_offset_t va;
    286   1.4   mycroft 	u_long length;
    287   1.4   mycroft 	int chan;
    288   1.1   mycroft {
    289   1.1   mycroft 	vm_offset_t phys, priorpage = 0, endva;
    290   1.5   mycroft 	u_int dma_pgmsk = (chan & 4) ?  ~(128*1024-1) : ~(64*1024-1);
    291   1.1   mycroft 
    292   1.1   mycroft 	endva = round_page(va + length);
    293   1.1   mycroft 	for (; va < endva ; va += NBPG) {
    294  1.11   mycroft 		phys = trunc_page(pmap_extract(pmap_kernel(), va));
    295   1.1   mycroft 		if (phys == 0)
    296   1.5   mycroft 			panic("isa_dmacheck: no physical page present");
    297   1.5   mycroft 		if (phys >= (1<<24))
    298   1.1   mycroft 			return 1;
    299   1.1   mycroft 		if (priorpage) {
    300   1.1   mycroft 			if (priorpage + NBPG != phys)
    301   1.1   mycroft 				return 1;
    302   1.1   mycroft 			/* check if crossing a DMA page boundary */
    303   1.1   mycroft 			if ((priorpage ^ phys) & dma_pgmsk)
    304   1.1   mycroft 				return 1;
    305   1.1   mycroft 		}
    306   1.1   mycroft 		priorpage = phys;
    307   1.1   mycroft 	}
    308   1.1   mycroft 	return 0;
    309   1.5   mycroft }
    310   1.5   mycroft 
    311   1.5   mycroft /* head of queue waiting for physmem to become available */
    312   1.5   mycroft struct buf isa_physmemq;
    313   1.5   mycroft 
    314   1.5   mycroft /* blocked waiting for resource to become free for exclusive use */
    315   1.5   mycroft static isaphysmemflag;
    316   1.5   mycroft /* if waited for and call requested when free (B_CALL) */
    317  1.19  christos static void (*isaphysmemunblock) __P((void)); /* needs to be a list */
    318   1.5   mycroft 
    319   1.5   mycroft /*
    320   1.5   mycroft  * Allocate contiguous physical memory for transfer, returning
    321   1.5   mycroft  * a *virtual* address to region. May block waiting for resource.
    322   1.5   mycroft  * (assumed to be called at splbio())
    323   1.5   mycroft  */
    324   1.5   mycroft caddr_t
    325  1.19  christos isa_allocphysmem(ca, length, func)
    326  1.19  christos 	caddr_t ca;
    327  1.19  christos 	unsigned length;
    328  1.19  christos 	void (*func) __P((void));
    329  1.19  christos {
    330   1.5   mycroft 
    331   1.5   mycroft 	isaphysmemunblock = func;
    332   1.5   mycroft 	while (isaphysmemflag & B_BUSY) {
    333   1.5   mycroft 		isaphysmemflag |= B_WANTED;
    334   1.5   mycroft 		sleep((caddr_t)&isaphysmemflag, PRIBIO);
    335   1.5   mycroft 	}
    336   1.5   mycroft 	isaphysmemflag |= B_BUSY;
    337   1.5   mycroft 
    338   1.5   mycroft 	return((caddr_t)isaphysmem);
    339   1.5   mycroft }
    340   1.5   mycroft 
    341   1.5   mycroft /*
    342   1.5   mycroft  * Free contiguous physical memory used for transfer.
    343   1.5   mycroft  * (assumed to be called at splbio())
    344   1.5   mycroft  */
    345   1.5   mycroft void
    346  1.19  christos isa_freephysmem(va, length)
    347  1.19  christos 	caddr_t va;
    348  1.19  christos 	unsigned length;
    349  1.19  christos {
    350   1.5   mycroft 
    351   1.5   mycroft 	isaphysmemflag &= ~B_BUSY;
    352   1.5   mycroft 	if (isaphysmemflag & B_WANTED) {
    353   1.5   mycroft 		isaphysmemflag &= B_WANTED;
    354   1.5   mycroft 		wakeup((caddr_t)&isaphysmemflag);
    355   1.5   mycroft 		if (isaphysmemunblock)
    356   1.5   mycroft 			(*isaphysmemunblock)();
    357   1.5   mycroft 	}
    358   1.1   mycroft }
    359