root/sys/dev/pci/trm.c
/*      $NetBSD: trm.c,v 1.44 2024/02/09 22:08:36 andvar 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.44 2024/02/09 22:08:36 andvar 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 <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;
        int fl = -1;
        char intrbuf[PCI_INTRSTR_LEN];

        sc->sc_dev = self;

        /*
         * Some 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 (command & PCI_COMMAND_MEM_ENABLE) {
                fl = pci_mapreg_map(pa, TRM_BAR_MMIO, PCI_MAPREG_TYPE_MEM, 0, &iot,
                    &ioh, NULL, NULL);
        }
        if (fl != 0) {
                aprint_verbose_dev(self, "couldn't map MMIO registers, trying PIO\n");
                if ((fl = pci_mapreg_map(pa, TRM_BAR_PIO, PCI_MAPREG_TYPE_IO,
                    0, &iot, &ioh, NULL, NULL)) != 0) {
                        aprint_error(": unable to map registers (%d)\n", fl);
                        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, intrbuf, sizeof(intrbuf));

        if (pci_intr_establish_xname(pa->pa_pc, ih, IPL_BIO, trm_intr, sc,
            device_xname(self)) == 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, CFARGS_NONE);
}

/*
 * 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_WAITOK | M_ZERO);
        DPRINTF(("all SRB size=%zx\n", sizeof(struct trm_srb) * TRM_MAX_SRB));

        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, 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;
                DPRINTF(("trm_scsipi_request.....\n"));
                DPRINTF(("target= %d lun= %d\n", xs->xs_periph->periph_target,
                    xs->xs_periph->periph_lun));
                if (xs->xs_control & XS_CTL_RESET) {
                        trm_reset(sc);
                        xs->error = 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 extended 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();
}