/*	$OpenBSD: trm.c,v 1.20 2010/06/19 21:43:16 krw Exp $
 * ------------------------------------------------------------
 *   O.S       : OpenBSD
 *   File Name : trm.c
 *   Device Driver for Tekram DC395U/UW/F,DC315/U
 *   PCI SCSI Bus Master Host Adapter
 *   (SCSI chip set used Tekram ASIC TRM-S1040)
 *
 * (C)Copyright 1995-1999 Tekram Technology Co., Ltd.
 * (C)Copyright 2001-2002 Ashley R. Martens and Kenneth R Westerback
 * ------------------------------------------------------------
 *    HISTORY:                    
 *                        
 *  REV#   DATE      NAME                  DESCRIPTION
 *  1.00   05/01/99  ERICH CHEN            First released for NetBSD 1.4.x
 *  1.01   00/00/00  MARTIN AKESSON        Port to OpenBSD 2.8
 *  1.02   09/19/01  ASHLEY MARTENS        Cleanup and formatting
 *  2.00   01/00/02  KENNETH R WESTERBACK  Rewrite of the bus and code logic
 * ------------------------------------------------------------
 *
 * 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. 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.
 *
 * ------------------------------------------------------------
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/device.h>

#include <machine/bus.h>

#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <scsi/scsi_message.h>

#include <dev/pci/pcidevs.h>
#include <dev/ic/trm.h>

/* #define TRM_DEBUG0 */

void	trm_minphys(struct buf *, struct scsi_link *);

void	trm_initSRB(struct trm_scsi_req_q *);

void	trm_check_eeprom(struct trm_adapter_nvram *, bus_space_tag_t, bus_space_handle_t);
void	trm_read_all    (struct trm_adapter_nvram *, bus_space_tag_t, bus_space_handle_t);
void	trm_write_all   (struct trm_adapter_nvram *, bus_space_tag_t, bus_space_handle_t);

void	trm_set_data (bus_space_tag_t, bus_space_handle_t, u_int8_t, u_int8_t);
void	trm_write_cmd(bus_space_tag_t, bus_space_handle_t, u_int8_t, u_int8_t);

u_int8_t trm_get_data(bus_space_tag_t, bus_space_handle_t, u_int8_t);

void	trm_wait_30us(bus_space_tag_t, bus_space_handle_t);

void	trm_scsi_cmd(struct scsi_xfer *);

struct trm_scsi_req_q *trm_GetFreeSRB(struct trm_softc *);

