/*	$NetBSD: esp.c,v 1.67 2023/02/03 23:17:49 tsutsui Exp $	*/

/*-
 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
 * Simulation Facility, NASA Ames Research Center.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1994 Peter Galbavy
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Peter Galbavy
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Based on aic6360 by Jarle Greipsland
 *
 * Acknowledgements: Many of the algorithms used in this driver are
 * inspired by the work of Julian Elischer (julian@tfs.com) and
 * Charles Hannum (mycroft@duality.gnu.ai.mit.edu).  Thanks a million!
 */

/*
 * Grabbed from the sparc port at revision 1.73 for the NeXT.
 * Darrin B. Jewell <dbj@NetBSD.org>  Sat Jul  4 15:41:32 1998
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: esp.c,v 1.67 2023/02/03 23:17:49 tsutsui Exp $");

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/queue.h>

#include <uvm/uvm_extern.h>

#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>
#include <dev/scsipi/scsi_message.h>

#include <machine/bus.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>

#include <dev/ic/ncr53c9xreg.h>
#include <dev/ic/ncr53c9xvar.h>

#include <next68k/next68k/isr.h>

#include <next68k/dev/intiovar.h>
#include <next68k/dev/nextdmareg.h>
#include <next68k/dev/nextdmavar.h>

#include <next68k/dev/espreg.h>
#include <next68k/dev/espvar.h>

#ifdef DEBUG
#undef ESP_DEBUG
#endif

#ifdef ESP_DEBUG
int esp_debug = 0;
#define DPRINTF(x) if (esp_debug) printf x;
#define NDTRACEIF(x) if (10) do {x;} while (0)
#else
#define DPRINTF(x)
#define NDTRACEIF(x)
#endif
#define PRINTF(x) printf x;


static int espmatch_intio(device_t, cfdata_t, void *);
static void espattach_intio(device_t, device_t, void *);

/* DMA callbacks */
static bus_dmamap_t esp_dmacb_continue(void *);
static void esp_dmacb_completed(bus_dmamap_t, void *);
static void esp_dmacb_shutdown(void *);

static void findchannel_defer(device_t);

#ifdef ESP_DEBUG
char esp_dma_dump[5*1024] = "";
struct ncr53c9x_softc *esp_debug_sc = 0;
void esp_dma_store(struct ncr53c9x_softc *);
void esp_dma_print(struct ncr53c9x_softc *);
int esp_dma_nest = 0;
int esptraceshow;
#endif


/* Linkup to the rest of the kernel */
CFATTACH_DECL_NEW(esp, sizeof(struct esp_softc),
    espmatch_intio, espattach_intio, NULL, NULL);

static int attached = 0;

/*
 * Functions and the switch for the MI code.
 */
static uint8_t esp_read_reg(struct ncr53c9x_softc *, int);
static void esp_write_reg(struct ncr53c9x_softc *, int, uint8_t);
static int esp_dma_isintr(struct ncr53c9x_softc *);
static void esp_dma_reset(struct ncr53c9x_softc *);
static int esp_dma_intr(struct ncr53c9x_softc *);
static int esp_dma_setup(struct ncr53c9x_softc *, uint8_t **, size_t *, int,
    size_t *);
static void esp_dma_go(struct ncr53c9x_softc *);
static void esp_dma_stop(struct ncr53c9x_softc *);
static int esp_dma_isactive(struct ncr53c9x_softc *);

static int doze(volatile int);

static struct ncr53c9x_glue esp_glue = {
	.gl_read_reg = esp_read_reg,
	.gl_write_reg = esp_write_reg,
	.gl_dma_isintr = esp_dma_isintr,
	.gl_dma_reset = esp_dma_reset,
	.gl_dma_intr = esp_dma_intr,
	.gl_dma_setup = esp_dma_setup,
	.gl_dma_go = esp_dma_go,
	.gl_dma_stop = esp_dma_stop,
	.gl_dma_isactive = esp_dma_isactive,
	.gl_clear_latched_intr = NULL
};

#define nd_bsr4(reg) \
	bus_space_read_4(nsc->sc_bst, nsc->sc_bsh, (reg))
#define nd_bsw4(reg, val) \
	bus_space_write_4(nsc->sc_bst, nsc->sc_bsh, (reg), (val))

#ifdef ESP_DEBUG
#define XCHR(x) hexdigits[(x) & 0xf]
static void
esp_hex_dump(unsigned char *pkt, size_t len)
{
	size_t i, j;

	printf("00000000  ");
	for(i = 0; i < len; i++) {
		printf("%c%c ", XCHR(pkt[i] >> 4), XCHR(pkt[i]));
		if ((i + 1) % 16 == 8) {
			printf(" ");
		}
		if ((i + 1) % 16 == 0) {
			printf(" %c", '|');
			for(j = 0; j < 16; j++) {
				printf("%c", pkt[i - 15 + j] >= 32 &&
				    pkt[i - 15 + j] < 127 ?
				    pkt[i - 15 + j] : '.');
			}
			printf("%c\n%c%c%c%c%c%c%c%c  ", '|',
			    XCHR((i + 1) >> 28), XCHR((i + 1) >> 24),
			    XCHR((i + 1) >> 20), XCHR((i + 1) >> 16),
			    XCHR((i + 1) >> 12), XCHR((i + 1) >>  8),
			    XCHR((i + 1) >>  4), XCHR(i + 1));
		}
	}
	printf("\n");
}
#endif

static int
espmatch_intio(device_t parent, cfdata_t cf, void *aux)
{
	struct intio_attach_args *ia = aux;

	if (attached)
		return 0;

	ia->ia_addr = (void *)NEXT_P_SCSI;

	return 1;
}

static void
findchannel_defer(device_t self)
{
	struct esp_softc *esc = device_private(self);
	struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x;
	int error;

	if (esc->sc_dma == NULL) {
		aprint_normal("%s", device_xname(sc->sc_dev));
		esc->sc_dma = nextdma_findchannel("scsi");
		if (esc->sc_dma == NULL)
			panic("%s: can't find DMA channel",
			       device_xname(sc->sc_dev));
	}

	nextdma_setconf(esc->sc_dma, shutdown_cb, &esp_dmacb_shutdown);
	nextdma_setconf(esc->sc_dma, continue_cb, &esp_dmacb_continue);
	nextdma_setconf(esc->sc_dma, completed_cb, &esp_dmacb_completed);
	nextdma_setconf(esc->sc_dma, cb_arg, sc);

	error = bus_dmamap_create(esc->sc_dma->sc_dmat,
	    sc->sc_maxxfer, sc->sc_maxxfer / PAGE_SIZE + 1, sc->sc_maxxfer,
	    0, BUS_DMA_ALLOCNOW, &esc->sc_main_dmamap);
	if (error != 0) {
		panic("%s: can't create main i/o DMA map, error = %d",
		    device_xname(sc->sc_dev), error);
	}

	error = bus_dmamap_create(esc->sc_dma->sc_dmat,
	    ESP_DMA_TAILBUFSIZE, 1, ESP_DMA_TAILBUFSIZE,
	    0, BUS_DMA_ALLOCNOW, &esc->sc_tail_dmamap);
	if (error != 0) {
		panic("%s: can't create tail i/o DMA map, error = %d",
		    device_xname(sc->sc_dev), error);
	}

#if 0
	/* Turn on target selection using the `DMA' method */
	sc->sc_features |= NCR_F_DMASELECT;
#endif

	/* Do the common parts of attachment. */
	sc->sc_adapter.adapt_minphys = minphys;
	sc->sc_adapter.adapt_request = ncr53c9x_scsipi_request;
	ncr53c9x_attach(sc);

	/* Establish interrupt channel */
	isrlink_autovec(ncr53c9x_intr, sc, NEXT_I_IPL(NEXT_I_SCSI), 0, NULL);
	INTR_ENABLE(NEXT_I_SCSI);

	/* register interrupt stats */
	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
	    device_xname(sc->sc_dev), "intr");

	aprint_normal_dev(sc->sc_dev, "using DMA channel %s\n",
	    device_xname(esc->sc_dma->sc_dev));
}

