166458Sdfr/*-
266458Sdfr * Copyright (c) 1991 The Regents of the University of California.
366458Sdfr * All rights reserved.
466458Sdfr *
566458Sdfr * This code is derived from software contributed to Berkeley by
666458Sdfr * William Jolitz.
766458Sdfr *
866458Sdfr * Redistribution and use in source and binary forms, with or without
966458Sdfr * modification, are permitted provided that the following conditions
1066458Sdfr * are met:
1166458Sdfr * 1. Redistributions of source code must retain the above copyright
1266458Sdfr *    notice, this list of conditions and the following disclaimer.
1366458Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1466458Sdfr *    notice, this list of conditions and the following disclaimer in the
1566458Sdfr *    documentation and/or other materials provided with the distribution.
1666458Sdfr * 4. Neither the name of the University nor the names of its contributors
1766458Sdfr *    may be used to endorse or promote products derived from this software
1866458Sdfr *    without specific prior written permission.
1966458Sdfr *
2066458Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2166458Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2266458Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2366458Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2466458Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2566458Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2666458Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2766458Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2866458Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2966458Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3066458Sdfr * SUCH DAMAGE.
3166458Sdfr *
3266458Sdfr *	from: @(#)isa.c	7.2 (Berkeley) 5/13/91
3366458Sdfr *	from: isa_dma.c,v 1.3 1999/05/09 23:56:00 peter Exp $
3466458Sdfr * $FreeBSD$
3566458Sdfr */
3666458Sdfr
3766458Sdfr/*
3866458Sdfr * code to manage AT bus
3966458Sdfr *
4066458Sdfr * 92/08/18  Frank P. MacLachlan (fpm@crash.cts.com):
4166458Sdfr * Fixed uninitialized variable problem and added code to deal
4266458Sdfr * with DMA page boundaries in isa_dmarangecheck().  Fixed word
4366458Sdfr * mode DMA count compution and reorganized DMA setup code in
4466458Sdfr * isa_dmastart()
4566458Sdfr */
4666458Sdfr
4766458Sdfr#include <sys/param.h>
4866458Sdfr#include <sys/systm.h>
4966458Sdfr#include <sys/malloc.h>
50117126Sscottl#include <sys/lock.h>
51117126Sscottl#include <sys/mutex.h>
5266458Sdfr#include <sys/bus.h>
5366458Sdfr#include <vm/vm.h>
5466458Sdfr#include <vm/vm_param.h>
5566458Sdfr#include <vm/pmap.h>
5666458Sdfr#include <isa/isareg.h>
5766458Sdfr#include <isa/isavar.h>
58146214Snyan#include <isa/isa_dmareg.h>
5966458Sdfr#include <machine/bus.h>
6066458Sdfr
6166458Sdfrstatic bus_dma_tag_t dma_tag[8];
6266458Sdfrstatic bus_dmamap_t dma_map[8];
6366458Sdfrstatic u_int8_t	dma_busy = 0;		/* Used in isa_dmastart() */
6466458Sdfrstatic u_int8_t	dma_inuse = 0;		/* User for acquire/release */
6566458Sdfrstatic u_int8_t dma_auto_mode = 0;
6666458Sdfrstatic u_int8_t dma_bounced = 0;
6766458Sdfr
6866458Sdfr#define VALID_DMA_MASK (7)
6966458Sdfr
7066458Sdfr/* high byte of address is stored in this port for i-th dma channel */
7166458Sdfrstatic int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
7266458Sdfr
7366458Sdfr/*
7466458Sdfr * Setup a DMA channel's bounce buffer.
7566458Sdfr */
76135262Sphkint
77135262Sphkisa_dma_init(int chan, u_int bouncebufsize, int flag __unused)
7866458Sdfr{
7966458Sdfr	static int initted = 0;
8066458Sdfr	bus_addr_t boundary = chan >= 4 ? 0x20000 : 0x10000;
8166458Sdfr
8266458Sdfr	if (!initted) {
8366458Sdfr		/*
8466458Sdfr		 * Reset the DMA hardware.
8566458Sdfr		 */
8666458Sdfr		outb(DMA1_RESET, 0);
8766458Sdfr		outb(DMA2_RESET, 0);
8866458Sdfr		isa_dmacascade(4);
8966458Sdfr
9066458Sdfr		initted = 1;
9166458Sdfr	}
9266458Sdfr
9366458Sdfr#ifdef DIAGNOSTIC
9466458Sdfr	if (chan & ~VALID_DMA_MASK)
95135262Sphk		panic("isa_dma_init: channel out of range");
9666458Sdfr
9766458Sdfr	if (dma_tag[chan] || dma_map[chan])
98135262Sphk		panic("isa_dma_init: impossible request");
9966458Sdfr#endif
10066458Sdfr
10166458Sdfr	if (bus_dma_tag_create(/*parent*/NULL,
10266458Sdfr			       /*alignment*/2,
10366458Sdfr			       /*boundary*/boundary,
10466458Sdfr			       /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
10566458Sdfr			       /*highaddr*/BUS_SPACE_MAXADDR,
10666458Sdfr			       /*filter*/NULL, /*filterarg*/NULL,
10766458Sdfr			       /*maxsize*/bouncebufsize,
10866458Sdfr			       /*nsegments*/1, /*maxsegz*/0x3ffff,
109177215Simp			       /*flags*/0,
110117126Sscottl			       /*lockfunc*/busdma_lock_mutex,
111117126Sscottl			       /*lockarg*/&Giant,
11266458Sdfr			       &dma_tag[chan]) != 0) {
113135262Sphk		panic("isa_dma_init: unable to create dma tag\n");
11466458Sdfr	}
11566458Sdfr
11666458Sdfr	if (bus_dmamap_create(dma_tag[chan], 0, &dma_map[chan])) {
117135262Sphk		panic("isa_dma_init: unable to create dma map\n");
11866458Sdfr	}
11966458Sdfr
120135262Sphk	return (0);
12166458Sdfr}
12266458Sdfr
12366458Sdfr/*
12466458Sdfr * Register a DMA channel's usage.  Usually called from a device driver
12566458Sdfr * in open() or during its initialization.
12666458Sdfr */
12766458Sdfrint
12866458Sdfrisa_dma_acquire(chan)
12966458Sdfr	int chan;
13066458Sdfr{
13166458Sdfr#ifdef DIAGNOSTIC
13266458Sdfr	if (chan & ~VALID_DMA_MASK)
13366458Sdfr		panic("isa_dma_acquire: channel out of range");
13466458Sdfr#endif
13566458Sdfr
13666458Sdfr	if (dma_inuse & (1 << chan)) {
13766458Sdfr		printf("isa_dma_acquire: channel %d already in use\n", chan);
13866458Sdfr		return (EBUSY);
13966458Sdfr	}
14066458Sdfr	dma_inuse |= (1 << chan);
14166458Sdfr	dma_auto_mode &= ~(1 << chan);
14266458Sdfr
14366458Sdfr	return (0);
14466458Sdfr}
14566458Sdfr
14666458Sdfr/*
14766458Sdfr * Unregister a DMA channel's usage.  Usually called from a device driver
14866458Sdfr * during close() or during its shutdown.
14966458Sdfr */
15066458Sdfrvoid
15166458Sdfrisa_dma_release(chan)
15266458Sdfr	int chan;
15366458Sdfr{
15466458Sdfr#ifdef DIAGNOSTIC
15566458Sdfr	if (chan & ~VALID_DMA_MASK)
15666458Sdfr		panic("isa_dma_release: channel out of range");
15766458Sdfr
15866458Sdfr	if ((dma_inuse & (1 << chan)) == 0)
15966458Sdfr		printf("isa_dma_release: channel %d not in use\n", chan);
16066458Sdfr#endif
16166458Sdfr
16266458Sdfr	if (dma_busy & (1 << chan)) {
16366458Sdfr		dma_busy &= ~(1 << chan);
16466458Sdfr		/*
16566458Sdfr		 * XXX We should also do "dma_bounced &= (1 << chan);"
16666458Sdfr		 * because we are acting on behalf of isa_dmadone() which
16766458Sdfr		 * was not called to end the last DMA operation.  This does
16866458Sdfr		 * not matter now, but it may in the future.
16966458Sdfr		 */
17066458Sdfr	}
17166458Sdfr
17266458Sdfr	dma_inuse &= ~(1 << chan);
17366458Sdfr	dma_auto_mode &= ~(1 << chan);
17466458Sdfr}
17566458Sdfr
17666458Sdfr/*
17766458Sdfr * isa_dmacascade(): program 8237 DMA controller channel to accept
17866458Sdfr * external dma control by a board.
17966458Sdfr */
18066458Sdfrvoid
18166458Sdfrisa_dmacascade(chan)
18266458Sdfr	int chan;
18366458Sdfr{
18466458Sdfr#ifdef DIAGNOSTIC
18566458Sdfr	if (chan & ~VALID_DMA_MASK)
18666458Sdfr		panic("isa_dmacascade: channel out of range");
18766458Sdfr#endif
18866458Sdfr
18966458Sdfr	/* set dma channel mode, and set dma channel mode */
19066458Sdfr	if ((chan & 4) == 0) {
19166458Sdfr		outb(DMA1_MODE, DMA37MD_CASCADE | chan);
19266458Sdfr		outb(DMA1_SMSK, chan);
19366458Sdfr	} else {
19466458Sdfr		outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
19566458Sdfr		outb(DMA2_SMSK, chan & 3);
19666458Sdfr	}
19766458Sdfr}
19866458Sdfr
19966458Sdfr/*
20066458Sdfr * isa_dmastart(): program 8237 DMA controller channel.
20166458Sdfr */
20266458Sdfr
20366458Sdfrstruct isa_dmastart_arg {
20466458Sdfr	caddr_t addr;
20566458Sdfr	int 	chan;
20666458Sdfr	int 	flags;
20766458Sdfr};
20866458Sdfr
20966458Sdfrstatic void isa_dmastart_cb(void *arg, bus_dma_segment_t *segs, int nseg,
21066458Sdfr			    int error)
21166458Sdfr{
21292676Speter#if 0
21366458Sdfr	caddr_t addr = ((struct isa_dmastart_arg *) arg)->addr;
21492676Speter#endif
21566458Sdfr	int chan = ((struct isa_dmastart_arg *) arg)->chan;
21666458Sdfr	int flags = ((struct isa_dmastart_arg *) arg)->flags;
21766458Sdfr	bus_addr_t phys = segs->ds_addr;
21866458Sdfr	int nbytes = segs->ds_len;
21966458Sdfr	int waport;
22066458Sdfr
22166458Sdfr	if (nseg != 1)
22266458Sdfr		panic("isa_dmastart: transfer mapping not contiguous");
22366458Sdfr
22466458Sdfr#if 0
22566458Sdfr	if ((chipset.sgmap == NULL) &&
22695710Speter	    (pmap_extract(kernel_pmap, (vm_offset_t)addr)
22766458Sdfr		> BUS_SPACE_MAXADDR_24BIT)) {
22866458Sdfr		/* we bounced */
22966458Sdfr		dma_bounced |= (1 << chan);
23066458Sdfr                /* copy bounce buffer on write */
23166458Sdfr                if (!(flags & ISADMA_READ))
23266458Sdfr                        bus_dmamap_sync(dma_tag[chan], dma_map[chan],
23366458Sdfr			                  BUS_DMASYNC_PREWRITE);
23466458Sdfr	}
23566458Sdfr#endif
23666458Sdfr
23766458Sdfr	if ((chan & 4) == 0) {
23866458Sdfr		/*
23966458Sdfr		 * Program one of DMA channels 0..3.  These are
24066458Sdfr		 * byte mode channels.
24166458Sdfr		 */
24266458Sdfr		/* set dma channel mode, and reset address ff */
24366458Sdfr
24466458Sdfr		/* If ISADMA_RAW flag is set, then use autoinitialise mode */
24566458Sdfr		if (flags & ISADMA_RAW) {
24666458Sdfr		  if (flags & ISADMA_READ)
24766458Sdfr			outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan);
24866458Sdfr		  else
24966458Sdfr			outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan);
25066458Sdfr		}
25166458Sdfr		else
25266458Sdfr		if (flags & ISADMA_READ)
25366458Sdfr			outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
25466458Sdfr		else
25566458Sdfr			outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
25666458Sdfr		outb(DMA1_FFC, 0);
25766458Sdfr
25866458Sdfr		/* send start address */
25966458Sdfr		waport =  DMA1_CHN(chan);
26066458Sdfr		outb(waport, phys);
26166458Sdfr		outb(waport, phys>>8);
26266458Sdfr		outb(dmapageport[chan], phys>>16);
26366458Sdfr
26466458Sdfr		/* send count */
26566458Sdfr		outb(waport + 1, --nbytes);
26666458Sdfr		outb(waport + 1, nbytes>>8);
26766458Sdfr
26866458Sdfr		/* unmask channel */
26966458Sdfr		outb(DMA1_SMSK, chan);
27066458Sdfr	} else {
27166458Sdfr		/*
27266458Sdfr		 * Program one of DMA channels 4..7.  These are
27366458Sdfr		 * word mode channels.
27466458Sdfr		 */
27566458Sdfr		/* set dma channel mode, and reset address ff */
27666458Sdfr
27766458Sdfr		/* If ISADMA_RAW flag is set, then use autoinitialise mode */
27866458Sdfr		if (flags & ISADMA_RAW) {
27966458Sdfr		  if (flags & ISADMA_READ)
28066458Sdfr			outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3));
28166458Sdfr		  else
28266458Sdfr			outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3));
28366458Sdfr		}
28466458Sdfr		else
28566458Sdfr		if (flags & ISADMA_READ)
28666458Sdfr			outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
28766458Sdfr		else
28866458Sdfr			outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
28966458Sdfr		outb(DMA2_FFC, 0);
29066458Sdfr
29166458Sdfr		/* send start address */
29266458Sdfr		waport = DMA2_CHN(chan - 4);
29366458Sdfr		outb(waport, phys>>1);
29466458Sdfr		outb(waport, phys>>9);
29566458Sdfr		outb(dmapageport[chan], phys>>16);
29666458Sdfr
29766458Sdfr		/* send count */
29866458Sdfr		nbytes >>= 1;
29966458Sdfr		outb(waport + 2, --nbytes);
30066458Sdfr		outb(waport + 2, nbytes>>8);
30166458Sdfr
30266458Sdfr		/* unmask channel */
30366458Sdfr		outb(DMA2_SMSK, chan & 3);
30466458Sdfr	}
30566458Sdfr}
30666458Sdfr
30766458Sdfrvoid
30866458Sdfrisa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan)
30966458Sdfr{
31066458Sdfr	struct isa_dmastart_arg args;
31166458Sdfr
31266458Sdfr#ifdef DIAGNOSTIC
31366458Sdfr	if (chan & ~VALID_DMA_MASK)
31466458Sdfr		panic("isa_dmastart: channel out of range");
31566458Sdfr
31666458Sdfr	if ((chan < 4 && nbytes > (1<<16))
31766458Sdfr	    || (chan >= 4 && (nbytes > (1<<17) || (uintptr_t)addr & 1)))
31866458Sdfr		panic("isa_dmastart: impossible request");
31966458Sdfr
32066458Sdfr	if ((dma_inuse & (1 << chan)) == 0)
32166458Sdfr		printf("isa_dmastart: channel %d not acquired\n", chan);
32266458Sdfr#endif
32366458Sdfr
32466458Sdfr#if 0
32566458Sdfr	/*
32666458Sdfr	 * XXX This should be checked, but drivers like ad1848 only call
32766458Sdfr	 * isa_dmastart() once because they use Auto DMA mode.  If we
32866458Sdfr	 * leave this in, drivers that do this will print this continuously.
32966458Sdfr	 */
33066458Sdfr	if (dma_busy & (1 << chan))
33166458Sdfr		printf("isa_dmastart: channel %d busy\n", chan);
33266458Sdfr#endif
33366458Sdfr
334171312Smarcel	if (!dma_tag[chan] || !dma_map[chan])
335135262Sphk		panic("isa_dmastart: called without isa_dma_init");
33666458Sdfr
33766458Sdfr	dma_busy |= (1 << chan);
33866458Sdfr
33966458Sdfr	if (flags & ISADMA_RAW) {
34066458Sdfr		dma_auto_mode |= (1 << chan);
34166458Sdfr	} else {
34266458Sdfr		dma_auto_mode &= ~(1 << chan);
34366458Sdfr	}
34466458Sdfr
34566458Sdfr	/*
34666458Sdfr	 * Freeze dma while updating registers.
34766458Sdfr	 */
34866458Sdfr	outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4);
34966458Sdfr
35066458Sdfr        args.addr = addr;
35166458Sdfr	args.chan = chan;
35266458Sdfr	args.flags = flags;
35366458Sdfr	bus_dmamap_load(dma_tag[chan], dma_map[chan], addr, nbytes,
35466458Sdfr			isa_dmastart_cb, &args, 0);
35566458Sdfr}
35666458Sdfr
35766458Sdfrvoid
35866458Sdfrisa_dmadone(int flags, caddr_t addr, int nbytes, int chan)
35966458Sdfr{
36066458Sdfr#ifdef DIAGNOSTIC
36166458Sdfr	if (chan & ~VALID_DMA_MASK)
36266458Sdfr		panic("isa_dmadone: channel out of range");
36366458Sdfr
36466458Sdfr	if ((dma_inuse & (1 << chan)) == 0)
36566458Sdfr		printf("isa_dmadone: channel %d not acquired\n", chan);
36666458Sdfr#endif
36766458Sdfr
36866458Sdfr	if (((dma_busy & (1 << chan)) == 0) &&
36966458Sdfr	    (dma_auto_mode & (1 << chan)) == 0 )
37066458Sdfr		printf("isa_dmadone: channel %d not busy\n", chan);
37166458Sdfr
37266458Sdfr	if (dma_bounced & (1 << chan)) {
37366458Sdfr		/* copy bounce buffer on read */
37466458Sdfr		if (flags & ISADMA_READ) {
37566458Sdfr			bus_dmamap_sync(dma_tag[chan], dma_map[chan],
37666458Sdfr			                  BUS_DMASYNC_POSTREAD);
37766458Sdfr		}
37866458Sdfr		dma_bounced &= ~(1 << chan);
37966458Sdfr	}
38066458Sdfr
38166458Sdfr	if ((dma_auto_mode & (1 << chan)) == 0) {
38266458Sdfr		outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4);
38366458Sdfr		bus_dmamap_unload(dma_tag[chan], dma_map[chan]);
38466458Sdfr	}
38566458Sdfr
38666458Sdfr	dma_busy &= ~(1 << chan);
38766458Sdfr}
38866458Sdfr
38966458Sdfr/*
39066458Sdfr * Query the progress of a transfer on a DMA channel.
39166458Sdfr *
39266458Sdfr * To avoid having to interrupt a transfer in progress, we sample
39366458Sdfr * each of the high and low databytes twice, and apply the following
39466458Sdfr * logic to determine the correct count.
39566458Sdfr *
39666458Sdfr * Reads are performed with interrupts disabled, thus it is to be
39766458Sdfr * expected that the time between reads is very small.  At most
39866458Sdfr * one rollover in the low count byte can be expected within the
39966458Sdfr * four reads that are performed.
40066458Sdfr *
40166458Sdfr * There are three gaps in which a rollover can occur :
40266458Sdfr *
40366458Sdfr * - read low1
40466458Sdfr *              gap1
40566458Sdfr * - read high1
40666458Sdfr *              gap2
40766458Sdfr * - read low2
40866458Sdfr *              gap3
40966458Sdfr * - read high2
41066458Sdfr *
41166458Sdfr * If a rollover occurs in gap1 or gap2, the low2 value will be
41266458Sdfr * greater than the low1 value.  In this case, low2 and high2 are a
41366458Sdfr * corresponding pair.
41466458Sdfr *
41566458Sdfr * In any other case, low1 and high1 can be considered to be correct.
41666458Sdfr *
41766458Sdfr * The function returns the number of bytes remaining in the transfer,
41866458Sdfr * or -1 if the channel requested is not active.
41966458Sdfr *
42066458Sdfr */
42166458Sdfrint
42266458Sdfrisa_dmastatus(int chan)
42366458Sdfr{
42466458Sdfr	u_long	cnt = 0;
42566458Sdfr	int	ffport, waport;
42666458Sdfr	u_long	low1, high1, low2, high2;
42766458Sdfr	int s;
42866458Sdfr
42966458Sdfr	/* channel active? */
43066458Sdfr	if ((dma_inuse & (1 << chan)) == 0) {
43166458Sdfr		printf("isa_dmastatus: channel %d not active\n", chan);
43266458Sdfr		return(-1);
43366458Sdfr	}
43466458Sdfr	/* channel busy? */
43566458Sdfr
43666458Sdfr	if (((dma_busy & (1 << chan)) == 0) &&
43766458Sdfr	    (dma_auto_mode & (1 << chan)) == 0 ) {
43866458Sdfr	    printf("chan %d not busy\n", chan);
43966458Sdfr	    return -2 ;
44066458Sdfr	}
44166458Sdfr	if (chan < 4) {			/* low DMA controller */
44266458Sdfr		ffport = DMA1_FFC;
44366458Sdfr		waport = DMA1_CHN(chan) + 1;
44466458Sdfr	} else {			/* high DMA controller */
44566458Sdfr		ffport = DMA2_FFC;
44666458Sdfr		waport = DMA2_CHN(chan - 4) + 2;
44766458Sdfr	}
44866458Sdfr
44966458Sdfr	s = splhigh();			/* no interrupts Mr Jones! */
45066458Sdfr	outb(ffport, 0);		/* clear register LSB flipflop */
45166458Sdfr	low1 = inb(waport);
45266458Sdfr	high1 = inb(waport);
45366458Sdfr	outb(ffport, 0);		/* clear again */
45466458Sdfr	low2 = inb(waport);
45566458Sdfr	high2 = inb(waport);
45666458Sdfr	splx(s);			/* enable interrupts again */
45766458Sdfr
45866458Sdfr	/*
45966458Sdfr	 * Now decide if a wrap has tried to skew our results.
46066458Sdfr	 * Note that after TC, the count will read 0xffff, while we want
46166458Sdfr	 * to return zero, so we add and then mask to compensate.
46266458Sdfr	 */
46366458Sdfr	if (low1 >= low2) {
46466458Sdfr		cnt = (low1 + (high1 << 8) + 1) & 0xffff;
46566458Sdfr	} else {
46666458Sdfr		cnt = (low2 + (high2 << 8) + 1) & 0xffff;
46766458Sdfr	}
46866458Sdfr
46966458Sdfr	if (chan >= 4)			/* high channels move words */
47066458Sdfr		cnt *= 2;
47166458Sdfr	return(cnt);
47266458Sdfr}
47366458Sdfr
47466458Sdfr/*
475141391Sphk * Reached terminal count yet ?
476141391Sphk */
477141391Sphkint
478141391Sphkisa_dmatc(int chan)
479141391Sphk{
480141391Sphk
481141391Sphk	if (chan < 4)
482141391Sphk		return(inb(DMA1_STATUS) & (1 << chan));
483141391Sphk	else
484141391Sphk		return(inb(DMA2_STATUS) & (1 << (chan & 3)));
485141391Sphk}
486141391Sphk
487141391Sphk/*
48866458Sdfr * Stop a DMA transfer currently in progress.
48966458Sdfr */
49066458Sdfrint
49166458Sdfrisa_dmastop(int chan)
49266458Sdfr{
49366458Sdfr	if ((dma_inuse & (1 << chan)) == 0)
49466458Sdfr		printf("isa_dmastop: channel %d not acquired\n", chan);
49566458Sdfr
49666458Sdfr	if (((dma_busy & (1 << chan)) == 0) &&
49766458Sdfr	    ((dma_auto_mode & (1 << chan)) == 0)) {
49866458Sdfr		printf("chan %d not busy\n", chan);
49966458Sdfr		return -2 ;
50066458Sdfr	}
50166458Sdfr
50266458Sdfr	if ((chan & 4) == 0) {
50366458Sdfr		outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */);
50466458Sdfr	} else {
50566458Sdfr		outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */);
50666458Sdfr	}
50766458Sdfr	return(isa_dmastatus(chan));
50866458Sdfr}
509