void	trm_DataOutPhase0(struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_DataInPhase0 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_StatusPhase0 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_MsgOutPhase0 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_MsgInPhase0  (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_DataOutPhase1(struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_DataInPhase1 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_CommandPhase1(struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_StatusPhase1 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_MsgOutPhase1 (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_MsgInPhase1  (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
void	trm_Nop          (struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);

void	trm_SetXferParams  (struct trm_softc *, struct trm_dcb *, int);

void	trm_DataIO_transfer(struct trm_softc *, struct trm_scsi_req_q *, u_int16_t);

int	trm_StartSRB    (struct trm_softc *, struct trm_scsi_req_q *);
void	trm_ReleaseSRB  (struct trm_softc *, struct trm_scsi_req_q *);
void	trm_RewaitSRB   (struct trm_softc *, struct trm_scsi_req_q *);
void	trm_FinishSRB   (struct trm_softc *, struct trm_scsi_req_q *);
void	trm_RequestSense(struct trm_softc *, struct trm_scsi_req_q *);

void	trm_initAdapter     (struct trm_softc *);
void	trm_Disconnect      (struct trm_softc *);
void	trm_Reselect        (struct trm_softc *);
void	trm_GoingSRB_Done   (struct trm_softc *);
void	trm_ScsiRstDetect   (struct trm_softc *);
void	trm_ResetSCSIBus    (struct trm_softc *);
void	trm_reset           (struct trm_softc *);
void	trm_StartWaitingSRB (struct trm_softc *);
void	trm_ResetAllDevParam(struct trm_softc *);
void	trm_RecoverSRB      (struct trm_softc *);
void	trm_linkSRB         (struct trm_softc *);

void	trm_initACB(struct trm_softc *, int);

void    trm_ResetDevParam(struct trm_softc *, struct trm_dcb *, u_int8_t);

void	trm_EnableMsgOut(struct trm_softc *, u_int8_t);

void	trm_timeout(void *);

void	trm_print_info(struct trm_softc *, struct trm_dcb *);

/*
 * Define structures
 */
struct  cfdriver trm_cd = {
        NULL, "trm", DV_DULL
};

struct scsi_adapter trm_switch = {
	trm_scsi_cmd,
	trm_minphys,
	NULL,
	NULL
};

static struct scsi_device trm_device = {
	NULL,            /* Use default error handler */
	NULL,            /* have a queue, served by this */
	NULL,            /* have no async handler */
	NULL,            /* Use default 'done' routine */
};

/* 
 * ------------------------------------------------------------
 *
 *          stateV = (void *) trm_SCSI_phase0[phase]
 *
 * ------------------------------------------------------------
 */
static void *trm_SCSI_phase0[8] = {
	trm_DataOutPhase0,    /* phase:0 */
	trm_DataInPhase0,     /* phase:1 */
	trm_Nop,              /* phase:2 */
	trm_StatusPhase0,     /* phase:3 */
	trm_Nop,              /* phase:4 */
	trm_Nop,              /* phase:5 */
	trm_MsgOutPhase0,     /* phase:6 */
	trm_MsgInPhase0,      /* phase:7 */
};

/*
 * ------------------------------------------------------------
 *
 *          stateV = (void *) trm_SCSI_phase1[phase]
 *
 * ------------------------------------------------------------
 */
static void *trm_SCSI_phase1[8] = {
	trm_DataOutPhase1,    /* phase:0 */
	trm_DataInPhase1,     /* phase:1 */
	trm_CommandPhase1,    /* phase:2 */
	trm_StatusPhase1,     /* phase:3 */
	trm_Nop,              /* phase:4 */
	trm_Nop,              /* phase:5 */
	trm_MsgOutPhase1,     /* phase:6 */
	trm_MsgInPhase1,      /* phase:7 */
};


struct trm_adapter_nvram trm_eepromBuf[TRM_MAX_ADAPTER_NUM];
/*
 *Fast20:  000     50ns, 20.0 Mbytes/s
 *         001     75ns, 13.3 Mbytes/s
 *         010    100ns, 10.0 Mbytes/s
 *         011    125ns,  8.0 Mbytes/s
 *         100    150ns,  6.6 Mbytes/s
 *         101    175ns,  5.7 Mbytes/s
 *         110    200ns,  5.0 Mbytes/s
 *         111    250ns,  4.0 Mbytes/s
 *
 *Fast40:  000     25ns, 40.0 Mbytes/s
 *         001     50ns, 20.0 Mbytes/s
 *         010     75ns, 13.3 Mbytes/s
 *         011    100ns, 10.0 Mbytes/s
 *         100    125ns,  8.0 Mbytes/s
 *         101    150ns,  6.6 Mbytes/s
 *         110    175ns,  5.7 Mbytes/s
 *         111    200ns,  5.0 Mbytes/s
 */

/*
 * real period:
 */
u_int8_t trm_clock_period[8] = {
	/* nanosecond divided by 4 */
	12,	/*  48 ns 20   MB/sec */
	18,	/*  72 ns 13.3 MB/sec */
	25,	/* 100 ns 10.0 MB/sec */
	31,	/* 124 ns  8.0 MB/sec */
	37,	/* 148 ns  6.6 MB/sec */
	43,	/* 172 ns  5.7 MB/sec */
	50,	/* 200 ns  5.0 MB/sec */
	62	/* 248 ns  4.0 MB/sec */
};

/*
 * ------------------------------------------------------------
 * Function : trm_GetFreeSRB
 * Purpose  : Get the first free SRB
 * Inputs   : 
 * Return   : NULL or a free SCSI Request block
 * ------------------------------------------------------------
 */
struct trm_scsi_req_q *
trm_GetFreeSRB(struct trm_softc *sc)
{
	struct trm_scsi_req_q *pSRB;

	/* ASSUME we are called from inside a splbio()/splx() region */

	pSRB = TAILQ_FIRST(&sc->freeSRB);

	if (pSRB != NULL)
		TAILQ_REMOVE(&sc->freeSRB, pSRB, link);

#ifdef TRM_DEBUG0
	printf("%s: trm_GetFreeSRB. pSRB = %p, next pSRB = %p\n",
	    sc->sc_device.dv_xname, pSRB, TAILQ_FIRST(&sc->freeSRB));
#endif

	return pSRB;
}

/*
 * ------------------------------------------------------------
 * Function : trm_RewaitSRB
 * Purpose  : Q back to pending Q
 * Inputs   : struct trm_dcb * - 
 *            struct trm_scsi_req_q * - 
 * ------------------------------------------------------------
 */
void
trm_RewaitSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB)
{
	int intflag;

	intflag = splbio();

	if ((pSRB->SRBFlag & TRM_ON_WAITING_SRB) != 0) {
		pSRB->SRBFlag &= ~TRM_ON_WAITING_SRB;
		TAILQ_REMOVE(&sc->waitingSRB, pSRB, link);
	}

	if ((pSRB->SRBFlag & TRM_ON_GOING_SRB) != 0) {
		pSRB->SRBFlag &= ~TRM_ON_GOING_SRB;
		TAILQ_REMOVE(&sc->goingSRB, pSRB, link);
	}

	pSRB->SRBState     = TRM_READY;
	pSRB->TargetStatus = SCSI_OK;
	pSRB->AdaptStatus  = TRM_STATUS_GOOD; 

	pSRB->SRBFlag |= TRM_ON_WAITING_SRB;
	TAILQ_INSERT_HEAD(&sc->waitingSRB, pSRB, link);

	splx(intflag);
}

/*
 * ------------------------------------------------------------
 * Function : trm_StartWaitingSRB
 * Purpose  : If there is no active DCB then run robin through
 *            the DCB's to find the next waiting SRB
 *            and move it to the going list.
 * Inputs   : struct trm_softc * - 
 * ------------------------------------------------------------
 */
void
trm_StartWaitingSRB(struct trm_softc *sc)
{
	struct trm_scsi_req_q *pSRB, *next;
	int intflag;

	intflag = splbio();

	if ((sc->pActiveDCB != NULL) ||
	    (TAILQ_EMPTY(&sc->waitingSRB)) ||
	    (sc->sc_Flag & (RESET_DETECT | RESET_DONE | RESET_DEV)) != 0)
		return;

	for (pSRB = TAILQ_FIRST(&sc->waitingSRB); pSRB != NULL; pSRB = next) {
		next = TAILQ_NEXT(pSRB, link);
		if (trm_StartSRB(sc, pSRB) == 0) {
			pSRB->SRBFlag &= ~TRM_ON_WAITING_SRB;
			TAILQ_REMOVE(&sc->waitingSRB, pSRB, link);
			pSRB->SRBFlag |= TRM_ON_GOING_SRB;
			TAILQ_INSERT_TAIL(&sc->goingSRB, pSRB, link);
			break;
		}
	}

	splx(intflag);
}

/*
 * ------------------------------------------------------------
 * Function : trm_scsi_cmd
 * Purpose  : enqueues a SCSI command
 * Inputs   :
 * Call By  : GENERIC SCSI driver
 * ------------------------------------------------------------
 */
void
trm_scsi_cmd(struct scsi_xfer *xs) 
{
	struct trm_scsi_req_q *pSRB;
	bus_space_handle_t ioh;
	struct trm_softc *sc;
	bus_space_tag_t	iot;
	struct trm_dcb *pDCB;
	u_int8_t target, lun;
	int i, error, intflag, xferflags;

	target = xs->sc_link->target;
	lun    = xs->sc_link->lun;

	sc  = (struct trm_softc *)xs->sc_link->adapter_softc;
	ioh = sc->sc_iohandle;
	iot = sc->sc_iotag;

#ifdef TRM_DEBUG0
	if ((xs->flags & SCSI_POLL) != 0)
	printf("%s: trm_scsi_cmd. sc = %p, xs = %p, targ/lun = %d/%d opcode = 0x%02x\n",
	    sc->sc_device.dv_xname, sc, xs, target, lun, xs->cmd->opcode);
#endif

	if (target >= TRM_MAX_TARGETS) {
		printf("%s: target=%d >= %d\n",
		    sc->sc_device.dv_xname, target, TRM_MAX_TARGETS);
		xs->error = XS_DRIVER_STUFFUP;
		scsi_done(xs);
		return;
	}
	if (lun >= TRM_MAX_LUNS) {
		printf("%s: lun=%d >= %d\n",
		    sc->sc_device.dv_xname, lun, TRM_MAX_LUNS);
		xs->error = XS_DRIVER_STUFFUP;
		scsi_done(xs);
		return;
	}

	pDCB = sc->pDCB[target][lun];
	if (pDCB == NULL) {
		/* Removed as a result of INQUIRY proving no device present */
		xs->error = XS_DRIVER_STUFFUP;
		scsi_done(xs);
		return;
 	}
 
	xferflags = xs->flags;
	if (xferflags & SCSI_RESET) {
#ifdef TRM_DEBUG0
		printf("%s: trm_reset\n", sc->sc_device.dv_xname);
#endif
		trm_reset(sc);
		xs->error = XS_NOERROR;
		scsi_done(xs);
		return;
	}

	xs->error  = XS_NOERROR;
	xs->status = SCSI_OK;
	xs->resid  = 0;

	intflag = splbio();

	pSRB = trm_GetFreeSRB(sc);

	if (pSRB == NULL) {
		splx(intflag);
		xs->error = XS_NO_CCB;
		scsi_done(xs);
		return;
	}

	/* 
	 * BuildSRB(pSRB,pDCB);
	 */
	if (xs->datalen != 0) {
#ifdef TRM_DEBUG0
		printf("%s: xs->datalen=%x\n", sc->sc_device.dv_xname,
		    (u_int32_t)xs->datalen);
		printf("%s: sc->sc_dmatag=0x%x\n", sc->sc_device.dv_xname,
		    (u_int32_t)sc->sc_dmatag);
		printf("%s: pSRB->dmamapxfer=0x%x\n", sc->sc_device.dv_xname,
		    (u_int32_t)pSRB->dmamapxfer);
		printf("%s: xs->data=0x%x\n", sc->sc_device.dv_xname,
		    (u_int32_t)xs->data);
#endif
		if ((error = bus_dmamap_load(sc->sc_dmatag, pSRB->dmamapxfer,
		    xs->data, xs->datalen, NULL,
		    (xferflags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT :
		    BUS_DMA_WAITOK)) != 0) {
			printf("%s: DMA transfer map unable to load, error = %d\n"
			    , sc->sc_device.dv_xname, error);
			xs->error = XS_DRIVER_STUFFUP;
			/*
			 * free SRB
			 */
			TAILQ_INSERT_HEAD(&sc->freeSRB, pSRB, link);
			splx(intflag);
			scsi_done(xs);
			return;
		}

		bus_dmamap_sync(sc->sc_dmatag, pSRB->dmamapxfer,
		    0, pSRB->dmamapxfer->dm_mapsize,
		    (xferflags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);

		/*
		 * Set up the scatter gather list
		 */
		for (i = 0; i < pSRB->dmamapxfer->dm_nsegs; i++) {
			pSRB->SegmentX[i].address = pSRB->dmamapxfer->dm_segs[i].ds_addr;
			pSRB->SegmentX[i].length  = pSRB->dmamapxfer->dm_segs[i].ds_len;
		}
		pSRB->SRBTotalXferLength = xs->datalen;
		pSRB->SRBSGCount         = pSRB->dmamapxfer->dm_nsegs;
	}

	pSRB->pSRBDCB    = pDCB;
	pSRB->xs         = xs;
	pSRB->ScsiCmdLen = xs->cmdlen;

	memcpy(pSRB->CmdBlock, xs->cmd, xs->cmdlen);

	timeout_set(&xs->stimeout, trm_timeout, pSRB);

	pSRB->SRBFlag |= TRM_ON_WAITING_SRB;
	TAILQ_INSERT_TAIL(&sc->waitingSRB, pSRB, link);
	trm_StartWaitingSRB(sc);

	if ((xferflags & SCSI_POLL) == 0) {
		timeout_add_msec(&xs->stimeout, xs->timeout);
		splx(intflag);
		return;
	}

	while ((--xs->timeout > 0) && ((xs->flags & ITSDONE) == 0)) {
		trm_Interrupt(sc);
		DELAY(1000);
	}

	if (xs->timeout == 0)
		trm_timeout(pSRB);

	splx(intflag);
}

/*
 * ------------------------------------------------------------
 * Function : trm_ResetAllDevParam
 * Purpose  :
 * Inputs   : struct trm_softc * 
 * ------------------------------------------------------------
 */
void
trm_ResetAllDevParam(struct trm_softc *sc)
{
	struct trm_adapter_nvram *pEEpromBuf;
	int target, quirks;

	pEEpromBuf = &trm_eepromBuf[sc->sc_AdapterUnit];

	for (target = 0; target < TRM_MAX_TARGETS; target++) {
		if (target == sc->sc_AdaptSCSIID)
			continue;

		if ((sc->pDCB[target][0]->DCBFlag & TRM_QUIRKS_VALID) == 0)
			quirks = SDEV_NOWIDE | SDEV_NOSYNC | SDEV_NOTAGS;
		else
			quirks = sc->pDCB[target][0]->sc_link->quirks;

		trm_ResetDevParam(sc, sc->pDCB[target][0], quirks);
	}
}

/*
 * ------------------------------------------------------------
 * Function : trm_ResetDevParam
 * Purpose  :
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_ResetDevParam(struct trm_softc *sc, struct trm_dcb *pDCB, u_int8_t quirks)
{
	struct trm_adapter_nvram *pEEpromBuf = &trm_eepromBuf[sc->sc_AdapterUnit];
	u_int8_t PeriodIndex;
	const int target = pDCB->target;

	pDCB->DCBFlag &= TRM_QUIRKS_VALID;
	pDCB->DCBFlag |= (TRM_WIDE_NEGO_ENABLE | TRM_SYNC_NEGO_ENABLE);

	pDCB->SyncPeriod    = 0;
	pDCB->SyncOffset    = 0;
	pDCB->MaxNegoPeriod = 0;

	pDCB->DevMode = pEEpromBuf->NvramTarget[target].NvmTarCfg0;
	
	pDCB->IdentifyMsg = MSG_IDENTIFY(pDCB->lun, ((pDCB->DevMode & TRM_DISCONNECT) != 0));
	
	if (((quirks & SDEV_NOWIDE) == 0) &&
	    (pDCB->DevMode & TRM_WIDE) &&
	    ((sc->sc_config & HCC_WIDE_CARD) != 0))
		pDCB->DCBFlag |= TRM_WIDE_NEGO_16BIT;
	
	if (((quirks & SDEV_NOSYNC) == 0) &&
	    ((pDCB->DevMode & TRM_SYNC) != 0)) {
		PeriodIndex   = pEEpromBuf->NvramTarget[target].NvmTarPeriod & 0x07;
		pDCB->MaxNegoPeriod = trm_clock_period[PeriodIndex];
	}

	if (((quirks & SDEV_NOTAGS) == 0) &&
	    ((pDCB->DevMode & TRM_TAG_QUEUING) != 0) &&
	    ((pDCB->DevMode & TRM_DISCONNECT) != 0))
		/* TODO XXXX: Every device(lun) gets to queue TagMaxNum commands? */
		pDCB->DCBFlag |= TRM_USE_TAG_QUEUING;

	trm_SetXferParams(sc, pDCB, 0);
}

/*
 * ------------------------------------------------------------
 * Function : trm_RecoverSRB
 * Purpose  : Moves all SRBs from Going to Waiting for all the Link DCBs
 * Inputs   : struct trm_softc * - 
 * ------------------------------------------------------------
 */
void
trm_RecoverSRB(struct trm_softc *sc)
{
	struct trm_scsi_req_q *pSRB;

	/* ASSUME we are inside splbio()/splx() */

	while ((pSRB = TAILQ_FIRST(&sc->goingSRB)) != NULL) {
		pSRB->SRBFlag &= ~TRM_ON_GOING_SRB;
		TAILQ_REMOVE(&sc->goingSRB, pSRB, link);
		pSRB->SRBFlag |= TRM_ON_WAITING_SRB;
		TAILQ_INSERT_HEAD(&sc->waitingSRB, pSRB, link);
	}
}

/*
 * ------------------------------------------------------------
 * Function : trm_reset
 * Purpose  : perform a hard reset on the SCSI bus (and TRM_S1040 chip).
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_reset (struct trm_softc *sc)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	int i, intflag;

#ifdef TRM_DEBUG0
	printf("%s: trm_reset", sc->sc_device.dv_xname);
#endif

	intflag = splbio();

	bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN,  0);
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_INTEN, 0);

	trm_ResetSCSIBus(sc);
	for (i = 0; i < 500; i++)
		DELAY(1000);

	/*
	 * Enable all SCSI interrupts except EN_SCAM
	 */
	bus_space_write_1(iot, ioh,
	    TRM_S1040_SCSI_INTEN,
	    (EN_SELECT | EN_SELTIMEOUT | EN_DISCONNECT | EN_RESELECTED |
		EN_SCSIRESET | EN_BUSSERVICE | EN_CMDDONE)); 
	/*
	 * Enable DMA interrupt
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN, EN_SCSIINTR);
	/*
	 * Clear DMA FIFO
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, CLRXFIFO);
	/*
	 * Clear SCSI FIFO
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO);

	trm_ResetAllDevParam(sc);
	trm_GoingSRB_Done(sc);
	sc->pActiveDCB = NULL;

	/*
	 * RESET_DETECT, RESET_DONE, RESET_DEV
	 */
	sc->sc_Flag = 0;
	trm_StartWaitingSRB(sc);

	splx(intflag);
}

/*
 * ------------------------------------------------------------
 * Function : trm_timeout
 * Purpose  : Prints a timeout message and aborts the timed out SCSI request
 * Inputs   : void * - A struct trm_scsi_req_q * structure pointer
 * ------------------------------------------------------------
 */
void
trm_timeout(void *arg1)
{
	struct trm_scsi_req_q *pSRB;
 	struct scsi_xfer *xs;
 	struct trm_softc *sc;
 
 	pSRB = (struct trm_scsi_req_q *)arg1;
 	xs   = pSRB->xs;

 	if (xs != NULL) {
 		sc = xs->sc_link->adapter_softc;
 		sc_print_addr(xs->sc_link);
 		printf("SCSI OpCode 0x%02x timed out\n",
 		    sc->sc_device.dv_xname, xs->cmd->opcode);
		pSRB->SRBFlag |= TRM_SCSI_TIMED_OUT;
 		trm_FinishSRB(sc, pSRB);
		trm_StartWaitingSRB(sc);
 	}
#ifdef TRM_DEBUG0
 	else
		printf("%s: trm_timeout called with xs == NULL\n",
		    sc->sc_device.dv_xname);
#endif
}

/*
 * ------------------------------------------------------------
 * Function : trm_StartSRB
 * Purpose  : Send the commands in the SRB to the device
 * Inputs   : struct trm_softc * -
 *            struct trm_scsi_req_q * - 
 * Return   : 0 - SCSI processor is unoccupied
 *            1 - SCSI processor is occupied with an SRB
 * ------------------------------------------------------------
 */
int
trm_StartSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	struct trm_dcb *pDCB = pSRB->pSRBDCB;
	u_int32_t tag_mask;
	u_int8_t tag_id, scsicommand;

#ifdef TRM_DEBUG0
	printf("%s: trm_StartSRB. sc = %p, pDCB = %p, pSRB = %p\n",
	    sc->sc_device.dv_xname, sc, pDCB, pSRB);
#endif
	/* 
	 * If the queue is full or the SCSI processor has a pending interrupt
	 * then try again later.
	 */
	if ((pDCB->DCBFlag & TRM_QUEUE_FULL) || (bus_space_read_2(iot, ioh,
	    TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT)) 
		return (1);

	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_HOSTID, sc->sc_AdaptSCSIID);
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_TARGETID, pDCB->target);
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod);
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset);

	if ((sc->pDCB[pDCB->target][0]->sc_link != NULL) &&
	    ((sc->pDCB[pDCB->target][0]->DCBFlag & TRM_QUIRKS_VALID) == 0)) {
		sc->pDCB[pDCB->target][0]->DCBFlag |= TRM_QUIRKS_VALID;
		trm_ResetDevParam(sc, sc->pDCB[pDCB->target][0], sc->pDCB[pDCB->target][0]->sc_link->quirks);
	}

	/*
	 * Flush FIFO
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO);

	sc->MsgCnt = 1;
	sc->MsgBuf[0] = pDCB->IdentifyMsg;
	if (((pSRB->xs->flags & SCSI_POLL) != 0) ||
	    (pSRB->CmdBlock[0] == INQUIRY) ||
	    (pSRB->CmdBlock[0] == REQUEST_SENSE))
		sc->MsgBuf[0] &= ~MSG_IDENTIFY_DISCFLAG;

	scsicommand = SCMD_SEL_ATN;

	if ((pDCB->DCBFlag & (TRM_WIDE_NEGO_ENABLE | TRM_SYNC_NEGO_ENABLE)) != 0) {
		scsicommand = SCMD_SEL_ATNSTOP;
		pSRB->SRBState = TRM_MSGOUT;

	} else if ((pDCB->DCBFlag & TRM_USE_TAG_QUEUING) == 0) {
		pDCB->DCBFlag |= TRM_QUEUE_FULL;			

	} else if ((sc->MsgBuf[0] & MSG_IDENTIFY_DISCFLAG) != 0) {
		if (pSRB->TagNumber == TRM_NO_TAG) {
			for (tag_id=1, tag_mask=2; tag_id < 32; tag_id++, tag_mask <<= 1)
				if ((tag_mask & pDCB->TagMask) == 0) {
					pDCB->TagMask  |= tag_mask;
					pSRB->TagNumber = tag_id;
					break;
				}

			if (tag_id >= 32) {
				pDCB->DCBFlag |= TRM_QUEUE_FULL;
				sc->MsgCnt = 0;
				return 1;
			}
		}

		/* TODO XXXX: Should send ORDERED_Q_TAG if metadata (non-block) i/o!? */
		sc->MsgBuf[sc->MsgCnt++] = MSG_SIMPLE_Q_TAG;
		sc->MsgBuf[sc->MsgCnt++] = pSRB->TagNumber;
		
		scsicommand = SCMD_SEL_ATN3;
	}

	pSRB->SRBState = TRM_START;
	pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */
	sc->pActiveDCB = pDCB;
	pDCB->pActiveSRB = pSRB;

	if (sc->MsgCnt > 0) {
		bus_space_write_1(iot, ioh, TRM_S1040_SCSI_FIFO, sc->MsgBuf[0]);
		if (sc->MsgCnt > 1) {
			DELAY(30);
			bus_space_write_multi_1(iot, ioh, TRM_S1040_SCSI_FIFO, &sc->MsgBuf[1], sc->MsgCnt - 1);
		}
		sc->MsgCnt = 0;
	}

	/*
	 * it's important for atn stop
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH | DO_HWRESELECT);
	/*
	 * SCSI command 
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, scsicommand);

	return 0;
}

/*
 * ------------------------------------------------------------
 * Function : trm_Interrupt
 * Purpose  : Catch an interrupt from the adapter           
 *            Process pending device interrupts.        
 * Inputs   : void * - struct trm_softc * structure pointer
 * ------------------------------------------------------------
 */
int
trm_Interrupt(void *vsc)
{
	void   (*stateV)(struct trm_softc *, struct trm_scsi_req_q *, u_int8_t *);
	struct trm_scsi_req_q *pSRB;
	bus_space_handle_t ioh;
	struct trm_softc *sc = (struct trm_softc *)vsc;
	bus_space_tag_t	iot;
	u_int16_t phase;
	u_int8_t scsi_status, scsi_intstatus;
	int intflag;

	intflag = splbio();

	if (sc == NULL) {
		splx(intflag);
		return 0;
	}

	ioh = sc->sc_iohandle;
	iot = sc->sc_iotag;

	scsi_status = bus_space_read_2(iot, ioh, TRM_S1040_SCSI_STATUS);
	if (!(scsi_status & SCSIINTERRUPT)) {
		splx(intflag);
		return 0;
	}
	scsi_intstatus = bus_space_read_1(iot, ioh, TRM_S1040_SCSI_INTSTATUS);

#ifdef TRM_DEBUG0
	printf("%s: trm_interrupt - scsi_status=0x%02x, scsi_intstatus=0x%02x\n",
	    sc->sc_device.dv_xname, scsi_status, scsi_intstatus);
#endif
	if ((scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) != 0)
		trm_Disconnect(sc);

	else if ((scsi_intstatus &  INT_RESELECTED) != 0)
		trm_Reselect(sc);

	else if ((scsi_intstatus &  INT_SCSIRESET) != 0)
		trm_ScsiRstDetect(sc);

	else if ((sc->pActiveDCB != NULL) && ((scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE)) != 0)) {
		pSRB = sc->pActiveDCB->pActiveSRB;
		/*
		 * software sequential machine
		 */
		phase = (u_int16_t) pSRB->ScsiPhase;  /* phase: */
		/* 
		 * 62037 or 62137
		 * call  trm_SCSI_phase0[]... "phase entry"
		 * handle every phase before start transfer
		 */
		stateV = trm_SCSI_phase0[phase];
		stateV(sc, pSRB, &scsi_status);
		/* 
		 * if any exception occurred
		 * scsi_status will be modified to bus free phase
		 * new scsi_status transfer out from previous stateV
		 */ 
		/*
		 * phase:0,1,2,3,4,5,6,7
		 */
		pSRB->ScsiPhase = scsi_status & PHASEMASK;
		phase = (u_int16_t) scsi_status & PHASEMASK;       
		/* 
		 * call  trm_SCSI_phase1[]... "phase entry"
		 * handle every phase do transfer
		 */
		stateV = trm_SCSI_phase1[phase];
		stateV(sc, pSRB, &scsi_status); 

	} else {
		splx(intflag);
		return 0;
	}

	splx(intflag);
	return 1;
}