static void
espattach_intio(device_t parent, device_t self, void *aux)
{
	struct esp_softc *esc = device_private(self);
	struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x;
	struct intio_attach_args *ia = aux;

	sc->sc_dev = self;

#ifdef ESP_DEBUG
	esp_debug_sc = sc;
#endif

	esc->sc_bst = ia->ia_bst;
	if (bus_space_map(esc->sc_bst, NEXT_P_SCSI,
	    ESP_DEVICE_SIZE, 0, &esc->sc_bsh)) {
		aprint_normal("\n");
		panic("%s: can't map ncr53c90 registers",
		    device_xname(self));
	}

	sc->sc_id = 7;
	sc->sc_freq = 20;	/* MHz */

	/*
	 * Set up glue for MI code early; we use some of it here.
	 */
	sc->sc_glue = &esp_glue;

	/*
	 * XXX More of this should be in ncr53c9x_attach(), but
	 * XXX should we really poke around the chip that much in
	 * XXX the MI code?  Think about this more...
	 */

	/*
	 * It is necessary to try to load the 2nd config register here,
	 * to find out what rev the esp chip is, else the ncr53c9x_reset
	 * will not set up the defaults correctly.
	 */
	sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB;
	sc->sc_cfg2 = NCRCFG2_SCSI2 | NCRCFG2_RPE;
	sc->sc_cfg3 = NCRCFG3_CDB;
	NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2);

	if ((NCR_READ_REG(sc, NCR_CFG2) & ~NCRCFG2_RSVD) !=
	    (NCRCFG2_SCSI2 | NCRCFG2_RPE)) {
		sc->sc_rev = NCR_VARIANT_ESP100;
	} else {
		sc->sc_cfg2 = NCRCFG2_SCSI2;
		NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2);
		sc->sc_cfg3 = 0;
		NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3);
		sc->sc_cfg3 = (NCRCFG3_CDB | NCRCFG3_FCLK);
		NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3);
		if (NCR_READ_REG(sc, NCR_CFG3) !=
		    (NCRCFG3_CDB | NCRCFG3_FCLK)) {
			sc->sc_rev = NCR_VARIANT_ESP100A;
		} else {
			/* NCRCFG2_FE enables > 64K transfers */
			sc->sc_cfg2 |= NCRCFG2_FE;
			sc->sc_cfg3 = 0;
			NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3);
			sc->sc_rev = NCR_VARIANT_ESP200;
		}
	}

	/*
	 * XXX minsync and maxxfer _should_ be set up in MI code,
	 * XXX but it appears to have some dependency on what sort
	 * XXX of DMA we're hooked up to, etc.
	 */

	/*
	 * This is the value used to start sync negotiations
	 * Note that the NCR register "SYNCTP" is programmed
	 * in "clocks per byte", and has a minimum value of 4.
	 * The SCSI period used in negotiation is one-fourth
	 * of the time (in nanoseconds) needed to transfer one byte.
	 * Since the chip's clock is given in MHz, we have the following
	 * formula: 4 * period = (1000 / freq) * 4
	 */
	sc->sc_minsync = /* 1000 / sc->sc_freq */ 0;

	/*
	 * Alas, we must now modify the value a bit, because it's
	 * only valid when can switch on FASTCLK and FASTSCSI bits
	 * in config register 3...
	 */
	switch (sc->sc_rev) {
	case NCR_VARIANT_ESP100:
		sc->sc_maxxfer = 64 * 1024;
		sc->sc_minsync = 0;	/* No synch on old chip? */
		break;

	case NCR_VARIANT_ESP100A:
		sc->sc_maxxfer = 64 * 1024;
		/* Min clocks/byte is 5 */
		sc->sc_minsync = /* ncr53c9x_cpb2stp(sc, 5) */ 0;
		break;

	case NCR_VARIANT_ESP200:
		sc->sc_maxxfer = 16 * 1024 * 1024;
		/* XXX - do actually set FAST* bits */
		break;
	}

	/* @@@ Some ESP_DCTL bits probably need setting */
	NCR_WRITE_REG(sc, ESP_DCTL,
	    ESPDCTL_16MHZ | ESPDCTL_INTENB | ESPDCTL_RESET);
	DELAY(10);
	DPRINTF(("esp dctl is 0x%02x\n", NCR_READ_REG(sc,ESP_DCTL)));
	NCR_WRITE_REG(sc, ESP_DCTL, ESPDCTL_16MHZ | ESPDCTL_INTENB);
	DELAY(10);
	DPRINTF(("esp dctl is 0x%02x\n", NCR_READ_REG(sc, ESP_DCTL)));

	esc->sc_dma = nextdma_findchannel("scsi");
	if (esc->sc_dma != NULL) {
		findchannel_defer(self);
	} else {
		aprint_normal("\n");
		config_defer(self, findchannel_defer);
	}

	attached = 1;
}

/*
 * Glue functions.
 */

static uint8_t
esp_read_reg(struct ncr53c9x_softc *sc, int reg)
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	return bus_space_read_1(esc->sc_bst, esc->sc_bsh, reg);
}

static void
esp_write_reg(struct ncr53c9x_softc *sc, int reg, uint8_t val)
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	bus_space_write_1(esc->sc_bst, esc->sc_bsh, reg, val);
}

volatile uint32_t save1;

#define xADDR 0x0211a000
static int
doze(volatile int c)
{
#if 0
	static int tmp1;
#endif
	uint32_t tmp1;
	volatile uint8_t tmp2;
	volatile uint8_t *reg = (volatile uint8_t *)IIOV(xADDR);

	if (c > 244)
		return 0;
	if (c == 0)
		return 0;
#if 0
	((*(volatile u_long *)IIOV(NEXT_P_INTRMASK)) &= (~NEXT_I_BIT(x)));
#endif
	(*reg) = 0;
	(*reg) = 0;
	do {
		save1 = (*reg);
		tmp2 = *(reg + 3);
		tmp1 = tmp2;
	} while (tmp1 <= c);
	return 0;
}

static int
esp_dma_isintr(struct ncr53c9x_softc *sc)
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	if (INTR_OCCURRED(NEXT_I_SCSI)) {
		NDTRACEIF(ndtrace_addc('i'));
		NCR_WRITE_REG(sc, ESP_DCTL,
		    ESPDCTL_16MHZ | ESPDCTL_INTENB |
		    (esc->sc_datain ? ESPDCTL_DMARD : 0));
		return 1;
	} else {
		return 0;
	}
}

