NetBSD-5.0.2/sys/dev/pci/trm.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: trm.c,v 1.30 2008/05/14 13:29:29 tsutsui Exp $	*/
/*-
 * Copyright (c) 2002 Izumi Tsutsui.  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.
 *
 * 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.
 */

/*
 * Device Driver for Tekram DC395U/UW/F, DC315/U
 * PCI SCSI Bus Master Host Adapter
 * (SCSI chip set used Tekram ASIC TRM-S1040)
 *
 * Copyright (c) 2001 Rui-Xiang Guo
 * 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. 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.
 */
/*
 * Ported from
 *   dc395x_trm.c
 *
 * Written for NetBSD 1.4.x by
 *   Erich Chen     (erich@tekram.com.tw)
 *
 * Provided by
 *   (C)Copyright 1995-1999 Tekram Technology Co., Ltd. All rights reserved.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: trm.c,v 1.30 2008/05/14 13:29:29 tsutsui Exp $");

/* #define TRM_DEBUG */
#ifdef TRM_DEBUG
int trm_debug = 1;
#define DPRINTF(arg)	if (trm_debug > 0) printf arg;
#else
#define DPRINTF(arg)
#endif

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

#include <sys/bus.h>
#include <sys/intr.h>

#include <uvm/uvm_extern.h>

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

#include <dev/pci/pcidevs.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/trmreg.h>

/*
 * feature of chip set MAX value
 */
#define TRM_MAX_TARGETS		16
#define TRM_MAX_LUNS		8
#define TRM_MAX_SG_ENTRIES	(MAXPHYS / PAGE_SIZE + 1)
#define TRM_MAX_SRB		32 /* XXX */
#define TRM_MAX_TAG		TRM_MAX_SRB /* XXX */
#define TRM_MAX_OFFSET		15
#define TRM_MAX_PERIOD		125

/*
 * Segment Entry
 */
struct trm_sg_entry {
	uint32_t address;
	uint32_t length;
};

#define TRM_SG_SIZE	(sizeof(struct trm_sg_entry) * TRM_MAX_SG_ENTRIES)

/*
 **********************************************************************
 * The SEEPROM structure for TRM_S1040
 **********************************************************************
 */
struct nvram_target {
	uint8_t config0;		/* Target configuration byte 0 */
#define NTC_DO_WIDE_NEGO	0x20	/* Wide negotiate	     */
#define NTC_DO_TAG_QUEUING	0x10	/* Enable SCSI tagged queuing  */
#define NTC_DO_SEND_START	0x08	/* Send start command SPINUP */
#define NTC_DO_DISCONNECT	0x04	/* Enable SCSI disconnect    */
#define NTC_DO_SYNC_NEGO	0x02	/* Sync negotiation	     */
#define NTC_DO_PARITY_CHK	0x01	/* Parity check enable 	     */
	uint8_t period;			/* Target period	       */
	uint8_t config2;		/* Target configuration byte 2 */
	uint8_t config3;		/* Target configuration byte 3 */
};

struct trm_nvram {
	uint8_t subvendor_id[2];		/* 0,1 Sub Vendor ID */
	uint8_t subsys_id[2];			/* 2,3 Sub System ID */
	uint8_t subclass;			/* 4   Sub Class */
	uint8_t vendor_id[2];			/* 5,6 Vendor ID */
	uint8_t device_id[2];			/* 7,8 Device ID */
	uint8_t reserved0;			/* 9   Reserved */
	struct nvram_target target[TRM_MAX_TARGETS];
						/* 10,11,12,13
						 * 14,15,16,17
						 * ....
						 * 70,71,72,73 */
	uint8_t scsi_id;			/* 74 Host Adapter SCSI ID */
	uint8_t channel_cfg;			/* 75 Channel configuration */
#define NAC_SCANLUN		0x20	/* Include LUN as BIOS device */
#define NAC_DO_PARITY_CHK	0x08    /* Parity check enable        */
#define NAC_POWERON_SCSI_RESET	0x04	/* Power on reset enable      */
#define NAC_GREATER_1G		0x02	/* > 1G support enable	      */
#define NAC_GT2DRIVES		0x01	/* Support more than 2 drives */
	uint8_t delay_time;			/* 76 Power on delay time */
	uint8_t max_tag;			/* 77 Maximum tags */
	uint8_t reserved1;			/* 78 */
	uint8_t boot_target;			/* 79 */
	uint8_t boot_lun;			/* 80 */
	uint8_t reserved2;			/* 81 */
	uint8_t reserved3[44];			/* 82,..125 */
	uint8_t checksum0;			/* 126 */
	uint8_t checksum1;			/* 127 */
#define TRM_NVRAM_CKSUM	0x1234
};

/* Nvram Initiater bits definition */
#define MORE2_DRV		0x00000001
#define GREATER_1G		0x00000002
#define RST_SCSI_BUS		0x00000004
#define ACTIVE_NEGATION		0x00000008
#define NO_SEEK			0x00000010
#define LUN_CHECK		0x00000020

#define trm_eeprom_wait()	DELAY(30)

/*
 *-----------------------------------------------------------------------
 *			SCSI Request Block
 *-----------------------------------------------------------------------
 */
struct trm_srb {
	TAILQ_ENTRY(trm_srb) next;

	struct trm_sg_entry *sgentry;
	struct scsipi_xfer *xs;		/* scsipi_xfer for this cmd */
	bus_dmamap_t dmap;
	bus_size_t sgoffset;		/* Xfer buf offset */

	uint32_t buflen;		/* Total xfer length */
	uint32_t sgaddr;		/* SGList physical starting address */

	int sgcnt;
	int sgindex;

	int hastat;			/* Host Adapter Status */
#define H_STATUS_GOOD		0x00
#define H_SEL_TIMEOUT		0x11
#define H_OVER_UNDER_RUN	0x12
#define H_UNEXP_BUS_FREE	0x13
#define H_TARGET_PHASE_F	0x14
#define H_INVALID_CCB_OP	0x16
#define H_LINK_CCB_BAD		0x17
#define H_BAD_TARGET_DIR	0x18
#define H_DUPLICATE_CCB		0x19
#define H_BAD_CCB_OR_SG		0x1A
#define H_ABORT			0xFF
	int tastat;			/* Target SCSI Status Byte */
	int flag;			/* SRBFlag */
#define AUTO_REQSENSE		0x0001
#define PARITY_ERROR		0x0002
#define SRB_TIMEOUT		0x0004

	int cmdlen;			/* SCSI command length */
	uint8_t cmd[12];	       	/* SCSI command */

	uint8_t tag[2];
};

/*
 * some info about each target and lun on the SCSI bus
 */
struct trm_linfo {
	int used;		/* number of slots in use */
	int avail;		/* where to start scanning */
	int busy;		/* lun in use */
	struct trm_srb *untagged;
	struct trm_srb *queued[TRM_MAX_TAG];
};

struct trm_tinfo {
	u_int flag;		/* Sync mode ? (1 sync):(0 async)  */
#define SYNC_NEGO_ENABLE	0x0001
#define SYNC_NEGO_DOING		0x0002
#define SYNC_NEGO_DONE		0x0004
#define WIDE_NEGO_ENABLE	0x0008
#define WIDE_NEGO_DOING		0x0010
#define WIDE_NEGO_DONE		0x0020
#define USE_TAG_QUEUING		0x0040
#define NO_RESELECT		0x0080
	struct trm_linfo *linfo[TRM_MAX_LUNS];

	uint8_t config0;	/* Target Config */
	uint8_t period;		/* Max Period for nego. */
	uint8_t synctl;		/* Sync control for reg. */
	uint8_t offset;		/* Sync offset for reg. and nego.(low nibble) */
};

/*
 *-----------------------------------------------------------------------
 *			Adapter Control Block
 *-----------------------------------------------------------------------
 */
struct trm_softc {
	device_t sc_dev;

	bus_space_tag_t sc_iot;
	bus_space_handle_t sc_ioh;
	bus_dma_tag_t sc_dmat;
	bus_dmamap_t sc_dmamap;	/* Map the control structures */

	struct trm_srb *sc_actsrb;
	struct trm_tinfo sc_tinfo[TRM_MAX_TARGETS];

	TAILQ_HEAD(, trm_srb) sc_freesrb,
			      sc_readysrb;
	struct trm_srb *sc_srb;	/* SRB array */

	struct trm_sg_entry *sc_sglist;

	int sc_maxid;
	/*
	 * Link to the generic SCSI driver
	 */
	struct scsipi_channel sc_channel;
	struct scsipi_adapter sc_adapter;

	int sc_id;		/* Adapter SCSI Target ID */

	int sc_state;			/* SRB State */
#define TRM_IDLE		0
#define TRM_WAIT		1
#define TRM_READY		2
#define TRM_MSGOUT		3	/* arbitration+msg_out 1st byte */
#define TRM_MSGIN		4
#define TRM_EXTEND_MSGIN	5
#define TRM_COMMAND		6
#define TRM_START		7	/* arbitration+msg_out+command_out */
#define TRM_DISCONNECTED	8
#define TRM_DATA_XFER		9
#define TRM_XFERPAD		10
#define TRM_STATUS		11
#define TRM_COMPLETED		12
#define TRM_ABORT_SENT		13
#define TRM_UNEXPECT_RESEL	14

	int sc_phase;			/* SCSI phase */
	int sc_config;
#define HCC_WIDE_CARD		0x01
#define HCC_SCSI_RESET		0x02
#define HCC_PARITY		0x04
#define HCC_AUTOTERM		0x08
#define HCC_LOW8TERM		0x10
#define HCC_UP8TERM		0x20

	int sc_flag;
#define RESET_DEV		0x01
#define RESET_DETECT		0x02
#define RESET_DONE		0x04
#define WAIT_TAGMSG		0x08	/* XXX */

	int sc_msgcnt;

	int resel_target; /* XXX */
	int resel_lun; /* XXX */

	uint8_t *sc_msg;
	uint8_t sc_msgbuf[6];
};

/*
 * SCSI Status codes not defined in scsi_all.h
 */
#define SCSI_COND_MET		0x04	/* Condition Met              */
#define SCSI_INTERM_COND_MET	0x14	/* Intermediate condition met */
#define SCSI_UNEXP_BUS_FREE	0xFD	/* Unexpected Bus Free        */
#define SCSI_BUS_RST_DETECT	0xFE	/* SCSI Bus Reset detected    */
#define SCSI_SEL_TIMEOUT	0xFF	/* Selection Timeout          */

static int  trm_match(device_t, cfdata_t, void *);
static void trm_attach(device_t, device_t, void *);

static int  trm_init(struct trm_softc *);

static void trm_scsipi_request(struct scsipi_channel *, scsipi_adapter_req_t,
    void *);
