xref: /freebsd/sys/x86/isa/isa_dma.c (revision 29363fb4)
132580301SAttilio Rao /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
432580301SAttilio Rao  * Copyright (c) 1991 The Regents of the University of California.
532580301SAttilio Rao  * All rights reserved.
632580301SAttilio Rao  *
732580301SAttilio Rao  * This code is derived from software contributed to Berkeley by
832580301SAttilio Rao  * William Jolitz.
932580301SAttilio Rao  *
1032580301SAttilio Rao  * Redistribution and use in source and binary forms, with or without
1132580301SAttilio Rao  * modification, are permitted provided that the following conditions
1232580301SAttilio Rao  * are met:
1332580301SAttilio Rao  * 1. Redistributions of source code must retain the above copyright
1432580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer.
1532580301SAttilio Rao  * 2. Redistributions in binary form must reproduce the above copyright
1632580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer in the
1732580301SAttilio Rao  *    documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
1932580301SAttilio Rao  *    may be used to endorse or promote products derived from this software
2032580301SAttilio Rao  *    without specific prior written permission.
2132580301SAttilio Rao  *
2232580301SAttilio Rao  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2332580301SAttilio Rao  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2432580301SAttilio Rao  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2532580301SAttilio Rao  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2632580301SAttilio Rao  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2732580301SAttilio Rao  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2832580301SAttilio Rao  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2932580301SAttilio Rao  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3032580301SAttilio Rao  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3132580301SAttilio Rao  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3232580301SAttilio Rao  * SUCH DAMAGE.
3332580301SAttilio Rao  */
3432580301SAttilio Rao 
3532580301SAttilio Rao #include <sys/cdefs.h>
3632580301SAttilio Rao /*
3732580301SAttilio Rao  * code to manage AT bus
3832580301SAttilio Rao  *
3932580301SAttilio Rao  * 92/08/18  Frank P. MacLachlan (fpm@crash.cts.com):
4032580301SAttilio Rao  * Fixed uninitialized variable problem and added code to deal
4132580301SAttilio Rao  * with DMA page boundaries in isa_dmarangecheck().  Fixed word
4232580301SAttilio Rao  * mode DMA count compution and reorganized DMA setup code in
4332580301SAttilio Rao  * isa_dmastart()
4432580301SAttilio Rao  */
4532580301SAttilio Rao 
4632580301SAttilio Rao #include <sys/param.h>
4732580301SAttilio Rao #include <sys/systm.h>
4832580301SAttilio Rao #include <sys/bus.h>
4932580301SAttilio Rao #include <sys/kernel.h>
5032580301SAttilio Rao #include <sys/malloc.h>
5132580301SAttilio Rao #include <sys/lock.h>
5232580301SAttilio Rao #include <sys/proc.h>
5332580301SAttilio Rao #include <sys/mutex.h>
5432580301SAttilio Rao #include <sys/module.h>
5532580301SAttilio Rao #include <vm/vm.h>
5632580301SAttilio Rao #include <vm/vm_param.h>
5732580301SAttilio Rao #include <vm/pmap.h>
5832580301SAttilio Rao #include <isa/isareg.h>
5932580301SAttilio Rao #include <isa/isavar.h>
6032580301SAttilio Rao #include <isa/isa_dmareg.h>
6132580301SAttilio Rao 
62f79309d2SWarner Losh #define	ISARAM_END	0x1000000
6332580301SAttilio Rao 
6432580301SAttilio Rao static int isa_dmarangecheck(caddr_t va, u_int length, int chan);
6532580301SAttilio Rao 
6632580301SAttilio Rao static caddr_t	dma_bouncebuf[8];
6732580301SAttilio Rao static u_int	dma_bouncebufsize[8];
6832580301SAttilio Rao static u_int8_t	dma_bounced = 0;
6932580301SAttilio Rao static u_int8_t	dma_busy = 0;		/* Used in isa_dmastart() */
7032580301SAttilio Rao static u_int8_t	dma_inuse = 0;		/* User for acquire/release */
7132580301SAttilio Rao static u_int8_t dma_auto_mode = 0;
7232580301SAttilio Rao static struct mtx isa_dma_lock;
7332580301SAttilio Rao MTX_SYSINIT(isa_dma_lock, &isa_dma_lock, "isa DMA lock", MTX_DEF);
7432580301SAttilio Rao 
7532580301SAttilio Rao #define VALID_DMA_MASK (7)
7632580301SAttilio Rao 
7732580301SAttilio Rao /* high byte of address is stored in this port for i-th dma channel */
7832580301SAttilio Rao static int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
7932580301SAttilio Rao 
8032580301SAttilio Rao /*
8132580301SAttilio Rao  * Setup a DMA channel's bounce buffer.
8232580301SAttilio Rao  */
8332580301SAttilio Rao int
isa_dma_init(int chan,u_int bouncebufsize,int flag)8432580301SAttilio Rao isa_dma_init(int chan, u_int bouncebufsize, int flag)
8532580301SAttilio Rao {
8632580301SAttilio Rao 	void *buf;
8732580301SAttilio Rao 	int contig;
8832580301SAttilio Rao 
8932580301SAttilio Rao #ifdef DIAGNOSTIC
9032580301SAttilio Rao 	if (chan & ~VALID_DMA_MASK)
9132580301SAttilio Rao 		panic("isa_dma_init: channel out of range");
9232580301SAttilio Rao #endif
9332580301SAttilio Rao 
9432580301SAttilio Rao 	/* Try malloc() first.  It works better if it works. */
9532580301SAttilio Rao 	buf = malloc(bouncebufsize, M_DEVBUF, flag);
9632580301SAttilio Rao 	if (buf != NULL) {
9732580301SAttilio Rao 		if (isa_dmarangecheck(buf, bouncebufsize, chan) != 0) {
9832580301SAttilio Rao 			free(buf, M_DEVBUF);
9932580301SAttilio Rao 			buf = NULL;
10032580301SAttilio Rao 		}
10132580301SAttilio Rao 		contig = 0;
10232580301SAttilio Rao 	}
10332580301SAttilio Rao 
10432580301SAttilio Rao 	if (buf == NULL) {
10532580301SAttilio Rao 		buf = contigmalloc(bouncebufsize, M_DEVBUF, flag, 0ul, 0xfffffful,
10632580301SAttilio Rao 			   1ul, chan & 4 ? 0x20000ul : 0x10000ul);
10732580301SAttilio Rao 		contig = 1;
10832580301SAttilio Rao 	}
10932580301SAttilio Rao 
11032580301SAttilio Rao 	if (buf == NULL)
11132580301SAttilio Rao 		return (ENOMEM);
11232580301SAttilio Rao 
11332580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
11432580301SAttilio Rao 	/*
11532580301SAttilio Rao 	 * If a DMA channel is shared, both drivers have to call isa_dma_init
11632580301SAttilio Rao 	 * since they don't know that the other driver will do it.
11732580301SAttilio Rao 	 * Just return if we're already set up good.
11832580301SAttilio Rao 	 * XXX: this only works if they agree on the bouncebuf size.  This
11932580301SAttilio Rao 	 * XXX: is typically the case since they are multiple instances of
12032580301SAttilio Rao 	 * XXX: the same driver.
12132580301SAttilio Rao 	 */
12232580301SAttilio Rao 	if (dma_bouncebuf[chan] != NULL) {
12332580301SAttilio Rao 		if (contig)
12432580301SAttilio Rao 			contigfree(buf, bouncebufsize, M_DEVBUF);
12532580301SAttilio Rao 		else
12632580301SAttilio Rao 			free(buf, M_DEVBUF);
12732580301SAttilio Rao 		mtx_unlock(&isa_dma_lock);
12832580301SAttilio Rao 		return (0);
12932580301SAttilio Rao 	}
13032580301SAttilio Rao 
13132580301SAttilio Rao 	dma_bouncebufsize[chan] = bouncebufsize;
13232580301SAttilio Rao 	dma_bouncebuf[chan] = buf;
13332580301SAttilio Rao 
13432580301SAttilio Rao 	mtx_unlock(&isa_dma_lock);
13532580301SAttilio Rao 
13632580301SAttilio Rao 	return (0);
13732580301SAttilio Rao }
13832580301SAttilio Rao 
13932580301SAttilio Rao /*
14032580301SAttilio Rao  * Register a DMA channel's usage.  Usually called from a device driver
14132580301SAttilio Rao  * in open() or during its initialization.
14232580301SAttilio Rao  */
14332580301SAttilio Rao int
isa_dma_acquire(int chan)1444e78ff70SEd Maste isa_dma_acquire(int chan)
14532580301SAttilio Rao {
14632580301SAttilio Rao #ifdef DIAGNOSTIC
14732580301SAttilio Rao 	if (chan & ~VALID_DMA_MASK)
14832580301SAttilio Rao 		panic("isa_dma_acquire: channel out of range");
14932580301SAttilio Rao #endif
15032580301SAttilio Rao 
15132580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
15232580301SAttilio Rao 	if (dma_inuse & (1 << chan)) {
15332580301SAttilio Rao 		printf("isa_dma_acquire: channel %d already in use\n", chan);
15432580301SAttilio Rao 		mtx_unlock(&isa_dma_lock);
15532580301SAttilio Rao 		return (EBUSY);
15632580301SAttilio Rao 	}
15732580301SAttilio Rao 	dma_inuse |= (1 << chan);
15832580301SAttilio Rao 	dma_auto_mode &= ~(1 << chan);
15932580301SAttilio Rao 	mtx_unlock(&isa_dma_lock);
16032580301SAttilio Rao 
16132580301SAttilio Rao 	return (0);
16232580301SAttilio Rao }
16332580301SAttilio Rao 
16432580301SAttilio Rao /*
16532580301SAttilio Rao  * Unregister a DMA channel's usage.  Usually called from a device driver
16632580301SAttilio Rao  * during close() or during its shutdown.
16732580301SAttilio Rao  */
16832580301SAttilio Rao void
isa_dma_release(int chan)1694e78ff70SEd Maste isa_dma_release(int chan)
17032580301SAttilio Rao {
17132580301SAttilio Rao #ifdef DIAGNOSTIC
17232580301SAttilio Rao 	if (chan & ~VALID_DMA_MASK)
17332580301SAttilio Rao 		panic("isa_dma_release: channel out of range");
17432580301SAttilio Rao 
17532580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
17632580301SAttilio Rao 	if ((dma_inuse & (1 << chan)) == 0)
17732580301SAttilio Rao 		printf("isa_dma_release: channel %d not in use\n", chan);
17832580301SAttilio Rao #else
17932580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
18032580301SAttilio Rao #endif
18132580301SAttilio Rao 
18232580301SAttilio Rao 	if (dma_busy & (1 << chan)) {
18332580301SAttilio Rao 		dma_busy &= ~(1 << chan);
18432580301SAttilio Rao 		/*
18532580301SAttilio Rao 		 * XXX We should also do "dma_bounced &= (1 << chan);"
18632580301SAttilio Rao 		 * because we are acting on behalf of isa_dmadone() which
18732580301SAttilio Rao 		 * was not called to end the last DMA operation.  This does
18832580301SAttilio Rao 		 * not matter now, but it may in the future.
18932580301SAttilio Rao 		 */
19032580301SAttilio Rao 	}
19132580301SAttilio Rao 
19232580301SAttilio Rao 	dma_inuse &= ~(1 << chan);
19332580301SAttilio Rao 	dma_auto_mode &= ~(1 << chan);
19432580301SAttilio Rao 
19532580301SAttilio Rao 	mtx_unlock(&isa_dma_lock);
19632580301SAttilio Rao }
19732580301SAttilio Rao 
19832580301SAttilio Rao /*
19932580301SAttilio Rao  * isa_dmacascade(): program 8237 DMA controller channel to accept
20032580301SAttilio Rao  * external dma control by a board.
20132580301SAttilio Rao  */
20232580301SAttilio Rao void
isa_dmacascade(int chan)2034e78ff70SEd Maste isa_dmacascade(int chan)
20432580301SAttilio Rao {
20532580301SAttilio Rao #ifdef DIAGNOSTIC
20632580301SAttilio Rao 	if (chan & ~VALID_DMA_MASK)
20732580301SAttilio Rao 		panic("isa_dmacascade: channel out of range");
20832580301SAttilio Rao #endif
20932580301SAttilio Rao 
21032580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
21132580301SAttilio Rao 	/* set dma channel mode, and set dma channel mode */
21232580301SAttilio Rao 	if ((chan & 4) == 0) {
21332580301SAttilio Rao 		outb(DMA1_MODE, DMA37MD_CASCADE | chan);
21432580301SAttilio Rao 		outb(DMA1_SMSK, chan);
21532580301SAttilio Rao 	} else {
21632580301SAttilio Rao 		outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
21732580301SAttilio Rao 		outb(DMA2_SMSK, chan & 3);
21832580301SAttilio Rao 	}
21932580301SAttilio Rao 	mtx_unlock(&isa_dma_lock);
22032580301SAttilio Rao }
22132580301SAttilio Rao 
22232580301SAttilio Rao /*
22332580301SAttilio Rao  * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
22432580301SAttilio Rao  * problems by using a bounce buffer.
22532580301SAttilio Rao  */
22632580301SAttilio Rao void
isa_dmastart(int flags,caddr_t addr,u_int nbytes,int chan)22732580301SAttilio Rao isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan)
22832580301SAttilio Rao {
22932580301SAttilio Rao 	vm_paddr_t phys;
23032580301SAttilio Rao 	int waport;
23132580301SAttilio Rao 	caddr_t newaddr;
23232580301SAttilio Rao 	int dma_range_checked;
23332580301SAttilio Rao 
23432580301SAttilio Rao 	dma_range_checked = isa_dmarangecheck(addr, nbytes, chan);
23532580301SAttilio Rao 
23632580301SAttilio Rao #ifdef DIAGNOSTIC
23732580301SAttilio Rao 	if (chan & ~VALID_DMA_MASK)
23832580301SAttilio Rao 		panic("isa_dmastart: channel out of range");
23932580301SAttilio Rao 
24032580301SAttilio Rao 	if ((chan < 4 && nbytes > (1<<16))
24132580301SAttilio Rao 	    || (chan >= 4 && (nbytes > (1<<17) || (uintptr_t)addr & 1)))
24232580301SAttilio Rao 		panic("isa_dmastart: impossible request");
24332580301SAttilio Rao 
24432580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
24532580301SAttilio Rao 	if ((dma_inuse & (1 << chan)) == 0)
24632580301SAttilio Rao 		printf("isa_dmastart: channel %d not acquired\n", chan);
24732580301SAttilio Rao #else
24832580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
24932580301SAttilio Rao #endif
25032580301SAttilio Rao 
25132580301SAttilio Rao #if 0
25232580301SAttilio Rao 	/*
25332580301SAttilio Rao 	 * XXX This should be checked, but drivers like ad1848 only call
25432580301SAttilio Rao 	 * isa_dmastart() once because they use Auto DMA mode.  If we
25532580301SAttilio Rao 	 * leave this in, drivers that do this will print this continuously.
25632580301SAttilio Rao 	 */
25732580301SAttilio Rao 	if (dma_busy & (1 << chan))
25832580301SAttilio Rao 		printf("isa_dmastart: channel %d busy\n", chan);
25932580301SAttilio Rao #endif
26032580301SAttilio Rao 
26132580301SAttilio Rao 	dma_busy |= (1 << chan);
26232580301SAttilio Rao 
26332580301SAttilio Rao 	if (dma_range_checked) {
26432580301SAttilio Rao 		if (dma_bouncebuf[chan] == NULL
26532580301SAttilio Rao 		    || dma_bouncebufsize[chan] < nbytes)
26632580301SAttilio Rao 			panic("isa_dmastart: bad bounce buffer");
26732580301SAttilio Rao 		dma_bounced |= (1 << chan);
26832580301SAttilio Rao 		newaddr = dma_bouncebuf[chan];
26932580301SAttilio Rao 
27032580301SAttilio Rao 		/* copy bounce buffer on write */
27132580301SAttilio Rao 		if (!(flags & ISADMA_READ))
27232580301SAttilio Rao 			bcopy(addr, newaddr, nbytes);
27332580301SAttilio Rao 		addr = newaddr;
27432580301SAttilio Rao 	}
27532580301SAttilio Rao 
27646092aeeSJohn Baldwin 	/* translate to physical */
27746092aeeSJohn Baldwin 	phys = pmap_extract(kernel_pmap, (vm_offset_t)addr);
27846092aeeSJohn Baldwin 
27932580301SAttilio Rao 	if (flags & ISADMA_RAW) {
28032580301SAttilio Rao 	    dma_auto_mode |= (1 << chan);
28132580301SAttilio Rao 	} else {
28232580301SAttilio Rao 	    dma_auto_mode &= ~(1 << chan);
28332580301SAttilio Rao 	}
28432580301SAttilio Rao 
28532580301SAttilio Rao 	if ((chan & 4) == 0) {
28632580301SAttilio Rao 		/*
28732580301SAttilio Rao 		 * Program one of DMA channels 0..3.  These are
28832580301SAttilio Rao 		 * byte mode channels.
28932580301SAttilio Rao 		 */
29032580301SAttilio Rao 		/* set dma channel mode, and reset address ff */
29132580301SAttilio Rao 
29232580301SAttilio Rao 		/* If ISADMA_RAW flag is set, then use autoinitialise mode */
29332580301SAttilio Rao 		if (flags & ISADMA_RAW) {
29432580301SAttilio Rao 		  if (flags & ISADMA_READ)
29532580301SAttilio Rao 			outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan);
29632580301SAttilio Rao 		  else
29732580301SAttilio Rao 			outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan);
29832580301SAttilio Rao 		}
29932580301SAttilio Rao 		else
30032580301SAttilio Rao 		if (flags & ISADMA_READ)
30132580301SAttilio Rao 			outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
30232580301SAttilio Rao 		else
30332580301SAttilio Rao 			outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
30432580301SAttilio Rao 		outb(DMA1_FFC, 0);
30532580301SAttilio Rao 
30632580301SAttilio Rao 		/* send start address */
30732580301SAttilio Rao 		waport =  DMA1_CHN(chan);
30832580301SAttilio Rao 		outb(waport, phys);
30932580301SAttilio Rao 		outb(waport, phys>>8);
31032580301SAttilio Rao 		outb(dmapageport[chan], phys>>16);
31132580301SAttilio Rao 
31232580301SAttilio Rao 		/* send count */
31332580301SAttilio Rao 		outb(waport + 1, --nbytes);
31432580301SAttilio Rao 		outb(waport + 1, nbytes>>8);
31532580301SAttilio Rao 
31632580301SAttilio Rao 		/* unmask channel */
31732580301SAttilio Rao 		outb(DMA1_SMSK, chan);
31832580301SAttilio Rao 	} else {
31932580301SAttilio Rao 		/*
32032580301SAttilio Rao 		 * Program one of DMA channels 4..7.  These are
32132580301SAttilio Rao 		 * word mode channels.
32232580301SAttilio Rao 		 */
32332580301SAttilio Rao 		/* set dma channel mode, and reset address ff */
32432580301SAttilio Rao 
32532580301SAttilio Rao 		/* If ISADMA_RAW flag is set, then use autoinitialise mode */
32632580301SAttilio Rao 		if (flags & ISADMA_RAW) {
32732580301SAttilio Rao 		  if (flags & ISADMA_READ)
32832580301SAttilio Rao 			outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3));
32932580301SAttilio Rao 		  else
33032580301SAttilio Rao 			outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3));
33132580301SAttilio Rao 		}
33232580301SAttilio Rao 		else
33332580301SAttilio Rao 		if (flags & ISADMA_READ)
33432580301SAttilio Rao 			outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
33532580301SAttilio Rao 		else
33632580301SAttilio Rao 			outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
33732580301SAttilio Rao 		outb(DMA2_FFC, 0);
33832580301SAttilio Rao 
33932580301SAttilio Rao 		/* send start address */
34032580301SAttilio Rao 		waport = DMA2_CHN(chan - 4);
34132580301SAttilio Rao 		outb(waport, phys>>1);
34232580301SAttilio Rao 		outb(waport, phys>>9);
34332580301SAttilio Rao 		outb(dmapageport[chan], phys>>16);
34432580301SAttilio Rao 
34532580301SAttilio Rao 		/* send count */
34632580301SAttilio Rao 		nbytes >>= 1;
34732580301SAttilio Rao 		outb(waport + 2, --nbytes);
34832580301SAttilio Rao 		outb(waport + 2, nbytes>>8);
34932580301SAttilio Rao 
35032580301SAttilio Rao 		/* unmask channel */
35132580301SAttilio Rao 		outb(DMA2_SMSK, chan & 3);
35232580301SAttilio Rao 	}
35332580301SAttilio Rao 	mtx_unlock(&isa_dma_lock);
35432580301SAttilio Rao }
35532580301SAttilio Rao 
35632580301SAttilio Rao void
isa_dmadone(int flags,caddr_t addr,int nbytes,int chan)35732580301SAttilio Rao isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)
35832580301SAttilio Rao {
35932580301SAttilio Rao #ifdef DIAGNOSTIC
36032580301SAttilio Rao 	if (chan & ~VALID_DMA_MASK)
36132580301SAttilio Rao 		panic("isa_dmadone: channel out of range");
36232580301SAttilio Rao 
36332580301SAttilio Rao 	if ((dma_inuse & (1 << chan)) == 0)
36432580301SAttilio Rao 		printf("isa_dmadone: channel %d not acquired\n", chan);
36532580301SAttilio Rao #endif
36632580301SAttilio Rao 
36732580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
36832580301SAttilio Rao 	if (((dma_busy & (1 << chan)) == 0) &&
36932580301SAttilio Rao 	    (dma_auto_mode & (1 << chan)) == 0 )
37032580301SAttilio Rao 		printf("isa_dmadone: channel %d not busy\n", chan);
37132580301SAttilio Rao 
37232580301SAttilio Rao 	if ((dma_auto_mode & (1 << chan)) == 0)
37332580301SAttilio Rao 		outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4);
37432580301SAttilio Rao 
37532580301SAttilio Rao 	if (dma_bounced & (1 << chan)) {
37632580301SAttilio Rao 		/* copy bounce buffer on read */
37732580301SAttilio Rao 		if (flags & ISADMA_READ)
37832580301SAttilio Rao 			bcopy(dma_bouncebuf[chan], addr, nbytes);
37932580301SAttilio Rao 
38032580301SAttilio Rao 		dma_bounced &= ~(1 << chan);
38132580301SAttilio Rao 	}
38232580301SAttilio Rao 	dma_busy &= ~(1 << chan);
38332580301SAttilio Rao 	mtx_unlock(&isa_dma_lock);
38432580301SAttilio Rao }
38532580301SAttilio Rao 
38632580301SAttilio Rao /*
38732580301SAttilio Rao  * Check for problems with the address range of a DMA transfer
38832580301SAttilio Rao  * (non-contiguous physical pages, outside of bus address space,
38932580301SAttilio Rao  * crossing DMA page boundaries).
39032580301SAttilio Rao  * Return true if special handling needed.
39132580301SAttilio Rao  */
39232580301SAttilio Rao 
39332580301SAttilio Rao static int
isa_dmarangecheck(caddr_t va,u_int length,int chan)39432580301SAttilio Rao isa_dmarangecheck(caddr_t va, u_int length, int chan)
39532580301SAttilio Rao {
39632580301SAttilio Rao 	vm_paddr_t phys, priorpage = 0;
39732580301SAttilio Rao 	vm_offset_t endva;
39832580301SAttilio Rao 	u_int dma_pgmsk = (chan & 4) ?  ~(128*1024-1) : ~(64*1024-1);
39932580301SAttilio Rao 
40032580301SAttilio Rao 	endva = (vm_offset_t)round_page((vm_offset_t)va + length);
40132580301SAttilio Rao 	for (; va < (caddr_t) endva ; va += PAGE_SIZE) {
40232580301SAttilio Rao 		phys = trunc_page(pmap_extract(kernel_pmap, (vm_offset_t)va));
40332580301SAttilio Rao 		if (phys == 0)
40432580301SAttilio Rao 			panic("isa_dmacheck: no physical page present");
40532580301SAttilio Rao 		if (phys >= ISARAM_END)
40632580301SAttilio Rao 			return (1);
40732580301SAttilio Rao 		if (priorpage) {
40832580301SAttilio Rao 			if (priorpage + PAGE_SIZE != phys)
40932580301SAttilio Rao 				return (1);
41032580301SAttilio Rao 			/* check if crossing a DMA page boundary */
41132580301SAttilio Rao 			if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk)
41232580301SAttilio Rao 				return (1);
41332580301SAttilio Rao 		}
41432580301SAttilio Rao 		priorpage = phys;
41532580301SAttilio Rao 	}
41632580301SAttilio Rao 	return (0);
41732580301SAttilio Rao }
41832580301SAttilio Rao 
41932580301SAttilio Rao /*
42032580301SAttilio Rao  * Query the progress of a transfer on a DMA channel.
42132580301SAttilio Rao  *
42232580301SAttilio Rao  * To avoid having to interrupt a transfer in progress, we sample
42332580301SAttilio Rao  * each of the high and low databytes twice, and apply the following
42432580301SAttilio Rao  * logic to determine the correct count.
42532580301SAttilio Rao  *
42632580301SAttilio Rao  * Reads are performed with interrupts disabled, thus it is to be
42732580301SAttilio Rao  * expected that the time between reads is very small.  At most
42832580301SAttilio Rao  * one rollover in the low count byte can be expected within the
42932580301SAttilio Rao  * four reads that are performed.
43032580301SAttilio Rao  *
43132580301SAttilio Rao  * There are three gaps in which a rollover can occur :
43232580301SAttilio Rao  *
43332580301SAttilio Rao  * - read low1
43432580301SAttilio Rao  *              gap1
43532580301SAttilio Rao  * - read high1
43632580301SAttilio Rao  *              gap2
43732580301SAttilio Rao  * - read low2
43832580301SAttilio Rao  *              gap3
43932580301SAttilio Rao  * - read high2
44032580301SAttilio Rao  *
44132580301SAttilio Rao  * If a rollover occurs in gap1 or gap2, the low2 value will be
44232580301SAttilio Rao  * greater than the low1 value.  In this case, low2 and high2 are a
44332580301SAttilio Rao  * corresponding pair.
44432580301SAttilio Rao  *
44532580301SAttilio Rao  * In any other case, low1 and high1 can be considered to be correct.
44632580301SAttilio Rao  *
44732580301SAttilio Rao  * The function returns the number of bytes remaining in the transfer,
44832580301SAttilio Rao  * or -1 if the channel requested is not active.
44932580301SAttilio Rao  *
45032580301SAttilio Rao  */
45132580301SAttilio Rao static int
isa_dmastatus_locked(int chan)45232580301SAttilio Rao isa_dmastatus_locked(int chan)
45332580301SAttilio Rao {
45432580301SAttilio Rao 	u_long	cnt = 0;
45532580301SAttilio Rao 	int	ffport, waport;
45632580301SAttilio Rao 	u_long	low1, high1, low2, high2;
45732580301SAttilio Rao 
45832580301SAttilio Rao 	mtx_assert(&isa_dma_lock, MA_OWNED);
45932580301SAttilio Rao 
46032580301SAttilio Rao 	/* channel active? */
46132580301SAttilio Rao 	if ((dma_inuse & (1 << chan)) == 0) {
46232580301SAttilio Rao 		printf("isa_dmastatus: channel %d not active\n", chan);
46332580301SAttilio Rao 		return(-1);
46432580301SAttilio Rao 	}
46532580301SAttilio Rao 	/* channel busy? */
46632580301SAttilio Rao 
46732580301SAttilio Rao 	if (((dma_busy & (1 << chan)) == 0) &&
46832580301SAttilio Rao 	    (dma_auto_mode & (1 << chan)) == 0 ) {
46932580301SAttilio Rao 	    printf("chan %d not busy\n", chan);
47032580301SAttilio Rao 	    return -2 ;
47132580301SAttilio Rao 	}
47232580301SAttilio Rao 	if (chan < 4) {			/* low DMA controller */
47332580301SAttilio Rao 		ffport = DMA1_FFC;
47432580301SAttilio Rao 		waport = DMA1_CHN(chan) + 1;
47532580301SAttilio Rao 	} else {			/* high DMA controller */
47632580301SAttilio Rao 		ffport = DMA2_FFC;
47732580301SAttilio Rao 		waport = DMA2_CHN(chan - 4) + 2;
47832580301SAttilio Rao 	}
47932580301SAttilio Rao 
48032580301SAttilio Rao 	disable_intr();			/* no interrupts Mr Jones! */
48132580301SAttilio Rao 	outb(ffport, 0);		/* clear register LSB flipflop */
48232580301SAttilio Rao 	low1 = inb(waport);
48332580301SAttilio Rao 	high1 = inb(waport);
48432580301SAttilio Rao 	outb(ffport, 0);		/* clear again */
48532580301SAttilio Rao 	low2 = inb(waport);
48632580301SAttilio Rao 	high2 = inb(waport);
48732580301SAttilio Rao 	enable_intr();			/* enable interrupts again */
48832580301SAttilio Rao 
48932580301SAttilio Rao 	/*
49032580301SAttilio Rao 	 * Now decide if a wrap has tried to skew our results.
49132580301SAttilio Rao 	 * Note that after TC, the count will read 0xffff, while we want
49232580301SAttilio Rao 	 * to return zero, so we add and then mask to compensate.
49332580301SAttilio Rao 	 */
49432580301SAttilio Rao 	if (low1 >= low2) {
49532580301SAttilio Rao 		cnt = (low1 + (high1 << 8) + 1) & 0xffff;
49632580301SAttilio Rao 	} else {
49732580301SAttilio Rao 		cnt = (low2 + (high2 << 8) + 1) & 0xffff;
49832580301SAttilio Rao 	}
49932580301SAttilio Rao 
50032580301SAttilio Rao 	if (chan >= 4)			/* high channels move words */
50132580301SAttilio Rao 		cnt *= 2;
50232580301SAttilio Rao 	return(cnt);
50332580301SAttilio Rao }
50432580301SAttilio Rao 
50532580301SAttilio Rao int
isa_dmastatus(int chan)50632580301SAttilio Rao isa_dmastatus(int chan)
50732580301SAttilio Rao {
50832580301SAttilio Rao 	int status;
50932580301SAttilio Rao 
51032580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
51132580301SAttilio Rao 	status = isa_dmastatus_locked(chan);
51232580301SAttilio Rao 	mtx_unlock(&isa_dma_lock);
51332580301SAttilio Rao 
51432580301SAttilio Rao 	return (status);
51532580301SAttilio Rao }
51632580301SAttilio Rao 
51732580301SAttilio Rao /*
51832580301SAttilio Rao  * Reached terminal count yet ?
51932580301SAttilio Rao  */
52032580301SAttilio Rao int
isa_dmatc(int chan)52132580301SAttilio Rao isa_dmatc(int chan)
52232580301SAttilio Rao {
52332580301SAttilio Rao 
52432580301SAttilio Rao 	if (chan < 4)
52532580301SAttilio Rao 		return(inb(DMA1_STATUS) & (1 << chan));
52632580301SAttilio Rao 	else
52732580301SAttilio Rao 		return(inb(DMA2_STATUS) & (1 << (chan & 3)));
52832580301SAttilio Rao }
52932580301SAttilio Rao 
53032580301SAttilio Rao /*
53132580301SAttilio Rao  * Stop a DMA transfer currently in progress.
53232580301SAttilio Rao  */
53332580301SAttilio Rao int
isa_dmastop(int chan)53432580301SAttilio Rao isa_dmastop(int chan)
53532580301SAttilio Rao {
53632580301SAttilio Rao 	int status;
53732580301SAttilio Rao 
53832580301SAttilio Rao 	mtx_lock(&isa_dma_lock);
53932580301SAttilio Rao 	if ((dma_inuse & (1 << chan)) == 0)
54032580301SAttilio Rao 		printf("isa_dmastop: channel %d not acquired\n", chan);
54132580301SAttilio Rao 
54232580301SAttilio Rao 	if (((dma_busy & (1 << chan)) == 0) &&
54332580301SAttilio Rao 	    ((dma_auto_mode & (1 << chan)) == 0)) {
54432580301SAttilio Rao 		printf("chan %d not busy\n", chan);
54532580301SAttilio Rao 		mtx_unlock(&isa_dma_lock);
54632580301SAttilio Rao 		return -2 ;
54732580301SAttilio Rao 	}
54832580301SAttilio Rao 
54932580301SAttilio Rao 	if ((chan & 4) == 0) {
55032580301SAttilio Rao 		outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */);
55132580301SAttilio Rao 	} else {
55232580301SAttilio Rao 		outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */);
55332580301SAttilio Rao 	}
55432580301SAttilio Rao 
55532580301SAttilio Rao 	status = isa_dmastatus_locked(chan);
55632580301SAttilio Rao 
55732580301SAttilio Rao 	mtx_unlock(&isa_dma_lock);
55832580301SAttilio Rao 
55932580301SAttilio Rao 	return (status);
56032580301SAttilio Rao }
56132580301SAttilio Rao 
56232580301SAttilio Rao /*
56332580301SAttilio Rao  * Attach to the ISA PnP descriptor for the AT DMA controller
56432580301SAttilio Rao  */
56532580301SAttilio Rao static struct isa_pnp_id atdma_ids[] = {
56632580301SAttilio Rao 	{ 0x0002d041 /* PNP0200 */, "AT DMA controller" },
56732580301SAttilio Rao 	{ 0 }
56832580301SAttilio Rao };
56932580301SAttilio Rao 
57032580301SAttilio Rao static int
atdma_probe(device_t dev)57132580301SAttilio Rao atdma_probe(device_t dev)
57232580301SAttilio Rao {
57332580301SAttilio Rao 	int result;
57432580301SAttilio Rao 
57532580301SAttilio Rao 	if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, atdma_ids)) <= 0)
57632580301SAttilio Rao 		device_quiet(dev);
57732580301SAttilio Rao 	return(result);
57832580301SAttilio Rao }
57932580301SAttilio Rao 
58032580301SAttilio Rao static int
atdma_attach(device_t dev)58132580301SAttilio Rao atdma_attach(device_t dev)
58232580301SAttilio Rao {
58332580301SAttilio Rao 	return(0);
58432580301SAttilio Rao }
58532580301SAttilio Rao 
58632580301SAttilio Rao static device_method_t atdma_methods[] = {
58732580301SAttilio Rao 	/* Device interface */
58832580301SAttilio Rao 	DEVMETHOD(device_probe,		atdma_probe),
58932580301SAttilio Rao 	DEVMETHOD(device_attach,	atdma_attach),
59032580301SAttilio Rao 	DEVMETHOD(device_detach,	bus_generic_detach),
59132580301SAttilio Rao 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
59232580301SAttilio Rao 	DEVMETHOD(device_suspend,	bus_generic_suspend),
59332580301SAttilio Rao 	DEVMETHOD(device_resume,	bus_generic_resume),
59432580301SAttilio Rao 	{ 0, 0 }
59532580301SAttilio Rao };
59632580301SAttilio Rao 
59732580301SAttilio Rao static driver_t atdma_driver = {
59832580301SAttilio Rao 	"atdma",
59932580301SAttilio Rao 	atdma_methods,
60032580301SAttilio Rao 	1,		/* no softc */
60132580301SAttilio Rao };
60232580301SAttilio Rao 
60380d2b3deSJohn Baldwin DRIVER_MODULE(atdma, isa, atdma_driver, 0, 0);
60480d2b3deSJohn Baldwin DRIVER_MODULE(atdma, acpi, atdma_driver, 0, 0);
605d6b66397SWarner Losh ISA_PNP_INFO(atdma_ids);
606