static int
esp_dma_intr(struct ncr53c9x_softc *sc)
{
	struct esp_softc *esc = (struct esp_softc *)sc;
	struct nextdma_softc *nsc = esc->sc_dma;
	struct nextdma_status *stat = &nsc->sc_stat;
	int r = (INTR_OCCURRED(NEXT_I_SCSI));
	int flushcount;

	r = 1;

	NDTRACEIF(ndtrace_addc('I'));
	if (r) {
#if 0
		printf("esp_dma_isintr start\n");
#endif
		{
			int s = spldma();
			void *ndmap = stat->nd_map;
			int ndidx = stat->nd_idx;
			splx(s);

			flushcount = 0;

#ifdef ESP_DEBUG
#if 0
			esp_dma_nest++;
#endif

			if (esp_debug) {
				char sbuf[256];

				snprintb(sbuf, sizeof(sbuf), NEXT_INTR_BITS,
				    (*(volatile u_long *)IIOV(NEXT_P_INTRSTAT)));

				printf("esp_dma_isintr = %s\n", sbuf);
			}
#endif

			mutex_exit(&sc->sc_lock);	/* for nextdma intr */
			while (!nextdma_finished(nsc)) {
				NDTRACEIF(ndtrace_addc('w'));
				NDTRACEIF(
					ndtrace_printf("f%dm%dl%dw",
					    NCR_READ_REG(sc, NCR_FFLAG) &
					    NCRFIFO_FF,
					    NCR_READ_REG((sc), NCR_TCM),
					    NCR_READ_REG((sc), NCR_TCL));
				);
				if (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF)
					flushcount = 5;
				NCR_WRITE_REG(sc, ESP_DCTL,
				    ESPDCTL_16MHZ | ESPDCTL_INTENB |
				    ESPDCTL_DMAMOD |
				    (esc->sc_datain ? ESPDCTL_DMARD : 0));

				s = spldma();
				while (ndmap == stat->nd_map &&
				    ndidx == stat->nd_idx &&
				    (nd_bsr4(DD_CSR) & 0x08000000) == 0 &&
				       ++flushcount < 5) {
					splx(s);
					NDTRACEIF(ndtrace_addc('F'));
					NCR_WRITE_REG(sc, ESP_DCTL,
					    ESPDCTL_FLUSH | ESPDCTL_16MHZ |
					    ESPDCTL_INTENB | ESPDCTL_DMAMOD |
					    (esc->sc_datain ?
					     ESPDCTL_DMARD : 0));
					doze(0x32);
					NCR_WRITE_REG(sc, ESP_DCTL,
					    ESPDCTL_16MHZ | ESPDCTL_INTENB |
					    ESPDCTL_DMAMOD |
					    (esc->sc_datain ?
					     ESPDCTL_DMARD : 0));
					doze(0x32);
					s = spldma();
				}
				NDTRACEIF(ndtrace_addc('0' + flushcount));
				if (flushcount > 4) {
					int next;
					int onext = 0;

					splx(s);
					DPRINTF(("DMA reset\n"));
					while (((next = nd_bsr4(DD_NEXT)) !=
					    (nd_bsr4(DD_LIMIT) & 0x7FFFFFFF)) &&
					     onext != next) {
						onext = next;
						DELAY(50);
					}
					NDTRACEIF(ndtrace_addc('R'));
					NCR_WRITE_REG(sc, ESP_DCTL,
					    ESPDCTL_16MHZ | ESPDCTL_INTENB);
					NDTRACEIF(
						ndtrace_printf(
						    "ff:%d tcm:%d tcl:%d ",
						    NCR_READ_REG(sc, NCR_FFLAG)
						    & NCRFIFO_FF,
						    NCR_READ_REG((sc), NCR_TCM),
						    NCR_READ_REG((sc),
						    NCR_TCL));
						);
					s = spldma();
					nextdma_reset(nsc);
					splx(s);
					goto out;
				}
				splx(s);

#ifdef DIAGNOSTIC
				if (flushcount > 4) {
					NDTRACEIF(ndtrace_addc('+'));
					printf("%s: unexpected flushcount"
					    " %d on %s\n",
					    device_xname(sc->sc_dev),
					    flushcount,
					    esc->sc_datain ? "read" : "write");
				}
#endif

				if (!nextdma_finished(nsc)) {
					NDTRACEIF(ndtrace_addc('1'));
				}
				flushcount = 0;
				s = spldma();
				ndmap = stat->nd_map;
				ndidx = stat->nd_idx;
				splx(s);

			}
		out:
			mutex_enter(&sc->sc_lock);	/* for nextdma intr */

#ifdef ESP_DEBUG
#if 0
			esp_dma_nest--;
#endif
#endif

		}

		doze(0x32);
		NCR_WRITE_REG(sc, ESP_DCTL,
		    ESPDCTL_16MHZ | ESPDCTL_INTENB |
		    (esc->sc_datain ? ESPDCTL_DMARD : 0));
		NDTRACEIF(ndtrace_addc('b'));

		while (esc->sc_datain != -1)
			DELAY(50);

		if (esc->sc_dmaaddr) {
			bus_size_t xfer_len = 0;
			int resid;

			NCR_WRITE_REG(sc, ESP_DCTL,
			    ESPDCTL_16MHZ | ESPDCTL_INTENB);
			if (stat->nd_exception == 0) {
				resid = NCR_READ_REG((sc), NCR_TCL) +
				    (NCR_READ_REG((sc), NCR_TCM) << 8);
				if (resid) {
					resid += (NCR_READ_REG(sc, NCR_FFLAG) &
					    NCRFIFO_FF);
#ifdef ESP_DEBUG
					if (NCR_READ_REG(sc, NCR_FFLAG) &
					    NCRFIFO_FF)
						if ((NCR_READ_REG(sc,
						    NCR_FFLAG) & NCRFIFO_FF) !=
						    16 ||
						    NCR_READ_REG((sc),
						    NCR_TCL) != 240)
							esptraceshow++;
#endif
				}
				xfer_len = esc->sc_dmasize - resid;
			} else {
#define ncr53c9x_sched_msgout(m) \
	do {							\
		NCR_MISC(("ncr53c9x_sched_msgout %x %d", m, __LINE__));	\
		NCRCMD(sc, NCRCMD_SETATN);			\
		sc->sc_flags |= NCR_ATN;			\
		sc->sc_msgpriq |= (m);				\
	} while (0)
				int i;

				xfer_len = 0;
				if (esc->sc_begin)
					xfer_len += esc->sc_begin_size;
				if (esc->sc_main_dmamap)
					xfer_len +=
					    esc->sc_main_dmamap->dm_xfer_len;
				if (esc->sc_tail_dmamap)
					xfer_len +=
					    esc->sc_tail_dmamap->dm_xfer_len;
				resid = 0;
				printf("X\n");
				for (i = 0; i < 16; i++) {
					NCR_WRITE_REG(sc, ESP_DCTL,
					    ESPDCTL_FLUSH | ESPDCTL_16MHZ |
					    ESPDCTL_INTENB |
					    (esc->sc_datain ?
					     ESPDCTL_DMARD : 0));
					NCR_WRITE_REG(sc, ESP_DCTL,
					    ESPDCTL_16MHZ | ESPDCTL_INTENB |
					    (esc->sc_datain ?
					     ESPDCTL_DMARD : 0));
				}
#if 0
				printf("ff:%02x tcm:%d tcl:%d esp_dstat:%02x"
				    " stat:%02x step: %02x intr:%02x"
				    " new stat:%02X\n",
				    NCR_READ_REG(sc, NCR_FFLAG),
				    NCR_READ_REG((sc), NCR_TCM),
				    NCR_READ_REG((sc), NCR_TCL),
				    NCR_READ_REG(sc, ESP_DSTAT),
				    sc->sc_espstat, sc->sc_espstep,
				    sc->sc_espintr,
				    NCR_READ_REG(sc, NCR_STAT));
				printf("sc->sc_state: %x sc->sc_phase: %x"
				    " sc->sc_espstep:%x sc->sc_prevphase:%x"
				    " sc->sc_flags:%x\n",
				    sc->sc_state, sc->sc_phase, sc->sc_espstep,
				    sc->sc_prevphase, sc->sc_flags);
#endif
#if 0
				sc->sc_flags &= ~NCR_ICCS;
#endif
				sc->sc_nexus->flags |= ECB_ABORT;
				if (sc->sc_phase == MESSAGE_IN_PHASE) {
#if 0
					ncr53c9x_sched_msgout(SEND_ABORT);
#endif
					ncr53c9x_abort(sc, sc->sc_nexus);
				} else if (sc->sc_phase != STATUS_PHASE) {
					printf("ATTENTION!!!  "
					    "not message/status phase: %d\n",
					    sc->sc_phase);
				}
			}

			NDTRACEIF(ndtrace_printf("f%dm%dl%ds%dx%dr%dS",
			    NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF,
			    NCR_READ_REG((sc), NCR_TCM),
			    NCR_READ_REG((sc), NCR_TCL),
			    esc->sc_dmasize, (int)xfer_len, resid);
			);

			*esc->sc_dmaaddr += xfer_len;
			*esc->sc_dmalen -= xfer_len;
			esc->sc_dmaaddr = 0;
			esc->sc_dmalen  = 0;
			esc->sc_dmasize = 0;
		}

		NDTRACEIF(ndtrace_addc('B'));
		sc->sc_espstat = NCR_READ_REG(sc, NCR_STAT) |
		    (sc->sc_espstat & NCRSTAT_INT);

		DPRINTF(("esp dctl is 0x%02x\n", NCR_READ_REG(sc, ESP_DCTL)));
#if 0
		printf("esp_dma_isintr DONE\n");
#endif
	}

	return r;
}

static void
esp_dma_reset(struct ncr53c9x_softc *sc)
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	DPRINTF(("esp DMA reset\n"));

#ifdef ESP_DEBUG
	if (esp_debug) {
		char sbuf[256];

		snprintb(sbuf, sizeof(sbuf), NEXT_INTR_BITS,
		    (*(volatile u_long *)IIOV(NEXT_P_INTRSTAT)));
		printf("  *intrstat = %s\n", sbuf);

		snprintb(sbuf, sizeof(sbuf), NEXT_INTR_BITS,
		    (*(volatile u_long *)IIOV(NEXT_P_INTRMASK)));
		printf("  *intrmask = %s\n", sbuf);
	}
#endif

#if 0
	/* Clear the DMAMOD bit in the DCTL register: */
	NCR_WRITE_REG(sc, ESP_DCTL, ESPDCTL_16MHZ | ESPDCTL_INTENB);
	DPRINTF(("esp dctl is 0x%02x\n",NCR_READ_REG(sc,ESP_DCTL)));
#endif

	nextdma_reset(esc->sc_dma);
	nextdma_init(esc->sc_dma);

	esc->sc_datain = -1;
	esc->sc_dmaaddr = 0;
	esc->sc_dmalen  = 0;
	esc->sc_dmasize = 0;

	esc->sc_loaded = 0;

	esc->sc_begin = 0;
	esc->sc_begin_size = 0;

	if (esc->sc_main_dmamap->dm_mapsize) {
		bus_dmamap_unload(esc->sc_dma->sc_dmat, esc->sc_main_dmamap);
	}
	esc->sc_main = 0;
	esc->sc_main_size = 0;

	if (esc->sc_tail_dmamap->dm_mapsize) {
		bus_dmamap_unload(esc->sc_dma->sc_dmat, esc->sc_tail_dmamap);
	}
	esc->sc_tail = 0;
	esc->sc_tail_size = 0;
}

/*
 * it appears that:
 * addr and len arguments to this need to be kept up to date
 * with the status of the transfter.
 * the dmasize of this is the actual length of the transfer
 * request, which is guaranteed to be less than maxxfer.
 * (len may be > maxxfer)
 */