static void trm_update_xfer_mode(struct trm_softc *, int);
static void trm_sched(struct trm_softc *);
static int  trm_select(struct trm_softc *, struct trm_srb *);
static void trm_reset(struct trm_softc *);
static void trm_timeout(void *);
static int  trm_intr(void *);

static void trm_dataout_phase0(struct trm_softc *, int);
static void trm_datain_phase0(struct trm_softc *, int);
static void trm_status_phase0(struct trm_softc *);
static void trm_msgin_phase0(struct trm_softc *);
static void trm_command_phase1(struct trm_softc *);
static void trm_status_phase1(struct trm_softc *);
static void trm_msgout_phase1(struct trm_softc *);
static void trm_msgin_phase1(struct trm_softc *);

static void trm_dataio_xfer(struct trm_softc *, int);
static void trm_disconnect(struct trm_softc *);
static void trm_reselect(struct trm_softc *);
static void trm_done(struct trm_softc *, struct trm_srb *);
static int  trm_request_sense(struct trm_softc *, struct trm_srb *);
static void trm_dequeue(struct trm_softc *, struct trm_srb *);

static void trm_scsi_reset_detect(struct trm_softc *);
static void trm_reset_scsi_bus(struct trm_softc *);

static void trm_check_eeprom(struct trm_softc *, struct trm_nvram *);
static void trm_eeprom_read_all(struct trm_softc *, struct trm_nvram *);
static void trm_eeprom_write_all(struct trm_softc *, struct trm_nvram *);
static void trm_eeprom_set_data(struct trm_softc *, uint8_t, uint8_t);
static void trm_eeprom_write_cmd(struct trm_softc *, uint8_t, uint8_t);
static uint8_t trm_eeprom_get_data(struct trm_softc *, uint8_t);

CFATTACH_DECL_NEW(trm, sizeof(struct trm_softc),
    trm_match, trm_attach, NULL, NULL);

/* real period: */
static const uint8_t trm_clock_period[] = {
	12,	/*  48 ns 20.0 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 */
};
#define NPERIOD	__arraycount(trm_clock_period)

static int
trm_match(device_t parent, cfdata_t cf, void *aux)
{
	struct pci_attach_args *pa = aux;

	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_TEKRAM2)
		switch (PCI_PRODUCT(pa->pa_id)) {
		case PCI_PRODUCT_TEKRAM2_DC315:
			return 1;
		}
	return 0;
}

/*
 * attach and init a host adapter
 */
static void
trm_attach(device_t parent, device_t self, void *aux)
{
	struct trm_softc *sc = device_private(self);
	struct pci_attach_args *const pa = aux;
	bus_space_tag_t iot;
	bus_space_handle_t ioh;
	pci_intr_handle_t ih;
	pcireg_t command;
	const char *intrstr;

	sc->sc_dev = self;

	/*
	 * These cards do not allow memory mapped accesses
	 * pa_pc:  chipset tag
	 * pa_tag: pci tag
	 */
	command = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
	if ((command & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE)) !=
	    (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE)) {
		command |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE;
		pci_conf_write(pa->pa_pc, pa->pa_tag,
		    PCI_COMMAND_STATUS_REG, command);
	}
	/*
	 * mask for get correct base address of pci IO port
	 */
	if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_IO, 0,
	    &iot, &ioh, NULL, NULL)) {
		aprint_error(": unable to map registers\n");
		return;
	}
	/*
	 * test checksum of eeprom.. & initialize softc...
	 */
	sc->sc_iot = iot;
	sc->sc_ioh = ioh;
	sc->sc_dmat = pa->pa_dmat;

	if (trm_init(sc) != 0) {
		/*
		 * Error during initialization!
		 */
		return;
	}
	/*
	 * Now try to attach all the sub-devices
	 */
	if ((sc->sc_config & HCC_WIDE_CARD) != 0)
		aprint_normal(": Tekram DC395UW/F (TRM-S1040) Fast40 "
		    "Ultra Wide SCSI Adapter\n");
	else
		aprint_normal(": Tekram DC395U, DC315/U (TRM-S1040) Fast20 "
		    "Ultra SCSI Adapter\n");

	/*
	 * Now tell the generic SCSI layer about our bus.
	 * map and establish interrupt
	 */
	if (pci_intr_map(pa, &ih)) {
		aprint_error_dev(self, "couldn't map interrupt\n");
		return;
	}
	intrstr = pci_intr_string(pa->pa_pc, ih);

	if (pci_intr_establish(pa->pa_pc, ih, IPL_BIO, trm_intr, sc) == NULL) {
		aprint_error_dev(self, "couldn't establish interrupt");
		if (intrstr != NULL)
			aprint_error(" at %s", intrstr);
		aprint_error("\n");
		return;
	}
	if (intrstr != NULL)
		aprint_normal_dev(self, "interrupting at %s\n", intrstr);

	sc->sc_adapter.adapt_dev = self;
	sc->sc_adapter.adapt_nchannels = 1;
	sc->sc_adapter.adapt_openings = TRM_MAX_SRB;
	sc->sc_adapter.adapt_max_periph = TRM_MAX_SRB;
	sc->sc_adapter.adapt_request = trm_scsipi_request;
	sc->sc_adapter.adapt_minphys = minphys;

	sc->sc_channel.chan_adapter = &sc->sc_adapter;
	sc->sc_channel.chan_bustype = &scsi_bustype;
	sc->sc_channel.chan_channel = 0;
	sc->sc_channel.chan_ntargets = sc->sc_maxid + 1;
	sc->sc_channel.chan_nluns = 8;
	sc->sc_channel.chan_id = sc->sc_id;

	config_found(self, &sc->sc_channel, scsiprint);
}

/*
 * initialize the internal structures for a given SCSI host
 */
static int
trm_init(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	bus_dma_segment_t seg;
	struct trm_nvram eeprom;
	struct trm_srb *srb;
	struct trm_tinfo *ti;
	struct nvram_target *tconf;
	int error, rseg, all_sgsize;
	int i, target;
	uint8_t bval;

	DPRINTF(("\n"));

	/*
	 * allocate the space for all SCSI control blocks (SRB) for DMA memory
	 */
	all_sgsize = TRM_MAX_SRB * TRM_SG_SIZE;
	if ((error = bus_dmamem_alloc(sc->sc_dmat, all_sgsize, PAGE_SIZE,
	    0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) {
		aprint_error(": unable to allocate SCSI REQUEST BLOCKS, "
		    "error = %d\n", error);
		return 1;
	}
	if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg,
	    all_sgsize, (void **) &sc->sc_sglist,
	    BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
		aprint_error(": unable to map SCSI REQUEST BLOCKS, "
		    "error = %d\n", error);
		return 1;
	}
	if ((error = bus_dmamap_create(sc->sc_dmat, all_sgsize, 1,
	    all_sgsize, 0, BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) {
		aprint_error(": unable to create SRB DMA maps, "
		    "error = %d\n", error);
		return 1;
	}
	if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap,
	    sc->sc_sglist, all_sgsize, NULL, BUS_DMA_NOWAIT)) != 0) {
		aprint_error(": unable to load SRB DMA maps, "
		    "error = %d\n", error);
		return 1;
	}
	DPRINTF(("all_sgsize=%x\n", all_sgsize));
	memset(sc->sc_sglist, 0, all_sgsize);

	/*
	 * EEPROM CHECKSUM
	 */
	trm_check_eeprom(sc, &eeprom);

	sc->sc_maxid = 7;
	sc->sc_config = HCC_AUTOTERM | HCC_PARITY;
	if (bus_space_read_1(iot, ioh, TRM_GEN_STATUS) & WIDESCSI) {
		sc->sc_config |= HCC_WIDE_CARD;
		sc->sc_maxid = 15;
	}
	if (eeprom.channel_cfg & NAC_POWERON_SCSI_RESET)
		sc->sc_config |= HCC_SCSI_RESET;

	sc->sc_actsrb = NULL;
	sc->sc_id = eeprom.scsi_id;
	sc->sc_flag = 0;

	/*
	 * initialize and link all device's SRB queues of this adapter
	 */
	TAILQ_INIT(&sc->sc_freesrb);
	TAILQ_INIT(&sc->sc_readysrb);

	sc->sc_srb = malloc(sizeof(struct trm_srb) * TRM_MAX_SRB,
	    M_DEVBUF, M_NOWAIT|M_ZERO);
	DPRINTF(("all SRB size=%x\n", sizeof(struct trm_srb) * TRM_MAX_SRB));
	if (sc->sc_srb == NULL) {
		aprint_error(": can not allocate SRB\n");
		return 1;
	}

	for (i = 0, srb = sc->sc_srb; i < TRM_MAX_SRB; i++) {
		srb->sgentry = sc->sc_sglist + TRM_MAX_SG_ENTRIES * i;
		srb->sgoffset = TRM_SG_SIZE * i;
		srb->sgaddr = sc->sc_dmamap->dm_segs[0].ds_addr + srb->sgoffset;
		/*
		 * map all SRB space to SRB_array
		 */
		if (bus_dmamap_create(sc->sc_dmat,
		    MAXPHYS, TRM_MAX_SG_ENTRIES, MAXPHYS, 0,
		    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &srb->dmap)) {
			aprint_error(": unable to create DMA transfer map.\n");
			free(sc->sc_srb, M_DEVBUF);
			return 1;
		}
		TAILQ_INSERT_TAIL(&sc->sc_freesrb, srb, next);
		srb++;
	}

	/*
	 * initialize all target info structures
	 */
	for (target = 0; target < TRM_MAX_TARGETS; target++) {
		ti = &sc->sc_tinfo[target];
		ti->synctl = 0;
		ti->offset = 0;
		tconf = &eeprom.target[target];
		ti->config0 = tconf->config0;
		ti->period = trm_clock_period[tconf->period & 0x07];
		ti->flag = 0;
		if ((ti->config0 & NTC_DO_DISCONNECT) != 0) {
#ifdef notyet
			if ((ti->config0 & NTC_DO_TAG_QUEUING) != 0)
				ti->flag |= USE_TAG_QUEUING;
#endif
		} else
			ti->flag |= NO_RESELECT;

		DPRINTF(("target %d: config0 = 0x%02x, period = 0x%02x",
		    target, ti->config0, ti->period));
		DPRINTF((", flag = 0x%02x\n", ti->flag));
	}

	/* program configuration 0 */
	bval = PHASELATCH | INITIATOR | BLOCKRST;
	if ((sc->sc_config & HCC_PARITY) != 0)
		bval |= PARITYCHECK;
	bus_space_write_1(iot, ioh, TRM_SCSI_CONFIG0, bval);

	/* program configuration 1 */
	bus_space_write_1(iot, ioh, TRM_SCSI_CONFIG1,
	    ACTIVE_NEG | ACTIVE_NEGPLUS);

	/* 250ms selection timeout */
	bus_space_write_1(iot, ioh, TRM_SCSI_TIMEOUT, SEL_TIMEOUT);

	/* Mask all interrupts */
	bus_space_write_1(iot, ioh, TRM_DMA_INTEN, 0);
	bus_space_write_1(iot, ioh, TRM_SCSI_INTEN, 0);

	/* Reset SCSI module */
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_RSTMODULE);

	/* program Host ID */
	bus_space_write_1(iot, ioh, TRM_SCSI_HOSTID, sc->sc_id);

	/* set asynchronous transfer */
	bus_space_write_1(iot, ioh, TRM_SCSI_OFFSET, 0);

	/* Turn LED control off */
	bus_space_write_2(iot, ioh, TRM_GEN_CONTROL,
	    bus_space_read_2(iot, ioh, TRM_GEN_CONTROL) & ~EN_LED);

	/* DMA config */
	bus_space_write_2(iot, ioh, TRM_DMA_CONFIG,
	    bus_space_read_2(iot, ioh, TRM_DMA_CONFIG) | DMA_ENHANCE);

	/* Clear pending interrupt status */
	(void)bus_space_read_1(iot, ioh, TRM_SCSI_INTSTATUS);

	/* Enable SCSI interrupt */
	bus_space_write_1(iot, ioh, TRM_SCSI_INTEN,
	    EN_SELECT | EN_SELTIMEOUT | EN_DISCONNECT | EN_RESELECTED |
	    EN_SCSIRESET | EN_BUSSERVICE | EN_CMDDONE);
	bus_space_write_1(iot, ioh, TRM_DMA_INTEN, EN_SCSIINTR);

	trm_reset(sc);

	return 0;
}