/*
 * ------------------------------------------------------------
 * Function : trm_MsgOutPhase0
 * Purpose  : Check the state machine before sending a message out
 * Inputs   : struct trm_softc * -
 *            struct trm_scsi_req_q * -
 *            u_int8_t * - scsi status, set to PH_BUS_FREE if not ready
 * ------------------------------------------------------------
 */
void
trm_MsgOutPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	switch (pSRB->SRBState) {
	case TRM_UNEXPECT_RESEL:
	case TRM_ABORT_SENT:
		*pscsi_status = PH_BUS_FREE; /* initial phase */
		break;
		
	default:
		break;
	}
}

/*
 * ------------------------------------------------------------
 * Function : trm_MsgOutPhase1
 * Purpose  : Write the message out to the bus
 * Inputs   : struct trm_softc * -
 *            struct trm_scsi_req_q * -
 *            u_int8_t * - unused
 * ------------------------------------------------------------
 */
void
trm_MsgOutPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	struct trm_dcb *pDCB = sc->pActiveDCB;

	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO);

	if ((pDCB->DCBFlag & TRM_WIDE_NEGO_ENABLE) != 0) {
		/*
		 * WIDE DATA TRANSFER REQUEST code (03h)
		 */
		pDCB->DCBFlag &= ~TRM_WIDE_NEGO_ENABLE;
		pDCB->DCBFlag |=  TRM_DOING_WIDE_NEGO; 

		sc->MsgBuf[0] = pDCB->IdentifyMsg & ~MSG_IDENTIFY_DISCFLAG;
		sc->MsgBuf[1] = MSG_EXTENDED;
		sc->MsgBuf[2] = MSG_EXT_WDTR_LEN;
		sc->MsgBuf[3] = MSG_EXT_WDTR;

		if ((pDCB->DCBFlag & TRM_WIDE_NEGO_16BIT) == 0)
			sc->MsgBuf[4] = MSG_EXT_WDTR_BUS_8_BIT;
		else
			sc->MsgBuf[4] = MSG_EXT_WDTR_BUS_16_BIT;

		sc->MsgCnt = 5;
			
	} else if ((pDCB->DCBFlag & TRM_SYNC_NEGO_ENABLE) != 0) {

		pDCB->DCBFlag &= ~TRM_SYNC_NEGO_ENABLE;
		pDCB->DCBFlag |= TRM_DOING_SYNC_NEGO;

		sc->MsgCnt = 0;

		if ((pDCB->DCBFlag & TRM_WIDE_NEGO_DONE) == 0)
			sc->MsgBuf[sc->MsgCnt++] = pDCB->IdentifyMsg & ~MSG_IDENTIFY_DISCFLAG;

		sc->MsgBuf[sc->MsgCnt++] = MSG_EXTENDED;
		sc->MsgBuf[sc->MsgCnt++] = MSG_EXT_SDTR_LEN;
		sc->MsgBuf[sc->MsgCnt++] = MSG_EXT_SDTR;
		sc->MsgBuf[sc->MsgCnt++] = pDCB->MaxNegoPeriod;

		if (pDCB->MaxNegoPeriod > 0)
			sc->MsgBuf[sc->MsgCnt++] = TRM_MAX_SYNC_OFFSET;
		else
			sc->MsgBuf[sc->MsgCnt++] = 0;
	}

	if (sc->MsgCnt > 0) {
		bus_space_write_multi_1(iot, ioh, TRM_S1040_SCSI_FIFO, &sc->MsgBuf[0], sc->MsgCnt);
		if (sc->MsgBuf[0] == MSG_ABORT)
			pSRB->SRBState = TRM_ABORT_SENT;
		sc->MsgCnt = 0;
	}
	/*
	 * it's important for atn stop
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
	/*
	 * Transfer information out
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT);
}

/*
 * ------------------------------------------------------------
 * Function : trm_CommandPhase1
 * Purpose  : Send commands to bus
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_CommandPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;

	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRATN | DO_CLRFIFO);

	bus_space_write_multi_1(iot, ioh, TRM_S1040_SCSI_FIFO, &pSRB->CmdBlock[0], pSRB->ScsiCmdLen);

	pSRB->SRBState = TRM_COMMAND;
	/*
	 * it's important for atn stop
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
	/*
	 * Transfer information out
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT);
}

/*
 * ------------------------------------------------------------
 * Function : trm_DataOutPhase0
 * Purpose  : Ready for Data Out, clear FIFO
 * Inputs   : u_int8_t * - SCSI status, used but not set
 * ------------------------------------------------------------
 */