static int
esp_dma_setup(struct ncr53c9x_softc *sc, uint8_t **addr, size_t *len,
    int datain, size_t *dmasize)
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	NDTRACEIF(ndtrace_addc('h'));
#ifdef DIAGNOSTIC
#ifdef ESP_DEBUG
	/*
	 * if this is a read DMA, pre-fill the buffer with 0xdeadbeef
	 * to identify bogus reads
	 */
	if (datain) {
		int *v = (int *)(*addr);
		int i;
		for (i = 0; i < ((*len) / 4); i++)
			v[i] = 0xdeadbeef;
		v = (int *)(&(esc->sc_tailbuf[0]));
		for (i = 0; i < ((sizeof(esc->sc_tailbuf) / 4)); i++)
			v[i] = 0xdeafbeef;
	} else {
		int *v;
		int i;
		v = (int *)(&(esc->sc_tailbuf[0]));
		for (i = 0; i < ((sizeof(esc->sc_tailbuf) / 4)); i++)
			v[i] = 0xfeeb1eed;
	}
#endif
#endif

	DPRINTF(("esp_dma_setup(%p,0x%08x,0x%08x)\n", *addr, *len, *dmasize));

#if 0
#ifdef DIAGNOSTIC
	/*
	 * @@@ this is ok sometimes. verify that we handle it ok
	 * and then remove this check
	 */
	if (*len != *dmasize) {
		panic("esp dmalen 0x%lx != size 0x%lx", *len, *dmasize);
	}
#endif
#endif

#ifdef DIAGNOSTIC
	if ((esc->sc_datain != -1) ||
	    (esc->sc_main_dmamap->dm_mapsize != 0) ||
	    (esc->sc_tail_dmamap->dm_mapsize != 0) ||
	    (esc->sc_dmasize != 0)) {
		panic("%s: map already loaded in esp_dma_setup"
		    "\tdatain = %d\n\tmain_mapsize=%ld\n"
		    "\tail_mapsize=%ld\n\tdmasize = %d",
		    device_xname(sc->sc_dev), esc->sc_datain,
		    esc->sc_main_dmamap->dm_mapsize,
		    esc->sc_tail_dmamap->dm_mapsize,
		    esc->sc_dmasize);
	}
#endif

	/* we are sometimes asked to DMA zero  bytes, that's easy */
	if (*dmasize <= 0) {
		return 0;
	}

	if (*dmasize > ESP_MAX_DMASIZE)
		*dmasize = ESP_MAX_DMASIZE;

	/* Save these in case we have to abort DMA */
	esc->sc_datain   = datain;
	esc->sc_dmaaddr  = addr;
	esc->sc_dmalen   = len;
	esc->sc_dmasize  = *dmasize;

	esc->sc_loaded = 0;

#define DMA_SCSI_ALIGNMENT 16
#define DMA_SCSI_ALIGN(type, addr)	\
	((type)(((unsigned int)(addr) + DMA_SCSI_ALIGNMENT - 1) \
	    & ~(DMA_SCSI_ALIGNMENT-1)))
#define DMA_SCSI_ALIGNED(addr) \
	(((unsigned int)(addr) & (DMA_SCSI_ALIGNMENT - 1)) == 0)

	{
		size_t slop_bgn_size; /* # bytes to be fifo'd at beginning */
		size_t slop_end_size; /* # bytes to be transferred in
					 tail buffer */

		{
			u_long bgn = (u_long)(*esc->sc_dmaaddr);
			u_long end = bgn + esc->sc_dmasize;

			slop_bgn_size =
			    DMA_SCSI_ALIGNMENT - (bgn % DMA_SCSI_ALIGNMENT);
			if (slop_bgn_size == DMA_SCSI_ALIGNMENT)
				slop_bgn_size = 0;
			slop_end_size = end % DMA_ENDALIGNMENT;
		}

		/*
		 * Force a minimum slop end size. This ensures that write
		 * requests will overrun, as required to get completion
		 * interrupts.
		 * In addition, since the tail buffer is guaranteed to be mapped
		 * in a single DMA segment, the overrun won't accidentally
		 * end up in its own segment.
		 */
		if (!esc->sc_datain) {
#if 0
			slop_end_size += ESP_DMA_MAXTAIL;
#else
			slop_end_size += 0x10;
#endif
		}

		/*
		 * Check to make sure we haven't counted extra slop
		 * as would happen for a very short DMA buffer, also
		 * for short buffers, just stuff the entire thing in the tail
		 */
		if ((slop_bgn_size+slop_end_size >= esc->sc_dmasize)
#if 0
		    || (esc->sc_dmasize <= ESP_DMA_MAXTAIL)
#endif
		    ) {
			slop_bgn_size = 0;
			slop_end_size = esc->sc_dmasize;
		}

		/* initialize the fifo buffer */
		if (slop_bgn_size != 0) {
			esc->sc_begin = *esc->sc_dmaaddr;
			esc->sc_begin_size = slop_bgn_size;
		} else {
			esc->sc_begin = 0;
			esc->sc_begin_size = 0;
		}

#if 1
		/* Load the normal DMA map */
		{
			esc->sc_main = *esc->sc_dmaaddr;
			esc->sc_main += slop_bgn_size;
			esc->sc_main_size =
			    (esc->sc_dmasize) - (slop_end_size + slop_bgn_size);

			if (esc->sc_main_size != 0) {
				int error;

				if (!esc->sc_datain ||
				    DMA_ENDALIGNED(esc->sc_main_size +
				    slop_end_size)) {
					KASSERT(DMA_SCSI_ALIGNMENT ==
					    DMA_ENDALIGNMENT);
					KASSERT(DMA_BEGINALIGNMENT ==
					    DMA_ENDALIGNMENT);
					esc->sc_main_size += slop_end_size;
					slop_end_size = 0;
					if (!esc->sc_datain) {
						esc->sc_main_size =
						    DMA_ENDALIGN(uint8_t *,
						    esc->sc_main +
						    esc->sc_main_size) -
						    esc->sc_main;
					}
				}

				error = bus_dmamap_load(esc->sc_dma->sc_dmat,
				    esc->sc_main_dmamap,
				    esc->sc_main, esc->sc_main_size,
				    NULL, BUS_DMA_NOWAIT);
				if (error != 0) {
#ifdef ESP_DEBUG
					printf("%s: esc->sc_main_dmamap->"
					    "_dm_size = %ld\n",
					    device_xname(sc->sc_dev),
					    esc->sc_main_dmamap->_dm_size);
					printf("%s: esc->sc_main_dmamap->"
					    "_dm_segcnt = %d\n",
					    device_xname(sc->sc_dev),
					    esc->sc_main_dmamap->_dm_segcnt);
#ifdef notdef
					printf("%s: esc->sc_main_dmamap->"
					    "_dm_maxsegsz = %ld\n",
					    device_xname(sc->sc_dev),
					    esc->sc_main_dmamap->_dm_maxsegsz);
#endif
					printf("%s: esc->sc_main_dmamap->"
					    "_dm_boundary = %ld\n",
					    device_xname(sc->sc_dev),
					    esc->sc_main_dmamap->_dm_boundary);
					esp_dma_print(sc);
#endif
					panic("%s: can't load main DMA map."
					    " error = %d, addr=%p, size=0x%08x",
					    device_xname(sc->sc_dev),
					    error, esc->sc_main,
					    esc->sc_main_size);
				}
				if (!esc->sc_datain) {
					/*
					 * patch the DMA map for write overrun
					*/
					esc->sc_main_dmamap->dm_mapsize +=
					    ESP_DMA_OVERRUN;
					esc->sc_main_dmamap->dm_segs[
					    esc->sc_main_dmamap->dm_nsegs -
					    1].ds_len +=
						ESP_DMA_OVERRUN;
				}
#if 0
				bus_dmamap_sync(esc->sc_dma->sc_dmat,
				    esc->sc_main_dmamap,
				    0, esc->sc_main_dmamap->dm_mapsize,
				    (esc->sc_datain ?  BUS_DMASYNC_PREREAD :
				     BUS_DMASYNC_PREWRITE));
				esc->sc_main_dmamap->dm_xfer_len = 0;
#endif
			} else {
				esc->sc_main = 0;
			}
		}

		/* Load the tail DMA map */
		if (slop_end_size != 0) {
			esc->sc_tail = DMA_ENDALIGN(uint8_t *,
			    esc->sc_tailbuf + slop_end_size) - slop_end_size;
			/*
			 * If the beginning of the tail is not correctly
			 * aligned, we have no choice but to align the start,
			 * which might then unalign the end.
			 */
			esc->sc_tail = DMA_SCSI_ALIGN(uint8_t *, esc->sc_tail);
			/*
			 * So therefore, we change the tail size to be
			 * end aligned again.
			 */
			esc->sc_tail_size = DMA_ENDALIGN(uint8_t *,
			    esc->sc_tail + slop_end_size) - esc->sc_tail;

			/* @@@ next DMA overrun lossage */
			if (!esc->sc_datain) {
				esc->sc_tail_size += ESP_DMA_OVERRUN;
			}

			{
				int error;
				error = bus_dmamap_load(esc->sc_dma->sc_dmat,
				    esc->sc_tail_dmamap,
				    esc->sc_tail, esc->sc_tail_size,
				    NULL, BUS_DMA_NOWAIT);
				if (error) {
					panic("%s: can't load tail DMA map."
					    " error = %d, addr=%p, size=0x%08x",
					    device_xname(sc->sc_dev), error,
					    esc->sc_tail,esc->sc_tail_size);
				}
#if 0
				bus_dmamap_sync(esc->sc_dma->sc_dmat,
				    esc->sc_tail_dmamap, 0,
				    esc->sc_tail_dmamap->dm_mapsize,
				    (esc->sc_datain ? BUS_DMASYNC_PREREAD :
				     BUS_DMASYNC_PREWRITE));
				esc->sc_tail_dmamap->dm_xfer_len = 0;
#endif
			}
		}
#else

		esc->sc_begin = *esc->sc_dmaaddr;
		slop_bgn_size = DMA_SCSI_ALIGNMENT -
		    ((u_long)esc->sc_begin % DMA_SCSI_ALIGNMENT);
		if (slop_bgn_size == DMA_SCSI_ALIGNMENT)
			slop_bgn_size = 0;
		slop_end_size = esc->sc_dmasize - slop_bgn_size;

		if (slop_bgn_size < esc->sc_dmasize) {
			int error;

			esc->sc_tail = 0;
			esc->sc_tail_size = 0;

			esc->sc_begin_size = slop_bgn_size;
			esc->sc_main = *esc->sc_dmaaddr;
			esc->sc_main += slop_bgn_size;
			esc->sc_main_size = DMA_ENDALIGN(uint8_t *,
			    esc->sc_main + esc->sc_dmasize - slop_bgn_size) -
			    esc->sc_main;

			if (!esc->sc_datain) {
				esc->sc_main_size += ESP_DMA_OVERRUN;
			}
			error = bus_dmamap_load(esc->sc_dma->sc_dmat,
			    esc->sc_main_dmamap,
			    esc->sc_main, esc->sc_main_size,
			    NULL, BUS_DMA_NOWAIT);
			if (error) {
				panic("%s: can't load main DMA map."
				    " error = %d, addr=%p, size=0x%08x",
				    device_xname(sc->sc_dev), error,
				    esc->sc_main,esc->sc_main_size);
			}
		} else {
			esc->sc_begin = 0;
			esc->sc_begin_size = 0;
			esc->sc_main = 0;
			esc->sc_main_size = 0;

#if 0
			esc->sc_tail = DMA_ENDALIGN(uint8_t *,
			    esc->sc_tailbuf + slop_bgn_size) - slop_bgn_size;
			/*
			 * If the beginning of the tail is not correctly
			 * aligned, we have no choice but to align the start,
			 * which might then unalign the end.
			 */
#endif
			esc->sc_tail = DMA_SCSI_ALIGN(void *, esc->sc_tailbuf);
			/*
			 * So therefore, we change the tail size to be
			 * end aligned again.
			 */
			esc->sc_tail_size = DMA_ENDALIGN(uint8_t *,
			    esc->sc_tail + esc->sc_dmasize) - esc->sc_tail;

			/* @@@ next DMA overrun lossage */
			if (!esc->sc_datain) {
				esc->sc_tail_size += ESP_DMA_OVERRUN;
			}

			{
				int error;
				error = bus_dmamap_load(esc->sc_dma->sc_dmat,
				    esc->sc_tail_dmamap,
				    esc->sc_tail, esc->sc_tail_size,
				    NULL, BUS_DMA_NOWAIT);
				if (error != 0) {
					panic("%s: can't load tail DMA map."
					    " error = %d, addr=%p, size=0x%08x",
					    device_xname(sc->sc_dev), error,
					    esc->sc_tail, esc->sc_tail_size);
				}
			}
		}
#endif

		DPRINTF(("%s: setup: %8p %d %8p %d %8p %d %8p %d\n",
		    device_xname(sc->sc_dev),
		    *esc->sc_dmaaddr, esc->sc_dmasize,
		    esc->sc_begin, esc->sc_begin_size,
		    esc->sc_main, esc->sc_main_size,
		    esc->sc_tail, esc->sc_tail_size));
	}

	return 0;
}