/*
 * enqueues a SCSI command
 * called by the higher level SCSI driver
 */
static void
trm_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
    void *arg)
{
	bus_space_tag_t iot;
	bus_space_handle_t ioh;
	struct trm_softc *sc;
	struct trm_srb *srb;
	struct scsipi_xfer *xs;
	int error, i, target, lun, s;

	sc = device_private(chan->chan_adapter->adapt_dev);
	iot = sc->sc_iot;
	ioh = sc->sc_ioh;

	switch (req) {
	case ADAPTER_REQ_RUN_XFER:
		xs = arg;
		target = xs->xs_periph->periph_target;
		lun = xs->xs_periph->periph_lun;
		DPRINTF(("trm_scsipi_request.....\n"));
		DPRINTF(("target= %d lun= %d\n", target, lun));
		if (xs->xs_control & XS_CTL_RESET) {
			trm_reset(sc);
			xs->error = XS_NOERROR | XS_RESET;
			return;
		}
		if (xs->xs_status & XS_STS_DONE) {
			printf("%s: Is it done?\n", device_xname(sc->sc_dev));
			xs->xs_status &= ~XS_STS_DONE;
		}

		s = splbio();

		/* Get SRB */
		srb = TAILQ_FIRST(&sc->sc_freesrb);
		if (srb != NULL) {
			TAILQ_REMOVE(&sc->sc_freesrb, srb, next);
		} else {
			xs->error = XS_RESOURCE_SHORTAGE;
			scsipi_done(xs);
			splx(s);
			return;
		}

		srb->xs = xs;
		srb->cmdlen = xs->cmdlen;
		memcpy(srb->cmd, xs->cmd, xs->cmdlen);

		if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) {
			if ((error = bus_dmamap_load(sc->sc_dmat, srb->dmap,
			    xs->data, xs->datalen, NULL,
			    ((xs->xs_control & XS_CTL_NOSLEEP) ?
			    BUS_DMA_NOWAIT : BUS_DMA_WAITOK) |
			    BUS_DMA_STREAMING |
			    ((xs->xs_control & XS_CTL_DATA_IN) ?
			    BUS_DMA_READ : BUS_DMA_WRITE))) != 0) {
				printf("%s: DMA transfer map unable to load, "
				    "error = %d\n", device_xname(sc->sc_dev),
				    error);
				xs->error = XS_DRIVER_STUFFUP;
				/*
				 * free SRB
				 */
				TAILQ_INSERT_TAIL(&sc->sc_freesrb, srb, next);
				splx(s);
				return;
			}
			bus_dmamap_sync(sc->sc_dmat, srb->dmap, 0,
			    srb->dmap->dm_mapsize,
			    (xs->xs_control & XS_CTL_DATA_IN) ?
			    BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);

			/* Set up the scatter gather list */
			for (i = 0; i < srb->dmap->dm_nsegs; i++) {
				srb->sgentry[i].address =
				    htole32(srb->dmap->dm_segs[i].ds_addr);
				srb->sgentry[i].length =
				    htole32(srb->dmap->dm_segs[i].ds_len);
			}
			srb->buflen = xs->datalen;
			srb->sgcnt = srb->dmap->dm_nsegs;
		} else {
			srb->sgentry[0].address = 0;
			srb->sgentry[0].length = 0;
			srb->buflen = 0;
			srb->sgcnt = 0;
		}
		bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
		    srb->sgoffset, TRM_SG_SIZE, BUS_DMASYNC_PREWRITE);

		sc->sc_phase = PH_BUS_FREE;	/* SCSI bus free Phase */

		srb->sgindex = 0;
		srb->hastat = 0;
		srb->tastat = 0;
		srb->flag = 0;

		TAILQ_INSERT_TAIL(&sc->sc_readysrb, srb, next);
		if (sc->sc_actsrb == NULL)
			trm_sched(sc);
		splx(s);

		if ((xs->xs_control & XS_CTL_POLL) != 0) {
			int timeout = xs->timeout;

			s = splbio();
			do {
				while (--timeout) {
					DELAY(1000);
					if (bus_space_read_2(iot, ioh,
					    TRM_SCSI_STATUS) & SCSIINTERRUPT)
						break;
				}
				if (timeout == 0) {
					trm_timeout(srb);
					break;
				} else
					trm_intr(sc);
			} while ((xs->xs_status & XS_STS_DONE) == 0);
			splx(s);
		}
		return;

	case ADAPTER_REQ_GROW_RESOURCES:
		/* XXX Not supported. */
		return;

	case ADAPTER_REQ_SET_XFER_MODE:
		{
			struct trm_tinfo *ti;
			struct scsipi_xfer_mode *xm;

			xm = arg;
			ti = &sc->sc_tinfo[xm->xm_target];
			ti->flag &= ~(SYNC_NEGO_ENABLE|WIDE_NEGO_ENABLE);

#ifdef notyet
			if ((xm->xm_mode & PERIPH_CAP_TQING) != 0)
				ti->flag |= USE_TAG_QUEUING;
			else
#endif
				ti->flag &= ~USE_TAG_QUEUING;

			if ((xm->xm_mode & PERIPH_CAP_WIDE16) != 0 && 
			    (sc->sc_config & HCC_WIDE_CARD) != 0 && 
			    (ti->config0 & NTC_DO_WIDE_NEGO) != 0) {
				ti->flag |= WIDE_NEGO_ENABLE;
				ti->flag &= ~WIDE_NEGO_DONE;
			}

			if ((xm->xm_mode & PERIPH_CAP_SYNC) != 0 &&
			    (ti->config0 & NTC_DO_SYNC_NEGO) != 0) {
				ti->flag |= SYNC_NEGO_ENABLE;
				ti->flag &= ~SYNC_NEGO_DONE;
				ti->period = trm_clock_period[0];
			}

			/*
			 * If we're not going to negotiate, send the
			 * notification now, since it won't happen later.
			 */
			if ((ti->flag & (WIDE_NEGO_DONE|SYNC_NEGO_DONE)) ==
			    (WIDE_NEGO_DONE|SYNC_NEGO_DONE))
				trm_update_xfer_mode(sc, xm->xm_target);

			return;
		}
	}
}

static void
trm_update_xfer_mode(struct trm_softc *sc, int target)
{
	struct scsipi_xfer_mode xm;
	struct trm_tinfo *ti;

	ti = &sc->sc_tinfo[target];
	xm.xm_target = target;
	xm.xm_mode = 0;
	xm.xm_period = 0;
	xm.xm_offset = 0;

	if ((ti->synctl & WIDE_SYNC) != 0)
		xm.xm_mode |= PERIPH_CAP_WIDE16;

	if (ti->period > 0) {
		xm.xm_mode |= PERIPH_CAP_SYNC;
		xm.xm_period = ti->period;
		xm.xm_offset = ti->offset;
	}

#ifdef notyet
	if ((ti->flag & USE_TAG_QUEUING) != 0)
		xm.xm_mode |= PERIPH_CAP_TQING;
#endif

	scsipi_async_event(&sc->sc_channel, ASYNC_EVENT_XFER_MODE, &xm);
}

static void
trm_sched(struct trm_softc *sc)
{
	struct trm_srb *srb;
	struct scsipi_periph *periph;
	struct trm_tinfo *ti;
	struct trm_linfo *li;
	int s, lun, tag;

	DPRINTF(("trm_sched...\n"));

	TAILQ_FOREACH(srb, &sc->sc_readysrb, next) {
		periph = srb->xs->xs_periph;
		ti = &sc->sc_tinfo[periph->periph_target];
		lun = periph->periph_lun;

		/* select type of tag for this command */
		if ((ti->flag & NO_RESELECT) != 0 ||
		    (ti->flag & USE_TAG_QUEUING) == 0 ||
		    (srb->flag & AUTO_REQSENSE) != 0 ||
		    (srb->xs->xs_control & XS_CTL_REQSENSE) != 0)
			tag = 0;
		else
			tag = srb->xs->xs_tag_type;
#if 0
		/* XXX use tags for polled commands? */
		if (srb->xs->xs_control & XS_CTL_POLL)
			tag = 0;
#endif

		s = splbio();
		li = ti->linfo[lun];
		if (li == NULL) {
			/* initialize lun info */
			if ((li = malloc(sizeof(*li), M_DEVBUF,
			    M_NOWAIT|M_ZERO)) == NULL) {
				splx(s);
				continue;
			}
			ti->linfo[lun] = li;
		}

		if (tag == 0) {
			/* try to issue this srb as an un-tagged command */
			if (li->untagged == NULL)
				li->untagged = srb;
		}
		if (li->untagged != NULL) {
			tag = 0;
			if (li->busy != 1 && li->used == 0) {
				/* we need to issue the untagged command now */
				srb = li->untagged;
				periph = srb->xs->xs_periph;
			} else {
				/* not ready yet */
				splx(s);
				continue;
			}
		}
		srb->tag[0] = tag;
		if (tag != 0) {
			li->queued[srb->xs->xs_tag_id] = srb;
			srb->tag[1] = srb->xs->xs_tag_id;
			li->used++;
		}

		if (li->untagged != NULL && li->busy != 1) {
			li->busy = 1;
			TAILQ_REMOVE(&sc->sc_readysrb, srb, next);
			sc->sc_actsrb = srb;
			trm_select(sc, srb);
			splx(s);
			break;
		}
		if (li->untagged == NULL && tag != 0) {
			TAILQ_REMOVE(&sc->sc_readysrb, srb, next);
			sc->sc_actsrb = srb;
			trm_select(sc, srb);
			splx(s);
			break;
		} else
			splx(s);
	}
}

