274 lines
6.2 KiB
C
274 lines
6.2 KiB
C
/* $NetBSD: isadma.c,v 1.8 1994/10/27 04:17:07 cgd Exp $ */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/file.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/uio.h>
|
|
|
|
#include <vm/vm.h>
|
|
|
|
#include <machine/pio.h>
|
|
|
|
#include <i386/isa/isareg.h>
|
|
#include <i386/isa/dmavar.h>
|
|
#include <i386/isa/dmareg.h>
|
|
|
|
/* region of physical memory known to be contiguous */
|
|
vm_offset_t isaphysmem;
|
|
static caddr_t dma_bounce[8]; /* XXX */
|
|
static char bounced[8]; /* XXX */
|
|
#define MAXDMASZ 512 /* XXX */
|
|
|
|
/* high byte of address is stored in this port for i-th dma channel */
|
|
static short dmapageport[8] =
|
|
{ 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
|
|
|
|
/*
|
|
* isa_dmacascade(): program 8237 DMA controller channel to accept
|
|
* external dma control by a board.
|
|
*/
|
|
void
|
|
isa_dmacascade(chan)
|
|
int chan;
|
|
{
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (chan < 0 || chan > 7)
|
|
panic("isa_dmacascade: impossible request");
|
|
#endif
|
|
|
|
/* set dma channel mode, and set dma channel mode */
|
|
if ((chan & 4) == 0) {
|
|
outb(DMA1_MODE, DMA37MD_CASCADE | chan);
|
|
outb(DMA1_SMSK, chan);
|
|
} else {
|
|
outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
|
|
outb(DMA2_SMSK, chan & 3);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
|
|
* problems by using a bounce buffer.
|
|
*/
|
|
void
|
|
isa_dmastart(flags, addr, nbytes, chan)
|
|
int flags;
|
|
caddr_t addr;
|
|
vm_size_t nbytes;
|
|
int chan;
|
|
{
|
|
vm_offset_t phys;
|
|
int waport;
|
|
caddr_t newaddr;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (chan < 0 || chan > 7 ||
|
|
((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) :
|
|
(nbytes >= (1<<16))))
|
|
panic("isa_dmastart: impossible request");
|
|
#endif
|
|
|
|
if (isa_dmarangecheck(addr, nbytes, chan)) {
|
|
if (dma_bounce[chan] == 0)
|
|
dma_bounce[chan] =
|
|
/*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
|
|
(caddr_t) isaphysmem + NBPG*chan;
|
|
bounced[chan] = 1;
|
|
newaddr = dma_bounce[chan];
|
|
*(int *) newaddr = 0; /* XXX */
|
|
/* copy bounce buffer on write */
|
|
if ((flags & B_READ) == 0)
|
|
bcopy(addr, newaddr, nbytes);
|
|
addr = newaddr;
|
|
}
|
|
|
|
/* translate to physical */
|
|
phys = pmap_extract(kernel_pmap, (vm_offset_t)addr);
|
|
|
|
if ((chan & 4) == 0) {
|
|
/*
|
|
* Program one of DMA channels 0..3. These are
|
|
* byte mode channels.
|
|
*/
|
|
/* set dma channel mode, and reset address ff */
|
|
if (flags & B_READ)
|
|
outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_WRITE);
|
|
else
|
|
outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_READ);
|
|
outb(DMA1_FFC, 0);
|
|
|
|
/* send start address */
|
|
waport = DMA1_CHN(chan);
|
|
outb(waport, phys);
|
|
outb(waport, phys>>8);
|
|
outb(dmapageport[chan], phys>>16);
|
|
|
|
/* send count */
|
|
outb(waport + 1, --nbytes);
|
|
outb(waport + 1, nbytes>>8);
|
|
|
|
/* unmask channel */
|
|
outb(DMA1_SMSK, chan | DMA37SM_CLEAR);
|
|
} else {
|
|
/*
|
|
* Program one of DMA channels 4..7. These are
|
|
* word mode channels.
|
|
*/
|
|
/* set dma channel mode, and reset address ff */
|
|
if (flags & B_READ)
|
|
outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_WRITE);
|
|
else
|
|
outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_READ);
|
|
outb(DMA2_FFC, 0);
|
|
|
|
/* send start address */
|
|
waport = DMA2_CHN(chan & 3);
|
|
outb(waport, phys>>1);
|
|
outb(waport, phys>>9);
|
|
outb(dmapageport[chan], phys>>16);
|
|
|
|
/* send count */
|
|
nbytes >>= 1;
|
|
outb(waport + 2, --nbytes);
|
|
outb(waport + 2, nbytes>>8);
|
|
|
|
/* unmask channel */
|
|
outb(DMA2_SMSK, (chan & 3) | DMA37SM_CLEAR);
|
|
}
|
|
}
|
|
|
|
void
|
|
isa_dmaabort(chan)
|
|
int chan;
|
|
{
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (chan < 0 || chan > 7)
|
|
panic("isa_dmaabort: impossible request");
|
|
#endif
|
|
|
|
bounced[chan] = 0;
|
|
|
|
/* mask channel */
|
|
if ((chan & 4) == 0)
|
|
outb(DMA1_SMSK, DMA37SM_SET | chan);
|
|
else
|
|
outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
|
|
}
|
|
|
|
void
|
|
isa_dmadone(flags, addr, nbytes, chan)
|
|
int flags;
|
|
caddr_t addr;
|
|
vm_size_t nbytes;
|
|
int chan;
|
|
{
|
|
u_char tc;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (chan < 0 || chan > 7)
|
|
panic("isa_dmadone: impossible request");
|
|
#endif
|
|
|
|
/* check that the terminal count was reached */
|
|
if ((chan & 4) == 0)
|
|
tc = inb(DMA1_SR) & (1 << chan);
|
|
else
|
|
tc = inb(DMA2_SR) & (1 << (chan & 3));
|
|
if (tc == 0)
|
|
/* XXX probably should panic or something */
|
|
log(LOG_ERR, "dma channel %d not finished\n", chan);
|
|
|
|
/* copy bounce buffer on read */
|
|
if (bounced[chan]) {
|
|
bcopy(dma_bounce[chan], addr, nbytes);
|
|
bounced[chan] = 0;
|
|
}
|
|
|
|
/* mask channel */
|
|
if ((chan & 4) == 0)
|
|
outb(DMA1_SMSK, DMA37SM_SET | chan);
|
|
else
|
|
outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
|
|
}
|
|
|
|
/*
|
|
* Check for problems with the address range of a DMA transfer
|
|
* (non-contiguous physical pages, outside of bus address space,
|
|
* crossing DMA page boundaries).
|
|
* Return true if special handling needed.
|
|
*/
|
|
int
|
|
isa_dmarangecheck(va, length, chan)
|
|
vm_offset_t va;
|
|
u_long length;
|
|
int chan;
|
|
{
|
|
vm_offset_t phys, priorpage = 0, endva;
|
|
u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1);
|
|
|
|
endva = round_page(va + length);
|
|
for (; va < endva ; va += NBPG) {
|
|
phys = trunc_page(pmap_extract(kernel_pmap, va));
|
|
if (phys == 0)
|
|
panic("isa_dmacheck: no physical page present");
|
|
if (phys >= (1<<24))
|
|
return 1;
|
|
if (priorpage) {
|
|
if (priorpage + NBPG != phys)
|
|
return 1;
|
|
/* check if crossing a DMA page boundary */
|
|
if ((priorpage ^ phys) & dma_pgmsk)
|
|
return 1;
|
|
}
|
|
priorpage = phys;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* head of queue waiting for physmem to become available */
|
|
struct buf isa_physmemq;
|
|
|
|
/* blocked waiting for resource to become free for exclusive use */
|
|
static isaphysmemflag;
|
|
/* if waited for and call requested when free (B_CALL) */
|
|
static void (*isaphysmemunblock)(); /* needs to be a list */
|
|
|
|
/*
|
|
* Allocate contiguous physical memory for transfer, returning
|
|
* a *virtual* address to region. May block waiting for resource.
|
|
* (assumed to be called at splbio())
|
|
*/
|
|
caddr_t
|
|
isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) {
|
|
|
|
isaphysmemunblock = func;
|
|
while (isaphysmemflag & B_BUSY) {
|
|
isaphysmemflag |= B_WANTED;
|
|
sleep((caddr_t)&isaphysmemflag, PRIBIO);
|
|
}
|
|
isaphysmemflag |= B_BUSY;
|
|
|
|
return((caddr_t)isaphysmem);
|
|
}
|
|
|
|
/*
|
|
* Free contiguous physical memory used for transfer.
|
|
* (assumed to be called at splbio())
|
|
*/
|
|
void
|
|
isa_freephysmem(caddr_t va, unsigned length) {
|
|
|
|
isaphysmemflag &= ~B_BUSY;
|
|
if (isaphysmemflag & B_WANTED) {
|
|
isaphysmemflag &= B_WANTED;
|
|
wakeup((caddr_t)&isaphysmemflag);
|
|
if (isaphysmemunblock)
|
|
(*isaphysmemunblock)();
|
|
}
|
|
}
|