void
trm_DataOutPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	struct SGentry *pseg;
	struct trm_dcb *pDCB;
	u_int32_t dLeftCounter, TempSRBXferredLength;
	u_int16_t scsi_status;
	u_int8_t TempDMAstatus, SGIndexTemp;

	dLeftCounter = 0;

	pDCB = pSRB->pSRBDCB;
	scsi_status = *pscsi_status;

	if (pSRB->SRBState != TRM_XFERPAD) {
		if ((scsi_status & PARITYERROR) != 0)
			pSRB->SRBFlag |= TRM_PARITY_ERROR;
		if ((scsi_status & SCSIXFERDONE) == 0) {
			/*
			 * when data transfer from DMA FIFO to SCSI FIFO
			 * if there was some data left in SCSI FIFO
			 */
			dLeftCounter = (u_int32_t)(bus_space_read_1(
			    iot, ioh, TRM_S1040_SCSI_FIFOCNT) & 0x1F);
			if (pDCB->SyncPeriod & WIDE_SYNC) {
				/*
				 * if WIDE scsi SCSI FIFOCNT unit is word
				 * so need to * 2
				 */
				dLeftCounter <<= 1;
			}
		}
		/*
		 * calculate all the residue data that not yet transferred
		 * SCSI transfer counter + left in SCSI FIFO data
		 *
		 * .....TRM_S1040_SCSI_COUNTER (24bits)
		 * The counter always decrement by one for every SCSI byte
		 * transfer.
		 * .....TRM_S1040_SCSI_FIFOCNT ( 5bits)
		 * The counter is SCSI FIFO offset counter
		 */
		dLeftCounter += bus_space_read_4(iot, ioh,
		    TRM_S1040_SCSI_COUNTER);
		if (dLeftCounter == 1) {
			dLeftCounter = 0;
			bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL,
			    DO_CLRFIFO);
		}
		if (dLeftCounter == 0 ||
		    (scsi_status & SCSIXFERCNT_2_ZERO) != 0) {   
			TempDMAstatus = bus_space_read_1(iot,
			    ioh, TRM_S1040_DMA_STATUS);
			while ((TempDMAstatus & DMAXFERCOMP) == 0) {
				TempDMAstatus = bus_space_read_1(iot,
				    ioh, TRM_S1040_DMA_STATUS);
			}
			pSRB->SRBTotalXferLength = 0;
		} else {
			/*
			 * Update SG list
			 */
			/*
			 * if transfer not yet complete
			 * there were some data residue in SCSI FIFO or
			 * SCSI transfer counter not empty
			 */
			if (pSRB->SRBTotalXferLength != dLeftCounter) {
				/*
				 * data that had transferred length
				 */
				TempSRBXferredLength = pSRB->SRBTotalXferLength
				    - dLeftCounter;
				/*
				 * next time to be transferred length
				 */
				pSRB->SRBTotalXferLength = dLeftCounter;
				/*
				 * parsing from last time disconnect SRBSGIndex
				 */
				pseg = &pSRB->SegmentX[pSRB->SRBSGIndex];
				for (SGIndexTemp = pSRB->SRBSGIndex;
				    SGIndexTemp < pSRB->SRBSGCount;
				    SGIndexTemp++) {
					/* 
					 * find last time which SG transfer be
					 * disconnect 
					 */
					if (TempSRBXferredLength >= pseg->length)
						TempSRBXferredLength -= pseg->length;
					else {
						/*
						 * update last time disconnected
						 * SG list
						 */
						/*
						 * residue data length
						 */
						pseg->length -=
						    TempSRBXferredLength;
						/*
						 * residue data pointer
						 */
						pseg->address +=
						    TempSRBXferredLength;
						pSRB->SRBSGIndex = SGIndexTemp;
						break;
					}
					pseg++;
				}
			}
		}
	}
	bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, STOPDMAXFER);
}

/*
 * ------------------------------------------------------------
 * Function : trm_DataOutPhase1
 * Purpose  : Transfers data out, calls trm_DataIO_transfer
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_DataOutPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	trm_DataIO_transfer(sc, pSRB, XFERDATAOUT);
}

/*
 * ------------------------------------------------------------
 * Function : trm_DataInPhase0
 * Purpose  : Prepare for reading data in from bus
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_DataInPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	struct SGentry *pseg;
	u_int32_t TempSRBXferredLength, dLeftCounter;
	u_int16_t scsi_status;
	u_int8_t SGIndexTemp;

	dLeftCounter = 0;

	scsi_status = *pscsi_status;
	if (pSRB->SRBState != TRM_XFERPAD) {
		if ((scsi_status & PARITYERROR) != 0)
			pSRB->SRBFlag |= TRM_PARITY_ERROR;
		dLeftCounter += bus_space_read_4(iot, ioh,
		    TRM_S1040_SCSI_COUNTER);
		if (dLeftCounter == 0 ||
		    (scsi_status & SCSIXFERCNT_2_ZERO) != 0) {
			while ((bus_space_read_1(iot, ioh, TRM_S1040_DMA_STATUS) & DMAXFERCOMP) == 0)
				;
			pSRB->SRBTotalXferLength = 0;
		} else {  
			/*
			 * phase changed
			 *
			 * parsing the case:
			 * when a transfer not yet complete 
			 * but be disconnected by uper layer
			 * if transfer not yet complete
			 * there were some data residue in SCSI FIFO or
			 * SCSI transfer counter not empty
			 */
			if (pSRB->SRBTotalXferLength != dLeftCounter) {
				/*
				 * data that had transferred length
				 */
				TempSRBXferredLength = pSRB->SRBTotalXferLength
				    - dLeftCounter;
				/*
				 * next time to be transferred length
				 */
				pSRB->SRBTotalXferLength = dLeftCounter;
				/*
				 * parsing from last time disconnect SRBSGIndex
				 */
				pseg = &pSRB->SegmentX[pSRB->SRBSGIndex];
				for (SGIndexTemp = pSRB->SRBSGIndex;
				    SGIndexTemp < pSRB->SRBSGCount;
				    SGIndexTemp++) {
					/* 
					 * find last time which SG transfer be
					 * disconnect 
					 */
					if (TempSRBXferredLength >=
					    pseg->length) {
						TempSRBXferredLength -= pseg->length;
					} else {
						/*
						 * update last time disconnected
						 * SG list
						 *
						 * residue data length
						 */
						pseg->length -= TempSRBXferredLength;
						/*
						 * residue data pointer
						 */
						pseg->address += TempSRBXferredLength;
						pSRB->SRBSGIndex = SGIndexTemp;
						break;
					} 
					pseg++;
				}
			}
		}
	}
}

/*
 * ------------------------------------------------------------
 * Function : trm_DataInPhase1
 * Purpose  : Transfer data in from bus, calls trm_DataIO_transfer
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_DataInPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	trm_DataIO_transfer(sc, pSRB, XFERDATAIN);
}

/*
 * ------------------------------------------------------------
 * Function : trm_DataIO_transfer
 * Purpose  : 
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_DataIO_transfer(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int16_t ioDir)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	struct trm_dcb *pDCB = pSRB->pSRBDCB;
	u_int8_t bval;

	if (pSRB->SRBSGIndex < pSRB->SRBSGCount) {
		if (pSRB->SRBTotalXferLength != 0) {
			/* 
			 * load what physical address of Scatter/Gather list
			 * table want to be transfer 
			 */
			pSRB->SRBState = TRM_DATA_XFER;
			bus_space_write_4(iot, ioh, TRM_S1040_DMA_XHIGHADDR, 0);
			bus_space_write_4(iot, ioh,
			    TRM_S1040_DMA_XLOWADDR, (pSRB->SRBSGPhyAddr +
			    ((u_int32_t)pSRB->SRBSGIndex << 3)));
			/*
			 * load how many bytes in the Scatter/Gather list table
			 */
			bus_space_write_4(iot, ioh, TRM_S1040_DMA_XCNT,
			    ((u_int32_t)(pSRB->SRBSGCount -
			    pSRB->SRBSGIndex) << 3));
			/*
			 * load total transfer length (24bits,
			 * pSRB->SRBTotalXferLength) max value 16Mbyte
			 */
			bus_space_write_4(iot, ioh,
			    TRM_S1040_SCSI_COUNTER, pSRB->SRBTotalXferLength);
			/*
			 * Start DMA transfer
			 */
			bus_space_write_2(iot,ioh,TRM_S1040_DMA_COMMAND, ioDir);
			/* bus_space_write_2(iot, ioh,
			    TRM_S1040_DMA_CONTROL, STARTDMAXFER);*/
			/*
			 * Set the transfer bus and direction
			 */
			bval = ioDir == XFERDATAOUT ? SCMD_DMA_OUT :SCMD_DMA_IN;
		} else {
			/*
			 * xfer pad
			 */
			if (pSRB->SRBSGCount)
				pSRB->AdaptStatus = TRM_OVER_UNDER_RUN;

			if (pDCB->SyncPeriod & WIDE_SYNC) {
				bus_space_write_4(iot, ioh,
				    TRM_S1040_SCSI_COUNTER, 2);
			} else {
				bus_space_write_4(iot, ioh,
				    TRM_S1040_SCSI_COUNTER, 1);
			}

			if (ioDir == XFERDATAOUT) {
				bus_space_write_2(iot,
				    ioh, TRM_S1040_SCSI_FIFO, 0);
			} else {
				bus_space_read_2(iot,
				    ioh, TRM_S1040_SCSI_FIFO);
			}
			pSRB->SRBState = TRM_XFERPAD;
			/*
			 * Set the transfer bus and direction
			 */
			bval = ioDir == XFERDATAOUT ? SCMD_FIFO_OUT : SCMD_FIFO_IN;
		}
		/*
		 * it's important for atn stop
		 */
		bus_space_write_2(iot,ioh,TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
		/*
		 * Tell the bus to do the transfer
		 */
		bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, bval);
	}
}

/*
 * ------------------------------------------------------------
 * Function : trm_StatusPhase0
 * Purpose  : Update Target Status with data from SCSI FIFO
 * Inputs   : u_int8_t * - Set to PH_BUS_FREE
 * ------------------------------------------------------------
 */
void
trm_StatusPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;

	pSRB->TargetStatus = bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFO);

	pSRB->SRBState = TRM_COMPLETED;
	/*
	 * initial phase
	 */
	*pscsi_status = PH_BUS_FREE;
	/*
	 * it's important for atn stop
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
	/*
	 * Tell bus that the message was accepted
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT);
}

/*
 * ------------------------------------------------------------
 * Function : trm_StatusPhase1
 * Purpose  : Clear FIFO of DMA and SCSI
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_StatusPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;

	if ((bus_space_read_2(iot, ioh, TRM_S1040_DMA_COMMAND) & 0x0001) != 0) {
		if ((bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFOCNT) & 0x40)
		    == 0) {
			bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL,
			    DO_CLRFIFO);
		}        
		if ((bus_space_read_2(iot, ioh,
		    TRM_S1040_DMA_FIFOCNT) & 0x8000) == 0) {
			bus_space_write_1(iot, ioh,
			    TRM_S1040_DMA_CONTROL, CLRXFIFO);
		}
	} else {
		if ((bus_space_read_2(iot, ioh,
		    TRM_S1040_DMA_FIFOCNT) & 0x8000) == 0) {
			bus_space_write_1(iot, ioh,
			    TRM_S1040_DMA_CONTROL, CLRXFIFO);
		}
		if ((bus_space_read_1(iot, ioh,
		    TRM_S1040_SCSI_FIFOCNT) & 0x40) == 0) {
			bus_space_write_2(iot, ioh,
			    TRM_S1040_SCSI_CONTROL, DO_CLRFIFO);
		}
	}
	pSRB->SRBState = TRM_STATUS;
	/*
	 * it's important for atn stop
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
	/*
	 * Tell the bus that the command is complete
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_COMP);
}

/*
 * ------------------------------------------------------------
 * Function : trm_MsgInPhase0
 * Purpose  : 
 * Inputs   : 
 *
 * extended message codes:
 *   code        description
 *   ----        -----------
 *    02h        Reserved
 *    00h        MODIFY DATA POINTER
 *    01h        SYNCHRONOUS DATA TRANSFER REQUEST
 *    03h        WIDE DATA TRANSFER REQUEST
 * 04h - 7Fh     Reserved
 * 80h - FFh     Vendor specific  
 *                
 * ------------------------------------------------------------
 */