#ifdef ESP_DEBUG
/* For debugging */
void
esp_dma_store(struct ncr53c9x_softc *sc)
{
	struct esp_softc *esc = (struct esp_softc *)sc;
	char *p = esp_dma_dump;
	size_t l = 0;
	size_t len = sizeof(esp_dma_dump);

	l += snprintf(p + l, len - l, "%s: sc_datain=%d\n",
	    device_xname(sc->sc_dev), esc->sc_datain);
	if (l > len)
		return;
	l += snprintf(p + l, len - l, "%s: sc_loaded=0x%08x\n",
	    device_xname(sc->sc_dev), esc->sc_loaded);
	if (l > len)
		return;

	if (esc->sc_dmaaddr != 0) {
		l += snprintf(p + l, len - l, "%s: sc_dmaaddr=%p\n",
		    device_xname(sc->sc_dev), *esc->sc_dmaaddr);
	} else {
		l += snprintf(p + l, len - l, "%s: sc_dmaaddr=NULL\n",
		    device_xname(sc->sc_dev));
	}
	if (l > len)
		return;
	if (esc->sc_dmalen) {
		l += snprintf(p + l, len - l, "%s: sc_dmalen=0x%08x\n",
		    device_xname(sc->sc_dev), *esc->sc_dmalen);
	} else {
		l += snprintf(p + l, len - l, "%s: sc_dmalen=NULL\n",
		    device_xname(sc->sc_dev));
	}
	if (l > len)
		return;
	l += snprintf(p + l, len - l, "%s: sc_dmasize=0x%08x\n",
	    device_xname(sc->sc_dev), esc->sc_dmasize);
	if (l > len)
		return;

	l += snprintf(p + l, len - l,
	    "%s: sc_begin = %p, sc_begin_size = 0x%08x\n",
	    device_xname(sc->sc_dev), esc->sc_begin, esc->sc_begin_size);
	if (l > len)
		return;
	l += snprintf(p + l, len - l,
	    "%s: sc_main = %p, sc_main_size = 0x%08x\n",
	    device_xname(sc->sc_dev), esc->sc_main, esc->sc_main_size);
	if (l > len)
		return;
#if 0
	if (esc->sc_main)
#endif
	{
		int i;
		bus_dmamap_t map = esc->sc_main_dmamap;
		l += snprintf(p + l, len - l, "%s: sc_main_dmamap."
		    " mapsize = 0x%08lx, nsegs = %d\n",
		    device_xname(sc->sc_dev), map->dm_mapsize, map->dm_nsegs);
		if (l > len)
			return;
		for(i = 0; i < map->dm_nsegs; i++) {
			l += snprintf(p + l, len - l, "%s:"
			    " map->dm_segs[%d].ds_addr = 0x%08lx,"
			    " len = 0x%08lx\n",
			    device_xname(sc->sc_dev),
			    i, map->dm_segs[i].ds_addr,
			    map->dm_segs[i].ds_len);
			    if (l > len)
				    return;
		}
	}
	l += snprintf(p + l, len - l,
	    "%s: sc_tail = %p, sc_tail_size = 0x%08x\n",
	    device_xname(sc->sc_dev), esc->sc_tail, esc->sc_tail_size);
	if (l > len)
		return;
#if 0
	if (esc->sc_tail)
#endif
	{
		int i;
		bus_dmamap_t map = esc->sc_tail_dmamap;
		l += snprintf(p + l, len - l, "%s: sc_tail_dmamap."
		    " mapsize = 0x%08lx, nsegs = %d\n",
		    device_xname(sc->sc_dev), map->dm_mapsize, map->dm_nsegs);
		if (l > len)
			return;
		for (i = 0; i < map->dm_nsegs; i++) {
			l += snprintf(p + l, len - l, "%s:"
			    " map->dm_segs[%d].ds_addr = 0x%08lx,"
			    " len = 0x%08lx\n",
			    device_xname(sc->sc_dev),
			    i, map->dm_segs[i].ds_addr,
			     map->dm_segs[i].ds_len);
			if (l > len)
				return;
		}
	}
}

void
esp_dma_print(struct ncr53c9x_softc *sc)
{

	esp_dma_store(sc);
	printf("%s", esp_dma_dump);
}
#endif