static int
trm_select(struct trm_softc *sc, struct trm_srb *srb)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct scsipi_periph *periph = srb->xs->xs_periph;
	int target = periph->periph_target;
	int lun = periph->periph_lun;
	struct trm_tinfo *ti = &sc->sc_tinfo[target];
	uint8_t scsicmd;

	DPRINTF(("trm_select.....\n"));

	if ((srb->xs->xs_control & XS_CTL_POLL) == 0) {
		callout_reset(&srb->xs->xs_callout, mstohz(srb->xs->timeout),
		    trm_timeout, srb);
	}

	bus_space_write_1(iot, ioh, TRM_SCSI_HOSTID, sc->sc_id);
	bus_space_write_1(iot, ioh, TRM_SCSI_TARGETID, target);
	bus_space_write_1(iot, ioh, TRM_SCSI_SYNC, ti->synctl);
	bus_space_write_1(iot, ioh, TRM_SCSI_OFFSET, ti->offset);
	/* Flush FIFO */
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);
	DELAY(10);

	sc->sc_phase = PH_BUS_FREE;	/* initial phase */

	DPRINTF(("cmd = 0x%02x\n", srb->cmd[0]));

	if (((ti->flag & WIDE_NEGO_ENABLE) &&
	     (ti->flag & WIDE_NEGO_DONE) == 0) ||
	    ((ti->flag & SYNC_NEGO_ENABLE) &&
	     (ti->flag & SYNC_NEGO_DONE) == 0)) {
		sc->sc_state = TRM_MSGOUT;
		bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
		    MSG_IDENTIFY(lun, 0));
		bus_space_write_multi_1(iot, ioh,
		    TRM_SCSI_FIFO, srb->cmd, srb->cmdlen);
		/* it's important for atn stop */
		bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
		    DO_DATALATCH | DO_HWRESELECT);
		/* SCSI command */
		bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_SEL_ATNSTOP);
		DPRINTF(("select with SEL_ATNSTOP\n"));
		return 0;
	}

	if (srb->tag[0] != 0) {
		/* Send identify message */
		bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
		    MSG_IDENTIFY(lun, 1));
		/* Send Tag id */
		bus_space_write_1(iot, ioh, TRM_SCSI_FIFO, srb->tag[0]);
		bus_space_write_1(iot, ioh, TRM_SCSI_FIFO, srb->tag[1]);
		scsicmd = SCMD_SEL_ATN3;
		DPRINTF(("select with SEL_ATN3\n"));
	} else {
		/* Send identify message */
		bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
		    MSG_IDENTIFY(lun,
		    (ti->flag & NO_RESELECT) == 0 &&
		    (srb->flag & AUTO_REQSENSE) == 0 &&
		    (srb->xs->xs_control & XS_CTL_REQSENSE) == 0));
		scsicmd = SCMD_SEL_ATN;
		DPRINTF(("select with SEL_ATN\n"));
	}
	sc->sc_state = TRM_START;

	/*
	 * Send CDB ..command block...
	 */
	bus_space_write_multi_1(iot, ioh, TRM_SCSI_FIFO, srb->cmd, srb->cmdlen);

	/*
	 * If trm_select returns 0: current interrupt status
	 * is interrupt enable.  It's said that SCSI processor is
	 * unoccupied.
	 */
	sc->sc_phase = PH_BUS_FREE;	/* SCSI bus free Phase */
	/* SCSI command */
	bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, scsicmd);
	return 0;
}

/*
 * perform a hard reset on the SCSI bus (and TRM_S1040 chip).
 */
static void
trm_reset(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	int s;

	DPRINTF(("trm_reset.........\n"));

	s = splbio();

	/* disable SCSI and DMA interrupt */
	bus_space_write_1(iot, ioh, TRM_DMA_INTEN, 0);
	bus_space_write_1(iot, ioh, TRM_SCSI_INTEN, 0);

	trm_reset_scsi_bus(sc);
	DELAY(100000);

	/* Enable SCSI interrupt */
	bus_space_write_1(iot, ioh, TRM_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_DMA_INTEN, EN_SCSIINTR);

	/* Clear DMA FIFO */
	bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, CLRXFIFO);

	/* Clear SCSI FIFO */
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);

	sc->sc_actsrb = NULL;
	sc->sc_flag = 0;	/* RESET_DETECT, RESET_DONE, RESET_DEV */

	splx(s);
}

static void
trm_timeout(void *arg)
{
	struct trm_srb *srb = (struct trm_srb *)arg;
	struct scsipi_xfer *xs;
	struct scsipi_periph *periph;
	struct trm_softc *sc;
	int s;

	if (srb == NULL) {
		printf("trm_timeout called with srb == NULL\n");
		return;
	}

	xs = srb->xs;
	if (xs == NULL) {
		printf("trm_timeout called with xs == NULL\n");
		return;
	}

	periph = xs->xs_periph;
	scsipi_printaddr(xs->xs_periph);
	printf("SCSI OpCode 0x%02x timed out\n", xs->cmd->opcode);

	sc = device_private(periph->periph_channel->chan_adapter->adapt_dev);

	trm_reset_scsi_bus(sc);
	s = splbio();
	srb->flag |= SRB_TIMEOUT;
	trm_done(sc, srb);
	/* XXX needs more.. */
	splx(s);
}

/*
 * Catch an interrupt from the adapter
 * Process pending device interrupts.
 */
static int
trm_intr(void *arg)
{
	bus_space_tag_t iot;
	bus_space_handle_t ioh;
	struct trm_softc *sc;
	int intstat, stat;

	DPRINTF(("trm_intr......\n"));
	sc = arg;
	if (sc == NULL)
		return 0;

	iot = sc->sc_iot;
	ioh = sc->sc_ioh;

	stat = bus_space_read_2(iot, ioh, TRM_SCSI_STATUS);
	if ((stat & SCSIINTERRUPT) == 0)
		return 0;

	DPRINTF(("stat = %04x, ", stat));
	intstat = bus_space_read_1(iot, ioh, TRM_SCSI_INTSTATUS);

	DPRINTF(("intstat=%02x, ", intstat));
	if (intstat & (INT_SELTIMEOUT | INT_DISCONNECT)) {
		DPRINTF(("\n"));
		trm_disconnect(sc);
		return 1;
	}
	if (intstat & INT_RESELECTED) {
		DPRINTF(("\n"));
		trm_reselect(sc);
		return 1;
	}
	if (intstat & INT_SCSIRESET) {
		DPRINTF(("\n"));
		trm_scsi_reset_detect(sc);
		return 1;
	}
	if (intstat & (INT_BUSSERVICE | INT_CMDDONE)) {
		DPRINTF(("sc->sc_phase = %2d, sc->sc_state = %2d\n",
		    sc->sc_phase, sc->sc_state));
		/*
		 * software sequential machine
		 */

		/*
		 * call phase0 functions... "phase entry" handle
		 * every phase before start transfer
		 */
		switch (sc->sc_phase) {
		case PH_DATA_OUT:
			trm_dataout_phase0(sc, stat);
			break;
		case PH_DATA_IN:
			trm_datain_phase0(sc, stat);
			break;
		case PH_COMMAND:
			break;
		case PH_STATUS:
			trm_status_phase0(sc);
			stat = PH_BUS_FREE;
			break;
		case PH_MSG_OUT:
			if (sc->sc_state == TRM_UNEXPECT_RESEL ||
			    sc->sc_state == TRM_ABORT_SENT)
				stat = PH_BUS_FREE;
			break;
		case PH_MSG_IN:
			trm_msgin_phase0(sc);
			stat = PH_BUS_FREE;
			break;
		case PH_BUS_FREE:
			break;
		default:
			printf("%s: unexpected phase in trm_intr() phase0\n",
			    device_xname(sc->sc_dev));
			break;
		}

		sc->sc_phase = stat & PHASEMASK;

		switch (sc->sc_phase) {
		case PH_DATA_OUT:
			trm_dataio_xfer(sc, XFERDATAOUT);
			break;
		case PH_DATA_IN:
			trm_dataio_xfer(sc, XFERDATAIN);
			break;
		case PH_COMMAND:
			trm_command_phase1(sc);
			break;
		case PH_STATUS:
			trm_status_phase1(sc);
			break;
		case PH_MSG_OUT:
			trm_msgout_phase1(sc);
			break;
		case PH_MSG_IN:
			trm_msgin_phase1(sc);
			break;
		case PH_BUS_FREE:
			break;
		default:
			printf("%s: unexpected phase in trm_intr() phase1\n",
			    device_xname(sc->sc_dev));
			break;
		}

		return 1;
	}
	return 0;
}