void
trm_MsgInPhase0(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	struct trm_dcb *pDCB;
	u_int8_t message_in_code, bIndex, message_in_tag_id;

	pDCB = sc->pActiveDCB;

	message_in_code = bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFO);

	if (pSRB->SRBState != TRM_EXTEND_MSGIN) {
		switch (message_in_code) {
		case MSG_DISCONNECT:
			pSRB->SRBState = TRM_DISCONNECTED;
			break;

		case MSG_EXTENDED:
		case MSG_SIMPLE_Q_TAG:
		case MSG_HEAD_OF_Q_TAG:
		case MSG_ORDERED_Q_TAG:
			pSRB->SRBState = TRM_EXTEND_MSGIN;
			/*
			 * extended message      (01h)
			 */
			bzero(&sc->MsgBuf[0], sizeof(sc->MsgBuf));
			sc->MsgBuf[0] = message_in_code;
			sc->MsgCnt    = 1;
			/*
			 * extended message length (n)
			 */
			break;

		case MSG_MESSAGE_REJECT:
			/*
			 * Reject message
			 */
			if ((pDCB->DCBFlag & TRM_DOING_WIDE_NEGO) != 0) {
				/*
				 * do wide nego reject
				 */
				pDCB = pSRB->pSRBDCB;

				pDCB->DCBFlag &= ~TRM_DOING_WIDE_NEGO;
				pDCB->DCBFlag |= TRM_WIDE_NEGO_DONE;

				if ((pDCB->DCBFlag & TRM_SYNC_NEGO_ENABLE) != 0) {
					/*
					 * Set ATN, in case ATN was clear
					 */
					pSRB->SRBState = TRM_MSGOUT;
					bus_space_write_2(iot, ioh,
					    TRM_S1040_SCSI_CONTROL, DO_SETATN);
				} else {   
					/*
					 * Clear ATN
					 */
					bus_space_write_2(iot, ioh,
					    TRM_S1040_SCSI_CONTROL, DO_CLRATN);
				}

			} else if ((pDCB->DCBFlag & TRM_DOING_SYNC_NEGO) != 0) { 
				/*
				 * do sync nego reject
				 */
				pDCB = pSRB->pSRBDCB;

				pDCB->DCBFlag &= ~TRM_DOING_SYNC_NEGO;

				pDCB->SyncPeriod = 0;
				pDCB->SyncOffset = 0;

				bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRATN);
				goto  re_prog;
			}
			break;

		case MSG_IGN_WIDE_RESIDUE:
			bus_space_write_4(iot, ioh, TRM_S1040_SCSI_COUNTER, 1);
			bus_space_read_1(iot, ioh, TRM_S1040_SCSI_FIFO);
			break;

		default:
			break;
		}

	} else {

		/*
		 * We are collecting an extended message. Save the latest byte and then
		 * check to see if the message is complete. If so, process it.
		 */
		sc->MsgBuf[sc->MsgCnt++] = message_in_code;
#ifdef TRM_DEBUG0
		printf("%s: sc->MsgBuf = 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		    sc->sc_device.dv_xname,
		    sc->MsgBuf[0], sc->MsgBuf[1], sc->MsgBuf[2], sc->MsgBuf[3], sc->MsgBuf[4], sc->MsgBuf[5] );
#endif
		switch (sc->MsgBuf[0]) {
		case MSG_SIMPLE_Q_TAG:
		case MSG_HEAD_OF_Q_TAG:
		case MSG_ORDERED_Q_TAG:
			if (sc->MsgCnt == 2) {
				pSRB->SRBState = TRM_FREE;
				message_in_tag_id = sc->MsgBuf[1];
				sc->MsgCnt = 0;
				TAILQ_FOREACH(pSRB, &sc->goingSRB, link) {
					if ((pSRB->pSRBDCB == pDCB) && (pSRB->TagNumber == message_in_tag_id))
						break;
				}
				if ((pSRB != NULL) && (pSRB->SRBState == TRM_DISCONNECTED)) {
					pDCB->pActiveSRB = pSRB;
					pSRB->SRBState = TRM_DATA_XFER;
				} else {
					pSRB = &sc->SRB[0];
					pSRB->SRBState = TRM_UNEXPECT_RESEL;
					pDCB->pActiveSRB = pSRB;
					trm_EnableMsgOut(sc, MSG_ABORT_TAG);
				}
			}
			break;
			
		case  MSG_EXTENDED:
			/* TODO XXXX: Correctly handling target initiated negotiations? */
			if ((sc->MsgBuf[2] == MSG_EXT_WDTR) && (sc->MsgCnt == 4)) {
				/*	
				 * ======================================
				 * WIDE DATA TRANSFER REQUEST	
				 * ======================================
				 * byte 0 :  Extended message (01h)	
				 * byte 1 :  Extended message length (02h)	
				 * byte 2 :  WIDE DATA TRANSFER code (03h)	
				 * byte 3 :  Transfer width exponent 
				 */
				
				pSRB->SRBState  = TRM_FREE;
				pDCB->DCBFlag  &= ~(TRM_WIDE_NEGO_ENABLE | TRM_DOING_WIDE_NEGO);

				if (sc->MsgBuf[1] != MSG_EXT_WDTR_LEN)
					goto reject_offer;

				switch (sc->MsgBuf[3]) {
				case MSG_EXT_WDTR_BUS_32_BIT:
					if ((pDCB->DCBFlag & TRM_WIDE_NEGO_16BIT) == 0)
						sc->MsgBuf[3] = MSG_EXT_WDTR_BUS_8_BIT;
					else 
						sc->MsgBuf[3] = MSG_EXT_WDTR_BUS_16_BIT;
					break;
					
				case MSG_EXT_WDTR_BUS_16_BIT:
					if ((pDCB->DCBFlag & TRM_WIDE_NEGO_16BIT) == 0) {
						sc->MsgBuf[3] = MSG_EXT_WDTR_BUS_8_BIT;
						break;
					}
					pDCB->SyncPeriod |= WIDE_SYNC;
					/* FALL THROUGH == ACCEPT OFFER */
					
				case MSG_EXT_WDTR_BUS_8_BIT:
					pSRB->SRBState  =  TRM_MSGOUT;
					pDCB->DCBFlag  |= (TRM_SYNC_NEGO_ENABLE | TRM_WIDE_NEGO_DONE);
					
					if (pDCB->MaxNegoPeriod == 0) {
						pDCB->SyncPeriod = 0;
						pDCB->SyncOffset = 0;
						goto re_prog;
					}
					break;
					
				default:
					pDCB->DCBFlag &= ~TRM_WIDE_NEGO_ENABLE; 
					pDCB->DCBFlag |= TRM_WIDE_NEGO_DONE;
reject_offer:
					sc->MsgCnt    = 1;
					sc->MsgBuf[0] = MSG_MESSAGE_REJECT;
					break;
				}
				
				/* Echo accepted offer, or send revised offer */
				bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_SETATN);

			} else if ((sc->MsgBuf[2] == MSG_EXT_SDTR) && (sc->MsgCnt == 5)) {
				/*
				 * =================================
				 * SYNCHRONOUS DATA TRANSFER REQUEST
				 * =================================
				 * byte 0 :  Extended message (01h)
				 * byte 1 :  Extended message length (03)
				 * byte 2 :  SYNCHRONOUS DATA TRANSFER code (01h)
				 * byte 3 :  Transfer period factor 
				 * byte 4 :  REQ/ACK offset  
				 */

				pSRB->SRBState  = TRM_FREE;
				pDCB->DCBFlag  &= ~(TRM_SYNC_NEGO_ENABLE | TRM_DOING_SYNC_NEGO);

				if (sc->MsgBuf[1] != MSG_EXT_SDTR_LEN)
					goto reject_offer;
				
				if ((sc->MsgBuf[3] == 0) || (sc->MsgBuf[4] == 0)) {
					/*
					 * Asynchronous transfers
					 */
					pDCB->SyncPeriod  = 0;
					pDCB->SyncOffset  = 0;
					
				} else {
					/*	
					 * Synchronous transfers
					 */
					/*
					 * REQ/ACK offset
					 */
					pDCB->SyncOffset = sc->MsgBuf[4];

					for (bIndex = 0; bIndex < 7; bIndex++)
						if (sc->MsgBuf[3] <= trm_clock_period[bIndex])
							break;

					pDCB->SyncPeriod |= (bIndex | ALT_SYNC);
				}

re_prog:			/*               
				 *   program SCSI control register
				 */
				bus_space_write_1(iot, ioh, TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod);
				bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset);

				trm_SetXferParams(sc, pDCB, (pDCB->DCBFlag & TRM_QUIRKS_VALID));
			}
			break;
			
		default:
			break;
		}
	}

	/*
	 * initial phase
	 */
	*pscsi_status = PH_BUS_FREE;
	/*
	 * it's important for atn stop
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
	/*
	 * Tell bus that the message was accepted
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT);
}

/*
 * ------------------------------------------------------------
 * Function : trm_MsgInPhase1
 * Purpose  : Clear the FIFO
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_MsgInPhase1(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;

	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO);
	bus_space_write_4(iot, ioh, TRM_S1040_SCSI_COUNTER, 1);

	/*
	 * it's important for atn stop
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
	/*
	 * SCSI command 
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_IN);
}

/*
 * ------------------------------------------------------------
 * Function : trm_Nop
 * Purpose  : EMPTY
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_Nop(struct trm_softc *sc, struct trm_scsi_req_q *pSRB, u_int8_t *pscsi_status)
{
}

/*
 * ------------------------------------------------------------
 * Function : trm_SetXferParams
 * Purpose  : Set the Sync period, offset and mode for each device that has
 *            the same target as the given one (struct trm_dcb *)
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_SetXferParams(struct trm_softc *sc, struct trm_dcb *pDCB, int print_info)
{
	struct trm_dcb *pDCBTemp;
	int lun, target;

	/*
	 * set all lun device's period, offset
	 */
#ifdef TRM_DEBUG0
	printf("%s: trm_SetXferParams\n", sc->sc_device.dv_xname);
#endif

	target = pDCB->target;
	for(lun = 0; lun < TRM_MAX_LUNS; lun++) {
		pDCBTemp = sc->pDCB[target][lun];
		if (pDCBTemp != NULL) {
			pDCBTemp->DevMode       = pDCB->DevMode;
			pDCBTemp->MaxNegoPeriod = pDCB->MaxNegoPeriod;
			pDCBTemp->SyncPeriod    = pDCB->SyncPeriod;
			pDCBTemp->SyncOffset    = pDCB->SyncOffset;
			pDCBTemp->DCBFlag       = pDCB->DCBFlag;
		}
	}

	if (print_info)
		trm_print_info(sc, pDCB);
}

/*
 * ------------------------------------------------------------
 * Function : trm_Disconnect
 * Purpose  :
 * Inputs   : 
 *
 *    ---SCSI bus phase
 *     PH_DATA_OUT          0x00     Data out phase                  
 *     PH_DATA_IN           0x01     Data in phase                
 *     PH_COMMAND           0x02     Command phase     
 *     PH_STATUS            0x03     Status phase
 *     PH_BUS_FREE          0x04     Invalid phase used as bus free    
 *     PH_BUS_FREE          0x05     Invalid phase used as bus free    
 *     PH_MSG_OUT           0x06     Message out phase
 *     PH_MSG_IN            0x07     Message in phase
 * ------------------------------------------------------------
 */
void
trm_Disconnect(struct trm_softc *sc)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	struct trm_scsi_req_q *pSRB, *pNextSRB;
	const bus_space_tag_t iot = sc->sc_iotag;
	struct trm_dcb *pDCB; 
	int j;

#ifdef TRM_DEBUG0
	printf("%s: trm_Disconnect\n", sc->sc_device.dv_xname);
#endif

	pDCB = sc->pActiveDCB;
	if (pDCB == NULL) {
		/* TODO: Why use a loop? Why not use DELAY(400)? */
		for(j = 400; j > 0; --j)
			DELAY(1); /* 1 msec */
		bus_space_write_2(iot, ioh,
		    TRM_S1040_SCSI_CONTROL, (DO_CLRFIFO | DO_HWRESELECT));
		return;
	}

	pSRB = pDCB->pActiveSRB;    
	sc->pActiveDCB = NULL;
	pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */
	bus_space_write_2(iot, ioh,
	    TRM_S1040_SCSI_CONTROL, (DO_CLRFIFO | DO_HWRESELECT));
	DELAY(100);

	switch (pSRB->SRBState) {
	case TRM_UNEXPECT_RESEL:
		pSRB->SRBState = TRM_FREE;
		break;

	case TRM_ABORT_SENT:
		pSRB = TAILQ_FIRST(&sc->goingSRB);
		while (pSRB != NULL) {
			/*
			 * Need to save pNextSRB because trm_FinishSRB() puts
			 * pSRB in freeSRB queue, and thus its links no longer
			 * point to members of the goingSRB queue. This is why
			 * TAILQ_FOREACH() will not work for this traversal.
			 */
			pNextSRB = TAILQ_NEXT(pSRB, link);
			if (pSRB->pSRBDCB == pDCB) {
				/* TODO XXXX: Is TIMED_OUT the best state to report? */
				pSRB->SRBFlag |= TRM_SCSI_TIMED_OUT;
				trm_FinishSRB(sc, pSRB);
			}
			pSRB = pNextSRB;
		}
		break;

	case TRM_START:
	case TRM_MSGOUT:
		/*
		 * Selection time out
		 */
		/* If not polling just keep trying until xs->stimeout expires */
		if ((pSRB->xs->flags & SCSI_POLL) == 0) {
			trm_RewaitSRB(sc, pSRB);
		} else {
			pSRB->TargetStatus = TRM_SCSI_SELECT_TIMEOUT;
			goto  disc1;
		}
		break;

	case TRM_COMPLETED:
disc1:
		/*
		 * TRM_COMPLETED - remove id from mask of active tags
		 */
		pDCB->pActiveSRB = NULL;
		trm_FinishSRB(sc, pSRB);
		break;
		
	default:
		break;
	}

	trm_StartWaitingSRB(sc);
}

