Home | History | Annotate | Line # | Download | only in isa
isadma.c revision 1.22
      1  1.22   mycroft /*	$NetBSD: isadma.c,v 1.22 1997/03/21 00:00:21 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.1   mycroft /*
     45   1.5   mycroft  * isa_dmacascade(): program 8237 DMA controller channel to accept
     46   1.1   mycroft  * external dma control by a board.
     47   1.1   mycroft  */
     48   1.1   mycroft void
     49   1.5   mycroft isa_dmacascade(chan)
     50   1.4   mycroft 	int chan;
     51   1.1   mycroft {
     52   1.4   mycroft 
     53  1.13   mycroft #ifdef ISADMA_DEBUG
     54   1.5   mycroft 	if (chan < 0 || chan > 7)
     55   1.5   mycroft 		panic("isa_dmacascade: impossible request");
     56   1.1   mycroft #endif
     57   1.1   mycroft 
     58   1.1   mycroft 	/* set dma channel mode, and set dma channel mode */
     59   1.1   mycroft 	if ((chan & 4) == 0) {
     60  1.15   mycroft 		outb(DMA1_MODE, chan | DMA37MD_CASCADE);
     61  1.22   mycroft 		outb(DMA1_SMSK, chan | DMA37SM_CLEAR);
     62   1.1   mycroft 	} else {
     63  1.15   mycroft 		chan &= 3;
     64  1.15   mycroft 
     65  1.15   mycroft 		outb(DMA2_MODE, chan | DMA37MD_CASCADE);
     66  1.22   mycroft 		outb(DMA2_SMSK, chan | DMA37SM_CLEAR);
     67   1.1   mycroft 	}
     68   1.1   mycroft }
     69   1.1   mycroft 
     70   1.1   mycroft /*
     71   1.5   mycroft  * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
     72   1.1   mycroft  * problems by using a bounce buffer.
     73   1.1   mycroft  */
     74   1.1   mycroft void
     75   1.5   mycroft isa_dmastart(flags, addr, nbytes, chan)
     76   1.5   mycroft 	int flags;
     77   1.1   mycroft 	caddr_t addr;
     78   1.1   mycroft 	vm_size_t nbytes;
     79   1.4   mycroft 	int chan;
     80   1.1   mycroft {
     81   1.1   mycroft 	vm_offset_t phys;
     82   1.1   mycroft 	int waport;
     83   1.1   mycroft 	caddr_t newaddr;
     84   1.1   mycroft 
     85  1.13   mycroft #ifdef ISADMA_DEBUG
     86   1.5   mycroft 	if (chan < 0 || chan > 7 ||
     87   1.5   mycroft 	    ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) :
     88   1.5   mycroft 	    (nbytes >= (1<<16))))
     89   1.5   mycroft 		panic("isa_dmastart: impossible request");
     90   1.4   mycroft #endif
     91   1.1   mycroft 
     92  1.19  christos 	if (isa_dmarangecheck((vm_offset_t) addr, nbytes, chan)) {
     93   1.5   mycroft 		if (dma_bounce[chan] == 0)
     94   1.5   mycroft 			dma_bounce[chan] =
     95   1.5   mycroft 			    /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
     96   1.5   mycroft 			    (caddr_t) isaphysmem + NBPG*chan;
     97   1.5   mycroft 		bounced[chan] = 1;
     98   1.5   mycroft 		newaddr = dma_bounce[chan];
     99   1.5   mycroft 		*(int *) newaddr = 0;	/* XXX */
    100   1.1   mycroft 		/* copy bounce buffer on write */
    101  1.15   mycroft 		if ((flags & DMAMODE_READ) == 0)
    102   1.1   mycroft 			bcopy(addr, newaddr, nbytes);
    103   1.1   mycroft 		addr = newaddr;
    104   1.1   mycroft 	}
    105   1.1   mycroft 
    106   1.1   mycroft 	/* translate to physical */
    107  1.11   mycroft 	phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
    108   1.1   mycroft 
    109  1.14   mycroft 	dma_finished &= ~(1 << chan);
    110  1.14   mycroft 
    111   1.1   mycroft 	if ((chan & 4) == 0) {
    112   1.1   mycroft 		/*
    113   1.1   mycroft 		 * Program one of DMA channels 0..3.  These are
    114   1.1   mycroft 		 * byte mode channels.
    115   1.1   mycroft 		 */
    116   1.1   mycroft 		/* set dma channel mode, and reset address ff */
    117  1.16   mycroft 		outb(DMA1_MODE, chan | dmamode[flags]);
    118   1.1   mycroft 		outb(DMA1_FFC, 0);
    119   1.1   mycroft 
    120   1.1   mycroft 		/* send start address */
    121  1.15   mycroft 		waport = DMA1_CHN(chan);
    122  1.18   mycroft 		outb(dmapageport[0][chan], phys>>16);
    123   1.1   mycroft 		outb(waport, phys);
    124   1.1   mycroft 		outb(waport, phys>>8);
    125   1.1   mycroft 
    126   1.1   mycroft 		/* send count */
    127   1.1   mycroft 		outb(waport + 1, --nbytes);
    128   1.1   mycroft 		outb(waport + 1, nbytes>>8);
    129   1.1   mycroft 
    130   1.1   mycroft 		/* unmask channel */
    131   1.5   mycroft 		outb(DMA1_SMSK, chan | DMA37SM_CLEAR);
    132   1.1   mycroft 	} else {
    133  1.15   mycroft 		chan &= 3;
    134  1.15   mycroft 
    135   1.1   mycroft 		/*
    136   1.1   mycroft 		 * Program one of DMA channels 4..7.  These are
    137   1.1   mycroft 		 * word mode channels.
    138   1.1   mycroft 		 */
    139   1.1   mycroft 		/* set dma channel mode, and reset address ff */
    140  1.16   mycroft 		outb(DMA2_MODE, chan | dmamode[flags]);
    141   1.1   mycroft 		outb(DMA2_FFC, 0);
    142   1.1   mycroft 
    143   1.1   mycroft 		/* send start address */
    144  1.15   mycroft 		waport = DMA2_CHN(chan);
    145  1.18   mycroft 		outb(dmapageport[1][chan], phys>>16);
    146  1.15   mycroft 		phys >>= 1;
    147  1.15   mycroft 		outb(waport, phys);
    148  1.15   mycroft 		outb(waport, phys>>8);
    149   1.1   mycroft 
    150   1.1   mycroft 		/* send count */
    151   1.1   mycroft 		nbytes >>= 1;
    152   1.1   mycroft 		outb(waport + 2, --nbytes);
    153   1.1   mycroft 		outb(waport + 2, nbytes>>8);
    154   1.1   mycroft 
    155   1.1   mycroft 		/* unmask channel */
    156  1.15   mycroft 		outb(DMA2_SMSK, chan | DMA37SM_CLEAR);
    157   1.1   mycroft 	}
    158   1.1   mycroft }
    159   1.1   mycroft 
    160   1.1   mycroft void
    161   1.5   mycroft isa_dmaabort(chan)
    162   1.4   mycroft 	int chan;
    163   1.1   mycroft {
    164   1.1   mycroft 
    165  1.13   mycroft #ifdef ISADMA_DEBUG
    166   1.5   mycroft 	if (chan < 0 || chan > 7)
    167   1.5   mycroft 		panic("isa_dmaabort: impossible request");
    168   1.1   mycroft #endif
    169   1.1   mycroft 
    170   1.1   mycroft 	bounced[chan] = 0;
    171   1.1   mycroft 
    172   1.1   mycroft 	/* mask channel */
    173  1.22   mycroft 	if ((chan & 4) == 0) {
    174  1.22   mycroft 		outb(DMA1_SMSK, chan | DMA37SM_SET);
    175  1.22   mycroft 	} else {
    176  1.22   mycroft 		chan &= 3;
    177  1.22   mycroft 
    178  1.22   mycroft 		outb(DMA2_SMSK, chan | DMA37SM_SET);
    179  1.22   mycroft 	}
    180  1.22   mycroft }
    181  1.22   mycroft 
    182  1.22   mycroft vm_size_t
    183  1.22   mycroft isa_dmacount(chan)
    184  1.22   mycroft 	int chan;
    185  1.22   mycroft {
    186  1.22   mycroft 	int waport;
    187  1.22   mycroft 	vm_size_t nbytes;
    188  1.22   mycroft 
    189  1.22   mycroft #ifdef ISADMA_DEBUG
    190  1.22   mycroft 	if (chan < 0 || chan > 7)
    191  1.22   mycroft 		panic("isa_dmafinished: impossible request");
    192  1.22   mycroft #endif
    193  1.22   mycroft 
    194  1.22   mycroft 	/* check that the terminal count was reached */
    195  1.22   mycroft 	if ((chan & 4) == 0) {
    196  1.22   mycroft 		/* mask channel */
    197  1.22   mycroft 		outb(DMA1_SMSK, chan | DMA37SM_SET);
    198  1.22   mycroft 		outb(DMA1_FFC, 0);
    199  1.22   mycroft 
    200  1.22   mycroft 		if (!isa_dmafinished(chan)) {
    201  1.22   mycroft 			/* read count */
    202  1.22   mycroft 			waport = DMA1_CHN(chan);
    203  1.22   mycroft 			nbytes = inb(waport + 1) + 1;
    204  1.22   mycroft 			nbytes += inb(waport + 1) << 8;
    205  1.22   mycroft 		} else
    206  1.22   mycroft 			nbytes = 0;
    207  1.22   mycroft 
    208  1.22   mycroft 		/* unmask channel */
    209  1.22   mycroft 		outb(DMA1_SMSK, chan | DMA37SM_CLEAR);
    210  1.22   mycroft 	} else {
    211  1.22   mycroft 		chan &= 3;
    212  1.22   mycroft 
    213  1.22   mycroft 		/* mask channel */
    214  1.22   mycroft 		outb(DMA2_SMSK, chan | DMA37SM_SET);
    215  1.22   mycroft 		outb(DMA2_FFC, 0);
    216  1.22   mycroft 
    217  1.22   mycroft 		if (!isa_dmafinished(chan | 4)) {
    218  1.22   mycroft 			/* read count */
    219  1.22   mycroft 			waport = DMA2_CHN(chan);
    220  1.22   mycroft 			nbytes = inb(waport + 2) + 1;
    221  1.22   mycroft 			nbytes += inb(waport + 2) << 8;
    222  1.22   mycroft 			nbytes <<= 1;
    223  1.22   mycroft 		} else
    224  1.22   mycroft 			nbytes = 0;
    225  1.22   mycroft 
    226  1.22   mycroft 		/* unmask channel */
    227  1.22   mycroft 		outb(DMA2_SMSK, chan | DMA37SM_CLEAR);
    228  1.22   mycroft 	}
    229  1.22   mycroft 
    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.14   mycroft 	if (!isa_dmafinished(chan))
    266  1.21  christos 		printf("isa_dmadone: channel %d not finished\n", chan);
    267   1.5   mycroft 
    268   1.9   mycroft 	/* mask channel */
    269  1.22   mycroft 	if ((chan & 4) == 0) {
    270  1.22   mycroft 		outb(DMA1_SMSK, chan | DMA37SM_SET);
    271  1.22   mycroft 	} else {
    272  1.22   mycroft 		chan &= 3;
    273  1.22   mycroft 
    274  1.22   mycroft 		outb(DMA2_SMSK, chan | DMA37SM_SET);
    275  1.22   mycroft 	}
    276   1.9   mycroft 
    277   1.1   mycroft 	/* copy bounce buffer on read */
    278   1.1   mycroft 	if (bounced[chan]) {
    279   1.5   mycroft 		bcopy(dma_bounce[chan], addr, nbytes);
    280   1.1   mycroft 		bounced[chan] = 0;
    281   1.1   mycroft 	}
    282   1.1   mycroft }
    283   1.1   mycroft 
    284   1.1   mycroft /*
    285   1.1   mycroft  * Check for problems with the address range of a DMA transfer
    286   1.1   mycroft  * (non-contiguous physical pages, outside of bus address space,
    287   1.1   mycroft  * crossing DMA page boundaries).
    288   1.1   mycroft  * Return true if special handling needed.
    289   1.1   mycroft  */
    290   1.1   mycroft int
    291   1.5   mycroft isa_dmarangecheck(va, length, chan)
    292   1.1   mycroft 	vm_offset_t va;
    293   1.4   mycroft 	u_long length;
    294   1.4   mycroft 	int chan;
    295   1.1   mycroft {
    296   1.1   mycroft 	vm_offset_t phys, priorpage = 0, endva;
    297   1.5   mycroft 	u_int dma_pgmsk = (chan & 4) ?  ~(128*1024-1) : ~(64*1024-1);
    298   1.1   mycroft 
    299   1.1   mycroft 	endva = round_page(va + length);
    300   1.1   mycroft 	for (; va < endva ; va += NBPG) {
    301  1.11   mycroft 		phys = trunc_page(pmap_extract(pmap_kernel(), va));
    302   1.1   mycroft 		if (phys == 0)
    303   1.5   mycroft 			panic("isa_dmacheck: no physical page present");
    304   1.5   mycroft 		if (phys >= (1<<24))
    305   1.1   mycroft 			return 1;
    306   1.1   mycroft 		if (priorpage) {
    307   1.1   mycroft 			if (priorpage + NBPG != phys)
    308   1.1   mycroft 				return 1;
    309   1.1   mycroft 			/* check if crossing a DMA page boundary */
    310   1.1   mycroft 			if ((priorpage ^ phys) & dma_pgmsk)
    311   1.1   mycroft 				return 1;
    312   1.1   mycroft 		}
    313   1.1   mycroft 		priorpage = phys;
    314   1.1   mycroft 	}
    315   1.1   mycroft 	return 0;
    316   1.5   mycroft }
    317   1.5   mycroft 
    318   1.5   mycroft /* head of queue waiting for physmem to become available */
    319   1.5   mycroft struct buf isa_physmemq;
    320   1.5   mycroft 
    321   1.5   mycroft /* blocked waiting for resource to become free for exclusive use */
    322   1.5   mycroft static isaphysmemflag;
    323   1.5   mycroft /* if waited for and call requested when free (B_CALL) */
    324  1.19  christos static void (*isaphysmemunblock) __P((void)); /* needs to be a list */
    325   1.5   mycroft 
    326   1.5   mycroft /*
    327   1.5   mycroft  * Allocate contiguous physical memory for transfer, returning
    328   1.5   mycroft  * a *virtual* address to region. May block waiting for resource.
    329   1.5   mycroft  * (assumed to be called at splbio())
    330   1.5   mycroft  */
    331   1.5   mycroft caddr_t
    332  1.19  christos isa_allocphysmem(ca, length, func)
    333  1.19  christos 	caddr_t ca;
    334  1.19  christos 	unsigned length;
    335  1.19  christos 	void (*func) __P((void));
    336  1.19  christos {
    337   1.5   mycroft 
    338   1.5   mycroft 	isaphysmemunblock = func;
    339   1.5   mycroft 	while (isaphysmemflag & B_BUSY) {
    340   1.5   mycroft 		isaphysmemflag |= B_WANTED;
    341   1.5   mycroft 		sleep((caddr_t)&isaphysmemflag, PRIBIO);
    342   1.5   mycroft 	}
    343   1.5   mycroft 	isaphysmemflag |= B_BUSY;
    344   1.5   mycroft 
    345   1.5   mycroft 	return((caddr_t)isaphysmem);
    346   1.5   mycroft }
    347   1.5   mycroft 
    348   1.5   mycroft /*
    349   1.5   mycroft  * Free contiguous physical memory used for transfer.
    350   1.5   mycroft  * (assumed to be called at splbio())
    351   1.5   mycroft  */
    352   1.5   mycroft void
    353  1.19  christos isa_freephysmem(va, length)
    354  1.19  christos 	caddr_t va;
    355  1.19  christos 	unsigned length;
    356  1.19  christos {
    357   1.5   mycroft 
    358   1.5   mycroft 	isaphysmemflag &= ~B_BUSY;
    359   1.5   mycroft 	if (isaphysmemflag & B_WANTED) {
    360   1.5   mycroft 		isaphysmemflag &= B_WANTED;
    361   1.5   mycroft 		wakeup((caddr_t)&isaphysmemflag);
    362   1.5   mycroft 		if (isaphysmemunblock)
    363   1.5   mycroft 			(*isaphysmemunblock)();
    364   1.5   mycroft 	}
    365   1.1   mycroft }
    366