static void
trm_msgout_phase1(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct trm_srb *srb;
	struct scsipi_periph *periph;
	struct trm_tinfo *ti;

	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);

	srb = sc->sc_actsrb;

	/* message out phase */
	if (srb != NULL) {
		periph = srb->xs->xs_periph;
		ti = &sc->sc_tinfo[periph->periph_target];

		if ((ti->flag & WIDE_NEGO_DOING) == 0 &&
		    (ti->flag & WIDE_NEGO_ENABLE)) {
			/* send WDTR */
			ti->flag &= ~SYNC_NEGO_DONE;

			sc->sc_msgbuf[0] = MSG_IDENTIFY(periph->periph_lun, 0);
			sc->sc_msgbuf[1] = MSG_EXTENDED;
			sc->sc_msgbuf[2] = MSG_EXT_WDTR_LEN;
			sc->sc_msgbuf[3] = MSG_EXT_WDTR;
			sc->sc_msgbuf[4] = MSG_EXT_WDTR_BUS_16_BIT;
			sc->sc_msgcnt = 5;

			ti->flag |= WIDE_NEGO_DOING;
		} else if ((ti->flag & SYNC_NEGO_DOING) == 0 &&
			   (ti->flag & SYNC_NEGO_ENABLE)) {
			/* send SDTR */
			int cnt = 0;

			if ((ti->flag & WIDE_NEGO_DONE) == 0)
				sc->sc_msgbuf[cnt++] =
				    MSG_IDENTIFY(periph->periph_lun, 0);

			sc->sc_msgbuf[cnt++] = MSG_EXTENDED;
			sc->sc_msgbuf[cnt++] = MSG_EXT_SDTR_LEN;
			sc->sc_msgbuf[cnt++] = MSG_EXT_SDTR;
			sc->sc_msgbuf[cnt++] = ti->period;
			sc->sc_msgbuf[cnt++] = TRM_MAX_OFFSET;
			sc->sc_msgcnt = cnt;
			ti->flag |= SYNC_NEGO_DOING;
		}
	}
	if (sc->sc_msgcnt == 0) {
		sc->sc_msgbuf[0] = MSG_ABORT;
		sc->sc_msgcnt = 1;
		sc->sc_state = TRM_ABORT_SENT;
	}

	DPRINTF(("msgout: cnt = %d, ", sc->sc_msgcnt));
	DPRINTF(("msgbuf = %02x %02x %02x %02x %02x %02x\n",
	   sc->sc_msgbuf[0], sc->sc_msgbuf[1], sc->sc_msgbuf[2],
	   sc->sc_msgbuf[3], sc->sc_msgbuf[4], sc->sc_msgbuf[5]));

	bus_space_write_multi_1(iot, ioh, TRM_SCSI_FIFO,
	    sc->sc_msgbuf, sc->sc_msgcnt);
	sc->sc_msgcnt = 0;
	memset(sc->sc_msgbuf, 0, sizeof(sc->sc_msgbuf));

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

	/*
	 * SCSI command
	 */
	bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_FIFO_OUT);
}

static void
trm_command_phase1(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct trm_srb *srb;

	srb = sc->sc_actsrb;
	if (srb == NULL) {
		DPRINTF(("trm_command_phase1: no active srb\n"));
		return;
	}

	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRATN | DO_CLRFIFO);
	bus_space_write_multi_1(iot, ioh, TRM_SCSI_FIFO, srb->cmd, srb->cmdlen);

	sc->sc_state = TRM_COMMAND;
	/* it's important for atn stop */
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);

	/*
	 * SCSI command
	 */
	bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_FIFO_OUT);
}

static void
trm_dataout_phase0(struct trm_softc *sc, int stat)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct trm_srb *srb;
	struct scsipi_periph *periph;
	struct trm_tinfo *ti;
	struct trm_sg_entry *sg;
	int sgindex;
	uint32_t xferlen, leftcnt = 0;

	if (sc->sc_state == TRM_XFERPAD)
		return;

	srb = sc->sc_actsrb;
	if (srb == NULL) {
		DPRINTF(("trm_dataout_phase0: no active srb\n"));
		return;
	}
	periph = srb->xs->xs_periph;
	ti = &sc->sc_tinfo[periph->periph_target];

	if ((stat & PARITYERROR) != 0)
		srb->flag |= PARITY_ERROR;

	if ((stat & SCSIXFERDONE) == 0) {
		/*
		 * when data transfer from DMA FIFO to SCSI FIFO
		 * if there was some data left in SCSI FIFO
		 */
		leftcnt = bus_space_read_1(iot, ioh, TRM_SCSI_FIFOCNT) &
		    SCSI_FIFOCNT_MASK;
		if (ti->synctl & WIDE_SYNC)
			/*
			 * if WIDE scsi SCSI FIFOCNT unit is word
			 * so need to * 2
			 */
			leftcnt <<= 1;
	}
	/*
	 * calculate all the residue data that was not yet transferred
	 * SCSI transfer counter + left in SCSI FIFO data
	 *
	 * .....TRM_SCSI_XCNT (24bits)
	 * The counter always decrements by one for every SCSI
	 * byte transfer.
	 * .....TRM_SCSI_FIFOCNT ( 5bits)
	 * The counter is SCSI FIFO offset counter
	 */
	leftcnt += bus_space_read_4(iot, ioh, TRM_SCSI_XCNT);
	if (leftcnt == 1) {
		leftcnt = 0;
		bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);
	}
	if ((leftcnt == 0) || (stat & SCSIXFERCNT_2_ZERO)) {
		while ((bus_space_read_1(iot, ioh, TRM_DMA_STATUS) &
		    DMAXFERCOMP) == 0)
			;	/* XXX needs timeout */

		srb->buflen = 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 (srb->buflen != leftcnt) {
			/* data that had transferred length */
			xferlen = srb->buflen - leftcnt;

			/* next time to be transferred length */
			srb->buflen = leftcnt;

			/*
			 * parsing from last time disconnect sgindex
			 */
			sg = srb->sgentry + srb->sgindex;
			for (sgindex = srb->sgindex;
			     sgindex < srb->sgcnt;
			     sgindex++, sg++) {
				/*
				 * find last time which SG transfer
				 * be disconnect
				 */
				if (xferlen >= le32toh(sg->length))
					xferlen -= le32toh(sg->length);
				else {
					/*
					 * update last time
					 * disconnected SG list
					 */
				        /* residue data length  */
					sg->length =
					    htole32(le32toh(sg->length)
					    - xferlen);
					/* residue data pointer */
					sg->address =
					    htole32(le32toh(sg->address)
					    + xferlen);
					srb->sgindex = sgindex;
					break;
				}
			}
			bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
			    srb->sgoffset, TRM_SG_SIZE, BUS_DMASYNC_PREWRITE);
		}
	}
	bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, STOPDMAXFER);
}

static void
trm_datain_phase0(struct trm_softc *sc, int stat)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct trm_srb *srb;
	struct trm_sg_entry *sg;
	int sgindex;
	uint32_t xferlen, leftcnt = 0;

	if (sc->sc_state == TRM_XFERPAD)
		return;

	srb = sc->sc_actsrb;
	if (srb == NULL) {
		DPRINTF(("trm_datain_phase0: no active srb\n"));
		return;
	}

	if (stat & PARITYERROR)
		srb->flag |= PARITY_ERROR;

	leftcnt += bus_space_read_4(iot, ioh, TRM_SCSI_XCNT);
	if ((leftcnt == 0) || (stat & SCSIXFERCNT_2_ZERO)) {
		while ((bus_space_read_1(iot, ioh, TRM_DMA_STATUS) &
		    DMAXFERCOMP) == 0)
			;	/* XXX needs timeout */

		srb->buflen = 0;
	} else {	/* phase changed */
		/*
		 * parsing the case:
		 * when a transfer not yet complete
		 * but be disconnected by upper layer
		 * if transfer not yet complete
		 * there were some data residue in SCSI FIFO or
		 * SCSI transfer counter not empty
		 */
		if (srb->buflen != leftcnt) {
			/*
			 * data that had transferred length
			 */
			xferlen = srb->buflen - leftcnt;

			/*
			 * next time to be transferred length
			 */
			srb->buflen = leftcnt;

			/*
			 * parsing from last time disconnect sgindex
			 */
			sg = srb->sgentry + srb->sgindex;
			for (sgindex = srb->sgindex;
			     sgindex < srb->sgcnt;
			     sgindex++, sg++) {
				/*
				 * find last time which SG transfer
				 * be disconnect
				 */
				if (xferlen >= le32toh(sg->length))
					xferlen -= le32toh(sg->length);
				else {
					/*
					 * update last time
					 * disconnected SG list
					 */
					/* residue data length  */
					sg->length =
					    htole32(le32toh(sg->length)
					    - xferlen);
					/* residue data pointer */
					sg->address =
					    htole32(le32toh(sg->address)
					    + xferlen);
					srb->sgindex = sgindex;
					break;
				}
			}
			bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
			    srb->sgoffset, TRM_SG_SIZE, BUS_DMASYNC_PREWRITE);
		}
	}
}

static void
trm_dataio_xfer(struct trm_softc *sc, int iodir)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct trm_srb *srb;
	struct scsipi_periph *periph;
	struct trm_tinfo *ti;

	srb = sc->sc_actsrb;
	if (srb == NULL) {
		DPRINTF(("trm_dataio_xfer: no active srb\n"));
		return;
	}
	periph = srb->xs->xs_periph;
	ti = &sc->sc_tinfo[periph->periph_target];

	if (srb->sgindex < srb->sgcnt) {
		if (srb->buflen > 0) {
			/*
			 * load what physical address of Scatter/Gather
			 * list table want to be transfer
			 */
			sc->sc_state = TRM_DATA_XFER;
			bus_space_write_4(iot, ioh, TRM_DMA_XHIGHADDR, 0);
			bus_space_write_4(iot, ioh, TRM_DMA_XLOWADDR,
			    srb->sgaddr +
			    srb->sgindex * sizeof(struct trm_sg_entry));
			/*
			 * load how many bytes in the Scatter/Gather list table
			 */
			bus_space_write_4(iot, ioh, TRM_DMA_XCNT,
			    (srb->sgcnt - srb->sgindex)
			    * sizeof(struct trm_sg_entry));
			/*
			 * load total xfer length (24bits) max value 16Mbyte
			 */
			bus_space_write_4(iot, ioh, TRM_SCSI_XCNT, srb->buflen);
			/* Start DMA transfer */
			bus_space_write_1(iot, ioh, TRM_DMA_COMMAND,
			    iodir | SGXFER);
			bus_space_write_1(iot, ioh, TRM_DMA_CONTROL,
			    STARTDMAXFER);

			/* Start SCSI transfer */
			/* it's important for atn stop */
			bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
			    DO_DATALATCH);

			/*
			 * SCSI command
			 */
			bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND,
			    (iodir == XFERDATAOUT) ?
			    SCMD_DMA_OUT : SCMD_DMA_IN);
		} else {	/* xfer pad */
			if (srb->sgcnt) {
				srb->hastat = H_OVER_UNDER_RUN;
			}
			bus_space_write_4(iot, ioh, TRM_SCSI_XCNT,
			    (ti->synctl & WIDE_SYNC) ? 2 : 1);

			if (iodir == XFERDATAOUT)
				bus_space_write_2(iot, ioh, TRM_SCSI_FIFO, 0);
			else
				(void)bus_space_read_2(iot, ioh, TRM_SCSI_FIFO);

			sc->sc_state = TRM_XFERPAD;
			/* it's important for atn stop */
			bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
			    DO_DATALATCH);

			/*
			 * SCSI command
			 */
			bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND,
			    (iodir == XFERDATAOUT) ?
			    SCMD_FIFO_OUT : SCMD_FIFO_IN);
		}
	}
}

static void
trm_status_phase0(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct trm_srb *srb;

	srb = sc->sc_actsrb;
	if (srb == NULL) {
		DPRINTF(("trm_status_phase0: no active srb\n"));
		return;
	}
	srb->tastat = bus_space_read_1(iot, ioh, TRM_SCSI_FIFO);
	sc->sc_state = TRM_COMPLETED;
	/* it's important for atn stop */
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);

	/*
	 * SCSI command
	 */
	bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_MSGACCEPT);
}