/*
 * ------------------------------------------------------------
 * Function : trm_Reselect
 * Purpose  :
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_Reselect(struct trm_softc *sc)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	struct trm_scsi_req_q *pSRB;
	struct trm_dcb *pDCB;
	u_int16_t RselTarLunId;
	u_int8_t target, lun;

#ifdef TRM_DEBUG0
	printf("%s: trm_Reselect\n", sc->sc_device.dv_xname);
#endif

	pDCB = sc->pActiveDCB;
	if (pDCB != NULL) {
		/*
		 * Arbitration lost but Reselection win
		 */
		pSRB = pDCB->pActiveSRB;
		trm_RewaitSRB(sc, pSRB);
	}

	/*
	 * Read Reselected Target Id and LUN
	 */
	RselTarLunId = bus_space_read_2(iot, ioh, TRM_S1040_SCSI_TARGETID) & 0x1FFF;
	/* TODO XXXX: Make endian independent! */
	target = RselTarLunId & 0xff;
	lun    = (RselTarLunId >> 8) & 0xff;

#ifdef TRM_DEBUG0
	printf("%s: reselect - target = %d, lun = %d\n",
	    sc->sc_device.dv_xname, target, lun);
#endif

	if ((target < TRM_MAX_TARGETS) && (lun < TRM_MAX_LUNS))
		pDCB = sc->pDCB[target][lun];
	else
		pDCB = NULL;

	if (pDCB == NULL)
		printf("%s: reselect - target = %d, lun = %d not found\n",
		    sc->sc_device.dv_xname, target, lun);

	sc->pActiveDCB = pDCB;

	/* TODO XXXX: This will crash if pDCB is ever NULL */
	if ((pDCB->DCBFlag & TRM_USE_TAG_QUEUING) != 0) {
		pSRB = &sc->SRB[0];
		pDCB->pActiveSRB = pSRB;
	} else {
		pSRB = pDCB->pActiveSRB;
		if (pSRB == NULL || (pSRB->SRBState != TRM_DISCONNECTED)) {
			/*
			 * abort command
			 */
			pSRB = &sc->SRB[0];
			pSRB->SRBState = TRM_UNEXPECT_RESEL;
			pDCB->pActiveSRB = pSRB;
			trm_EnableMsgOut(sc, MSG_ABORT);
		} else
			pSRB->SRBState = TRM_DATA_XFER;
	}
	pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */

	/* 
	 * Program HA ID, target ID, period and offset
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_TARGETID, target);
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_HOSTID, sc->sc_AdaptSCSIID);
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod);
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset);

	/*
	 * it's important for atn stop
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
	DELAY(30);

	/*
	 * SCSI command 
	 * to rls the /ACK signal
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT);
}

/*
 * ------------------------------------------------------------
 * Function : trm_FinishSRB
 * Purpose  : Complete execution of a SCSI command
 *            Signal completion to the generic SCSI driver    
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_FinishSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB)
{
	struct scsi_inquiry_data *ptr;
	struct scsi_sense_data *s1, *s2;
	struct scsi_xfer *xs = pSRB->xs;
	struct trm_dcb *pDCB = pSRB->pSRBDCB;
	int target, lun;

#ifdef TRM_DEBUG0
	printf("%s: trm_FinishSRB. sc = %p, pSRB = %p\n",
	    sc->sc_device.dv_xname, sc, pSRB);
#endif
	pDCB->DCBFlag &= ~TRM_QUEUE_FULL;

	if (xs == NULL) {
		trm_ReleaseSRB(sc, pSRB);
		return;
	}

	timeout_del(&xs->stimeout);

	xs->status = pSRB->TargetStatus;

	switch (xs->status) {
	case SCSI_INTERM_COND_MET:
	case SCSI_COND_MET:
	case SCSI_INTERM:
	case SCSI_OK:
		switch (pSRB->AdaptStatus) {
		case TRM_STATUS_GOOD:
			if ((pSRB->SRBFlag & TRM_PARITY_ERROR) != 0) {
#ifdef TRM_DEBUG0
				printf("%s: trm_FinishSRB. TRM_PARITY_ERROR\n",
				    sc->sc_device.dv_xname);
#endif		
				xs->error = XS_DRIVER_STUFFUP;

			} else if ((pSRB->SRBFlag & TRM_SCSI_TIMED_OUT) != 0) {
				xs->error = XS_TIMEOUT;

			} else if ((pSRB->SRBFlag & TRM_AUTO_REQSENSE) != 0) {
				s1 = &pSRB->scsisense;
				s2 = &xs->sense;
				
				*s2 = *s1;
				
				xs->status = SCSI_CHECK;
				xs->error  = XS_SENSE;

			} else
				xs->error = XS_NOERROR;
			break;

		case TRM_OVER_UNDER_RUN:
#ifdef TRM_DEBUG0
			printf("%s: trm_FinishSRB. TRM_OVER_UNDER_RUN\n",
			    sc->sc_device.dv_xname);
#endif
			xs->error = XS_DRIVER_STUFFUP;
			break;

		default:
#ifdef TRM_DEBUG0
			printf("%s: trm_FinishSRB. AdaptStatus Error = 0x%02x\n",
			    sc->sc_device.dv_xname, pSRB->AdaptStatus);
#endif	
			xs->error = XS_DRIVER_STUFFUP;
			break;
		}
		break;
		
	case SCSI_TERMINATED:
	case SCSI_ACA_ACTIVE:
	case SCSI_CHECK:
		if ((pSRB->SRBFlag & TRM_AUTO_REQSENSE) != 0)
			xs->error = XS_DRIVER_STUFFUP;
		else {
			trm_RequestSense(sc, pSRB);
			return;
		}
		break;

	case SCSI_QUEUE_FULL:
		/* this says no more until someone completes */
		pDCB->DCBFlag |= TRM_QUEUE_FULL;
		trm_RewaitSRB(sc, pSRB);
		return;
	
	case SCSI_RESV_CONFLICT:
	case SCSI_BUSY:
		xs->error = XS_BUSY;
		break;

	case TRM_SCSI_UNEXP_BUS_FREE:
		xs->status = SCSI_OK;
		xs->error  = XS_DRIVER_STUFFUP;
		break;

	case TRM_SCSI_BUS_RST_DETECTED:
		xs->status = SCSI_OK;
		xs->error  = XS_RESET;
		break;

	case TRM_SCSI_SELECT_TIMEOUT:
		xs->status = SCSI_OK;
		xs->error  = XS_SELTIMEOUT;
		break;

	default:
		xs->error = XS_DRIVER_STUFFUP;
		break;
	}

	target = xs->sc_link->target;
	lun    = xs->sc_link->lun;   

	if ((xs->flags & SCSI_POLL) != 0) {

		if (xs->cmd->opcode == INQUIRY) {

			ptr = (struct scsi_inquiry_data *) xs->data; 

			if ((xs->error != XS_NOERROR) ||
			    ((ptr->device & SID_QUAL_BAD_LU) == SID_QUAL_BAD_LU)) {
#ifdef TRM_DEBUG0
				printf("%s: trm_FinishSRB NO Device:target= %d,lun= %d\n",
				    sc->sc_device.dv_xname, target, lun);
#endif		
				free(pDCB, M_DEVBUF);
				sc->pDCB[target][lun] = NULL;
				pDCB = NULL;

			} else
				pDCB->sc_link = xs->sc_link;
		}
	}

	trm_ReleaseSRB(sc, pSRB);

	/*
	 * Notify cmd done
	 */
#ifdef TRM_DEBUG0
	if ((xs->error != 0) || (xs->status != 0) || ((xs->flags & SCSI_POLL) != 0))
		printf("%s: trm_FinishSRB. %d/%d xs->cmd->opcode = 0x%02x, xs->error = %d, xs->status = %d\n",
		    sc->sc_device.dv_xname, target, lun, xs->cmd->opcode, xs->error, xs->status);
#endif

	scsi_done(xs);
}

/*
 * ------------------------------------------------------------
 * Function : trm_ReleaseSRB
 * Purpose  : 
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_ReleaseSRB(struct trm_softc *sc, struct trm_scsi_req_q *pSRB)
{
	struct scsi_xfer *xs = pSRB->xs;
	int intflag;

	intflag = splbio();

	if (pSRB->TagNumber != TRM_NO_TAG) {
		pSRB->pSRBDCB->TagMask &= ~(1 << pSRB->TagNumber);
		pSRB->TagNumber = TRM_NO_TAG;
	}

	if (xs != NULL) {
		timeout_del(&xs->stimeout);

		if (xs->datalen != 0) {
			bus_dmamap_sync(sc->sc_dmatag, pSRB->dmamapxfer,
			    0, pSRB->dmamapxfer->dm_mapsize,
			    (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD :
			    BUS_DMASYNC_POSTWRITE);
			bus_dmamap_unload(sc->sc_dmatag, pSRB->dmamapxfer);
		}
	}

	/* SRB may have started & finished, or be waiting and timed out */
	if ((pSRB->SRBFlag & TRM_ON_WAITING_SRB) != 0) {
		pSRB->SRBFlag &= ~TRM_ON_WAITING_SRB;
		TAILQ_REMOVE(&sc->waitingSRB, pSRB, link);
	}
	if ((pSRB->SRBFlag & TRM_ON_GOING_SRB) != 0) {
		pSRB->SRBFlag &= ~TRM_ON_GOING_SRB;
		TAILQ_REMOVE(&sc->goingSRB, pSRB, link);
	}

	bzero(&pSRB->SegmentX[0], sizeof(pSRB->SegmentX));
	bzero(&pSRB->CmdBlock[0], sizeof(pSRB->CmdBlock));
	bzero(&pSRB->scsisense,   sizeof(pSRB->scsisense));

	pSRB->SRBTotalXferLength = 0;
	pSRB->SRBSGCount         = 0;
	pSRB->SRBSGIndex         = 0;
	pSRB->SRBFlag            = 0;

	pSRB->SRBState     = TRM_FREE;
	pSRB->AdaptStatus  = TRM_STATUS_GOOD;
	pSRB->TargetStatus = SCSI_OK;
	pSRB->ScsiPhase    = PH_BUS_FREE; /* SCSI bus free Phase */

	pSRB->xs      = NULL;
	pSRB->pSRBDCB = NULL;

	if (pSRB != &sc->SRB[0])
		TAILQ_INSERT_TAIL(&sc->freeSRB, pSRB, link);

	splx(intflag);
}

/*
 * ------------------------------------------------------------
 * Function : trm_GoingSRB_Done
 * Purpose  :
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_GoingSRB_Done(struct trm_softc *sc)
{
	struct trm_scsi_req_q *pSRB;

	/* ASSUME we are inside a splbio()/splx() pair */

	while ((pSRB = TAILQ_FIRST(&sc->goingSRB)) != NULL) {
		/* TODO XXXX: Is TIMED_OUT the best status? */
		pSRB->SRBFlag |= TRM_SCSI_TIMED_OUT;
		trm_FinishSRB(sc, pSRB);
	}
}

/*
 * ------------------------------------------------------------
 * Function : trm_ResetSCSIBus
 * Purpose  : Reset the SCSI bus
 * Inputs   : struct trm_softc * - 
 * ------------------------------------------------------------
 */
void
trm_ResetSCSIBus(struct trm_softc *sc)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	int intflag;

	intflag = splbio();

	sc->sc_Flag |= RESET_DEV;

	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_RSTSCSI);
	while ((bus_space_read_2(iot, ioh,
	    TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET) == 0);

	splx(intflag);
}

/*
 * ------------------------------------------------------------
 * Function : trm_ScsiRstDetect
 * Purpose  :
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_ScsiRstDetect(struct trm_softc *sc)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	int wlval;

#ifdef TRM_DEBUG0
	printf("%s: trm_ScsiRstDetect\n", sc->sc_device.dv_xname);
#endif

	wlval = 1000;
	/*
	 * delay 1 sec
	 */
	while (--wlval != 0)
		DELAY(1000);

	bus_space_write_1(iot, ioh, TRM_S1040_DMA_CONTROL, STOPDMAXFER);
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO);

	if ((sc->sc_Flag & RESET_DEV) != 0)
		sc->sc_Flag |= RESET_DONE;
	else {
		sc->sc_Flag |= RESET_DETECT;
		trm_ResetAllDevParam(sc);
		trm_RecoverSRB(sc);
		sc->pActiveDCB = NULL;
		sc->sc_Flag = 0;
		trm_StartWaitingSRB(sc);
	}
}

