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