static void
trm_status_phase1(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;

	if (bus_space_read_1(iot, ioh, TRM_DMA_COMMAND) & XFERDATAIN) {
		if ((bus_space_read_1(iot, ioh, TRM_SCSI_FIFOCNT)
		    & SCSI_FIFO_EMPTY) == 0)
			bus_space_write_2(iot, ioh,
			    TRM_SCSI_CONTROL, DO_CLRFIFO);
		if ((bus_space_read_1(iot, ioh, TRM_DMA_FIFOSTATUS)
		    & DMA_FIFO_EMPTY) == 0)
			bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, CLRXFIFO);
	} else {
		if ((bus_space_read_1(iot, ioh, TRM_DMA_FIFOSTATUS)
		    & DMA_FIFO_EMPTY) == 0)
			bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, CLRXFIFO);
		if ((bus_space_read_1(iot, ioh, TRM_SCSI_FIFOCNT)
		    & SCSI_FIFO_EMPTY) == 0)
			bus_space_write_2(iot, ioh,
			    TRM_SCSI_CONTROL, DO_CLRFIFO);
	}
	sc->sc_state = TRM_STATUS;
	/* it's important for atn stop */
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);

	/*
	 * SCSI command
	 */
	bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_COMP);
}

static void
trm_msgin_phase0(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct trm_srb *srb;
	struct scsipi_periph *periph;
	struct trm_tinfo *ti;
	int index;
	uint8_t msgin_code;

	msgin_code = bus_space_read_1(iot, ioh, TRM_SCSI_FIFO);
	if (sc->sc_state != TRM_EXTEND_MSGIN) {
		DPRINTF(("msgin: code = %02x\n", msgin_code));
		switch (msgin_code) {
		case MSG_DISCONNECT:
			sc->sc_state = TRM_DISCONNECTED;
			break;

		case MSG_SAVEDATAPOINTER:
			break;

		case MSG_EXTENDED:
		case MSG_SIMPLE_Q_TAG:
		case MSG_HEAD_OF_Q_TAG:
		case MSG_ORDERED_Q_TAG:
			sc->sc_state = TRM_EXTEND_MSGIN;
			/* extended message (01h) */
			sc->sc_msgbuf[0] = msgin_code;

			sc->sc_msgcnt = 1;
			/* extended message length (n) */
			sc->sc_msg = &sc->sc_msgbuf[1];

			break;
		case MSG_MESSAGE_REJECT:
			/* Reject message */
			srb = sc->sc_actsrb;
			if (srb == NULL) {
				DPRINTF(("trm_msgin_phase0: "
				    " message reject without actsrb\n"));
				break;
			}
			periph = srb->xs->xs_periph;
			ti = &sc->sc_tinfo[periph->periph_target];

			if (ti->flag & WIDE_NEGO_ENABLE) {
				/* do wide nego reject */
				ti->flag |= WIDE_NEGO_DONE;
				ti->flag &=
				    ~(SYNC_NEGO_DONE | WIDE_NEGO_ENABLE);
				if ((ti->flag & SYNC_NEGO_ENABLE) &&
				    (ti->flag & SYNC_NEGO_DONE) == 0) {
					/* Set ATN, in case ATN was clear */
					sc->sc_state = TRM_MSGOUT;
					bus_space_write_2(iot, ioh,
					    TRM_SCSI_CONTROL, DO_SETATN);
				} else
					/* Clear ATN */
					bus_space_write_2(iot, ioh,
					    TRM_SCSI_CONTROL, DO_CLRATN);
			} else if (ti->flag & SYNC_NEGO_ENABLE) {
				/* do sync nego reject */
				bus_space_write_2(iot, ioh,
				    TRM_SCSI_CONTROL, DO_CLRATN);
				if (ti->flag & SYNC_NEGO_DOING) {
					ti->flag &=~(SYNC_NEGO_ENABLE |
					    SYNC_NEGO_DONE);
					ti->synctl = 0;
					ti->offset = 0;
					bus_space_write_1(iot, ioh,
					    TRM_SCSI_SYNC, ti->synctl);
					bus_space_write_1(iot, ioh,
					    TRM_SCSI_OFFSET, ti->offset);
				}
			}
			break;

		case MSG_IGN_WIDE_RESIDUE:
			bus_space_write_4(iot, ioh, TRM_SCSI_XCNT, 1);
			(void)bus_space_read_1(iot, ioh, TRM_SCSI_FIFO);
			break;

		default:
			/*
			 * Restore data pointer message
			 * Save data pointer message
			 * Completion message
			 * NOP message
			 */
			break;
		}
	} else {
		/*
		 * when extend message in: sc->sc_state = TRM_EXTEND_MSGIN
		 * Parsing incoming extented messages
		 */
		*sc->sc_msg++ = msgin_code;
		sc->sc_msgcnt++;

		DPRINTF(("extended_msgin: cnt = %d, ", sc->sc_msgcnt));
		DPRINTF(("msgbuf = %02x %02x %02x %02x %02x %02x\n",
		    sc->sc_msgbuf[0], sc->sc_msgbuf[1], sc->sc_msgbuf[2],
		    sc->sc_msgbuf[3], sc->sc_msgbuf[4], sc->sc_msgbuf[5]));

		switch (sc->sc_msgbuf[0]) {
		case MSG_SIMPLE_Q_TAG:
		case MSG_HEAD_OF_Q_TAG:
		case MSG_ORDERED_Q_TAG:
			/*
			 * is QUEUE tag message :
			 *
			 * byte 0:
			 *        HEAD    QUEUE TAG (20h)
			 *        ORDERED QUEUE TAG (21h)
			 *        SIMPLE  QUEUE TAG (22h)
			 * byte 1:
			 *        Queue tag (00h - FFh)
			 */
			if (sc->sc_msgcnt == 2 && sc->sc_actsrb == NULL) {
				/* XXX XXX XXX */
				struct trm_linfo *li;
				int tagid;

				sc->sc_flag &= ~WAIT_TAGMSG;
				tagid = sc->sc_msgbuf[1];
				ti = &sc->sc_tinfo[sc->resel_target];
				li = ti->linfo[sc->resel_lun];
				srb = li->queued[tagid];
				if (srb != NULL) {
					sc->sc_actsrb = srb;
					sc->sc_state = TRM_DATA_XFER;
					break;
				} else {
					printf("%s: invalid tag id\n",
					    device_xname(sc->sc_dev));
				}

				sc->sc_state = TRM_UNEXPECT_RESEL;
				sc->sc_msgbuf[0] = MSG_ABORT_TAG;
				sc->sc_msgcnt = 1;
				bus_space_write_2(iot, ioh,
				    TRM_SCSI_CONTROL, DO_SETATN);
			} else
				sc->sc_state = TRM_IDLE;
			break;

		case MSG_EXTENDED:
			srb = sc->sc_actsrb;
			if (srb == NULL) {
				DPRINTF(("trm_msgin_phase0: "
				    "extended message without actsrb\n"));
				break;
			}
			periph = srb->xs->xs_periph;
			ti = &sc->sc_tinfo[periph->periph_target];

			if (sc->sc_msgbuf[2] == MSG_EXT_WDTR &&
			    sc->sc_msgcnt == 4) {
				/*
				 * is Wide data xfer Extended message :
				 * ======================================
				 * 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
				 */
				if (sc->sc_msgbuf[1] != MSG_EXT_WDTR_LEN) {
					/* Length is wrong, reject it */
					ti->flag &= ~(WIDE_NEGO_ENABLE |
					    WIDE_NEGO_DONE);
					sc->sc_state = TRM_MSGOUT;
					sc->sc_msgbuf[0] = MSG_MESSAGE_REJECT;
					sc->sc_msgcnt = 1;
					bus_space_write_2(iot, ioh,
					    TRM_SCSI_CONTROL, DO_SETATN);
					break;
				}

				if ((ti->flag & WIDE_NEGO_ENABLE) == 0)
					sc->sc_msgbuf[3] =
					    MSG_EXT_WDTR_BUS_8_BIT;

				if (sc->sc_msgbuf[3] >
				    MSG_EXT_WDTR_BUS_32_BIT) {
					/* reject_msg: */
					ti->flag &= ~(WIDE_NEGO_ENABLE |
					    WIDE_NEGO_DONE);
					sc->sc_state = TRM_MSGOUT;
					sc->sc_msgbuf[0] = MSG_MESSAGE_REJECT;
					sc->sc_msgcnt = 1;
					bus_space_write_2(iot, ioh,
					    TRM_SCSI_CONTROL, DO_SETATN);
					break;
				}
				if (sc->sc_msgbuf[3] == MSG_EXT_WDTR_BUS_32_BIT)
					/* do 16 bits */
					sc->sc_msgbuf[3] =
					    MSG_EXT_WDTR_BUS_16_BIT;
				if ((ti->flag & WIDE_NEGO_DONE) == 0) {
					ti->flag |= WIDE_NEGO_DONE;
					ti->flag &= ~(SYNC_NEGO_DONE |
					    WIDE_NEGO_ENABLE);
					if (sc->sc_msgbuf[3] !=
					    MSG_EXT_WDTR_BUS_8_BIT)
						/* is Wide data xfer */
						ti->synctl |= WIDE_SYNC;
					trm_update_xfer_mode(sc,
					    periph->periph_target);
				}

				sc->sc_state = TRM_MSGOUT;
				bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
				    DO_SETATN);
				break;

			} else if (sc->sc_msgbuf[2] == MSG_EXT_SDTR &&
			 	   sc->sc_msgcnt == 5) {
				/*
				 * is 8bit transfer Extended message :
				 * =================================
				 * SYNCHRONOUS DATA TRANSFER REQUEST
				 * =================================
				 * byte 0 :  Extended message (01h)
				 * byte 1 :  Extended message length (03)
				 * byte 2 :  SYNC DATA TRANSFER code (01h)
				 * byte 3 :  Transfer period factor
				 * byte 4 :  REQ/ACK offset
				 */
				if (sc->sc_msgbuf[1] != MSG_EXT_SDTR_LEN) {
					/* reject_msg */
					sc->sc_state = TRM_MSGOUT;
					sc->sc_msgbuf[0] = MSG_MESSAGE_REJECT;
					sc->sc_msgcnt = 1;
					bus_space_write_2(iot, ioh,
					    TRM_SCSI_CONTROL, DO_SETATN);
					break;
				}

				if ((ti->flag & SYNC_NEGO_DONE) == 0) {
					ti->flag &=
					    ~(SYNC_NEGO_ENABLE|SYNC_NEGO_DOING);
					ti->flag |= SYNC_NEGO_DONE;
					if (sc->sc_msgbuf[3] >= TRM_MAX_PERIOD)
						sc->sc_msgbuf[3] = 0;
					if (sc->sc_msgbuf[4] > TRM_MAX_OFFSET)
						sc->sc_msgbuf[4] =
						    TRM_MAX_OFFSET;

					if (sc->sc_msgbuf[3] == 0 ||
					    sc->sc_msgbuf[4] == 0) {
						/* set async */
						ti->synctl = 0;
						ti->offset = 0;
					} else {
						/* set sync */
						/* Transfer period factor */
						ti->period = sc->sc_msgbuf[3];
						/* REQ/ACK offset */
						ti->offset = sc->sc_msgbuf[4];
						for (index = 0;
						    index < NPERIOD;
						    index++)
							if (ti->period <=
							    trm_clock_period[
							    index])
								break;

						ti->synctl |= ALT_SYNC | index;
					}
					/*
					 * program SCSI control register
					 */
					bus_space_write_1(iot, ioh,
					    TRM_SCSI_SYNC, ti->synctl);
					bus_space_write_1(iot, ioh,
					    TRM_SCSI_OFFSET, ti->offset);
					trm_update_xfer_mode(sc,
					    periph->periph_target);
				}
				sc->sc_state = TRM_IDLE;
			}
			break;
		default:
			break;
		}
	}

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

	/*
	 * SCSI command
	 */
	bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_MSGACCEPT);
}