/*
 * ------------------------------------------------------------
 * Function : trm_RequestSense
 * Purpose  :
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_RequestSense(struct trm_softc *sc, struct trm_scsi_req_q *pSRB)
{
	pSRB->SRBFlag |= TRM_AUTO_REQSENSE;

	/*
	 * Status of initiator/target
	 */
	pSRB->AdaptStatus  = TRM_STATUS_GOOD;
	pSRB->TargetStatus = SCSI_OK;
	/*
	 * Status of initiator/target
	 */

	pSRB->SegmentX[0].address = pSRB->scsisensePhyAddr;
	pSRB->SegmentX[0].length  = sizeof(struct scsi_sense_data);
	pSRB->SRBTotalXferLength  = sizeof(struct scsi_sense_data);
	pSRB->SRBSGCount          = 1;
	pSRB->SRBSGIndex          = 0;

	bzero(&pSRB->CmdBlock[0], sizeof(pSRB->CmdBlock));

	pSRB->CmdBlock[0] = REQUEST_SENSE;
	pSRB->CmdBlock[1] = (pSRB->xs->sc_link->lun) << 5;
	pSRB->CmdBlock[4] = sizeof(struct scsi_sense_data);

	pSRB->ScsiCmdLen = 6;

	if ((pSRB->xs != NULL) && ((pSRB->xs->flags & SCSI_POLL) == 0))
		timeout_add_msec(&pSRB->xs->stimeout, pSRB->xs->timeout);

	if (trm_StartSRB(sc, pSRB) != 0)
		trm_RewaitSRB(sc, pSRB);
}

/*
 * ------------------------------------------------------------
 * Function : trm_EnableMsgOut
 * Purpose  : set up MsgBuf to send out a single byte message
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_EnableMsgOut(struct trm_softc *sc, u_int8_t msg)
{
	sc->MsgBuf[0] = msg;
	sc->MsgCnt    = 1;

	bus_space_write_2(sc->sc_iotag, sc->sc_iohandle, TRM_S1040_SCSI_CONTROL, DO_SETATN);
}

/*
 * ------------------------------------------------------------
 * Function : trm_linkSRB
 * Purpose  :
 * Inputs   :
 * ------------------------------------------------------------
 */
void
trm_linkSRB(struct trm_softc *sc)
{
	struct trm_scsi_req_q *pSRB;
	int i, intflag;

	intflag = splbio();

	for (i = 0; i < TRM_MAX_SRB_CNT; i++) {
		pSRB = &sc->SRB[i];

		pSRB->PhysSRB = sc->sc_dmamap_control->dm_segs[0].ds_addr
			+ i * sizeof(struct trm_scsi_req_q);

		pSRB->SRBSGPhyAddr = sc->sc_dmamap_control->dm_segs[0].ds_addr
			+ i * sizeof(struct trm_scsi_req_q)
			+ offsetof(struct trm_scsi_req_q, SegmentX);

		pSRB->scsisensePhyAddr = sc->sc_dmamap_control->dm_segs[0].ds_addr
			+ i * sizeof(struct trm_scsi_req_q)
			+ offsetof(struct trm_scsi_req_q, scsisense);

		/*
		 * map all SRB space
		 */
		if (bus_dmamap_create(sc->sc_dmatag, TRM_MAX_PHYSG_BYTE,
		    TRM_MAX_SG_LISTENTRY, TRM_MAX_PHYSG_BYTE, 0,
		    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
		    &pSRB->dmamapxfer) != 0) {
			printf("%s: unable to create DMA transfer map\n",
			    sc->sc_device.dv_xname);
			splx(intflag);
			return;
		}

		if (i > 0)
			/* We use sc->SRB[0] directly, so *don't* link it */
			TAILQ_INSERT_TAIL(&sc->freeSRB, pSRB, link);
#ifdef TRM_DEBUG0
		printf("pSRB = %p ", pSRB);
#endif
	}
#ifdef TRM_DEBUG0
	printf("\n ");
#endif
	splx(intflag);
}

/*
 * ------------------------------------------------------------
 * Function : trm_minphys
 * Purpose  : limited by the number of segments in the dma segment list
 * Inputs   : *buf
 * ------------------------------------------------------------
 */
void
trm_minphys(struct buf *bp, struct scsi_link *sl)
{
	if (bp->b_bcount > (TRM_MAX_SG_LISTENTRY-1) * (long) NBPG) {
		bp->b_bcount = (TRM_MAX_SG_LISTENTRY-1) * (long) NBPG;
	}
	minphys(bp);
}

/*
 * ------------------------------------------------------------
 * Function : trm_initACB
 * Purpose  : initialize the internal structures for a given SCSI host
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_initACB(struct trm_softc *sc, int unit)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	struct trm_adapter_nvram *pEEpromBuf;
	struct trm_dcb *pDCB;
	int target, lun;

	pEEpromBuf = &trm_eepromBuf[unit];
	sc->sc_config = HCC_AUTOTERM | HCC_PARITY;

	if ((bus_space_read_1(iot, ioh, TRM_S1040_GEN_STATUS) & WIDESCSI) != 0)
		sc->sc_config |= HCC_WIDE_CARD;

	if ((pEEpromBuf->NvramChannelCfg & NAC_POWERON_SCSI_RESET) != 0)
		sc->sc_config |= HCC_SCSI_RESET;

	TAILQ_INIT(&sc->freeSRB);
	TAILQ_INIT(&sc->waitingSRB);
	TAILQ_INIT(&sc->goingSRB);

	sc->pActiveDCB     = NULL;
	sc->sc_AdapterUnit = unit;
	sc->sc_AdaptSCSIID = pEEpromBuf->NvramScsiId;
	sc->sc_TagMaxNum   = 2 << pEEpromBuf->NvramMaxTag;
	sc->sc_Flag        = 0;

	/* 
	 * put all SRB's (except [0]) onto the freeSRB list
	 */
	trm_linkSRB(sc);

	/*
	 * allocate DCB array
	 */
	for (target = 0; target < TRM_MAX_TARGETS; target++) {
		if (target == sc->sc_AdaptSCSIID)
			continue;

		for (lun = 0; lun < TRM_MAX_LUNS; lun++) {
			pDCB = (struct trm_dcb *)malloc(sizeof(struct trm_dcb),
			    M_DEVBUF, M_NOWAIT | M_ZERO);
			sc->pDCB[target][lun] = pDCB;
			
			if (pDCB == NULL)
				continue;

			pDCB->target     = target;
			pDCB->lun        = lun;
			pDCB->pActiveSRB = NULL;
		}
	}

	sc->sc_adapter.scsi_cmd     = trm_scsi_cmd; 
	sc->sc_adapter.scsi_minphys = trm_minphys;

	sc->sc_link.adapter_softc    = sc;
	sc->sc_link.adapter_target   = sc->sc_AdaptSCSIID;
	sc->sc_link.openings         = 30; /* So TagMask (32 bit integer) always has space */
	sc->sc_link.device           = &trm_device;
	sc->sc_link.adapter          = &sc->sc_adapter;
	sc->sc_link.adapter_buswidth = ((sc->sc_config & HCC_WIDE_CARD) == 0) ? 8:16;

	trm_reset(sc);
}

/*
 * ------------------------------------------------------------
 * Function     : trm_write_all            
 * Description  : write pEEpromBuf 128 bytes to seeprom    
 * Input        : iot, ioh - chip's base address        
 * Output       : none                        
 * ------------------------------------------------------------
 */
void
trm_write_all(struct trm_adapter_nvram *pEEpromBuf,  bus_space_tag_t iot,
    bus_space_handle_t ioh)
{
	u_int8_t *bpEeprom = (u_int8_t *)pEEpromBuf;
	u_int8_t  bAddr;

	/*
	 * Enable SEEPROM
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL,
	    (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) | EN_EEPROM));
	/*
	 * Write enable
	 */
	trm_write_cmd(iot, ioh, 0x04, 0xFF);
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0);
	trm_wait_30us(iot, ioh);
	for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++)
		trm_set_data(iot, ioh, bAddr, *bpEeprom);
	/* 
	 * Write disable
	 */
	trm_write_cmd(iot, ioh, 0x04, 0x00);
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0);
	trm_wait_30us(iot, ioh);
	/*
	 * Disable SEEPROM
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL,
	    (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) & ~EN_EEPROM));
}

/*
 * ------------------------------------------------------------
 * Function     : trm_set_data                    
 * Description  : write one byte to seeprom
 * Input        : iot, ioh - chip's base address    
 *                  bAddr - address of SEEPROM        
 *                  bData - data of SEEPROM    
 * Output       : none                
 * ------------------------------------------------------------
 */
void
trm_set_data(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t bAddr,
    u_int8_t bData)
{
	u_int8_t bSendData;
	int i;

	/* 
	 * Send write command & address    
	 */
	trm_write_cmd(iot, ioh, 0x05, bAddr);
	/* 
	 * Write data 
	 */
	for (i = 0; i < 8; i++, bData <<= 1) {
		bSendData = NVR_SELECT;
		if ((bData & 0x80) != 0) {      /* Start from bit 7    */
			bSendData |= NVR_BITOUT;
		}
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, bSendData);
		trm_wait_30us(iot, ioh);
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM,
		    (bSendData | NVR_CLOCK));
		trm_wait_30us(iot, ioh);
	}
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT);
	trm_wait_30us(iot, ioh);
	/*
	 * Disable chip select 
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0);
	trm_wait_30us(iot, ioh);
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT);
	trm_wait_30us(iot, ioh);
	/* 
	 * Wait for write ready    
	 */
	for (;;) {
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM,
		    (NVR_SELECT | NVR_CLOCK));
		trm_wait_30us(iot, ioh);
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT);
		trm_wait_30us(iot, ioh);
		if (bus_space_read_1(iot, ioh, TRM_S1040_GEN_NVRAM) & NVR_BITIN)
			break;
	}
	/* 
	 * Disable chip select 
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0);
}

/*
 * ------------------------------------------------------------
 * Function     : trm_read_all                    
 * Description  : read seeprom 128 bytes to pEEpromBuf
 * Input        : pEEpromBuf, iot, ioh - chip's base address        
 * Output       : none                        
 * ------------------------------------------------------------
 */
void
trm_read_all(struct trm_adapter_nvram *pEEpromBuf,  bus_space_tag_t iot,
    bus_space_handle_t ioh)
{
	u_int8_t *bpEeprom = (u_int8_t *)pEEpromBuf;
	u_int8_t  bAddr;
	
	/*
	 * Enable SEEPROM 
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL,
	    (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) | EN_EEPROM));

	for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++)
		*bpEeprom = trm_get_data(iot, ioh, bAddr);

	/* 
	 * Disable SEEPROM 
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_CONTROL,
	    (bus_space_read_1(iot, ioh, TRM_S1040_GEN_CONTROL) & ~EN_EEPROM));
}

/*
 * ------------------------------------------------------------
 * Function     : trm_get_data                        
 * Description  : read one byte from seeprom    
 * Input        : iot, ioh - chip's base address
 *                     bAddr - address of SEEPROM        
 * Output       : bData - data of SEEPROM        
 * ------------------------------------------------------------
 */
u_int8_t
trm_get_data( bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t bAddr)
{
	u_int8_t bReadData, bData;
	int i;

	bData = 0;

	/* 
	 * Send read command & address
	 */
	trm_write_cmd(iot, ioh, 0x06, bAddr);
				
	for (i = 0; i < 8; i++) {
		/* 
		 * Read data
		 */
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM,
		    (NVR_SELECT | NVR_CLOCK));
		trm_wait_30us(iot, ioh);
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT);
		/* 
		 * Get data bit while falling edge 
		 */
		bReadData = bus_space_read_1(iot, ioh, TRM_S1040_GEN_NVRAM);
		bData <<= 1;
		if ((bReadData & NVR_BITIN) != 0)
			bData |= 1;
		trm_wait_30us(iot, ioh);
	}
	/* 
	 * Disable chip select 
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, 0);

	return bData;
}

/*
 * ------------------------------------------------------------
 * Function     : trm_wait_30us                    
 * Description  : wait 30 us                        
 * Input        : iot, ioh - chip's base address            
 * Output       : none                            
 * ------------------------------------------------------------
 */