static void
esp_dma_go(struct ncr53c9x_softc *sc)
{
	struct esp_softc *esc = (struct esp_softc *)sc;
	struct nextdma_softc *nsc = esc->sc_dma;
	struct nextdma_status *stat = &nsc->sc_stat;
#if 0
	int s = spldma();
#endif

#ifdef ESP_DEBUG
	if (!ndtrace_empty()) {
		if (esptraceshow) {
			printf("esp ndtrace: %s\n", ndtrace_get());
			esptraceshow = 0;
		} else {
			DPRINTF(("X"));
		}
		ndtrace_reset();
	}
#endif

	DPRINTF(("%s: esp_dma_go(datain = %d)\n",
	    device_xname(sc->sc_dev), esc->sc_datain));

#ifdef ESP_DEBUG
	if (esp_debug)
		esp_dma_print(sc);
	else
		esp_dma_store(sc);
#endif

#ifdef ESP_DEBUG
	{
		int n = NCR_READ_REG(sc, NCR_FFLAG);
		DPRINTF(("%s: fifo size = %d, seq = 0x%x\n",
		    device_xname(sc->sc_dev),
		    n & NCRFIFO_FF, (n & NCRFIFO_SS) >> 5));
	}
#endif

	/* zero length DMA transfers are boring */
	if (esc->sc_dmasize == 0) {
#if 0
		splx(s);
#endif
		return;
	}

#if defined(DIAGNOSTIC)
	if ((esc->sc_begin_size == 0) &&
	    (esc->sc_main_dmamap->dm_mapsize == 0) &&
	    (esc->sc_tail_dmamap->dm_mapsize == 0)) {
#ifdef ESP_DEBUG
		esp_dma_print(sc);
#endif
		panic("%s: No DMA requested!", device_xname(sc->sc_dev));
	}
#endif

	/* Stuff the fifo with the begin buffer */
	if (esc->sc_datain) {
		int i;
		DPRINTF(("%s: FIFO read of %d bytes:",
		    device_xname(sc->sc_dev), esc->sc_begin_size));
		for (i = 0; i < esc->sc_begin_size; i++) {
			esc->sc_begin[i] = NCR_READ_REG(sc, NCR_FIFO);
			DPRINTF((" %02x", esc->sc_begin[i] & 0xff));
		}
		DPRINTF(("\n"));
	} else {
		int i;
		DPRINTF(("%s: FIFO write of %d bytes:",
		    device_xname(sc->sc_dev), esc->sc_begin_size));
		for (i = 0; i < esc->sc_begin_size; i++) {
			NCR_WRITE_REG(sc, NCR_FIFO, esc->sc_begin[i]);
			DPRINTF((" %02x",esc->sc_begin[i] & 0xff));
		}
		DPRINTF(("\n"));
	}

	if (esc->sc_main_dmamap->dm_mapsize != 0) {
		bus_dmamap_sync(esc->sc_dma->sc_dmat, esc->sc_main_dmamap,
		    0, esc->sc_main_dmamap->dm_mapsize,
		    (esc->sc_datain ?
		     BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE));
		esc->sc_main_dmamap->dm_xfer_len = 0;
	}

	if (esc->sc_tail_dmamap->dm_mapsize != 0) {
		/* if we are a DMA write cycle, copy the end slop */
		if (!esc->sc_datain) {
			memcpy(esc->sc_tail, *esc->sc_dmaaddr +
			    esc->sc_begin_size+esc->sc_main_size,
			    esc->sc_dmasize -
			    (esc->sc_begin_size + esc->sc_main_size));
		}
		bus_dmamap_sync(esc->sc_dma->sc_dmat, esc->sc_tail_dmamap,
		    0, esc->sc_tail_dmamap->dm_mapsize,
		    (esc->sc_datain ?
		     BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE));
		esc->sc_tail_dmamap->dm_xfer_len = 0;
	}

	stat->nd_exception = 0;
	nextdma_start(nsc, (esc->sc_datain ? DMACSR_SETREAD : DMACSR_SETWRITE));

	if (esc->sc_datain) {
		NCR_WRITE_REG(sc, ESP_DCTL,
		    ESPDCTL_16MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD |
		    ESPDCTL_DMARD);
	} else {
		NCR_WRITE_REG(sc, ESP_DCTL,
		    ESPDCTL_16MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD);
	}
	DPRINTF(("esp dctl is 0x%02x\n",NCR_READ_REG(sc,ESP_DCTL)));

	NDTRACEIF(
		if (esc->sc_begin_size != 0) {
			ndtrace_addc('1');
			ndtrace_addc('A' + esc->sc_begin_size);
		}
	);
	NDTRACEIF(
		if (esc->sc_main_size != 0) {
			ndtrace_addc('2');
			ndtrace_addc('0' + esc->sc_main_dmamap->dm_nsegs);
		}
	);
	NDTRACEIF(
		if (esc->sc_tail_size != 0) {
			ndtrace_addc('3');
			ndtrace_addc('A' + esc->sc_tail_size);
		}
	);

#if 0
	splx(s);
#endif
}

static void
esp_dma_stop(struct ncr53c9x_softc *sc)
{
	struct esp_softc *esc = (struct esp_softc *)sc;

	nextdma_print(esc->sc_dma);
#ifdef ESP_DEBUG
	esp_dma_print(sc);
#endif
#if 1
	panic("%s: stop not yet implemented", device_xname(sc->sc_dev));
#endif
}

static int
esp_dma_isactive(struct ncr53c9x_softc *sc)
{
	struct esp_softc *esc = (struct esp_softc *)sc;
	int r;

#if 0
	r = !nextdma_finished(esc->sc_dma);
#else
	r = (esc->sc_dmaaddr != NULL);
#endif
	DPRINTF(("esp_dma_isactive = %d\n",r));
	return r;
}

/****************************************************************/

