/* * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment * problems by using a bounce buffer. */ void isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) { vm_paddr_t phys; int waport; caddr_t newaddr; int dma_range_checked; dma_range_checked = isa_dmarangecheck(addr, nbytes, chan); #ifdef DIAGNOSTIC if (chan & ~VALID_DMA_MASK) panic("isa_dmastart: channel out of range"); if ((chan < 4 && nbytes > (1<<16)) || (chan >= 4 && (nbytes > (1<<17) || (uintptr_t)addr & 1))) panic("isa_dmastart: impossible request"); mtx_lock(&isa_dma_lock); if ((dma_inuse & (1 << chan)) == 0) printf("isa_dmastart: channel %d not acquired\n", chan); #else mtx_lock(&isa_dma_lock); #endif #if 0 /* * XXX This should be checked, but drivers like ad1848 only call * isa_dmastart() once because they use Auto DMA mode. If we * leave this in, drivers that do this will print this continuously. */ if (dma_busy & (1 << chan)) printf("isa_dmastart: channel %d busy\n", chan); #endif dma_busy |= (1 << chan); if (dma_range_checked) { if (dma_bouncebuf[chan] == NULL || dma_bouncebufsize[chan] < nbytes) panic("isa_dmastart: bad bounce buffer"); dma_bounced |= (1 << chan); newaddr = dma_bouncebuf[chan]; /* copy bounce buffer on write */ if (!(flags & ISADMA_READ)) bcopy(addr, newaddr, nbytes); addr = newaddr; } /* translate to physical */ phys = pmap_extract(kernel_pmap, (vm_offset_t)addr); if (flags & ISADMA_RAW) { dma_auto_mode |= (1 << chan); } else { dma_auto_mode &= ~(1 << chan); } 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 ISADMA_RAW flag is set, then use autoinitialise mode */ if (flags & ISADMA_RAW) { if (flags & ISADMA_READ) outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); else outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); } else if (flags & ISADMA_READ) outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); else outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); 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); } else { /* * Program one of DMA channels 4..7. These are * word mode channels. */ /* set dma channel mode, and reset address ff */ /* If ISADMA_RAW flag is set, then use autoinitialise mode */ if (flags & ISADMA_RAW) {
/* * _isa_dmastart(): program 8237 DMA controller channel and set it * in motion. */ int _isa_dmastart(struct isa_dma_state *ids, int chan, void *addr, bus_size_t nbytes, struct proc *p, int flags, int busdmaflags) { bus_dmamap_t dmam; bus_addr_t dmaaddr; int waport; int ochan = chan & 3; int error; if (chan < 0 || chan > 7) { printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); goto lose; } #ifdef ISADMA_DEBUG printf("_isa_dmastart: drq %d, addr %p, nbytes 0x%lx, p %p, " "flags 0x%x, dmaflags 0x%x\n", chan, addr, (u_long)nbytes, p, flags, busdmaflags); #endif if (ISA_DMA_DRQ_ISFREE(ids, chan)) { printf("%s: dma start on free channel %d\n", device_xname(ids->ids_dev), chan); goto lose; } if (chan & 4) { if (nbytes > (1 << 17) || nbytes & 1 || (u_long)addr & 1) { printf("%s: drq %d, nbytes 0x%lx, addr %p\n", device_xname(ids->ids_dev), chan, (unsigned long) nbytes, addr); goto lose; } } else { if (nbytes > (1 << 16)) { printf("%s: drq %d, nbytes 0x%lx\n", device_xname(ids->ids_dev), chan, (unsigned long) nbytes); goto lose; } } dmam = ids->ids_dmamaps[chan]; if (dmam == NULL) panic("_isa_dmastart: no DMA map for chan %d", chan); error = bus_dmamap_load(ids->ids_dmat, dmam, addr, nbytes, p, busdmaflags | ((flags & DMAMODE_READ) ? BUS_DMA_READ : BUS_DMA_WRITE)); if (error) return (error); #ifdef ISADMA_DEBUG __asm(".globl isa_dmastart_afterload ; isa_dmastart_afterload:"); #endif if (flags & DMAMODE_READ) { bus_dmamap_sync(ids->ids_dmat, dmam, 0, dmam->dm_mapsize, BUS_DMASYNC_PREREAD); ids->ids_dmareads |= (1 << chan); } else { bus_dmamap_sync(ids->ids_dmat, dmam, 0, dmam->dm_mapsize, BUS_DMASYNC_PREWRITE); ids->ids_dmareads &= ~(1 << chan); } dmaaddr = dmam->dm_segs[0].ds_addr; #ifdef ISADMA_DEBUG printf(" dmaaddr %#" PRIxPADDR "\n", dmaaddr); __asm(".globl isa_dmastart_aftersync ; isa_dmastart_aftersync:"); #endif ids->ids_dmalength[chan] = nbytes; _isa_dmamask(ids, chan); ids->ids_dmafinished &= ~(1 << chan); if ((chan & 4) == 0) { /* set dma channel mode */ bus_space_write_1(ids->ids_bst, ids->ids_dma1h, DMA1_MODE, ochan | dmamode[flags]); /* send start address */ waport = DMA1_CHN(ochan); bus_space_write_1(ids->ids_bst, ids->ids_dmapgh, dmapageport[0][ochan], (dmaaddr >> 16) & 0xff); bus_space_write_1(ids->ids_bst, ids->ids_dma1h, waport, dmaaddr & 0xff); bus_space_write_1(ids->ids_bst, ids->ids_dma1h, waport, (dmaaddr >> 8) & 0xff); /* send count */ bus_space_write_1(ids->ids_bst, ids->ids_dma1h, waport + 1, (--nbytes) & 0xff); bus_space_write_1(ids->ids_bst, ids->ids_dma1h, waport + 1, (nbytes >> 8) & 0xff); } else {