void
trm_wait_30us(bus_space_tag_t iot, bus_space_handle_t ioh)
{
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_TIMER, 5);

	while ((bus_space_read_1(iot, ioh, TRM_S1040_GEN_STATUS) & GTIMEOUT)
	    == 0);
}

/*
 * ------------------------------------------------------------
 * Function     : trm_write_cmd                        
 * Description  : write SB and Op Code into seeprom    
 * Input        : iot, ioh - chip's base address            
 *                  bCmd     - SB + Op Code                
 *                  bAddr    - address of SEEPROM        
 * Output       : none                    
 * ------------------------------------------------------------
 */
void
trm_write_cmd( bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t bCmd,
    u_int8_t bAddr)
{
	u_int8_t bSendData;
	int i;

	for (i = 0; i < 3; i++, bCmd <<= 1) {
		/* 
		 * Program SB + OP code        
		 */
		bSendData = NVR_SELECT;
		if (bCmd & 0x04)        /* Start from bit 2        */
			bSendData |= NVR_BITOUT;
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, bSendData);
		trm_wait_30us(iot, ioh);
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM,
		    (bSendData | NVR_CLOCK));
		trm_wait_30us(iot, ioh);
	}
				
	for (i = 0; i < 7; i++, bAddr <<= 1) {
		/* 
		 * Program address        
		 */
		bSendData = NVR_SELECT;
		if (bAddr & 0x40) {        /* Start from bit 6        */
			bSendData |= NVR_BITOUT;
		}
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, bSendData);
		trm_wait_30us(iot, ioh);
		bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM,
		    (bSendData | NVR_CLOCK));
		trm_wait_30us(iot, ioh);
	}
	bus_space_write_1(iot, ioh, TRM_S1040_GEN_NVRAM, NVR_SELECT);
	trm_wait_30us(iot, ioh);
}

/*
 * ------------------------------------------------------------
 * Function     : trm_check_eeprom                
 * Description  : read eeprom 128 bytes to pEEpromBuf and check
 *                  checksum. If it is wrong, updated with default value.
 * Input        : eeprom, iot, ioh - chip's base address        
 * Output       : none                    
 * ------------------------------------------------------------
 */
void
trm_check_eeprom(struct trm_adapter_nvram *pEEpromBuf, bus_space_tag_t iot,
    bus_space_handle_t ioh)
{
	u_int32_t *dpEeprom = (u_int32_t *)pEEpromBuf->NvramTarget;
	u_int32_t  dAddr;
	u_int16_t *wpEeprom = (u_int16_t *)pEEpromBuf;
	u_int16_t  wAddr, wCheckSum;

#ifdef TRM_DEBUG0
	printf("\ntrm_check_eeprom\n");
#endif
	trm_read_all(pEEpromBuf, iot, ioh);
	wCheckSum = 0;
	for (wAddr = 0; wAddr < 64; wAddr++, wpEeprom++)
		wCheckSum += *wpEeprom;

	if (wCheckSum != 0x1234) {  
#ifdef TRM_DEBUG0
		printf("TRM_S1040 EEPROM Check Sum ERROR (load default)\n");
#endif
		/* 
		 * Checksum error, load default    
		 */
		pEEpromBuf->NvramSubVendorID[0] = (u_int8_t)PCI_VENDOR_TEKRAM2;
		pEEpromBuf->NvramSubVendorID[1] = (u_int8_t)(PCI_VENDOR_TEKRAM2
		    >> 8);
		pEEpromBuf->NvramSubSysID[0] = (u_int8_t)
		    PCI_PRODUCT_TEKRAM2_DC3X5U;
		pEEpromBuf->NvramSubSysID[1] = (u_int8_t)
		    (PCI_PRODUCT_TEKRAM2_DC3X5U >> 8);
		pEEpromBuf->NvramSubClass    = 0;
		pEEpromBuf->NvramVendorID[0] = (u_int8_t)PCI_VENDOR_TEKRAM2;
		pEEpromBuf->NvramVendorID[1] = (u_int8_t)(PCI_VENDOR_TEKRAM2
		    >> 8);
		pEEpromBuf->NvramDeviceID[0] = (u_int8_t)
		    PCI_PRODUCT_TEKRAM2_DC3X5U;
		pEEpromBuf->NvramDeviceID[1] = (u_int8_t)
		    (PCI_PRODUCT_TEKRAM2_DC3X5U >> 8);
		pEEpromBuf->NvramReserved    = 0;

		for (dAddr = 0; dAddr < 16; dAddr++, dpEeprom++)
			/*
			 * NvmTarCfg3,NvmTarCfg2,NvmTarPeriod,NvmTarCfg0
			 */
			*dpEeprom = 0x00000077;

		/*
		 * NvramMaxTag,NvramDelayTime,NvramChannelCfg,NvramScsiId
		 */
		*dpEeprom++ = 0x04000F07;

		/*
		 * NvramReserved1,NvramBootLun,NvramBootTarget,NvramReserved0
		 */
		*dpEeprom++ = 0x00000015;
		for (dAddr = 0; dAddr < 12; dAddr++, dpEeprom++)
			*dpEeprom = 0;

		pEEpromBuf->NvramCheckSum = 0;
		for (wAddr = 0, wCheckSum =0; wAddr < 63; wAddr++, wpEeprom++)
			wCheckSum += *wpEeprom;

		*wpEeprom = 0x1234 - wCheckSum;
		trm_write_all(pEEpromBuf, iot, ioh);
	}
}

/*
 * ------------------------------------------------------------
 * Function : trm_initAdapter
 * Purpose  : initialize the SCSI chip ctrl registers
 * Inputs   : psh - pointer to this host adapter's structure
 * ------------------------------------------------------------
 */
void
trm_initAdapter(struct trm_softc *sc)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	u_int16_t wval;
	u_int8_t bval;

	/*
	 * program configuration 0
	 */
	if ((sc->sc_config & HCC_PARITY) != 0) {
		bval = PHASELATCH | INITIATOR | BLOCKRST | PARITYCHECK;
	} else {
		bval = PHASELATCH | INITIATOR | BLOCKRST;
	}
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_CONFIG0, bval); 
	/*
	 * program configuration 1
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_CONFIG1, 0x13); 
	/*
	 * 250ms selection timeout
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_TIMEOUT, TRM_SEL_TIMEOUT);
	/*
	 * Mask all the interrupt
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN,  0);    
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_INTEN, 0);     
	/*
	 * Reset SCSI module
	 */
	bus_space_write_2(iot, ioh, TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); 
	/*
	 * program Host ID
	 */
	bval = sc->sc_AdaptSCSIID;
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_HOSTID, bval); 
	/*
	 * set ansynchronous transfer
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_OFFSET, 0); 
	/*
	 * Turn LED control off
	 */
	wval = bus_space_read_2(iot, ioh, TRM_S1040_GEN_CONTROL) & 0x7F;
	bus_space_write_2(iot, ioh, TRM_S1040_GEN_CONTROL, wval); 
	/*
	 * DMA config
	 */
	wval = bus_space_read_2(iot, ioh, TRM_S1040_DMA_CONFIG) | DMA_ENHANCE;
	bus_space_write_2(iot, ioh, TRM_S1040_DMA_CONFIG, wval); 
	/*
	 * Clear pending interrupt status
	 */
	bus_space_read_1(iot, ioh, TRM_S1040_SCSI_INTSTATUS);
	/*
	 * Enable SCSI interrupts
	 */
	bus_space_write_1(iot, ioh, TRM_S1040_SCSI_INTEN,
	    (EN_SELECT | EN_SELTIMEOUT | EN_DISCONNECT | EN_RESELECTED |
		EN_SCSIRESET | EN_BUSSERVICE | EN_CMDDONE)); 
	bus_space_write_1(iot, ioh, TRM_S1040_DMA_INTEN, EN_SCSIINTR); 
}

/*
 * ------------------------------------------------------------
 * Function      : trm_init
 * Purpose       : initialize the internal structures for a given SCSI host
 * Inputs        : host - pointer to this host adapter's structure
 * Preconditions : when this function is called, the chip_type field of 
 *                 the ACB structure MUST have been set.
 * ------------------------------------------------------------
 */
int
trm_init(struct trm_softc *sc, int unit)
{
	const bus_space_handle_t ioh = sc->sc_iohandle;
	const bus_space_tag_t iot = sc->sc_iotag;
	bus_dma_segment_t seg;
	int error, rseg, all_srbs_size;

	/*
	 * EEPROM CHECKSUM
	 */
	trm_check_eeprom(&trm_eepromBuf[unit], iot, ioh);

	/*
	 * MEMORY ALLOCATE FOR ADAPTER CONTROL BLOCK
	 */
	/*
	 * allocate the space for all SCSI control blocks (SRB) for DMA memory.
	 */
	all_srbs_size = TRM_MAX_SRB_CNT * sizeof(struct trm_scsi_req_q);

	error = bus_dmamem_alloc(sc->sc_dmatag, all_srbs_size, NBPG, 0, &seg,
	    1, &rseg, BUS_DMA_NOWAIT | BUS_DMA_ZERO);
	if (error != 0) {
		printf("%s: unable to allocate SCSI REQUEST BLOCKS, error = %d\n",
		    sc->sc_device.dv_xname, error);
		/*errx(error, "%s: unable to allocate SCSI request blocks",
		    sc->sc_device.dv_xname);*/
		return -1;
	}

	error = bus_dmamem_map(sc->sc_dmatag, &seg, rseg, all_srbs_size,
	    (caddr_t *)&sc->SRB, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
	if (error != 0) {
		printf("%s: unable to map SCSI REQUEST BLOCKS, error = %d\n",
		    sc->sc_device.dv_xname, error);
		/*errx(error, "unable to map SCSI request blocks");*/
		return -1;
	}

	error = bus_dmamap_create(sc->sc_dmatag, all_srbs_size, 1,
	    all_srbs_size, 0, BUS_DMA_NOWAIT,&sc->sc_dmamap_control);
	if (error != 0) {
		printf("%s: unable to create SRB DMA maps, error = %d\n",
		    sc->sc_device.dv_xname, error);
		/*errx(error, "unable to create SRB DMA maps");*/
		return -1;
	}

	error = bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap_control,
	    sc->SRB, all_srbs_size, NULL, BUS_DMA_NOWAIT);
	if (error != 0) {
		printf("%s: unable to load SRB DMA maps, error = %d\n",
		    sc->sc_device.dv_xname, error);
		/*errx(error, "unable to load SRB DMA maps");*/
		return -1;
	}
#ifdef TRM_DEBUG0
	printf("\n\n%s: all_srbs_size=%x\n", 
	    sc->sc_device.dv_xname, all_srbs_size);
#endif
	trm_initACB(sc, unit);
	trm_initAdapter(sc);

	return 0;
}

/* ------------------------------------------------------------
 * Function : trm_print_info
 * Purpose  : Print the DCB negotiation information
 * Inputs   : 
 * ------------------------------------------------------------
 */
void
trm_print_info(struct trm_softc *sc, struct trm_dcb *pDCB)
{
	int syncXfer, index;

	index = pDCB->SyncPeriod & ~(WIDE_SYNC | ALT_SYNC);

	printf("%s: target %d using ", sc->sc_device.dv_xname, pDCB->target);
	if ((pDCB->SyncPeriod & WIDE_SYNC) != 0)
		printf("16 bit ");
	else
		printf("8 bit ");

	if (pDCB->SyncOffset == 0)
		printf("Asynchronous ");
	else {
		syncXfer = 100000 / (trm_clock_period[index] * 4);
		printf("%d.%01d MHz, Offset %d ",
		    syncXfer / 100, syncXfer % 100, pDCB->SyncOffset);
	}
	printf("data transfers ");

	if ((pDCB->DCBFlag & TRM_USE_TAG_QUEUING) != 0)
		printf("with Tag Queuing");

	printf("\n");
}