static void
trm_msgin_phase1(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;

	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);
	bus_space_write_4(iot, ioh, TRM_SCSI_XCNT, 1);
	if (sc->sc_state != TRM_MSGIN && sc->sc_state != TRM_EXTEND_MSGIN) {
		sc->sc_state = TRM_MSGIN;
	}

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

	/*
	 * SCSI command
	 */
	bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_FIFO_IN);
}

static void
trm_disconnect(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct trm_srb *srb;
	int s;

	s = splbio();

	srb = sc->sc_actsrb;
	DPRINTF(("trm_disconnect...............\n"));

	if (srb == NULL) {
		DPRINTF(("trm_disconnect: no active srb\n"));
		DELAY(1000);	/* 1 msec */

		bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
		    DO_CLRFIFO | DO_HWRESELECT);
		return;
	}
	sc->sc_phase = PH_BUS_FREE;	/* SCSI bus free Phase */
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
	    DO_CLRFIFO | DO_HWRESELECT);
	DELAY(100);

	switch (sc->sc_state) {
	case TRM_UNEXPECT_RESEL:
		sc->sc_state = TRM_IDLE;
		break;

	case TRM_ABORT_SENT:
		goto finish;

	case TRM_START:
	case TRM_MSGOUT:
		{
			/* Selection time out - discard all LUNs if empty */
			struct scsipi_periph *periph;
			struct trm_tinfo *ti;
			struct trm_linfo *li;
			int lun;

			DPRINTF(("selection timeout\n"));

			srb->tastat = SCSI_SEL_TIMEOUT; /* XXX Ok? */

			periph = srb->xs->xs_periph;
			ti = &sc->sc_tinfo[periph->periph_target];
			for (lun = 0; lun < TRM_MAX_LUNS; lun++) {
				li = ti->linfo[lun];
				if (li != NULL &&
				    li->untagged == NULL && li->used == 0) {
					ti->linfo[lun] = NULL;
					free(li, M_DEVBUF);
				}
			}
		}
		goto finish;

	case TRM_DISCONNECTED:
		sc->sc_actsrb = NULL;
		sc->sc_state = TRM_IDLE;
		goto sched;

	case TRM_COMPLETED:
		goto finish;
	}

 out:
	splx(s);
	return;

 finish:
	sc->sc_state = TRM_IDLE;
	trm_done(sc, srb);
	goto out;

 sched:
	trm_sched(sc);
	goto out;
}

static void
trm_reselect(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct trm_tinfo *ti;
	struct trm_linfo *li;
	int target, lun;

	DPRINTF(("trm_reselect.................\n"));

	if (sc->sc_actsrb != NULL) {
		/* arbitration lost but reselection win */
		sc->sc_state = TRM_READY;
		target = sc->sc_actsrb->xs->xs_periph->periph_target;
		ti = &sc->sc_tinfo[target];
	} else {
		/* Read Reselected Target Id and LUN */
		target = bus_space_read_1(iot, ioh, TRM_SCSI_TARGETID);
		lun = bus_space_read_1(iot, ioh, TRM_SCSI_IDMSG) & 0x07;
		ti = &sc->sc_tinfo[target];
		li = ti->linfo[lun];
		DPRINTF(("target = %d, lun = %d\n", target, lun));

		/*
		 * Check to see if we are running an un-tagged command.
		 * Otherwise ack the IDENTIFY and wait for a tag message.
		 */
		if (li != NULL) {
			if (li->untagged != NULL && li->busy) {
				sc->sc_actsrb = li->untagged;
				sc->sc_state = TRM_DATA_XFER;
			} else {
				sc->resel_target = target;
				sc->resel_lun = lun;
				/* XXX XXX XXX */
				sc->sc_flag |= WAIT_TAGMSG;
			}
		}

		if ((ti->flag & USE_TAG_QUEUING) == 0 &&
		    sc->sc_actsrb == NULL) {
			printf("%s: reselect from target %d lun %d "
			    "without nexus; sending abort\n",
			    device_xname(sc->sc_dev), target, lun);
			sc->sc_state = TRM_UNEXPECT_RESEL;
			sc->sc_msgbuf[0] = MSG_ABORT_TAG;
			sc->sc_msgcnt = 1;
			bus_space_write_2(iot, ioh,
			    TRM_SCSI_CONTROL, DO_SETATN);
		}
	}
	sc->sc_phase = PH_BUS_FREE;	/* SCSI bus free Phase */
	/*
	 * Program HA ID, target ID, period and offset
	 */
	/* target ID */
	bus_space_write_1(iot, ioh, TRM_SCSI_TARGETID, target);

	/* host ID */
	bus_space_write_1(iot, ioh, TRM_SCSI_HOSTID, sc->sc_id);

	/* period */
	bus_space_write_1(iot, ioh, TRM_SCSI_SYNC, ti->synctl);

	/* offset */
	bus_space_write_1(iot, ioh, TRM_SCSI_OFFSET, ti->offset);

	/* it's important for atn stop */
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);
	/*
	 * SCSI command
	 */
	/* to rls the /ACK signal */
	bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_MSGACCEPT);
}

/*
 * Complete execution of a SCSI command
 * Signal completion to the generic SCSI driver
 */
static void
trm_done(struct trm_softc *sc, struct trm_srb *srb)
{
	struct scsipi_xfer *xs = srb->xs;

	DPRINTF(("trm_done..................\n"));

	if (xs == NULL)
		return;

	if ((xs->xs_control & XS_CTL_POLL) == 0)
		callout_stop(&xs->xs_callout);

	if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT) ||
	    srb->flag & AUTO_REQSENSE) {
		bus_dmamap_sync(sc->sc_dmat, srb->dmap, 0,
		    srb->dmap->dm_mapsize,
		    ((xs->xs_control & XS_CTL_DATA_IN) ||
		    (srb->flag & AUTO_REQSENSE)) ?
		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
		bus_dmamap_unload(sc->sc_dmat, srb->dmap);
	}

	/*
	 * target status
	 */
	xs->status = srb->tastat;

	DPRINTF(("xs->status = 0x%02x\n", xs->status));

	switch (xs->status) {
	case SCSI_OK:
		/*
		 * process initiator status......
		 * Adapter (initiator) status
		 */
		if ((srb->hastat & H_OVER_UNDER_RUN) != 0) {
			printf("%s: over/under run error\n",
			    device_xname(sc->sc_dev));
			srb->tastat = 0;
			/* Illegal length (over/under run) */
			xs->error = XS_DRIVER_STUFFUP;
		} else if ((srb->flag & PARITY_ERROR) != 0) {
			printf("%s: parity error\n", device_xname(sc->sc_dev));
			/* Driver failed to perform operation */
			xs->error = XS_DRIVER_STUFFUP; /* XXX */
		} else if ((srb->flag & SRB_TIMEOUT) != 0) {
			xs->resid = srb->buflen;
			xs->error = XS_TIMEOUT;
		} else {
			/* No error */
			xs->resid = srb->buflen;
			srb->hastat = 0;
			if (srb->flag & AUTO_REQSENSE) {
				/* there is no error, (sense is invalid) */
				xs->error = XS_SENSE;
			} else {
				srb->tastat = 0;
				xs->error = XS_NOERROR;
			}
		}
		break;

	case SCSI_CHECK:
		if ((srb->flag & AUTO_REQSENSE) != 0 ||
		    trm_request_sense(sc, srb) != 0) {
			printf("%s: request sense failed\n",
			    device_xname(sc->sc_dev));
			xs->error = XS_DRIVER_STUFFUP;
			break;
		}
		xs->error = XS_SENSE;
		return;

	case SCSI_SEL_TIMEOUT:
		srb->hastat = H_SEL_TIMEOUT;
		srb->tastat = 0;
		xs->error = XS_SELTIMEOUT;
		break;

	case SCSI_QUEUE_FULL:
	case SCSI_BUSY:
		xs->error = XS_BUSY;
		break;

	case SCSI_RESV_CONFLICT:
		DPRINTF(("%s: target reserved at ", device_xname(sc->sc_dev)));
		DPRINTF(("%s %d\n", __FILE__, __LINE__));
		xs->error = XS_BUSY;
		break;

	default:
		srb->hastat = 0;
		printf("%s: trm_done(): unknown status = %02x\n",
		    device_xname(sc->sc_dev), xs->status);
		xs->error = XS_DRIVER_STUFFUP;
		break;
	}

	trm_dequeue(sc, srb);
	if (srb == sc->sc_actsrb) {
		sc->sc_actsrb = NULL;
		trm_sched(sc);
	}

	TAILQ_INSERT_TAIL(&sc->sc_freesrb, srb, next);

	/* Notify cmd done */
	scsipi_done(xs);
}