int esp_dma_int(void *);	/* XXX: called from nextdma.c */
int esp_dma_int(void *arg)
{
	void nextdma_rotate(struct nextdma_softc *);		/* XXX */
	void nextdma_setup_curr_regs(struct nextdma_softc *);	/* XXX */
	void nextdma_setup_cont_regs(struct nextdma_softc *);	/* XXX */

	struct ncr53c9x_softc *sc = (struct ncr53c9x_softc *)arg;
	struct esp_softc *esc = (struct esp_softc *)sc;
	struct nextdma_softc *nsc = esc->sc_dma;
	struct nextdma_status *stat = &nsc->sc_stat;
	unsigned int state;

	NDTRACEIF(ndtrace_addc('E'));

	state = nd_bsr4(DD_CSR);

#if 1
	NDTRACEIF(
		if (state & DMACSR_COMPLETE)
			ndtrace_addc('c');
		if (state & DMACSR_ENABLE)
			ndtrace_addc('e');
		if (state & DMACSR_BUSEXC)
			ndtrace_addc('b');
		if (state & DMACSR_READ)
			ndtrace_addc('r');
		if (state & DMACSR_SUPDATE)
			ndtrace_addc('s');
		);

	NDTRACEIF(ndtrace_addc('E'));

#ifdef ESP_DEBUG
	if (0)
		if ((state & DMACSR_BUSEXC) && (state & DMACSR_ENABLE))
			esptraceshow++;
	if (0)
		if ((state & DMACSR_SUPDATE))
			esptraceshow++;
#endif
#endif

	if ((stat->nd_exception == 0) &&
	    ((state & DMACSR_COMPLETE) != 0) &&
	    ((state & DMACSR_ENABLE) != 0)) {
		stat->nd_map->dm_xfer_len +=
		    stat->nd_map->dm_segs[stat->nd_idx].ds_len;
	}

	if ((stat->nd_idx + 1) == stat->nd_map->dm_nsegs) {
		if (nsc->sc_conf.nd_completed_cb)
			(*nsc->sc_conf.nd_completed_cb)(stat->nd_map,
			    nsc->sc_conf.nd_cb_arg);
	}
	nextdma_rotate(nsc);

	if ((state & DMACSR_COMPLETE) != 0 &&
	    (state & DMACSR_ENABLE) != 0) {
#if 0
		int l = nd_bsr4(DD_LIMIT) & 0x7FFFFFFF;
		int s = nd_bsr4(DD_STOP);
#endif
#if 0
		nextdma_setup_cont_regs(nsc);
#endif
		if (stat->nd_map_cont != NULL) {
			nd_bsw4(DD_START, stat->nd_map_cont->dm_segs[
			    stat->nd_idx_cont].ds_addr);
			nd_bsw4(DD_STOP, (stat->nd_map_cont->dm_segs[
			    stat->nd_idx_cont].ds_addr +
			    stat->nd_map_cont->dm_segs[
			    stat->nd_idx_cont].ds_len));
		}

		nd_bsw4(DD_CSR, DMACSR_CLRCOMPLETE |
		    (state & DMACSR_READ ? DMACSR_SETREAD : DMACSR_SETWRITE) |
		     (stat->nd_map_cont ? DMACSR_SETSUPDATE : 0));

#if 0
#ifdef ESP_DEBUG
		if ((state & DMACSR_BUSEXC) != 0) {
			ndtrace_printf("CE/BUSEXC: %08lX %08X %08X\n",
			    (stat->nd_map->dm_segs[stat->nd_idx].ds_addr +
			     stat->nd_map->dm_segs[stat->nd_idx].ds_len),
			    l, s);
		}
#endif
#endif
	} else {
#if 0
		if ((state & DMACSR_BUSEXC) != 0) {
			while (nd_bsr4(DD_NEXT) !=
			       (nd_bsr4(DD_LIMIT) & 0x7FFFFFFF))
				printf("Y"); /* DELAY(50); */
			state = nd_bsr4(DD_CSR);
		}
#endif

		if ((state & DMACSR_SUPDATE) == 0) {
			nextdma_rotate(nsc);
		} else {
			nd_bsw4(DD_CSR, DMACSR_CLRCOMPLETE |
			    DMACSR_INITBUF | DMACSR_RESET |
			    (state & DMACSR_READ ?
			     DMACSR_SETREAD : DMACSR_SETWRITE));

			nd_bsw4(DD_NEXT,
			    stat->nd_map->dm_segs[stat->nd_idx].ds_addr);
			nd_bsw4(DD_LIMIT,
			    (stat->nd_map->dm_segs[stat->nd_idx].ds_addr +
			    stat->nd_map->dm_segs[stat->nd_idx].ds_len) |
			    0/* x80000000 */);
			if (stat->nd_map_cont) {
				nd_bsw4(DD_START,
				    stat->nd_map_cont->dm_segs[
				    stat->nd_idx_cont].ds_addr);
				nd_bsw4(DD_STOP,
				    (stat->nd_map_cont->dm_segs[
				     stat->nd_idx_cont].ds_addr +
				     stat->nd_map_cont->dm_segs[
				     stat->nd_idx_cont].ds_len) |
				     0/* x80000000 */);
			}
			nd_bsw4(DD_CSR, DMACSR_SETENABLE | DMACSR_CLRCOMPLETE |
			    (state & DMACSR_READ ?
			     DMACSR_SETREAD : DMACSR_SETWRITE) |
			    (stat->nd_map_cont ? DMACSR_SETSUPDATE : 0));
#if 1
#ifdef ESP_DEBUG
			ndtrace_printf("supdate ");
			ndtrace_printf("%08X %08X %08X %08X ",
			    nd_bsr4(DD_NEXT),
			    nd_bsr4(DD_LIMIT) & 0x7FFFFFFF,
			    nd_bsr4(DD_START),
			    nd_bsr4(DD_STOP) & 0x7FFFFFFF);
#endif
#endif
			stat->nd_exception++;
			return 1;
			/* NCR_WRITE_REG(sc, ESP_DCTL, ctl); */
			goto restart;
		}

		if (stat->nd_map != NULL) {
#if 1
#ifdef ESP_DEBUG
			ndtrace_printf("%08X %08X %08X %08X ",
			    nd_bsr4(DD_NEXT),
			    nd_bsr4(DD_LIMIT) & 0x7FFFFFFF,
			    nd_bsr4(DD_START),
			    nd_bsr4(DD_STOP) & 0x7FFFFFFF);
#endif
#endif

#if 0
			nd_bsw4(DD_CSR, DMACSR_CLRCOMPLETE | DMACSR_RESET);

			nd_bsw4(DD_CSR, 0);
#endif
#if 1
 /* 6/2 */
			nd_bsw4(DD_CSR, DMACSR_CLRCOMPLETE |
			    DMACSR_INITBUF | DMACSR_RESET |
			    (state & DMACSR_READ ?
			     DMACSR_SETREAD : DMACSR_SETWRITE));

			/* nextdma_setup_curr_regs(nsc); */
			nd_bsw4(DD_NEXT,
			    stat->nd_map->dm_segs[stat->nd_idx].ds_addr);
			nd_bsw4(DD_LIMIT,
			    (stat->nd_map->dm_segs[stat->nd_idx].ds_addr +
			    stat->nd_map->dm_segs[stat->nd_idx].ds_len) |
			    0/* x80000000 */);
			/* nextdma_setup_cont_regs(nsc); */
			if (stat->nd_map_cont != NULL) {
				nd_bsw4(DD_START,
				    stat->nd_map_cont->dm_segs[
				    stat->nd_idx_cont].ds_addr);
				nd_bsw4(DD_STOP,
				    (stat->nd_map_cont->dm_segs[
				    stat->nd_idx_cont].ds_addr +
				    stat->nd_map_cont->dm_segs[
				    stat->nd_idx_cont].ds_len) |
				    0/* x80000000 */);
			}

			nd_bsw4(DD_CSR, DMACSR_SETENABLE |
			    (stat->nd_map_cont ? DMACSR_SETSUPDATE : 0) |
			    (state & DMACSR_READ ?
			     DMACSR_SETREAD : DMACSR_SETWRITE));
#ifdef ESP_DEBUG
#if 0
			esptraceshow++;
#endif
#endif
			stat->nd_exception++;
			return 1;
#endif
#if 0
			NCR_WRITE_REG(sc, ESP_DCTL, ctl);
#endif
			goto restart;
		restart:
#if 1
#ifdef ESP_DEBUG
			ndtrace_printf("restart %08lX %08lX\n",
			    stat->nd_map->dm_segs[stat->nd_idx].ds_addr,
			    stat->nd_map->dm_segs[stat->nd_idx].ds_addr +
			    stat->nd_map->dm_segs[stat->nd_idx].ds_len);
			if (stat->nd_map_cont != NULL) {
				ndtrace_printf(" %08lX %08lX\n",
				    stat->nd_map_cont->dm_segs[
				    stat->nd_idx_cont].ds_addr,
				    stat->nd_map_cont->dm_segs[
				    stat->nd_idx_cont].ds_addr +
				    stat->nd_map_cont->dm_segs[
				    stat->nd_idx_cont].ds_len);
			}
#endif
#endif
			nextdma_print(nsc);
			NCR_WRITE_REG(sc, ESP_DCTL,
			    ESPDCTL_16MHZ | ESPDCTL_INTENB);
			printf("ff:%02x tcm:%d tcl:%d esp_dstat:%02x"
			    " state:%02x step: %02x intr:%02x state:%08X\n",
			    NCR_READ_REG(sc, NCR_FFLAG),
			    NCR_READ_REG((sc), NCR_TCM),
			    NCR_READ_REG((sc), NCR_TCL),
			    NCR_READ_REG(sc, ESP_DSTAT),
			    NCR_READ_REG(sc, NCR_STAT),
			    NCR_READ_REG(sc, NCR_STEP),
			    NCR_READ_REG(sc, NCR_INTR), state);
#ifdef ESP_DEBUG
			printf("ndtrace: %s\n", ndtrace_get());
#endif
			panic("%s: busexc/supdate occurred."
			    "  Please email this output to chris@pin.lu.",
			    device_xname(sc->sc_dev));
#ifdef ESP_DEBUG
			esptraceshow++;
#endif
		} else {
			nd_bsw4(DD_CSR, DMACSR_CLRCOMPLETE | DMACSR_RESET);
			if (nsc->sc_conf.nd_shutdown_cb)
				(*nsc->sc_conf.nd_shutdown_cb)(
				    nsc->sc_conf.nd_cb_arg);
		}
	}
	return 1;
}

/* Internal DMA callback routines */
static bus_dmamap_t
esp_dmacb_continue(void *arg)
{
	struct ncr53c9x_softc *sc = arg;
	struct esp_softc *esc = (struct esp_softc *)sc;

	NDTRACEIF(ndtrace_addc('x'));
	DPRINTF(("%s: DMA continue\n", device_xname(sc->sc_dev)));

#ifdef DIAGNOSTIC
	if (esc->sc_datain < 0 || esc->sc_datain > 1) {
		panic("%s: map not loaded in DMA continue callback,"
		    " datain = %d",
		    device_xname(sc->sc_dev), esc->sc_datain);
	}
#endif

	if ((esc->sc_loaded & ESP_LOADED_MAIN) == 0 &&
	    esc->sc_main_dmamap->dm_mapsize != 0) {
		DPRINTF(("%s: Loading main map\n", device_xname(sc->sc_dev)));
#if 0
		bus_dmamap_sync(esc->sc_dma->sc_dmat, esc->sc_main_dmamap,
		    0, esc->sc_main_dmamap->dm_mapsize,
		    (esc->sc_datain ?
		     BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE));
		    esc->sc_main_dmamap->dm_xfer_len = 0;
#endif
		esc->sc_loaded |= ESP_LOADED_MAIN;
		return esc->sc_main_dmamap;
	}

	if ((esc->sc_loaded & ESP_LOADED_TAIL) == 0 &&
	    esc->sc_tail_dmamap->dm_mapsize != 0) {
		DPRINTF(("%s: Loading tail map\n", device_xname(sc->sc_dev)));
#if 0
		bus_dmamap_sync(esc->sc_dma->sc_dmat, esc->sc_tail_dmamap,
		    0, esc->sc_tail_dmamap->dm_mapsize,
		    (esc->sc_datain ?
		     BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE));
		esc->sc_tail_dmamap->dm_xfer_len = 0;
#endif
		esc->sc_loaded |= ESP_LOADED_TAIL;
		return esc->sc_tail_dmamap;
	}

	DPRINTF(("%s: not loading map\n", device_xname(sc->sc_dev)));
	return 0;
}