static int
trm_request_sense(struct trm_softc *sc, struct trm_srb *srb)
{
	struct scsipi_xfer *xs;
	struct scsipi_periph *periph;
	struct trm_tinfo *ti;
	struct trm_linfo *li;
	struct scsi_request_sense *ss = (struct scsi_request_sense *)srb->cmd;
	int error;

	DPRINTF(("trm_request_sense...\n"));

	xs = srb->xs;
	periph = xs->xs_periph;

	srb->flag |= AUTO_REQSENSE;

	/* Status of initiator/target */
	srb->hastat = 0;
	srb->tastat = 0;

	memset(ss, 0, sizeof(*ss));
	ss->opcode = SCSI_REQUEST_SENSE;
	ss->byte2 = periph->periph_lun << SCSI_CMD_LUN_SHIFT;
	ss->length = sizeof(struct scsi_sense_data);

	srb->buflen = sizeof(struct scsi_sense_data);
	srb->sgcnt = 1;
	srb->sgindex = 0;
	srb->cmdlen = sizeof(struct scsi_request_sense);

	if ((error = bus_dmamap_load(sc->sc_dmat, srb->dmap,
	    &xs->sense.scsi_sense, srb->buflen, NULL,
	    BUS_DMA_READ|BUS_DMA_NOWAIT)) != 0) {
		return error;
	}
	bus_dmamap_sync(sc->sc_dmat, srb->dmap, 0,
	    srb->buflen, BUS_DMASYNC_PREREAD);

	srb->sgentry[0].address = htole32(srb->dmap->dm_segs[0].ds_addr);
	srb->sgentry[0].length = htole32(sizeof(struct scsi_sense_data));
	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, srb->sgoffset,
	    TRM_SG_SIZE, BUS_DMASYNC_PREWRITE);

	ti = &sc->sc_tinfo[periph->periph_target];
	li = ti->linfo[periph->periph_lun];
	if (li->busy > 0)
		li->busy = 0;
	trm_dequeue(sc, srb);
	li->untagged = srb;	/* must be executed first to fix C/A */
	li->busy = 2;

	if (srb == sc->sc_actsrb)
		trm_select(sc, srb);
	else {
		TAILQ_INSERT_HEAD(&sc->sc_readysrb, srb, next);
		if (sc->sc_actsrb == NULL)
			trm_sched(sc);
	}
	return 0;
}

static void
trm_dequeue(struct trm_softc *sc, struct trm_srb *srb)
{
	struct scsipi_periph *periph;
	struct trm_tinfo *ti;
	struct trm_linfo *li;

	periph = srb->xs->xs_periph;
	ti = &sc->sc_tinfo[periph->periph_target];
	li = ti->linfo[periph->periph_lun];

	if (li->untagged == srb) {
		li->busy = 0;
		li->untagged = NULL;
	}
	if (srb->tag[0] != 0 && li->queued[srb->tag[1]] != NULL) {
		li->queued[srb->tag[1]] = NULL;
		li->used--;
	}
}

static void
trm_reset_scsi_bus(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	int timeout, s;

	DPRINTF(("trm_reset_scsi_bus.........\n"));

	s = splbio();

	sc->sc_flag |= RESET_DEV;
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_RSTSCSI);
	for (timeout = 20000; timeout >= 0; timeout--) {
		DELAY(1);
		if ((bus_space_read_2(iot, ioh, TRM_SCSI_INTSTATUS) &
		    INT_SCSIRESET) == 0)
			break;
	}
	if (timeout == 0)
		printf(": scsibus reset timeout\n");

	splx(s);
}

static void
trm_scsi_reset_detect(struct trm_softc *sc)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	int s;

	DPRINTF(("trm_scsi_reset_detect...............\n"));
	DELAY(1000000);		/* delay 1 sec */

	s = splbio();

	bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, STOPDMAXFER);
	bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);

	if (sc->sc_flag & RESET_DEV) {
		sc->sc_flag |= RESET_DONE;
	} else {
		sc->sc_flag |= RESET_DETECT;
		sc->sc_actsrb = NULL;
		sc->sc_flag = 0;
		trm_sched(sc);
	}
	splx(s);
}

/*
 * read seeprom 128 bytes to struct eeprom and check checksum.
 * If it is wrong, update with default value.
 */
static void
trm_check_eeprom(struct trm_softc *sc, struct trm_nvram *eeprom)
{
	struct nvram_target *target;
	uint16_t *ep;
	uint16_t chksum;
	int i;

	DPRINTF(("trm_check_eeprom......\n"));
	trm_eeprom_read_all(sc, eeprom);
	ep = (uint16_t *)eeprom;
	chksum = 0;
	for (i = 0; i < 64; i++)
		chksum += le16toh(*ep++);

	if (chksum != TRM_NVRAM_CKSUM) {
		DPRINTF(("TRM_S1040 EEPROM Check Sum ERROR (load default).\n"));
		/*
		 * Checksum error, load default
		 */
		eeprom->subvendor_id[0] = PCI_VENDOR_TEKRAM2 & 0xFF;
		eeprom->subvendor_id[1] = PCI_VENDOR_TEKRAM2 >> 8;
		eeprom->subsys_id[0] = PCI_PRODUCT_TEKRAM2_DC315 & 0xFF;
		eeprom->subsys_id[1] = PCI_PRODUCT_TEKRAM2_DC315 >> 8;
		eeprom->subclass = 0x00;
		eeprom->vendor_id[0] = PCI_VENDOR_TEKRAM2 & 0xFF;
		eeprom->vendor_id[1] = PCI_VENDOR_TEKRAM2 >> 8;
		eeprom->device_id[0] = PCI_PRODUCT_TEKRAM2_DC315 & 0xFF;
		eeprom->device_id[1] = PCI_PRODUCT_TEKRAM2_DC315 >> 8;
		eeprom->reserved0 = 0x00;

		for (i = 0, target = eeprom->target;
		     i < TRM_MAX_TARGETS;
		     i++, target++) {
			target->config0 = 0x77;
			target->period = 0x00;
			target->config2 = 0x00;
			target->config3 = 0x00;
		}

		eeprom->scsi_id = 7;
		eeprom->channel_cfg = 0x0F;
		eeprom->delay_time = 0;
		eeprom->max_tag = 4;
		eeprom->reserved1 = 0x15;
		eeprom->boot_target = 0;
		eeprom->boot_lun = 0;
		eeprom->reserved2 = 0;
		memset(eeprom->reserved3, 0, sizeof(eeprom->reserved3));

		chksum = 0;
		ep = (uint16_t *)eeprom;
		for (i = 0; i < 63; i++)
			chksum += le16toh(*ep++);

		chksum = TRM_NVRAM_CKSUM - chksum;
		eeprom->checksum0 = chksum & 0xFF;
		eeprom->checksum1 = chksum >> 8;

		trm_eeprom_write_all(sc, eeprom);
	}
}

/*
 * write struct eeprom 128 bytes to seeprom
 */
static void
trm_eeprom_write_all(struct trm_softc *sc, struct trm_nvram *eeprom)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	uint8_t *sbuf = (uint8_t *)eeprom;
	uint8_t addr;

	/* Enable SEEPROM */
	bus_space_write_1(iot, ioh, TRM_GEN_CONTROL,
	    bus_space_read_1(iot, ioh, TRM_GEN_CONTROL) | EN_EEPROM);

	/*
	 * Write enable
	 */
	trm_eeprom_write_cmd(sc, 0x04, 0xFF);
	bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
	trm_eeprom_wait();

	for (addr = 0; addr < 128; addr++, sbuf++)
		trm_eeprom_set_data(sc, addr, *sbuf);

	/*
	 * Write disable
	 */
	trm_eeprom_write_cmd(sc, 0x04, 0x00);
	bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
	trm_eeprom_wait();

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

/*
 * write one byte to seeprom
 */
static void
trm_eeprom_set_data(struct trm_softc *sc, uint8_t addr, uint8_t data)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	int i;
	uint8_t send;

	/*
	 * Send write command & address
	 */
	trm_eeprom_write_cmd(sc, 0x05, addr);
	/*
	 * Write data
	 */
	for (i = 0; i < 8; i++, data <<= 1) {
		send = NVR_SELECT;
		if (data & 0x80)	/* Start from bit 7 */
			send |= NVR_BITOUT;

		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send);
		trm_eeprom_wait();
		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send | NVR_CLOCK);
		trm_eeprom_wait();
	}
	bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
	trm_eeprom_wait();
	/*
	 * Disable chip select
	 */
	bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
	trm_eeprom_wait();
	bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
	trm_eeprom_wait();
	/*
	 * Wait for write ready
	 */
	for (;;) {
		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM,
		    NVR_SELECT | NVR_CLOCK);
		trm_eeprom_wait();
		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
		trm_eeprom_wait();
		if (bus_space_read_1(iot, ioh, TRM_GEN_NVRAM) & NVR_BITIN)
			break;
	}
	/*
	 * Disable chip select
	 */
	bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
}

/*
 * read seeprom 128 bytes to struct eeprom
 */
static void
trm_eeprom_read_all(struct trm_softc *sc, struct trm_nvram *eeprom)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	uint8_t *sbuf = (uint8_t *)eeprom;
	uint8_t addr;

	/*
	 * Enable SEEPROM
	 */
	bus_space_write_1(iot, ioh, TRM_GEN_CONTROL,
	    bus_space_read_1(iot, ioh, TRM_GEN_CONTROL) | EN_EEPROM);

	for (addr = 0; addr < 128; addr++)
		*sbuf++ = trm_eeprom_get_data(sc, addr);

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

/*
 * read one byte from seeprom
 */
static uint8_t
trm_eeprom_get_data(struct trm_softc *sc, uint8_t addr)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	int i;
	uint8_t read, data = 0;

	/*
	 * Send read command & address
	 */
	trm_eeprom_write_cmd(sc, 0x06, addr);

	for (i = 0; i < 8; i++) { /* Read data */
		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM,
		    NVR_SELECT | NVR_CLOCK);
		trm_eeprom_wait();
		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
		/*
		 * Get data bit while falling edge
		 */
		read = bus_space_read_1(iot, ioh, TRM_GEN_NVRAM);
		data <<= 1;
		if (read & NVR_BITIN)
			data |= 1;

		trm_eeprom_wait();
	}
	/*
	 * Disable chip select
	 */
	bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
	return data;
}

/*
 * write SB and Op Code into seeprom
 */
static void
trm_eeprom_write_cmd(struct trm_softc *sc, uint8_t cmd, uint8_t addr)
{
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	int i;
	uint8_t send;

	/* Program SB+OP code */
	for (i = 0; i < 3; i++, cmd <<= 1) {
		send = NVR_SELECT;
		if (cmd & 0x04)	/* Start from bit 2 */
			send |= NVR_BITOUT;

		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send);
		trm_eeprom_wait();
		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send | NVR_CLOCK);
		trm_eeprom_wait();
	}

	/* Program address */
	for (i = 0; i < 7; i++, addr <<= 1) {
		send = NVR_SELECT;
		if (addr & 0x40)	/* Start from bit 6 */
			send |= NVR_BITOUT;

		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send);
		trm_eeprom_wait();
		bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send | NVR_CLOCK);
		trm_eeprom_wait();
	}
	bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
	trm_eeprom_wait();
}