static void
esp_dmacb_completed(bus_dmamap_t map, void *arg)
{
	struct ncr53c9x_softc *sc = (struct ncr53c9x_softc *)arg;
	struct esp_softc *esc = (struct esp_softc *)sc;

	NDTRACEIF(ndtrace_addc('X'));
	DPRINTF(("%s: DMA completed\n", device_xname(sc->sc_dev)));

#ifdef DIAGNOSTIC
	if (esc->sc_datain < 0 || esc->sc_datain > 1) {
		panic("%s: invalid DMA direction in completed callback,"
		    " datain = %d",
		    device_xname(sc->sc_dev), esc->sc_datain);
	}
#endif

#if defined(DIAGNOSTIC) && 0
	{
		int i;
		for(i = 0; i < map->dm_nsegs; i++) {
			if (map->dm_xfer_len != map->dm_mapsize) {
				printf("%s: map->dm_mapsize = %d\n",
				    device_xname(sc->sc_dev), map->dm_mapsize);
				printf("%s: map->dm_nsegs = %d\n",
				    device_xname(sc->sc_dev), map->dm_nsegs);
				printf("%s: map->dm_xfer_len = %d\n",
				    device_xname(sc->sc_dev), map->dm_xfer_len);
				for(i = 0; i < map->dm_nsegs; i++) {
					printf("%s: map->dm_segs[%d].ds_addr ="
					    " 0x%08lx\n",
					    device_xname(sc->sc_dev), i,
					    map->dm_segs[i].ds_addr);
					printf("%s: map->dm_segs[%d].ds_len ="
					    " %d\n",
					    device_xname(sc->sc_dev), i,
					    map->dm_segs[i].ds_len);
				}
				panic("%s: incomplete DMA transfer",
				    device_xname(sc->sc_dev));
			}
		}
	}
#endif

	if (map == esc->sc_main_dmamap) {
#ifdef DIAGNOSTIC
		if ((esc->sc_loaded & ESP_UNLOADED_MAIN) ||
		    (esc->sc_loaded & ESP_LOADED_MAIN) == 0) {
			panic("%s: unexpected completed call for main map",
			    device_xname(sc->sc_dev));
		}
#endif
		esc->sc_loaded |= ESP_UNLOADED_MAIN;
	} else if (map == esc->sc_tail_dmamap) {
#ifdef DIAGNOSTIC
		if ((esc->sc_loaded & ESP_UNLOADED_TAIL) ||
		    (esc->sc_loaded & ESP_LOADED_TAIL) == 0) {
			panic("%s: unexpected completed call for tail map",
			    device_xname(sc->sc_dev));
		}
#endif
		esc->sc_loaded |= ESP_UNLOADED_TAIL;
	}
#ifdef DIAGNOSTIC
	 else {
		panic("%s: unexpected completed map", device_xname(sc->sc_dev));
	}
#endif

#ifdef ESP_DEBUG
	if (esp_debug) {
		if (map == esc->sc_main_dmamap) {
			printf("%s: completed main map\n",
			    device_xname(sc->sc_dev));
		} else if (map == esc->sc_tail_dmamap) {
			printf("%s: completed tail map\n",
			    device_xname(sc->sc_dev));
		}
	}
#endif

#if 0
	if ((map == esc->sc_tail_dmamap) ||
	    ((esc->sc_tail_size == 0) && (map == esc->sc_main_dmamap))) {

		/*
		 * Clear the DMAMOD bit in the DCTL register to give control
		 * back to the scsi chip.
		 */
		if (esc->sc_datain) {
			NCR_WRITE_REG(sc, ESP_DCTL,
			    ESPDCTL_16MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD);
		} else {
			NCR_WRITE_REG(sc, ESP_DCTL,
			    ESPDCTL_16MHZ | ESPDCTL_INTENB);
		}
		DPRINTF(("esp dctl is 0x%02x\n", NCR_READ_REG(sc, ESP_DCTL)));
	}
#endif


#if 0
	bus_dmamap_sync(esc->sc_dma->sc_dmat, map,
	    0, map->dm_mapsize,
	    (esc->sc_datain ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE));
#endif

}

static void
esp_dmacb_shutdown(void *arg)
{
	struct ncr53c9x_softc *sc = (struct ncr53c9x_softc *)arg;
	struct esp_softc *esc = (struct esp_softc *)sc;

	NDTRACEIF (ndtrace_addc('S'));
	DPRINTF(("%s: DMA shutdown\n", device_xname(sc->sc_dev)));

	if (esc->sc_loaded == 0)
		return;

#if 0
	{
		/* Clear the DMAMOD bit in the DCTL register to give control
		 * back to the scsi chip.
		 */
		if (esc->sc_datain) {
			NCR_WRITE_REG(sc, ESP_DCTL,
			    ESPDCTL_16MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD);
		} else {
			NCR_WRITE_REG(sc, ESP_DCTL,
			    ESPDCTL_16MHZ | ESPDCTL_INTENB);
		}
		DPRINTF(("esp dctl is 0x%02x\n", NCR_READ_REG(sc, ESP_DCTL)));
	}
#endif

	DPRINTF(("%s: esp_dma_nest == %d\n",
	    device_xname(sc->sc_dev), esp_dma_nest));

	/* Stuff the end slop into fifo */

#ifdef ESP_DEBUG
	if (esp_debug) {
		int n = NCR_READ_REG(sc, NCR_FFLAG);

		DPRINTF(("%s: fifo size = %d, seq = 0x%x\n",
		    device_xname(sc->sc_dev), n & NCRFIFO_FF,
		    (n & NCRFIFO_SS) >> 5));
	}
#endif

	if (esc->sc_main_dmamap->dm_mapsize != 0) {
		if (!esc->sc_datain) {
			/* unpatch the DMA map for write overrun */
			esc->sc_main_dmamap->dm_mapsize -= ESP_DMA_OVERRUN;
			esc->sc_main_dmamap->dm_segs[
			    esc->sc_main_dmamap->dm_nsegs - 1].ds_len -=
			    ESP_DMA_OVERRUN;
		}
		bus_dmamap_sync(esc->sc_dma->sc_dmat, esc->sc_main_dmamap,
		    0, esc->sc_main_dmamap->dm_mapsize,
		    (esc->sc_datain ?
		     BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE));
		bus_dmamap_unload(esc->sc_dma->sc_dmat, esc->sc_main_dmamap);
		NDTRACEIF(
			ndtrace_printf("m%ld",
			    esc->sc_main_dmamap->dm_xfer_len);
		);
	}

	if (esc->sc_tail_dmamap->dm_mapsize != 0) {
		bus_dmamap_sync(esc->sc_dma->sc_dmat, esc->sc_tail_dmamap,
		    0, esc->sc_tail_dmamap->dm_mapsize,
		    (esc->sc_datain ?
		     BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE));
		bus_dmamap_unload(esc->sc_dma->sc_dmat, esc->sc_tail_dmamap);
		/* copy the tail DMA buffer data for read transfers */
		if (esc->sc_datain) {
			memcpy(*esc->sc_dmaaddr + esc->sc_begin_size +
			    esc->sc_main_size, esc->sc_tail,
			    esc->sc_dmasize -
			    (esc->sc_begin_size + esc->sc_main_size));
		}
		NDTRACEIF(
			ndtrace_printf("t%ld",
			    esc->sc_tail_dmamap->dm_xfer_len);
		);
	}

#ifdef ESP_DEBUG
	if (esp_debug) {
		printf("%s: dma_shutdown: addr=%p,len=0x%08x,size=0x%08x\n",
		    device_xname(sc->sc_dev),
		    *esc->sc_dmaaddr, *esc->sc_dmalen, esc->sc_dmasize);
		if (esp_debug > 10) {
			esp_hex_dump(*(esc->sc_dmaaddr), esc->sc_dmasize);
			printf("%s: tail=%p,tailbuf=%p,tail_size=0x%08x\n",
			    device_xname(sc->sc_dev),
			    esc->sc_tail, &(esc->sc_tailbuf[0]),
			    esc->sc_tail_size);
			esp_hex_dump(&(esc->sc_tailbuf[0]),
			    sizeof(esc->sc_tailbuf));
		}
	}
#endif

	esc->sc_main = 0;
	esc->sc_main_size = 0;
	esc->sc_tail = 0;
	esc->sc_tail_size = 0;

	esc->sc_datain = -1;
#if 0
	esc->sc_dmaaddr = 0;
	esc->sc_dmalen  = 0;
	esc->sc_dmasize = 0;
#endif

	esc->sc_loaded = 0;

	esc->sc_begin = 0;
	esc->sc_begin_size = 0;

#ifdef ESP_DEBUG
	if (esp_debug) {
		char sbuf[256];

		snprintb(sbuf, sizeof(sbuf), NEXT_INTR_BITS,
		    (*(volatile u_long *)IIOV(NEXT_P_INTRSTAT)));
		printf("  *intrstat = %s\n", sbuf);

		snprintb(sbuf, sizeof(sbuf), NEXT_INTR_BITS,
		    (*(volatile u_long *)IIOV(NEXT_P_INTRMASK)));
		printf("  *intrmask = %s\n", sbuf);
	}
#endif
}
