diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ic/iha.c | 2978 | ||||
-rw-r--r-- | sys/dev/ic/iha.h | 495 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 6 | ||||
-rw-r--r-- | sys/dev/pci/iha_pci.c | 138 |
4 files changed, 3616 insertions, 1 deletions
diff --git a/sys/dev/ic/iha.c b/sys/dev/ic/iha.c new file mode 100644 index 00000000000..b4ac2a02379 --- /dev/null +++ b/sys/dev/ic/iha.c @@ -0,0 +1,2978 @@ +/* $OpenBSD: iha.c,v 1.1 2001/01/23 04:19:41 krw Exp $ */ +/* + * Initio INI-9xxxU/UW SCSI Device Driver + * + * Copyright (c) 2000 Ken Westerback + * 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, + * without modification, immediately at the beginning of the file. + * 2. 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 OR HIS RELATIVES 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 MIND, 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 i91u.c, provided by Initio Corporation, which credits: + * + * Device driver for the INI-9XXXU/UW or INIC-940/950 PCI SCSI Controller. + * + * FreeBSD + * + * Written for 386bsd and FreeBSD by + * Winston Hung <winstonh@initio.com> + * + * Copyright (c) 1997-99 Initio Corp. All rights reserved. + * + *------------------------------------------------------------------------- + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/buf.h> +#include <sys/device.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/iha.h> + +struct cfdriver iha_cd = { + NULL, "iha", DV_DULL +}; + +struct scsi_adapter iha_switch = { + iha_scsi_cmd, /* int (*scsi_cmd) __P((struct scsi_xfer *)); */ + iha_minphys, /* void (*scsi_minphys) __P((struct buf *)); */ + NULL, /* int (*open_target_lu) __P((void)); */ + NULL /* int (*close_target_lu) __P((void)); */ +}; + +struct scsi_device iha_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + +/* + * SCSI Rate Table, indexed by FLAG_SCSI_RATE field of + * TCS_Flags. + */ +static u_int8_t tul_rate_tbl[8] = { + /* fast 20 */ + /* nanosecond divide by 4 */ + 12, /* 50ns, 20M */ + 18, /* 75ns, 13.3M */ + 25, /* 100ns, 10M */ + 31, /* 125ns, 8M */ + 37, /* 150ns, 6.6M */ + 43, /* 175ns, 5.7M */ + 50, /* 200ns, 5M */ + 62 /* 250ns, 4M */ +}; + +static struct nvram tul_nvram; + +static u_int8_t tul_dftNvRam[64] = { + /* -- Header ------------------------------------ */ + 0x25, 0xc9, /* NVM_Signature */ + 0x40, /* NVM_Size */ + 0x01, /* NVM_Revision */ + + /* -- Host Adapter Structure -------------------- */ + 0x95, /* NVM_ModelByte0 */ + 0x00, /* NVM_ModelByte1 */ + 0x00, /* NVM_ModelInfo */ + 0x01, /* NVM_NumOfCh */ + BIOSCFG_DEFAULT, /* NVM_BIOSConfig1 */ + 0, /* NVM_BIOSConfig2 */ + 0, /* NVM_HAConfig1 */ + 0, /* NVM_HAConfig2 */ + + /* -- NVM_Scsi[0] ------------------------------- */ + 7, /* NVM_SCSI_Id */ + CFG_DEFAULT, /* NVM_SCSI_Cfg */ + 0, /* NVM_SCSI_CfgByte2 */ + 8, /* NVM_SCSI_Targets */ + /* NVM_SCSI_TargetFlags */ + FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, + FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, + FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, + FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, + + /* -- NVM_Scsi[1] ------------------------------- */ + 7, /* NVM_SCSI_Id */ + CFG_DEFAULT, /* NVM_SCSI_Cfg */ + 0, /* NVM_SCSI_CfgByte2 */ + 8, /* NVM_SCSI_Targets */ + /* NVM_SCSI_TargetFlags */ + FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, + FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, + FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, + FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, FLAG_DEFAULT, + + 0,0,0,0,0,0,0,0,0,0, /* NVM_Reserved[10] */ + 0,0 /* NVM_CheckSum */ +}; + +static u_int8_t tul_data_over_run __P((struct iha_scsi_req_q *)); + +static void tul_push_sense_request __P((struct iha_softc *, + struct iha_scsi_req_q *)); +static void tul_timeout __P((void *)); +static int tul_alloc_scbs __P((struct iha_softc *)); + +static void tul_read_eeprom __P((bus_space_tag_t, bus_space_handle_t)); +static void tul_se2_update_all __P((bus_space_tag_t, bus_space_handle_t)); +static int tul_se2_rd_all __P((bus_space_tag_t, bus_space_handle_t)); +static void tul_se2_wr __P((bus_space_tag_t, bus_space_handle_t, + u_int8_t, u_int16_t)); +static void tul_se2_instr __P((bus_space_tag_t, bus_space_handle_t, + u_int8_t)); +static u_int16_t tul_se2_rd __P((bus_space_tag_t, bus_space_handle_t, + u_int8_t)); + +static void tul_reset_scsi_bus __P((struct iha_softc *)); +static void tul_reset_chip __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static void tul_reset_dma __P((bus_space_tag_t, bus_space_handle_t)); + +static void tul_reset_tcs __P((struct tcs *, u_int8_t)); + +static void tul_print_info __P((struct iha_softc *, int)); + +static void tul_done_scb __P((struct iha_softc *, struct iha_scsi_req_q *)); +static void tul_exec_scb __P((struct iha_softc *, struct iha_scsi_req_q *)); + +static void tul_main __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static void tul_scsi __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); + +static int tul_wait __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t, u_int8_t)); + +static void tul_mark_busy_scb __P((struct iha_scsi_req_q *)); + +static void tul_append_free_scb __P((struct iha_softc *, + struct iha_scsi_req_q *)); +static struct iha_scsi_req_q *tul_pop_free_scb __P((struct iha_softc *)); + +static void tul_append_done_scb __P((struct iha_softc *, + struct iha_scsi_req_q *, u_int8_t)); +static struct iha_scsi_req_q *tul_pop_done_scb __P((struct iha_softc *)); + +static void tul_append_pend_scb __P((struct iha_softc *, + struct iha_scsi_req_q *)); +static void tul_push_pend_scb __P((struct iha_softc *, + struct iha_scsi_req_q *)); +static void tul_del_pend_scb __P((struct iha_softc *, + struct iha_scsi_req_q *)); +static struct iha_scsi_req_q *tul_find_pend_scb __P((struct iha_softc *)); + +static void tul_sync_done __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static void tul_wdtr_done __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static void tul_bad_seq __P((struct iha_softc *)); + +static int tul_next_state __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_state_1 __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_state_2 __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_state_3 __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_state_4 __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_state_5 __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_state_6 __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_state_8 __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); + +static void tul_set_ssig __P((bus_space_tag_t, + bus_space_handle_t, u_int8_t, u_int8_t)); + +static int tul_xpad_in __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_xpad_out __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); + +static int tul_xfer_data __P((struct iha_scsi_req_q *, + bus_space_tag_t, bus_space_handle_t, + int direction)); + +static int tul_status_msg __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); + +static int tul_msgin __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_msgin_sync __P((struct iha_softc *)); +static int tul_msgin_extend __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_msgin_ignore_wid_resid __P((struct iha_softc *, + bus_space_tag_t, + bus_space_handle_t)); + +static int tul_msgout __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t, + u_int8_t)); +static void tul_msgout_abort __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t, + u_int8_t)); +static int tul_msgout_reject __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_msgout_sync __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_msgout_wide __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); + +static void tul_select __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t, + struct iha_scsi_req_q *, u_int8_t)); + +static void tul_busfree __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); +static int tul_resel __P((struct iha_softc *, + bus_space_tag_t, bus_space_handle_t)); + +static void tul_abort_xs __P((struct iha_softc *, + struct scsi_xfer *, u_int8_t)); + +/* + * iha_intr - the interrupt service routine for the iha driver + */ +int +iha_intr(arg) + void *arg; +{ + bus_space_handle_t ioh; + struct iha_softc *sc; + bus_space_tag_t iot; + int s; + + sc = (struct iha_softc *)arg; + iot = sc->sc_iot; + ioh = sc->sc_ioh; + + if ((bus_space_read_1(iot, ioh, TUL_STAT0) & INTPD) == 0) + return (0); + + s = splbio(); /* XXX - Or are interrupts off when ISR's are called? */ + + if (sc->HCS_Semaph != SEMAPH_IN_MAIN) { + /* XXX - need these inside a splbio()/splx()? */ + bus_space_write_1(iot, ioh, TUL_IMSK, MASK_ALL); + sc->HCS_Semaph = SEMAPH_IN_MAIN; + + tul_main(sc, iot, ioh); + + sc->HCS_Semaph = ~SEMAPH_IN_MAIN; + bus_space_write_1(iot, ioh, TUL_IMSK, (MASK_ALL & ~MSCMP)); + } + + splx(s); + + return (1); +} + +/* + * iha_scsi_cmd - start execution of a SCSI command. This is called + * from the generic SCSI driver via the field + * sc_adapter.scsi_cmd of iha_softc. + */ +int +iha_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct iha_scsi_req_q *pScb; + struct iha_sg_element *sg; + struct scsi_link *sc_link = xs->sc_link; + struct iha_softc *sc = sc_link->adapter_softc; + bus_dmamap_t dm; + int error, nseg, i; + + if ((xs->cmdlen > 12) || (sc_link->target >= IHA_MAX_TARGETS)) { + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + + pScb = tul_pop_free_scb(sc); + if (pScb == NULL) { + /* XXX - different xs->error/return if + * SCSI_POLL/_NOSLEEP? */ + xs->error = XS_BUSY; + return (TRY_AGAIN_LATER); + } + + pScb->SCB_Target = sc_link->target; + pScb->SCB_Lun = sc_link->lun; + pScb->SCB_Tcs = &sc->HCS_Tcs[pScb->SCB_Target]; + pScb->SCB_Flags = xs->flags; + pScb->SCB_Ident = IDENT_IDENTITY | (pScb->SCB_Lun & IDENT_LUN); + + if ((xs->cmd->opcode != REQUEST_SENSE) + && ((pScb->SCB_Flags & SCSI_POLL) == 0)) + pScb->SCB_Ident |= IDENT_DISC_PRIV; + + pScb->SCB_Xs = xs; + pScb->SCB_CDBLen = xs->cmdlen; + bcopy(xs->cmd, &pScb->SCB_CDB, xs->cmdlen); + + pScb->SCB_BufLen = xs->datalen; + + if (pScb->SCB_BufLen > 0) { +#ifdef TFS + if (xs->flags & SCSI_DATA_UIO) + error = bus_dmamap_load_uio(sc->sc_dmat, + pScb->SCB_Dmamap, (struct uio *)xs->data, + (xs->flags & SCSI_NOSLEEP) ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + else +#endif /* TFS */ + error = bus_dmamap_load(sc->sc_dmat, pScb->SCB_Dmamap, + xs->data, pScb->SCB_BufLen, NULL, + (xs->flags & SCSI_NOSLEEP) ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + + if (error) { + if (error == EFBIG) + printf("%s: buffer needs >%d dma segments\n", + sc->sc_dev.dv_xname, IHA_MAX_SG_ENTRIES); + else + printf("%s: error %d loading dma map\n", + sc->sc_dev.dv_xname, error); + + /* XXX - need to scsi_done() xs? i.e. put in doneq? */ + tul_append_free_scb(sc, pScb); + + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + + dm = pScb->SCB_Dmamap; + nseg = dm->dm_nsegs; + + if (nseg > 1) { + sg = pScb->SCB_SGList; + + for (i=0; i < nseg; i++) { + sg[i].SG_Len = dm->dm_segs[i].ds_len; + sg[i].SG_Ptr = dm->dm_segs[i].ds_addr; + } + + pScb->SCB_Flags |= FLAG_SG; + pScb->SCB_SGLen = nseg; + + pScb->SCB_BufPAddr = pScb->SCB_SGPAddr; + } else + pScb->SCB_BufPAddr = dm->dm_segs[0].ds_addr; + + bus_dmamap_sync(sc->sc_dmat, pScb->SCB_Dmamap, + (pScb->SCB_Flags & SCSI_DATA_IN) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + } + + pScb->SCB_SGMax = pScb->SCB_SGLen; + pScb->SCB_Timeout = xs->timeout; + + /* + * Always initialize the stimeout structure as it may + * contain garbage that confuses timeout_del() later on. + * But, timeout_add() ONLY if we are not polling. + */ + timeout_set(&xs->stimeout, tul_timeout, pScb); + if ((xs->flags & SCSI_POLL) == 0) + timeout_add(&xs->stimeout, (xs->timeout/1000) * hz); + + tul_exec_scb(sc, pScb); + + if ((xs->flags & ITSDONE) == 0) + return (SUCCESSFULLY_QUEUED); + else + return (COMPLETE); +} + +/* + * iha_init_tulip - initialize the inic-950 card and the rest of the + * IHA_SOFTC structure supplied + */ +int +iha_init_tulip(sc) + struct iha_softc *sc; +{ + struct iha_scsi_req_q *pScb; + bus_space_handle_t ioh; + struct nvram_scsi *pScsi; + bus_space_tag_t iot; + int i, error; + + iot = sc->sc_iot; + ioh = sc->sc_ioh; + + tul_read_eeprom(iot, ioh); + + pScsi = &tul_nvram.NVM_Scsi[0]; + + /* + * fill in the prototype scsi_link. + */ + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter = &iha_switch; + sc->sc_link.device = &iha_dev; + sc->sc_link.openings = 4; /* # xs's allowed per device */ + sc->sc_link.adapter_target = pScsi->NVM_SCSI_Id; + sc->sc_link.adapter_buswidth = pScsi->NVM_SCSI_Targets; + + /* + * fill in the rest of the IHA_SOFTC fields + */ + sc->HCS_Semaph = ~SEMAPH_IN_MAIN; + sc->HCS_JSStatus0 = 0; + sc->HCS_ActScb = NULL; + + TAILQ_INIT(&sc->HCS_FreeScb); + TAILQ_INIT(&sc->HCS_PendScb); + TAILQ_INIT(&sc->HCS_DoneScb); + + error = tul_alloc_scbs(sc); + if (error != 0) + return (error); + + for (i = 0, pScb = sc->HCS_Scb; i < IHA_MAX_SCB; i++, pScb++) { + pScb->SCB_TagId = i; + pScb->SCB_SGPAddr = sc->sc_dmamap->dm_segs[0].ds_addr + + i*sizeof(struct iha_scsi_req_q) + + OFFSETOF(struct iha_scsi_req_q, SCB_SGList); + + pScb->SCB_SenseLen = sizeof(struct scsi_sense_data); + pScb->SCB_SensePAddr = sc->sc_dmamap->dm_segs[0].ds_addr + + i*sizeof(struct iha_scsi_req_q) + + OFFSETOF(struct iha_scsi_req_q, SCB_ScsiSenseData); + + error = bus_dmamap_create(sc->sc_dmat, + (IHA_MAX_SG_ENTRIES-1) * PAGE_SIZE, IHA_MAX_SG_ENTRIES, + (IHA_MAX_SG_ENTRIES-1) * PAGE_SIZE, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &pScb->SCB_Dmamap); + + if (error != 0) { + printf("%s: couldn't create SCB DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + return (error); + } + TAILQ_INSERT_TAIL(&sc->HCS_FreeScb, pScb, SCB_ScbList); + } + + /* Mask all the interrupts */ + bus_space_write_1(iot, ioh, TUL_IMSK, MASK_ALL); + + /* Stop any I/O and reset the scsi module */ + tul_reset_dma(iot, ioh); + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSMOD); + + /* Program HBA's SCSI ID */ + bus_space_write_1(iot, ioh, TUL_SID, sc->sc_link.adapter_target << 4); + + /* + * Configure the channel as requested by the NVRAM settings read + * into tul_nvram by tul_read_eeprom() above. + */ + + if ((pScsi->NVM_SCSI_Cfg & CFG_EN_PAR) != 0) + sc->HCS_SConf1 = (SCONFIG0DEFAULT | SPCHK); + else + sc->HCS_SConf1 = (SCONFIG0DEFAULT); + bus_space_write_1(iot, ioh, TUL_SCONFIG0, sc->HCS_SConf1); + + /* selection time out in units of 1.6385 millisecond = 250 ms */ + bus_space_write_1(iot, ioh, TUL_STIMO, 153); + + /* Enable desired SCSI termination configuration read from eeprom */ + bus_space_write_1(iot, ioh, TUL_DCTRL0, + (pScsi->NVM_SCSI_Cfg & (CFG_ACT_TERM1 | CFG_ACT_TERM2))); + + bus_space_write_1(iot, ioh, TUL_GCTRL1, + ((pScsi->NVM_SCSI_Cfg & CFG_AUTO_TERM) >> 4) + | (bus_space_read_1(iot, ioh, TUL_GCTRL1) & (~ATDEN))); + + for (i = 0; i < IHA_MAX_TARGETS; i++) { + sc->HCS_Tcs[i].TCS_Flags = pScsi->NVM_SCSI_TargetFlags[i]; + tul_reset_tcs(&sc->HCS_Tcs[i], sc->HCS_SConf1); + } + + tul_reset_chip(sc, iot, ioh); + bus_space_write_1(iot, ioh, TUL_SIEN, ALL_INTERRUPTS); + + return (0); +} + +/* + * iha_minphys - reduce bp->b_bcount to something less than + * or equal to the largest I/O possible through + * the adapter. Called from higher layers + * via sc->sc_adapter.scsi_minphys. + */ +void +iha_minphys(bp) + struct buf *bp; +{ + if (bp->b_bcount > ((IHA_MAX_SG_ENTRIES - 1) * PAGE_SIZE)) + bp->b_bcount = ((IHA_MAX_SG_ENTRIES - 1) * PAGE_SIZE); + + minphys(bp); +} + +/* + * tul_reset_dma - abort any active DMA xfer, reset tulip FIFO. + */ +static void +tul_reset_dma(iot, ioh) + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + if ((bus_space_read_1(iot, ioh, TUL_ISTUS1) & XPEND) != 0) { + /* if DMA xfer is pending, abort DMA xfer */ + bus_space_write_1(iot, ioh, TUL_DCMD, ABTXFR); + /* wait Abort DMA xfer done */ + while ((bus_space_read_1(iot, ioh, TUL_ISTUS0) & DABT) == 0) + ; + } + + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); +} + +/* + * tul_pop_free_scb - return the first free SCB, or NULL if there are none. + */ +static struct iha_scsi_req_q * +tul_pop_free_scb(sc) + struct iha_softc *sc; +{ + struct iha_scsi_req_q *pScb; + int s; + + s = splbio(); + + pScb = TAILQ_FIRST(&sc->HCS_FreeScb); + + if (pScb != NULL) { + pScb->SCB_Status = STATUS_RENT; + TAILQ_REMOVE(&sc->HCS_FreeScb, pScb, SCB_ScbList); + } + + splx(s); + + return (pScb); +} + +/* + * tul_append_free_scb - append the supplied SCB to the tail of the + * HCS_FreeScb queue after clearing and resetting + * everything possible. + */ +static void +tul_append_free_scb(sc, pScb) + struct iha_softc *sc; + struct iha_scsi_req_q *pScb; +{ + int s; + + s = splbio(); + + if (pScb->SCB_Xs != NULL) + timeout_del(&pScb->SCB_Xs->stimeout); + + if (pScb == sc->HCS_ActScb) + sc->HCS_ActScb = NULL; + + pScb->SCB_Status = STATUS_QUEUED; + pScb->SCB_HaStat = HOST_OK; + pScb->SCB_TaStat = SCSI_OK; + + pScb->SCB_NxtStat = 0; + pScb->SCB_SGIdx = 0; + pScb->SCB_SGMax = 0; + pScb->SCB_Flags = 0; + pScb->SCB_Target = 0; + pScb->SCB_Lun = 0; + pScb->SCB_BufLen = 0; + pScb->SCB_SGLen = 0; + pScb->SCB_CDBLen = 0; + pScb->SCB_Ident = 0; + pScb->SCB_TagMsg = 0; + pScb->SCB_Timeout = 0; + pScb->SCB_BufPAddr = 0; + + pScb->SCB_Xs = NULL; + pScb->SCB_Tcs = NULL; + + bzero( pScb->SCB_CDB, sizeof(pScb->SCB_CDB)); + bzero(&pScb->SCB_ScsiSenseData, sizeof(pScb->SCB_ScsiSenseData)); + bzero( pScb->SCB_SGList, sizeof(pScb->SCB_SGList)); + + /* + * SCB_TagId, SCB_SGPAddr, SCB_SenseLen, SCB_SGList + * SCB_SensePtr are set at initialization + * and never change + */ + + TAILQ_INSERT_TAIL(&sc->HCS_FreeScb, pScb, SCB_ScbList); + + splx(s); +} + +static void +tul_append_pend_scb(sc, pScb) + struct iha_softc *sc; + struct iha_scsi_req_q *pScb; +{ + /* ASSUMPTION: only called within a splbio()/splx() pair */ + + if (pScb == sc->HCS_ActScb) + sc->HCS_ActScb = NULL; + + pScb->SCB_Status = STATUS_QUEUED; + + TAILQ_INSERT_TAIL(&sc->HCS_PendScb, pScb, SCB_ScbList); +} + +static void +tul_push_pend_scb(sc, pScb) + struct iha_softc *sc; + struct iha_scsi_req_q *pScb; +{ + int s; + + s = splbio(); + + if (pScb == sc->HCS_ActScb) + sc->HCS_ActScb = NULL; + + pScb->SCB_Status = STATUS_QUEUED; + + TAILQ_INSERT_HEAD(&sc->HCS_PendScb, pScb, SCB_ScbList); + + splx(s); +} + +/* + * tul_find_pend_scb - scan the pending queue for a SCB that can be + * processed immediately. Return NULL if none found + * and a pointer to the SCB if one is found. If there + * is an active SCB, return NULL! + */ +static struct iha_scsi_req_q * +tul_find_pend_scb(sc) + struct iha_softc *sc; +{ + struct iha_scsi_req_q *pScb; + struct tcs *pTcs; + int s; + + s = splbio(); + + if (sc->HCS_ActScb != NULL) + pScb = NULL; + + else + TAILQ_FOREACH(pScb, &sc->HCS_PendScb, SCB_ScbList) { + if ((pScb->SCB_Flags & SCSI_RESET) != 0) + /* ALWAYS willing to reset a device */ + break; + + pTcs = pScb->SCB_Tcs; + + if ((pScb->SCB_TagMsg) != 0) { + /* + * A Tagged I/O. OK to start If no + * non-tagged I/O is active on the same + * target + */ + if (pTcs->TCS_NonTagScb == NULL) + break; + + } else if (pScb->SCB_CDB[0] == REQUEST_SENSE) { + /* + * OK to do a non-tagged request sense + * even if a non-tagged I/O has been + * started, 'cuz we don't allow any + * disconnect during a request sense op + */ + break; + + } else if (pTcs->TCS_TagCnt == 0) { + /* + * No tagged I/O active on this target, + * ok to start a non-tagged one if one + * is not already active + */ + if (pTcs->TCS_NonTagScb == NULL) + break; + } + } + + splx(s); + + return (pScb); +} + +/* + * tul_del_pend_scb - remove pScb from HCS_PendScb + */ +static void +tul_del_pend_scb(sc, pScb) + struct iha_softc *sc; + struct iha_scsi_req_q *pScb; +{ + int s; + + s = splbio(); + + TAILQ_REMOVE(&sc->HCS_PendScb, pScb, SCB_ScbList); + + splx(s); +} + +static void +tul_mark_busy_scb(pScb) + struct iha_scsi_req_q *pScb; +{ + int s; + + s = splbio(); + + pScb->SCB_Status = STATUS_BUSY; + + if (pScb->SCB_TagMsg == 0) + pScb->SCB_Tcs->TCS_NonTagScb = pScb; + else + pScb->SCB_Tcs->TCS_TagCnt++; + + splx(s); +} + +static void +tul_append_done_scb(sc, pScb, hastat) + struct iha_softc *sc; + struct iha_scsi_req_q *pScb; + u_int8_t hastat; +{ + struct tcs *pTcs; + int s; + + s = splbio(); + + if (pScb->SCB_Xs != NULL) + timeout_del(&pScb->SCB_Xs->stimeout); + + if (pScb == sc->HCS_ActScb) + sc->HCS_ActScb = NULL; + + pTcs = pScb->SCB_Tcs; + + if (pScb->SCB_TagMsg != 0) { + if (pTcs->TCS_TagCnt) + pTcs->TCS_TagCnt--; + } else if (pTcs->TCS_NonTagScb == pScb) + pTcs->TCS_NonTagScb = NULL; + + pScb->SCB_Status = STATUS_QUEUED; + pScb->SCB_HaStat = hastat; + + TAILQ_INSERT_TAIL(&sc->HCS_DoneScb, pScb, SCB_ScbList); + + splx(s); +} + +static struct iha_scsi_req_q * +tul_pop_done_scb(sc) + struct iha_softc *sc; +{ + struct iha_scsi_req_q *pScb; + int s; + + s = splbio(); + + pScb = TAILQ_FIRST(&sc->HCS_DoneScb); + + if (pScb != NULL) { + pScb->SCB_Status = STATUS_RENT; + TAILQ_REMOVE(&sc->HCS_DoneScb, pScb, SCB_ScbList); + } + + splx(s); + + return (pScb); +} + +/* + * tul_abort_xs - find the SCB associated with the supplied xs and + * stop all processing on it, moving it to the done + * queue with the supplied host status value. + */ +static void +tul_abort_xs(sc, xs, hastat) + struct iha_softc *sc; + struct scsi_xfer *xs; + u_int8_t hastat; +{ + struct iha_scsi_req_q *pScb; + int i, s; + + s = splbio(); + + /* Check the pending queue for the SCB pointing to xs */ + + TAILQ_FOREACH(pScb, &sc->HCS_PendScb, SCB_ScbList) + if (pScb->SCB_Xs == xs) { + tul_del_pend_scb(sc, pScb); + tul_append_done_scb(sc, pScb, hastat); + splx(s); + return; + } + + /* + * If that didn't work, check all BUSY/SELECTING SCB's for one + * pointing to xs + */ + + for (i = 0, pScb = sc->HCS_Scb; i < IHA_MAX_SCB; i++, pScb++) + switch (pScb->SCB_Status) { + case STATUS_BUSY: + case STATUS_SELECT: + if (pScb->SCB_Xs == xs) { + tul_append_done_scb(sc, pScb, hastat); + splx(s); + return; + } + break; + default: + break; + } + + splx(s); +} + +/* + * tul_bad_seq - a SCSI bus phase was encountered out of the + * correct/expected sequence. Reset the SCSI bus. + */ +static void +tul_bad_seq(sc) + struct iha_softc *sc; +{ + struct iha_scsi_req_q *pScb = sc->HCS_ActScb; + + if (pScb != NULL) + tul_append_done_scb(sc, pScb, HOST_BAD_PHAS); + + tul_reset_scsi_bus(sc); + tul_reset_chip(sc, sc->sc_iot, sc->sc_ioh); +} + +/* + * tul_push_sense_request - obtain auto sense data by pushing the + * SCB needing it back onto the pending + * queue with a REQUEST_SENSE CDB. + */ +static void +tul_push_sense_request(sc, pScb) + struct iha_softc *sc; + struct iha_scsi_req_q *pScb; +{ + pScb->SCB_BufLen = pScb->SCB_SenseLen; + pScb->SCB_BufPAddr = pScb->SCB_SensePAddr; + + pScb->SCB_Flags &= ~(FLAG_SG | FLAG_DIR); + pScb->SCB_Flags |= FLAG_RSENS | SCSI_DATA_IN; + + pScb->SCB_Ident &= ~IDENT_DISC_PRIV; + + pScb->SCB_TagMsg = 0; + pScb->SCB_TaStat = SCSI_OK; + + bzero(pScb->SCB_CDB, sizeof(pScb->SCB_CDB)); + + pScb->SCB_CDBLen = 6; + pScb->SCB_CDB[0] = REQUEST_SENSE; + pScb->SCB_CDB[4] = pScb->SCB_SenseLen; + + if ((pScb->SCB_Flags & SCSI_POLL) == 0) + timeout_add(&pScb->SCB_Xs->stimeout, + (pScb->SCB_Timeout/1000) * hz); + + tul_push_pend_scb(sc, pScb); +} + +/* + * tul_main - process the active SCB, taking one off pending and making it + * active if necessary, and any done SCB's created as + * a result until there are no interrupts pending and no pending + * SCB's that can be started. + */ +static void +tul_main(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb; + + for (;;) { +tul_scsi_label: + tul_scsi(sc, iot, ioh); + + while ((pScb = tul_pop_done_scb(sc)) != NULL) { + + switch (pScb->SCB_TaStat) { + case SCSI_CMD_TERMINATED: + case SCSI_ACA_ACTIVE: + case SCSI_CHECK: + pScb->SCB_Tcs->TCS_Flags &= + ~(FLAG_SYNC_DONE | FLAG_WIDE_DONE); + + if ((pScb->SCB_Flags & FLAG_RSENS) != 0) + /* Check condition on check condition*/ + pScb->SCB_HaStat = HOST_BAD_PHAS; + + else { + tul_push_sense_request(sc, pScb); + goto tul_scsi_label; + } + break; + + default: + if ((pScb->SCB_Flags & FLAG_RSENS) != 0) + /* + * Return the original SCSI_CHECK, not + * the status of the request sense + * command! + */ + pScb->SCB_TaStat = SCSI_CHECK; + break; + } + + tul_done_scb(sc, pScb); + } + + /* + * If there are no interrupts pending, or we can't start + * a pending sc, break out of the for(;;). Otherwise + * continue the good work with another call to + * tul_scsi(). + */ + if (((bus_space_read_1(iot, ioh, TUL_STAT0) & INTPD) == 0) + && (tul_find_pend_scb(sc) == NULL)) + break; + } +} + +/* + * tul_scsi - service any outstanding interrupts. If there are none, try to + * start another SCB currently in the pending queue. + */ +static void +tul_scsi(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb; + struct tcs *pTcs; + u_int8_t stat; + + /* service pending interrupts asap */ + + stat = bus_space_read_1(iot, ioh, TUL_STAT0); + if ((stat & INTPD) != 0) { + sc->HCS_JSStatus0 = stat; + sc->HCS_JSStatus1 = bus_space_read_1(iot, ioh, TUL_STAT1); + sc->HCS_JSInt = bus_space_read_1(iot, ioh, TUL_SISTAT); + + sc->HCS_Phase = sc->HCS_JSStatus0 & PH_MASK; + + if ((sc->HCS_JSInt & SRSTD) != 0) { + tul_reset_scsi_bus(sc); + return; + } + + if ((sc->HCS_JSInt & RSELED) != 0) { + tul_resel(sc, iot, ioh); + return; + } + + if ((sc->HCS_JSInt & (STIMEO | DISCD)) != 0) { + tul_busfree(sc, iot, ioh); + return; + } + + if ((sc->HCS_JSInt & (SCMDN | SBSRV)) != 0) { + tul_next_state(sc, iot, ioh); + return; + } + + if ((sc->HCS_JSInt & SELED) != 0) + tul_set_ssig(iot, ioh, 0, 0); + } + + /* + * There were no interrupts pending which required action elsewhere, so + * see if it is possible to start the selection phase on a pending SCB + */ + if ((pScb = tul_find_pend_scb(sc)) == NULL) + return; + + pTcs = pScb->SCB_Tcs; + + /* program HBA's SCSI ID & target SCSI ID */ + bus_space_write_1(iot, ioh, TUL_SID, + (sc->sc_link.adapter_target << 4) | pScb->SCB_Target); + + if ((pScb->SCB_Flags & SCSI_RESET) == 0) { + bus_space_write_1(iot, ioh, TUL_SYNCM, pTcs->TCS_JS_Period); + + if ((pTcs->TCS_Flags & FLAG_NO_NEGOTIATE) == 0) + tul_select(sc, iot, ioh, pScb, SELATNSTOP); + + else if (pScb->SCB_TagMsg != 0) + tul_select(sc, iot, ioh, pScb, SEL_ATN3); + + else + tul_select(sc, iot, ioh, pScb, SEL_ATN); + + } else { + tul_select(sc, iot, ioh, pScb, SELATNSTOP); + pScb->SCB_NxtStat = 8; + } + + if (pScb->SCB_Flags & SCSI_POLL) { + for (; pScb->SCB_Timeout > 0; pScb->SCB_Timeout--) { + if (tul_wait(sc, iot, ioh, NO_OP) == -1) + return; + if (tul_next_state(sc, iot, ioh) == -1) + return; + delay(1000); /* Only happens in boot, so it's ok */ + } + tul_timeout(pScb); + } +} + +/* + * tul_data_over_run - return HOST_OK for all SCSI opcodes where BufLen + * is an 'Allocation Length'. All other SCSI opcodes + * get HOST_DO_DU as they SHOULD have xferred all the + * data requested. + * + * The list of opcodes using 'Allocation Length' was + * found by scanning all the SCSI-3 T10 drafts. See + * www.t10.org for the curious with a .pdf reader. + */ +static u_int8_t +tul_data_over_run(pScb) + struct iha_scsi_req_q *pScb; +{ + switch (pScb->SCB_CDB[0]) { + case 0x03: /* Request Sense SPC-2 */ + case 0x12: /* Inquiry SPC-2 */ + case 0x1a: /* Mode Sense (6 byte version) SPC-2 */ + case 0x1c: /* Receive Diagnostic Results SPC-2 */ + case 0x23: /* Read Format Capacities MMC-2 */ + case 0x29: /* Read Generation SBC */ + case 0x34: /* Read Position SSC-2 */ + case 0x37: /* Read Defect Data SBC */ + case 0x3c: /* Read Buffer SPC-2 */ + case 0x42: /* Read Sub Channel MMC-2 */ + case 0x43: /* Read TOC/PMA/ATIP MMC */ + + /* XXX - 2 with same opcode of 0x44? */ + case 0x44: /* Read Header/Read Density Suprt MMC/SSC*/ + + case 0x46: /* Get Configuration MMC-2 */ + case 0x4a: /* Get Event/Status Notification MMC-2 */ + case 0x4d: /* Log Sense SPC-2 */ + case 0x51: /* Read Disc Information MMC */ + case 0x52: /* Read Track Information MMC */ + case 0x59: /* Read Master CUE MMC */ + case 0x5a: /* Mode Sense (10 byte version) SPC-2 */ + case 0x5c: /* Read Buffer Capacity MMC */ + case 0x5e: /* Persistant Reserve In SPC-2 */ + case 0x84: /* Receive Copy Results SPC-2 */ + case 0xa0: /* Report LUNs SPC-2 */ + case 0xa3: /* Various Report requests SBC-2/SCC-2*/ + case 0xa4: /* Report Key MMC-2 */ + case 0xad: /* Read DVD Structure MMC-2 */ + case 0xb4: /* Read Element Status (Attached) SMC */ + case 0xb5: /* Request Volume Element Address SMC */ + case 0xb7: /* Read Defect Data (12 byte ver.) SBC */ + case 0xb8: /* Read Element Status (Independ.) SMC */ + case 0xba: /* Report Redundancy SCC-2 */ + case 0xbd: /* Mechanism Status MMC */ + case 0xbe: /* Report Basic Redundancy SCC-2 */ + + return (HOST_OK); + break; + + default: + return (HOST_DO_DU); + break; + } +} + +/* + * tul_next_state - prcess the current SCB as requested in it's + * SCB_NxtStat member. + */ +static int +tul_next_state(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + if (sc->HCS_ActScb == NULL) + return (-1); + + switch (sc->HCS_ActScb->SCB_NxtStat) { + case 1: + if (tul_state_1(sc, iot, ioh) == 3) + goto state_3; + break; + + case 2: + switch (tul_state_2(sc, iot, ioh)) { + case 3: goto state_3; + case 4: goto state_4; + default: break; + } + break; + + case 3: + state_3: + if (tul_state_3(sc, iot, ioh) == 4) + goto state_4; + break; + + case 4: + state_4: + switch (tul_state_4(sc, iot, ioh)) { + case 0: return (0); + case 6: goto state_6; + default: break; + } + break; + + case 5: + switch (tul_state_5(sc, iot, ioh)) { + case 4: goto state_4; + case 6: goto state_6; + default: break; + } + break; + + case 6: + state_6: + tul_state_6(sc, iot, ioh); + break; + + case 8: + tul_state_8(sc, iot, ioh); + break; + + default: +#ifdef IHA_DEBUG_STATE + printf("[debug] -unknown state: %i-\n", + sc->HCS_ActScb->SCB_NxtStat); +#endif + tul_bad_seq(sc); + break; + } + + return (-1); +} + +/* + * tul_state_1 - selection is complete after a SELATNSTOP. If the target + * has put the bus into MSG_OUT phase start wide/sync + * negotiation. Otherwise clear the FIFO and go to state 3, + * which will send the SCSI CDB to the target. + */ +static int +tul_state_1(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb = sc->HCS_ActScb; + struct tcs *pTcs; + u_int16_t flags; + + tul_mark_busy_scb(pScb); + + pTcs = pScb->SCB_Tcs; + + bus_space_write_1(iot, ioh, TUL_SCONFIG0, pTcs->TCS_SConfig0); + + /* + * If we are in PHASE_MSG_OUT, send + * a) IDENT message (with tags if appropriate) + * b) WDTR if the target is configured to negotiate wide xfers + * ** OR ** + * c) SDTR if the target is configured to negotiate sync xfers + * but not wide ones + * + * If we are NOT, then the target is not asking for anything but + * the data/command, so go straight to state 3. + */ + if (sc->HCS_Phase == PHASE_MSG_OUT) { + bus_space_write_1(iot, ioh, TUL_SCTRL1, (ESBUSIN | EHRSL)); + bus_space_write_1(iot, ioh, TUL_SFIFO, pScb->SCB_Ident); + + if (pScb->SCB_TagMsg != 0) { + bus_space_write_1(iot, ioh, TUL_SFIFO, + pScb->SCB_TagMsg); + bus_space_write_1(iot, ioh, TUL_SFIFO, + pScb->SCB_TagId); + } + + flags = pTcs->TCS_Flags; + if ((flags & FLAG_NO_NEG_WIDE) == 0) { + if (tul_msgout_wide(sc, iot, ioh) == -1) + return (-1); + } else if ((flags & FLAG_NO_NEG_SYNC) == 0) { + if (tul_msgout_sync(sc, iot, ioh) == -1) + return (-1); + } + + } else { + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + tul_set_ssig(iot, ioh, REQ | BSY | SEL | ATN, 0); + } + + return (3); +} + +/* + * tul_state_2 - selection is complete after a SEL_ATN or SEL_ATN3. If the SCSI + * CDB has already been send, go to state 4 to start the data + * xfer. Otherwise reset the FIFO and go to state 3, sending + * the SCSI CDB. + */ +static int +tul_state_2(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb = sc->HCS_ActScb; + + tul_mark_busy_scb(pScb); + + bus_space_write_1(iot, ioh, TUL_SCONFIG0, pScb->SCB_Tcs->TCS_SConfig0); + + if ((sc->HCS_JSStatus1 & CPDNE) != 0) + return (4); + + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + + tul_set_ssig(iot, ioh, REQ | BSY | SEL | ATN, 0); + + return (3); +} + +/* + * tul_state_3 - send the SCSI CDB to the target, processing any status + * or other messages received until that is done or + * abandoned. + */ +static int +tul_state_3(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb = sc->HCS_ActScb; + u_int16_t flags; + + for (;;) + switch (sc->HCS_Phase) { + case PHASE_CMD_OUT: + bus_space_write_multi_1(iot, ioh, TUL_SFIFO, + pScb->SCB_CDB, pScb->SCB_CDBLen); + if (tul_wait(sc, iot, ioh, XF_FIFO_OUT) == -1) + return (-1); + else if (sc->HCS_Phase == PHASE_CMD_OUT) { + tul_bad_seq(sc); + return (-1); + } else + return (4); + + case PHASE_MSG_IN: + pScb->SCB_NxtStat = 3; + if (tul_msgin(sc, iot, ioh) == -1) + return (-1); + break; + + case PHASE_STATUS_IN: + if (tul_status_msg(sc, iot, ioh) == -1) + return (-1); + break; + + case PHASE_MSG_OUT: + flags = pScb->SCB_Tcs->TCS_Flags; + if ((flags & FLAG_NO_NEG_SYNC) != 0) { + if (tul_msgout(sc, iot, ioh, MSG_NOP) == -1) + return (-1); + } else if (tul_msgout_sync(sc, iot, ioh) == -1) + return (-1); + break; + + default: + printf("[debug] -s3- bad phase = %d\n", sc->HCS_Phase); + tul_bad_seq(sc); + return (-1); + } +} + +/* + * tul_state_4 - start a data xfer. Handle any bus state + * transitions until PHASE_DATA_IN/_OUT + * or the attempt is abandoned. If there is + * no data to xfer, go to state 6 and finish + * processing the current SCB. + */ +static int +tul_state_4(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb = sc->HCS_ActScb; + + if ((pScb->SCB_Flags & FLAG_DIR) == FLAG_DIR) + return (6); /* Both dir flags set => NO xfer was requested */ + + for (;;) { + if (pScb->SCB_BufLen == 0) + return (6); + + switch (sc->HCS_Phase) { + case PHASE_STATUS_IN: + if ((pScb->SCB_Flags & FLAG_DIR) != 0) + pScb->SCB_HaStat = tul_data_over_run(pScb); + if ((tul_status_msg(sc, iot, ioh)) == -1) + return (-1); + break; + + case PHASE_MSG_IN: + pScb->SCB_NxtStat = 4; + if (tul_msgin(sc, iot, ioh) == -1) + return (-1); + break; + + case PHASE_MSG_OUT: + if ((sc->HCS_JSStatus0 & SPERR) != 0) { + pScb->SCB_BufLen = 0; + pScb->SCB_HaStat = HOST_SPERR; + if (tul_msgout(sc, iot, ioh, MSG_IDE) == -1) + return (-1); + else + return (6); + } else { + if (tul_msgout(sc, iot, ioh, MSG_NOP) == -1) + return (-1); + } + break; + + case PHASE_DATA_IN: + return (tul_xfer_data(pScb, iot, ioh, SCSI_DATA_IN)); + + case PHASE_DATA_OUT: + return (tul_xfer_data(pScb, iot, ioh, SCSI_DATA_OUT)); + + default: + tul_bad_seq(sc); + return (-1); + } + } +} + +/* + * tul_state_5 - handle the partial or final completion of the current + * data xfer. If DMA is still active stop it. If there is + * more data to xfer, go to state 4 and start the xfer. + * If not go to state 6 and finish the SCB. + */ +static int +tul_state_5(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb = sc->HCS_ActScb; + struct iha_sg_element *pSg; + u_int32_t cnt; + u_int16_t period; + u_int8_t stat; + long xcnt; /* cannot use unsigned!! see code: if (xcnt < 0) */ + int i; + + cnt = bus_space_read_4(iot, ioh, TUL_STCNT0) & TCNT; + + /* + * Stop any pending DMA activity and check for parity error. + */ + + if ((bus_space_read_1(iot, ioh, TUL_DCMD) & XDIR) != 0) { + /* Input Operation */ + if ((sc->HCS_JSStatus0 & SPERR) != 0) + pScb->SCB_HaStat = HOST_SPERR; + + if ((bus_space_read_1(iot, ioh, TUL_ISTUS1) & XPEND) != 0) { + bus_space_write_1(iot, ioh, TUL_DCTRL0, + bus_space_read_1(iot, ioh, TUL_DCTRL0) | SXSTP); + while (bus_space_read_1(iot, ioh, TUL_ISTUS1) & XPEND) + ; + } + + } else { + /* Output Operation */ + if ((sc->HCS_JSStatus1 & SXCMP) == 0) { + period = pScb->SCB_Tcs->TCS_JS_Period; + if ((period & PERIOD_WIDE_SCSI) != 0) + cnt += (bus_space_read_1(iot, ioh, + TUL_SFIFOCNT) & FIFOC) << 1; + else + cnt += (bus_space_read_1(iot, ioh, + TUL_SFIFOCNT) & FIFOC); + } + + if ((bus_space_read_1(iot, ioh, TUL_ISTUS1) & XPEND) != 0) { + bus_space_write_1(iot, ioh, TUL_DCMD, ABTXFR); + do + stat = bus_space_read_1(iot, ioh, TUL_ISTUS0); + while ((stat & DABT) == 0); + } + + if ((cnt == 1) && (sc->HCS_Phase == PHASE_DATA_OUT)) { + if (tul_wait(sc, iot, ioh, XF_FIFO_OUT) == -1) + return (-1); + cnt = 0; + + } else if ((sc->HCS_JSStatus1 & SXCMP) == 0) + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + } + + if (cnt == 0) { + pScb->SCB_BufLen = 0; + return (6); + } + + /* Update active data pointer and restart the I/O at the new point */ + + xcnt = pScb->SCB_BufLen - cnt; /* xcnt == bytes xferred */ + pScb->SCB_BufLen = cnt; /* cnt == bytes left */ + + if ((pScb->SCB_Flags & FLAG_SG) != 0) { + pSg = &pScb->SCB_SGList[pScb->SCB_SGIdx]; + for (i = pScb->SCB_SGIdx; i < pScb->SCB_SGMax; pSg++, i++) { + xcnt -= pSg->SG_Len; + if (xcnt < 0) { + xcnt += pSg->SG_Len; + + pSg->SG_Ptr += xcnt; + pSg->SG_Len -= xcnt; + + pScb->SCB_BufPAddr += (i - pScb->SCB_SGIdx) + * sizeof(struct iha_sg_element); + pScb->SCB_SGLen = pScb->SCB_SGMax - i; + pScb->SCB_SGIdx = i; + + return (4); + } + } + return (6); + + } else + pScb->SCB_BufPAddr += xcnt; + + return (4); +} + +/* + * tul_state_6 - finish off the active scb (may require several + * iterations if PHASE_MSG_IN) and return -1 to indicate + * the bus is free. + */ +static int +tul_state_6(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + for (;;) + switch (sc->HCS_Phase) { + case PHASE_STATUS_IN: + if (tul_status_msg(sc, iot, ioh) == -1) + return (-1); + break; + + case PHASE_MSG_IN: + sc->HCS_ActScb->SCB_NxtStat = 6; + if ((tul_msgin(sc, iot, ioh)) == -1) + return (-1); + break; + + case PHASE_MSG_OUT: + if ((tul_msgout(sc, iot, ioh, MSG_NOP)) == -1) + return (-1); + break; + + case PHASE_DATA_IN: + if (tul_xpad_in(sc, iot, ioh) == -1) + return (-1); + break; + + case PHASE_DATA_OUT: + if (tul_xpad_out(sc, iot, ioh) == -1) + return (-1); + break; + + default: + tul_bad_seq(sc); + return (-1); + } +} + +/* + * tul_state_8 - reset the active device and all busy SCBs using it + */ +static int +tul_state_8(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb; + u_int32_t i; + u_int8_t tar; + + if (sc->HCS_Phase == PHASE_MSG_OUT) { + bus_space_write_1(iot, ioh, TUL_SFIFO, MSG_DEVRST); + + pScb = sc->HCS_ActScb; + + /* This SCB finished correctly -- resetting the device */ + tul_append_done_scb(sc, pScb, HOST_OK); + + tul_reset_tcs(pScb->SCB_Tcs, sc->HCS_SConf1); + + tar = pScb->SCB_Target; + for (i = 0, pScb = sc->HCS_Scb; i < IHA_MAX_SCB; i++, pScb++) + if (pScb->SCB_Target == tar) + switch (pScb->SCB_Status) { + case STATUS_BUSY: + tul_append_done_scb(sc, + pScb, HOST_DEV_RST); + break; + + case STATUS_SELECT: + tul_push_pend_scb(sc, pScb); + break; + + default: + break; + } + + sc->HCS_Flags |= FLAG_EXPECT_DISC; + + if (tul_wait(sc, iot, ioh, XF_FIFO_OUT) == -1) + return (-1); + } + + tul_bad_seq(sc); + return (-1); +} + +/* + * tul_xfer_data - initiate the DMA xfer of the data + */ +static int +tul_xfer_data(pScb, iot, ioh, direction) + struct iha_scsi_req_q *pScb; + bus_space_tag_t iot; + bus_space_handle_t ioh; + int direction; +{ + u_int32_t xferlen; + u_int8_t xfertype; + + if ((pScb->SCB_Flags & FLAG_DIR) != direction) + return (6); /* wrong direction, abandon I/O */ + + bus_space_write_4(iot, ioh, TUL_STCNT0, pScb->SCB_BufLen); + + if ((pScb->SCB_Flags & FLAG_SG) == 0) { + xferlen = pScb->SCB_BufLen; + xfertype = (direction == SCSI_DATA_IN) ? ST_X_IN : ST_X_OUT; + + } else { + xferlen = pScb->SCB_SGLen * sizeof(struct iha_sg_element); + xfertype = (direction == SCSI_DATA_IN) ? ST_SG_IN : ST_SG_OUT; + } + + bus_space_write_4(iot, ioh, TUL_DXC, xferlen); + bus_space_write_4(iot, ioh, TUL_DXPA, pScb->SCB_BufPAddr); + bus_space_write_1(iot, ioh, TUL_DCMD, xfertype); + + bus_space_write_1(iot, ioh, TUL_SCMD, + (direction == SCSI_DATA_IN) ? XF_DMA_IN : XF_DMA_OUT); + + pScb->SCB_NxtStat = 5; + + return (0); +} + +static int +tul_xpad_in(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb = sc->HCS_ActScb; + + if ((pScb->SCB_Flags & FLAG_DIR) != 0) + pScb->SCB_HaStat = HOST_DO_DU; + + for (;;) { + if ((pScb->SCB_Tcs->TCS_JS_Period & PERIOD_WIDE_SCSI) != 0) + bus_space_write_4(iot, ioh, TUL_STCNT0, 2); + else + bus_space_write_4(iot, ioh, TUL_STCNT0, 1); + + switch (tul_wait(sc, iot, ioh, XF_FIFO_IN)) { + case -1: + return (-1); + + case PHASE_DATA_IN: + bus_space_read_1(iot, ioh, TUL_SFIFO); + break; + + default: + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + return (6); + } + } +} + +static int +tul_xpad_out(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb = sc->HCS_ActScb; + + if ((pScb->SCB_Flags & FLAG_DIR) != 0) + pScb->SCB_HaStat = HOST_DO_DU; + + for (;;) { + if ((pScb->SCB_Tcs->TCS_JS_Period & PERIOD_WIDE_SCSI) != 0) + bus_space_write_4(iot, ioh, TUL_STCNT0, 2); + else + bus_space_write_4(iot, ioh, TUL_STCNT0, 1); + + bus_space_write_1(iot, ioh, TUL_SFIFO, 0); + + switch (tul_wait(sc, iot, ioh, XF_FIFO_OUT)) { + case -1: + return (-1); + + case PHASE_DATA_OUT: + break; + + default: + /* Disable wide CPU to allow read 16 bits */ + bus_space_write_1(iot, ioh, TUL_SCTRL1, EHRSL); + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + return (6); + } + } +} + +static int +tul_status_msg(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb; + u_int8_t msg; + int phase; + + if ((phase = tul_wait(sc, iot, ioh, CMD_COMP)) == -1) + return (-1); + + pScb = sc->HCS_ActScb; + + pScb->SCB_TaStat = bus_space_read_1(iot, ioh, TUL_SFIFO); + + if (phase == PHASE_MSG_OUT) { + if ((sc->HCS_JSStatus0 & SPERR) == 0) + bus_space_write_1(iot, ioh, TUL_SFIFO, MSG_NOP); + else + bus_space_write_1(iot, ioh, TUL_SFIFO, MSG_PARITY); + + return (tul_wait(sc, iot, ioh, XF_FIFO_OUT)); + + } else if (phase == PHASE_MSG_IN) { + msg = bus_space_read_1(iot, ioh, TUL_SFIFO); + + if ((sc->HCS_JSStatus0 & SPERR) != 0) + switch (tul_wait(sc, iot, ioh, MSG_ACCEPT)) { + case -1: + return (-1); + case PHASE_MSG_OUT: + bus_space_write_1(iot, ioh, TUL_SFIFO, + MSG_PARITY); + return (tul_wait(sc, iot, ioh, XF_FIFO_OUT)); + default: + tul_bad_seq(sc); + return (-1); + } + + if (msg == MSG_COMP) { + if ((pScb->SCB_TaStat + & (SCSI_INTERM | SCSI_BUSY)) == SCSI_INTERM) { + tul_bad_seq(sc); + return (-1); + } + sc->HCS_Flags |= FLAG_EXPECT_DONE_DISC; + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + return (tul_wait(sc, iot, ioh, MSG_ACCEPT)); + } + + if ((msg == MSG_LINK_COMP) || (msg == MSG_LINK_FLAG)) { + if ((pScb->SCB_TaStat + & (SCSI_INTERM | SCSI_BUSY)) == SCSI_INTERM) + return (tul_wait(sc, iot, ioh, MSG_ACCEPT)); + } + } + + tul_bad_seq(sc); + return (-1); +} + +/* + * tul_busfree - SCSI bus free detected as a result of a TIMEOUT or + * DISCONNECT interrupt. Reset the tulip FIFO and + * SCONFIG0 and enable hardware reselect. Move any active + * SCB to HCS_DoneScb list. Return an appropriate host status + * if an I/O was active. + */ +static void +tul_busfree(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb; + + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + bus_space_write_1(iot, ioh, TUL_SCONFIG0, SCONFIG0DEFAULT); + bus_space_write_1(iot, ioh, TUL_SCTRL1, EHRSL); + + pScb = sc->HCS_ActScb; + + if (pScb != NULL) { + if (pScb->SCB_Status == STATUS_SELECT) + /* selection timeout */ + tul_append_done_scb(sc, pScb, HOST_SEL_TOUT); + else + /* Unexpected bus free */ + tul_append_done_scb(sc, pScb, HOST_BAD_PHAS); + + } +} + +static void +tul_reset_scsi_bus(sc) + struct iha_softc *sc; +{ + struct iha_scsi_req_q *pScb; + struct tcs *pTcs; + int i, s; + + s = splbio(); + + tul_reset_dma(sc->sc_iot, sc->sc_ioh); + + for (i = 0, pScb = sc->HCS_Scb; i < IHA_MAX_SCB; i++, pScb++) + switch (pScb->SCB_Status) { + case STATUS_BUSY: + tul_append_done_scb(sc, pScb, HOST_SCSI_RST); + break; + + case STATUS_SELECT: + tul_push_pend_scb(sc, pScb); + break; + + default: + break; + } + + for (i = 0, pTcs = sc->HCS_Tcs; i < IHA_MAX_TARGETS; i++, pTcs++) + tul_reset_tcs(pTcs, sc->HCS_SConf1); + + splx(s); +} + +/* + * tul_resel - handle a detected SCSI bus reselection request. + */ +static int +tul_resel(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct iha_scsi_req_q *pScb; + struct tcs *pTcs; + u_int8_t tag, target, lun, msg, abortmsg; + + if (sc->HCS_ActScb != NULL) { + if ((sc->HCS_ActScb->SCB_Status == STATUS_SELECT)) + tul_push_pend_scb(sc, sc->HCS_ActScb); /* sets ActScb to NULL */ + else + sc->HCS_ActScb = NULL; + } + + target = bus_space_read_1(iot, ioh, TUL_SBID); + lun = bus_space_read_1(iot, ioh, TUL_SALVC) & IDENT_LUN; + + pTcs = &sc->HCS_Tcs[target]; + + bus_space_write_1(iot, ioh, TUL_SCONFIG0, pTcs->TCS_SConfig0); + bus_space_write_1(iot, ioh, TUL_SYNCM, pTcs->TCS_JS_Period); + + abortmsg = MSG_ABORT; /* until a valid tag has been obtained */ + + if (pTcs->TCS_NonTagScb != NULL) + /* There is a non-tagged I/O active on the target */ + pScb = pTcs->TCS_NonTagScb; + + else { + /* + * Since there is no active non-tagged operation + * read the tag type, the tag itself, and find + * the appropriate pScb by indexing HCS_Scb with + * the tag. + */ + + switch (tul_wait(sc, iot, ioh, MSG_ACCEPT)) { + case -1: + return (-1); + case PHASE_MSG_IN: + bus_space_write_4(iot, ioh, TUL_STCNT0, 1); + if ((tul_wait(sc, iot, ioh, XF_FIFO_IN)) == -1) + return (-1); + break; + default: + goto abort; + } + + msg = bus_space_read_1(iot, ioh, TUL_SFIFO); /* Read Tag Msg */ + + if ((msg < MSG_STAG) || (msg > MSG_OTAG)) + goto abort; + + switch (tul_wait(sc, iot, ioh, MSG_ACCEPT)) { + case -1: + return (-1); + case PHASE_MSG_IN: + bus_space_write_4(iot, ioh, TUL_STCNT0, 1); + if ((tul_wait(sc, iot, ioh, XF_FIFO_IN)) == -1) + return (-1); + break; + default: + goto abort; + } + + tag = bus_space_read_1(iot, ioh, TUL_SFIFO); /* Read Tag ID */ + pScb = &sc->HCS_Scb[tag]; + + abortmsg = MSG_ABORT_TAG; /* Now that we have valdid tag! */ + } + + if ((pScb->SCB_Target != target) + || (pScb->SCB_Lun != lun) + || (pScb->SCB_Status != STATUS_BUSY)) { +abort: + tul_msgout_abort(sc, iot, ioh, abortmsg); + return (-1); + } + + sc->HCS_ActScb = pScb; + + if (tul_wait(sc, iot, ioh, MSG_ACCEPT) == -1) + return (-1); + + return(tul_next_state(sc, iot, ioh)); +} + +static int +tul_msgin(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + u_int16_t flags; + u_int8_t msg; + int phase; + + for (;;) { + if ((bus_space_read_1(iot, ioh, TUL_SFIFOCNT) & FIFOC) > 0) + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + + bus_space_write_4(iot, ioh, TUL_STCNT0, 1); + + phase = tul_wait(sc, iot, ioh, XF_FIFO_IN); + msg = bus_space_read_1(iot, ioh, TUL_SFIFO); + + switch (msg) { + case MSG_DISC: + sc->HCS_Flags |= FLAG_EXPECT_DISC; + if (tul_wait(sc, iot, ioh, MSG_ACCEPT) != -1) + tul_bad_seq(sc); + phase = -1; + break; + case MSG_SDP: + case MSG_RESTORE: + case MSG_NOP: + phase = tul_wait(sc, iot, ioh, MSG_ACCEPT); + break; + case MSG_REJ: + /* XXX - need to clear FIFO like other 'Clear ATN'?*/ + tul_set_ssig(iot, ioh, REQ | BSY | SEL | ATN, 0); + flags = sc->HCS_ActScb->SCB_Tcs->TCS_Flags; + if ((flags & FLAG_NO_NEG_SYNC) == 0) + tul_set_ssig(iot, ioh, REQ | BSY | SEL, ATN); + phase = tul_wait(sc, iot, ioh, MSG_ACCEPT); + break; + case MSG_EXTEND: + phase = tul_msgin_extend(sc, iot, ioh); + break; + case MSG_IGNOREWIDE: + phase = tul_msgin_ignore_wid_resid(sc, iot, ioh); + break; + case MSG_COMP: + sc->HCS_Flags |= FLAG_EXPECT_DONE_DISC; + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + phase = tul_wait(sc, iot, ioh, MSG_ACCEPT); + if (phase != -1) { + tul_bad_seq(sc); + return (-1); + } + break; + default: + printf("[debug] tul_msgin: bad msg type: %d\n", msg); + phase = tul_msgout_reject(sc, iot, ioh); + break; + } + + if (phase != PHASE_MSG_IN) + return (phase); + } + /* NOTREACHED */ +} + +static int +tul_msgin_ignore_wid_resid(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + int phase; + + phase = tul_wait(sc, iot, ioh, MSG_ACCEPT); + + if (phase == PHASE_MSG_IN) { + if (tul_wait(sc, iot, ioh, XF_FIFO_IN) == -1) + return (-1); + + bus_space_write_1(iot, ioh, TUL_SFIFO, 0); /* put pad */ + bus_space_read_1 (iot, ioh, TUL_SFIFO); /* get IGNORE */ + bus_space_read_1 (iot, ioh, TUL_SFIFO); /* get pad */ + + return (tul_wait(sc, iot, ioh, MSG_ACCEPT)); + } + else + return (phase); +} + +static int +tul_msgin_extend(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + u_int16_t flags; + int i, phase, msglen, msgcode; + + /* XXX - can we just stop reading and reject, or do we have to + * read all input, discarding the excess, and then reject + */ + for (i = 0; i < IHA_MAX_EXTENDED_MSG; i++) { + phase = tul_wait(sc, iot, ioh, MSG_ACCEPT); + + if (phase != PHASE_MSG_IN) + return (phase); + + bus_space_write_4(iot, ioh, TUL_STCNT0, 1); + + if (tul_wait(sc, iot, ioh, XF_FIFO_IN) == -1) + return (-1); + + sc->HCS_Msg[i] = bus_space_read_1(iot, ioh, TUL_SFIFO); + + if (sc->HCS_Msg[0] == i) + break; + } + + msglen = sc->HCS_Msg[0]; + msgcode = sc->HCS_Msg[1]; + + if ((msglen == MSG_LEN_SYNC_XFER) && (msgcode == MSG_CODE_SYNC_XFER)) { + if (tul_msgin_sync(sc) == 0) { + tul_sync_done(sc, iot, ioh); + return (tul_wait(sc, iot, ioh, MSG_ACCEPT)); + } + + tul_set_ssig(iot, ioh, REQ | BSY | SEL, ATN); + + phase = tul_wait(sc, iot, ioh, MSG_ACCEPT); + if (phase != PHASE_MSG_OUT) + return (phase); + + /* Clear FIFO for important message - final SYNC offer */ + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + + tul_sync_done(sc, iot, ioh); /* This is our final offer */ + + bus_space_write_1(iot, ioh, TUL_SFIFO, MSG_EXTEND); + bus_space_write_1(iot, ioh, TUL_SFIFO, 3); + bus_space_write_1(iot, ioh, TUL_SFIFO, 1); + bus_space_write_1(iot, ioh, TUL_SFIFO, sc->HCS_Msg[2]); + bus_space_write_1(iot, ioh, TUL_SFIFO, sc->HCS_Msg[3]); + + } else if ((msglen == MSG_LEN_WIDE_XFER) + && (msgcode == MSG_CODE_WIDE_XFER)) { + + flags = sc->HCS_ActScb->SCB_Tcs->TCS_Flags; + + if ((flags & FLAG_NO_WIDE) != 0) + sc->HCS_Msg[2] = 0; /* Offer async xfers only */ + + else if (sc->HCS_Msg[2] > 2) /* BAD MSG: 2 is max value */ + return (tul_msgout_reject(sc, iot, ioh)); + + else if (sc->HCS_Msg[2] == 2) /* a request for 32 bit xfers*/ + sc->HCS_Msg[2] = 1; /* Offer 16 instead */ + + else { + tul_wdtr_done(sc, iot, ioh); + if ((flags & FLAG_NO_NEG_SYNC) == 0) + tul_set_ssig(iot, ioh, REQ | BSY | SEL, ATN); + return (tul_wait(sc, iot, ioh, MSG_ACCEPT)); + } + + tul_set_ssig(iot, ioh, REQ | BSY | SEL, ATN); + + phase = tul_wait(sc, iot, ioh, MSG_ACCEPT); + if (phase != PHASE_MSG_OUT) + return (phase); + + /* WDTR msg out */ + bus_space_write_1(iot, ioh, TUL_SFIFO, MSG_EXTEND); + bus_space_write_1(iot, ioh, TUL_SFIFO, 2); + bus_space_write_1(iot, ioh, TUL_SFIFO, 3); + bus_space_write_1(iot, ioh, TUL_SFIFO, sc->HCS_Msg[2]); + + } else + return (tul_msgout_reject(sc, iot, ioh)); + + return (tul_wait(sc, iot, ioh, XF_FIFO_OUT)); +} + +/* + * tul_msgin_sync - check SDTR msg in HCS_Msg. If the offer is + * acceptable leave HCS_Msg as is and return 0. + * If the negotiation must continue, modify HCS_Msg + * as needed and return 1. Else return 0. + */ +static int +tul_msgin_sync(sc) + struct iha_softc *sc; +{ + u_int16_t flags; + u_int8_t default_period; + int newoffer; + + flags = sc->HCS_ActScb->SCB_Tcs->TCS_Flags; + + default_period = tul_rate_tbl[flags & FLAG_SCSI_RATE]; + + if (sc->HCS_Msg[3] == 0) /* target offered async only. Accept it. */ + return (0); + + newoffer = 0; + + if ((flags & FLAG_NO_SYNC) != 0) { + sc->HCS_Msg[3] = 0; + newoffer = 1; + } + + if (sc->HCS_Msg[3] > IHA_MAX_TARGETS-1) { + sc->HCS_Msg[3] = IHA_MAX_TARGETS-1; + newoffer = 1; + } + + if (sc->HCS_Msg[2] < default_period) { + sc->HCS_Msg[2] = default_period; + newoffer = 1; + } + + if (sc->HCS_Msg[2] >= 59) { + sc->HCS_Msg[3] = 0; + newoffer = 1; + } + + return (newoffer); +} + +static int +tul_msgout(sc, iot, ioh, msg) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int8_t msg; +{ + bus_space_write_1(iot, ioh, TUL_SFIFO, msg); + + return (tul_wait(sc, iot, ioh, XF_FIFO_OUT)); +} + +static void +tul_msgout_abort(sc, iot, ioh, aborttype) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int8_t aborttype; +{ + tul_set_ssig(iot, ioh, REQ | BSY | SEL, ATN); + + switch (tul_wait(sc, iot, ioh, MSG_ACCEPT)) { + case -1: + break; + + case PHASE_MSG_OUT: + bus_space_write_1(iot, ioh, TUL_SFIFO, aborttype ); + + sc->HCS_Flags |= FLAG_EXPECT_DISC; + + if (tul_wait(sc, iot, ioh, XF_FIFO_OUT) != -1) + tul_bad_seq(sc); + break; + + default: + tul_bad_seq(sc); + break; + } +} + +static int +tul_msgout_reject(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + int phase; + + tul_set_ssig(iot, ioh, REQ | BSY | SEL, ATN); + + if ((phase = tul_wait(sc, iot, ioh, MSG_ACCEPT)) == -1) + return (-1); + + if (phase == PHASE_MSG_OUT) { + bus_space_write_1(iot, ioh, TUL_SFIFO, MSG_REJ); + return (tul_wait(sc, iot, ioh, XF_FIFO_OUT)); + } + + return (phase); +} + +static int +tul_msgout_wide(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + int phase; + + sc->HCS_ActScb->SCB_Tcs->TCS_Flags |= FLAG_WIDE_DONE; + + bus_space_write_1(iot, ioh, TUL_SFIFO, MSG_EXTEND); + bus_space_write_1(iot, ioh, TUL_SFIFO, 2); /* Message length*/ + bus_space_write_1(iot, ioh, TUL_SFIFO, 3); /* WDTR request */ + bus_space_write_1(iot, ioh, TUL_SFIFO, 1); /* 16 bits xfer */ + + phase = tul_wait(sc, iot, ioh, XF_FIFO_OUT); + + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + tul_set_ssig(iot, ioh, REQ | BSY | SEL | ATN, 0); + + return (phase); +} + +static int +tul_msgout_sync(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + u_int16_t rateindex; + u_int8_t sync_rate; + int phase; + + rateindex = sc->HCS_ActScb->SCB_Tcs->TCS_Flags & FLAG_SCSI_RATE; + + sync_rate = tul_rate_tbl[rateindex]; + + bus_space_write_1(iot, ioh, TUL_SFIFO, MSG_EXTEND); + bus_space_write_1(iot, ioh, TUL_SFIFO, 3); /* Msg len*/ + bus_space_write_1(iot, ioh, TUL_SFIFO, 1); /* SDTR */ + bus_space_write_1(iot, ioh, TUL_SFIFO, sync_rate); + bus_space_write_1(iot, ioh, TUL_SFIFO, IHA_MAX_TARGETS-1); /* REQ/ACK*/ + + phase = tul_wait(sc, iot, ioh, XF_FIFO_OUT); + + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + tul_set_ssig(iot, ioh, REQ | BSY | SEL | ATN, 0); + + return (phase); +} + +static void +tul_wdtr_done(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct tcs *pTcs = sc->HCS_ActScb->SCB_Tcs; + + pTcs->TCS_JS_Period = 0; + + if (sc->HCS_Msg[2] != 0) + pTcs->TCS_JS_Period |= PERIOD_WIDE_SCSI; + + pTcs->TCS_SConfig0 &= ~ALTPD; + pTcs->TCS_Flags &= ~FLAG_SYNC_DONE; + pTcs->TCS_Flags |= FLAG_WIDE_DONE; + + bus_space_write_1(iot, ioh, TUL_SCONFIG0, pTcs->TCS_SConfig0); + bus_space_write_1(iot, ioh, TUL_SYNCM, pTcs->TCS_JS_Period); +} + +static void +tul_sync_done(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + struct tcs *pTcs = sc->HCS_ActScb->SCB_Tcs; + int i; + + if ((pTcs->TCS_Flags & FLAG_SYNC_DONE) == 0) { + if (sc->HCS_Msg[3] != 0) { + pTcs->TCS_JS_Period |= sc->HCS_Msg[3]; + + /* pick the highest possible rate */ + for (i = 0; i < 8; i++) + if (tul_rate_tbl[i] >= sc->HCS_Msg[2]) + break; + + pTcs->TCS_JS_Period |= (i << 4); + pTcs->TCS_SConfig0 |= ALTPD; + } + + pTcs->TCS_Flags |= FLAG_SYNC_DONE; + + bus_space_write_1(iot, ioh, TUL_SCONFIG0, pTcs->TCS_SConfig0); + bus_space_write_1(iot, ioh, TUL_SYNCM, pTcs->TCS_JS_Period); + } +} + +void +tul_reset_chip(sc, iot, ioh) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + int i; + + /* reset tulip chip */ + + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSCSI); + + do + sc->HCS_JSInt = bus_space_read_1(iot, ioh, TUL_SISTAT); + while((sc->HCS_JSInt & SRSTD) == 0); + + tul_set_ssig(iot, ioh, 0, 0); + + /* + * Stall for 2 seconds, wait for target's firmware ready. + */ + for (i = 0; i < 2000; i++) + DELAY (1000); + + bus_space_read_1(iot, ioh, TUL_SISTAT); /* Clear any active interrupt*/ +} + +static void +tul_select(sc, iot, ioh, pScb, select_type) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct iha_scsi_req_q *pScb; + u_int8_t select_type; +{ + switch (select_type) { + case SEL_ATN: + bus_space_write_1(iot, ioh, TUL_SFIFO, pScb->SCB_Ident); + bus_space_write_multi_1(iot, ioh, TUL_SFIFO, + pScb->SCB_CDB, pScb->SCB_CDBLen); + + pScb->SCB_NxtStat = 2; + break; + + case SELATNSTOP: + pScb->SCB_NxtStat = 1; + break; + + case SEL_ATN3: + bus_space_write_1(iot, ioh, TUL_SFIFO, pScb->SCB_Ident); + bus_space_write_1(iot, ioh, TUL_SFIFO, pScb->SCB_TagMsg); + bus_space_write_1(iot, ioh, TUL_SFIFO, pScb->SCB_TagId); + + bus_space_write_multi_1(iot, ioh, TUL_SFIFO, pScb->SCB_CDB, + pScb->SCB_CDBLen); + + pScb->SCB_NxtStat = 2; + break; + + default: + printf("[debug] tul_select() - unknown select type = 0x%02x\n", + select_type); + return; + } + + tul_del_pend_scb(sc, pScb); + pScb->SCB_Status = STATUS_SELECT; + + sc->HCS_ActScb = pScb; + + bus_space_write_1(iot, ioh, TUL_SCMD, select_type); +} + +/* + * tul_wait - wait for an interrupt to service or a SCSI bus phase change + * after writing the supplied command to the tulip chip. If + * the command is NO_OP, skip the command writing. + */ +static int +tul_wait(sc, iot, ioh, cmd) + struct iha_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int8_t cmd; +{ + if (cmd != NO_OP) + bus_space_write_1(iot, ioh, TUL_SCMD, cmd); + + /* + * Have to do this here, in addition to in iha_isr, because + * interrupts might be turned off when we get here. + */ + do + sc->HCS_JSStatus0 = bus_space_read_1(iot, ioh, TUL_STAT0); + while ((sc->HCS_JSStatus0 & INTPD) == 0); + + sc->HCS_JSStatus1 = bus_space_read_1(iot, ioh, TUL_STAT1); + sc->HCS_JSInt = bus_space_read_1(iot, ioh, TUL_SISTAT); + + sc->HCS_Phase = sc->HCS_JSStatus0 & PH_MASK; + + if ((sc->HCS_JSInt & SRSTD) != 0) { + /* SCSI bus reset interrupt */ + tul_reset_scsi_bus(sc); + return (-1); + } + + if ((sc->HCS_JSInt & RSELED) != 0) + /* Reselection interrupt */ + return (tul_resel(sc, iot, ioh)); + + if ((sc->HCS_JSInt & STIMEO) != 0) { + /* selected/reselected timeout interrupt */ + tul_busfree(sc, iot, ioh); + return (-1); + } + + if ((sc->HCS_JSInt & DISCD) != 0) { + /* BUS disconnection interrupt */ + if ((sc->HCS_Flags & FLAG_EXPECT_DONE_DISC) != 0) { + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + bus_space_write_1(iot, ioh, TUL_SCONFIG0, + SCONFIG0DEFAULT); + bus_space_write_1(iot, ioh, TUL_SCTRL1, EHRSL); + tul_append_done_scb(sc, sc->HCS_ActScb, HOST_OK); + sc->HCS_Flags &= ~FLAG_EXPECT_DONE_DISC; + + } else if ((sc->HCS_Flags & FLAG_EXPECT_DISC) != 0) { + bus_space_write_1(iot, ioh, TUL_SCTRL0, RSFIFO); + bus_space_write_1(iot, ioh, TUL_SCONFIG0, + SCONFIG0DEFAULT); + bus_space_write_1(iot, ioh, TUL_SCTRL1, EHRSL); + sc->HCS_ActScb = NULL; + sc->HCS_Flags &= ~FLAG_EXPECT_DISC; + + } else + tul_busfree(sc, iot, ioh); + + return (-1); + } + + return (sc->HCS_Phase); +} + +/* + * tul_done_scb - We have a scb which has been processed by the + * adaptor, now we look to see how the operation went. + */ +static void +tul_done_scb(sc, pScb) + struct iha_softc *sc; + struct iha_scsi_req_q *pScb; +{ + struct scsi_sense_data *s1, *s2; + struct scsi_xfer *xs = pScb->SCB_Xs; + + if (xs != NULL) { + xs->status = pScb->SCB_TaStat; + + switch (pScb->SCB_HaStat) { + case HOST_OK: + switch (pScb->SCB_TaStat) { + case SCSI_OK: + case SCSI_CONDITION_MET: + case SCSI_INTERM: + case SCSI_INTERM_COND_MET: + if (((pScb->SCB_Flags & SCSI_POLL) != 0) + && (pScb->SCB_CDB[0] == INQUIRY) + && (pScb->SCB_Lun == 0)) + tul_print_info(sc, pScb->SCB_Target); + + xs->resid = pScb->SCB_BufLen; + xs->error = XS_NOERROR; + break; + + case SCSI_RSERV_CONFLICT: + case SCSI_BUSY: + case SCSI_QUEUE_FULL: + xs->error = XS_BUSY; + break; + + case SCSI_CMD_TERMINATED: + case SCSI_ACA_ACTIVE: + case SCSI_CHECK: + s1 = &pScb->SCB_ScsiSenseData; + s2 = &xs->sense; + *s2 = *s1; + + xs->error = XS_SENSE; + break; + + default: + xs->error = XS_DRIVER_STUFFUP; + break; + } + break; + + case HOST_SEL_TOUT: + xs->error = XS_SELTIMEOUT; + break; + + case HOST_SCSI_RST: + case HOST_DEV_RST: + xs->error = XS_RESET; + break; + + case HOST_SPERR: + printf("%s: SCSI Parity error detected\n", + sc->sc_dev.dv_xname); + xs->error = XS_DRIVER_STUFFUP; + break; + + case HOST_TIMED_OUT: + xs->error = XS_TIMEOUT; + break; + + case HOST_DO_DU: + case HOST_BAD_PHAS: + default: + xs->error = XS_DRIVER_STUFFUP; + break; + } + + if (xs->datalen > 0) { + bus_dmamap_sync(sc->sc_dmat, pScb->SCB_Dmamap + ,(xs->flags & SCSI_DATA_IN) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, pScb->SCB_Dmamap); + } + + xs->flags |= ITSDONE; + scsi_done(xs); + } + + if (xs->flags & SCSI_RESET) + printf("[debug] tul_done_scb - finished a reset request\n"); + + tul_append_free_scb(sc, pScb); +} + +static void +tul_timeout(arg) + void *arg; +{ + struct iha_scsi_req_q *pScb = (struct iha_scsi_req_q *)arg; + struct scsi_xfer *xs = pScb->SCB_Xs; + struct iha_softc *sc; + + if (xs == NULL) + printf("[debug] tul_timeout called with xs == NULL\n"); + + else { + sc = xs->sc_link->adapter_softc; + sc_print_addr(xs->sc_link); + printf("SCSI OpCode 0x%02x timed out\n", xs->cmd->opcode); + + tul_abort_xs(xs->sc_link->adapter_softc, xs, HOST_TIMED_OUT); + } +} + +static void +tul_exec_scb(sc, pScb) + struct iha_softc *sc; + struct iha_scsi_req_q *pScb; +{ + bus_space_handle_t ioh; + bus_space_tag_t iot; + int s; + + s = splbio(); + + if (((pScb->SCB_Flags & SCSI_RESET) != 0) + || (pScb->SCB_CDB[0] == REQUEST_SENSE)) + tul_push_pend_scb(sc, pScb); /* Insert SCB at head of Pend */ + else + tul_append_pend_scb(sc, pScb); /* Append SCB to tail of Pend */ + + /* + * Run through tul_main() to ensure something is active, if + * only this new SCB. + */ + if (sc->HCS_Semaph != SEMAPH_IN_MAIN) { + iot = sc->sc_iot; + ioh = sc->sc_ioh; + + bus_space_write_1(iot, ioh, TUL_IMSK, MASK_ALL); + sc->HCS_Semaph = SEMAPH_IN_MAIN;; + + splx(s); + tul_main(sc, iot, ioh); + s = splbio(); + + sc->HCS_Semaph = ~SEMAPH_IN_MAIN;; + bus_space_write_1(iot, ioh, TUL_IMSK, (MASK_ALL & ~MSCMP)); + } + + splx(s); +} + + +/* + * tul_set_ssig - read the current scsi signal mask, then write a new + * one which turns off/on the specified signals. + */ +static void +tul_set_ssig( iot, ioh, offsigs, onsigs) + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int8_t offsigs, onsigs; +{ + u_int8_t currsigs; + + currsigs = bus_space_read_1(iot, ioh, TUL_SSIGI); + bus_space_write_1(iot, ioh, TUL_SSIGO, (currsigs & ~offsigs) | onsigs); +} + +static void +tul_print_info(sc, target) + struct iha_softc *sc; + int target; +{ + struct tcs *pTcs = &sc->HCS_Tcs[target]; + int rate; + + printf("%s: target %d ", sc->sc_dev.dv_xname, target); + + if ((pTcs->TCS_JS_Period & PERIOD_WIDE_SCSI) != 0) + printf("using 16 bit "); + else + printf("using 8 bit "); + + if ((pTcs->TCS_JS_Period & PERIOD_SYOFS) != 0) { + printf("synchronous transfers at "); + rate = (pTcs->TCS_JS_Period & PERIOD_SYXPD) >> 4; + if ((pTcs->TCS_SConfig0 & ALTPD) == 0) + rate = 100 + rate * 50; + else + rate = 50 + rate * 25; + rate = 1000000000 / rate; + printf("%d.%d MHz ", rate / 1000000 + ,(rate % 1000000 + 99999) / 100000); + } + else + printf("asynchronous transfers "); + + printf("with "); + if ((pTcs->TCS_SConfig0 & SPCHK) == 0) + printf("no "); + printf("parity\n"); +} + + +/* + * tul_alloc_scbs - allocate and map the SCB's for the supplied IHA_SOFTC + */ +static int +tul_alloc_scbs(sc) + struct iha_softc *sc; +{ + bus_dma_segment_t seg; + int error, rseg; + + /* + * Allocate dma-safe memory for the SCB's + */ + if ((error = bus_dmamem_alloc(sc->sc_dmat, + sizeof(struct iha_scsi_req_q)*IHA_MAX_SCB, + NBPG, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) + != 0) { + printf("%s: unable to allocate SCBs," + " error = %d\n", sc->sc_dev.dv_xname, error); + return (error); + } + if ((error = bus_dmamem_map(sc->sc_dmat, + &seg, rseg, sizeof(struct iha_scsi_req_q)*IHA_MAX_SCB, + (caddr_t *)&sc->HCS_Scb, BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) + != 0) { + printf("%s: unable to map SCBs, error = %d\n", + sc->sc_dev.dv_xname, error); + return (error); + } + + /* + * Create and load the DMA map used for the SCBs + */ + if ((error = bus_dmamap_create(sc->sc_dmat, + sizeof(struct iha_scsi_req_q)*IHA_MAX_SCB, + 1, sizeof(struct iha_scsi_req_q)*IHA_MAX_SCB, + 0, BUS_DMA_NOWAIT, &sc->sc_dmamap)) + != 0) { + printf("%s: unable to create control DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + return (error); + } + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, + sc->HCS_Scb, sizeof(struct iha_scsi_req_q)*IHA_MAX_SCB, + NULL, BUS_DMA_NOWAIT)) + != 0) { + printf("%s: unable to load control DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + return (error); + } + + bzero(sc->HCS_Scb, sizeof(struct iha_scsi_req_q)*IHA_MAX_SCB); + + return (0); +} + +/* + * tul_read_eeprom - read Serial EEPROM value & set to defaults + * if required. XXX - Writing does NOT work! + */ +void +tul_read_eeprom(iot, ioh) + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + u_int8_t gctrl; + + /*------Enable EEProm programming ---*/ + gctrl = bus_space_read_1(iot, ioh, TUL_GCTRL0) | EEPRG; + bus_space_write_1(iot, ioh, TUL_GCTRL0, gctrl); + + /*------ Program default pattern ----*/ + if (tul_se2_rd_all(iot, ioh) == 0) { + tul_se2_update_all(iot, ioh); + if(tul_se2_rd_all(iot, ioh) == 0) + panic("could not program iha Tulip EEPROM\n"); + } + + /*------ Disable EEProm programming ---*/ + gctrl = bus_space_read_1(iot, ioh, TUL_GCTRL0) & ~EEPRG; + bus_space_write_1(iot, ioh, TUL_GCTRL0, gctrl); +} + +/* + * tul_se2_update_all - Update SCSI H/A configuration parameters from + * serial EEPROM Setup default pattern. Only + * change those values different from the values + * in tul_nvram. + */ +void +tul_se2_update_all(iot, ioh) + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + u_int16_t *np, *np1; + u_int32_t chksum; + u_int8_t i; + + /* Enable erase/write state of EEPROM */ + tul_se2_instr(iot, ioh, ENABLE_ERASE); + bus_space_write_1(iot, ioh, TUL_NVRAM, 0); + DELAY(5); + + np = (u_int16_t *)tul_dftNvRam; + np1 = (u_int16_t *)&tul_nvram; + + for (i = 0, chksum = 0; i < 31; i++, np++, np1++) { + if (*np != *np1) + tul_se2_wr(iot, ioh, i, *np); + chksum += *np; + } + + chksum &= 0x0000ffff; + tul_se2_wr(iot, ioh, 31, chksum); + + /* Disable erase/write state of EEPROM */ + tul_se2_instr(iot, ioh, 0); + bus_space_write_1(iot, ioh, TUL_NVRAM, 0); + DELAY(5); +} + +/* + * tul_se2_wr - write the given 16 bit value into the Serial EEPROM + * at the specified offset + */ +void +tul_se2_wr(iot, ioh, addr, writeWord) + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int8_t addr; + u_int16_t writeWord; +{ + u_int8_t bit; + int i; + + /* send 'WRITE' Instruction == address | WRITE bit */ + tul_se2_instr(iot, ioh, (addr | WRITE)); + + for (i = 15; i >= 0; i--, writeWord <<= 1) { + if (writeWord & 0x8000) + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS | NVRDO); + else + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS); + DELAY(5); + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS | NVRCK); + DELAY(5); + } + + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS); + DELAY(5); + bus_space_write_1(iot, ioh, TUL_NVRAM, 0); + DELAY(5); + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS); + DELAY(5); + + for (;;) { + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS | NVRCK); + DELAY(5); + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS); + DELAY(5); + bit = bus_space_read_1(iot, ioh, TUL_NVRAM) & NVRDI; + DELAY(5); + if (bit != 0) + break; /* write complete */ + } + + bus_space_write_1(iot, ioh, TUL_NVRAM, 0); + DELAY(5); +} + +/* + * tul_se2_rd - read & return the 16 bit value at the specified + * offset in the Serial E2PROM + * + */ +u_int16_t +tul_se2_rd(iot, ioh, addr) + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int8_t addr; +{ + u_int16_t readWord; + u_int8_t bit; + int i; + + /* Send 'READ' instruction == address | READ bit */ + tul_se2_instr(iot, ioh, (addr | READ)); + + readWord = 0; + for (i = 15; i >= 0; i--) { + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS | NVRCK); + DELAY(5); + + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS); + DELAY(5); + + /* sample data after the following edge of clock */ + bit = bus_space_read_1(iot, ioh, TUL_NVRAM) & NVRDI; + DELAY(5); + + readWord += bit << i; + } + + bus_space_write_1(iot, ioh, TUL_NVRAM, 0); + DELAY(5); + + return (readWord); +} + +/* + * tul_se2_rd_all - Read SCSI H/A config parameters from serial EEPROM + * into tul_nvram static variable. + */ +int +tul_se2_rd_all(iot, ioh) + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + u_int16_t *np, *np1; + u_int32_t chksum; + u_int8_t i; + + np = (u_int16_t *)&tul_nvram; + np1 = (u_int16_t *)&tul_dftNvRam; + + for (i = 0, chksum = 0; i < 31; i++, np++, np1++) { + *np = tul_se2_rd(iot, ioh, i); + chksum += *np; + } + *np = tul_se2_rd(iot, ioh, 31); /* just read checksum */ + + chksum &= 0x0000ffff; /* checksum is lower 16 bits of sum */ + + return (tul_nvram.NVM_Signature == SIGNATURE) + && + (tul_nvram.NVM_CheckSum == chksum); +} + +/* + * tul_se2_instr - write an octet to serial E2PROM one bit at a time + */ +void +tul_se2_instr(iot, ioh, instr) + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int8_t instr; +{ + u_int8_t b; + int i; + + b = NVRCS | NVRDO; /* Write the start bit (== 1) */ + + bus_space_write_1(iot, ioh, TUL_NVRAM, b); + DELAY(5); + bus_space_write_1(iot, ioh, TUL_NVRAM, b | NVRCK); + DELAY(5); + + for (i = 0; i < 8; i++, instr <<= 1) { + if (instr & 0x80) + b = NVRCS | NVRDO; /* Write a 1 bit */ + else + b = NVRCS; /* Write a 0 bit */ + + bus_space_write_1(iot, ioh, TUL_NVRAM, b); + DELAY(5); + bus_space_write_1(iot, ioh, TUL_NVRAM, b | NVRCK); + DELAY(5); + } + + bus_space_write_1(iot, ioh, TUL_NVRAM, NVRCS); + DELAY(5); + + return; +} + +/* + * tul_reset_tcs - reset the target control structure pointed + * to by pTcs to default values. TCS_Flags + * only has the negotiation done bits reset as + * the other bits are fixed at initialization. + */ +void +tul_reset_tcs(pTcs, config0) + struct tcs *pTcs; + u_int8_t config0; +{ + pTcs->TCS_Flags &= ~(FLAG_SYNC_DONE | FLAG_WIDE_DONE); + pTcs->TCS_JS_Period = 0; + pTcs->TCS_SConfig0 = config0; + pTcs->TCS_TagCnt = 0; + pTcs->TCS_NonTagScb = NULL; +} diff --git a/sys/dev/ic/iha.h b/sys/dev/ic/iha.h new file mode 100644 index 00000000000..6ba6fe18761 --- /dev/null +++ b/sys/dev/ic/iha.h @@ -0,0 +1,495 @@ +/* $OpenBSD: iha.h,v 1.1 2001/01/23 04:19:42 krw Exp $ */ +/* + * Initio INI-9xxxU/UW SCSI Device Driver + * + * Copyright (c) 2000 Ken Westerback + * 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, + * without modification, immediately at the beginning of the file. + * 2. 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 OR HIS RELATIVES 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 MIND, 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 i91uscsi.h, provided by Initio Corporation, which probably + * came from the same people who provided i91u.c: + * + * Device driver for the INI-9XXXU/UW or INIC-940/950 PCI SCSI Controller. + * + * FreeBSD + * + * Written for 386bsd and FreeBSD by + * Winston Hung <winstonh@initio.com> + * + * Copyright (c) 1997-99 Initio Corp. All rights reserved. + * + *------------------------------------------------------------------------- + */ + +#define IHA_MAX_SG_ENTRIES 33 +#define IHA_MAX_TARGETS 16 +#define IHA_MAX_SCB 32 +#define IHA_MAX_EXTENDED_MSG 4 /* SDTR(3) and WDTR(4) only */ + +#define OFFSETOF(type, member) ((size_t)(&((type *)0)->member)) + +#define SCSI_CONDITION_MET 0x04 /* SCSI Status codes not defined */ +#define SCSI_INTERM_COND_MET 0x14 /* in scsi_all.h */ +#define SCSI_RSERV_CONFLICT 0x18 +#define SCSI_CMD_TERMINATED 0x22 +#define SCSI_ACA_ACTIVE 0x30 + +/* + * Scatter-Gather Element Structure + */ +struct iha_sg_element { + u_int32_t SG_Ptr; /* Data Pointer */ + u_int32_t SG_Len; /* Data Length */ +}; + +/* + * iha_scsi_req_q - SCSI Request structure used by the + * Tulip (aka inic-950). Note that 32 + * bit pointers and ints are assumed! + */ + +struct iha_scsi_req_q { + TAILQ_ENTRY(iha_scsi_req_q) SCB_ScbList; + + int SCB_Status; /* Current status of the SCB */ +#define STATUS_QUEUED 0 /* SCB one of Free/Done/Pend */ +#define STATUS_RENT 1 /* SCB allocated, not queued */ +#define STATUS_SELECT 2 /* SCB being selected */ +#define STATUS_BUSY 3 /* SCB I/O is active */ + u_int8_t SCB_NxtStat; /* Next state function to apply */ + u_int16_t SCB_SGIdx; /* Scatter/Gather Index */ + u_int16_t SCB_SGMax; /* Scatter/Gather # valid entries */ + int SCB_Flags; /* SCB Flags (xs->flags + private)*/ +#define FLAG_RSENS 0x00010000 /* Request Sense sent */ +#define FLAG_SG 0x00020000 /* Scatter/Gather used */ +#define FLAG_DIR (SCSI_DATA_IN | SCSI_DATA_OUT) + u_int8_t SCB_Target; /* Target Id */ + u_int8_t SCB_Lun; /* Lun */ + u_int32_t SCB_BufPAddr; /* Data Buffer Physical Addr */ + u_int32_t SCB_BufLen; /* Data Allocation Length */ + u_int8_t SCB_HaStat; /* Status of Host Adapter */ +#define HOST_OK 0x00 /* OK - operation a success */ +#define HOST_TIMED_OUT 0x01 /* Request timed out */ +#define HOST_SPERR 0x10 /* SCSI parity error */ +#define HOST_SEL_TOUT 0x11 /* Selection Timeout */ +#define HOST_DO_DU 0x12 /* Data Over/Underrun */ +#define HOST_BAD_PHAS 0x14 /* Unexpected SCSI bus phase */ +#define HOST_SCSI_RST 0x1B /* SCSI bus was reset */ +#define HOST_DEV_RST 0x1C /* Device was reset */ + u_int8_t SCB_TaStat; /* SCSI Status Byte */ + u_int8_t SCB_SGLen; /* # of valid entries in SGList */ + u_int8_t SCB_SenseLen; /* Sense Data Allocation Length */ + u_int8_t SCB_CDBLen; /* CDB Length */ + u_int8_t SCB_Ident; /* Identity */ +#define IDENT_IDENTITY 0x80 /* Must ALWAYS be set */ +#define IDENT_DISC_PRIV 0x40 /* Disconnect allowed */ +#define IDENT_LUN 0x3f /* Target LUN */ + u_int8_t SCB_TagMsg; /* Tag Message */ + u_int8_t SCB_TagId; /* Queue Tag */ + u_int8_t SCB_CDB[12]; /* SCSI Command */ + u_int32_t SCB_SGPAddr; /* SGList Physical Address */ + u_int32_t SCB_SensePAddr; /* Sense Data PhysicalAddress */ + + struct scsi_xfer *SCB_Xs; /* xs this SCB is executing */ + + /* Start of SG list */ + struct iha_sg_element SCB_SGList[IHA_MAX_SG_ENTRIES]; + + struct scsi_sense_data SCB_ScsiSenseData; + bus_dmamap_t SCB_Dmamap;/* maps xs->buf xfer buffer */ + int SCB_Timeout; /* in milliseconds */ + struct tcs *SCB_Tcs; /* tcs for SCB_Target */ +}; + +/* + * Target Device Control Structure + */ +struct tcs { + u_int16_t TCS_Flags; +#define FLAG_SCSI_RATE 0x0007 /* Index into tul_rate_tbl[] */ +#define FLAG_EN_DISC 0x0008 /* Enable disconnect */ +#define FLAG_NO_SYNC 0x0010 /* No sync data transfer */ +#define FLAG_NO_WIDE 0x0020 /* No wide data transfer */ +#define FLAG_1GIGA 0x0040 /* 255 hd/63 sec (64/32) */ +#define FLAG_SPINUP 0x0080 /* Start disk drive */ +#define FLAG_WIDE_DONE 0x0100 /* WDTR msg has been sent */ +#define FLAG_SYNC_DONE 0x0200 /* SDTR msg has been sent */ +#define FLAG_NO_NEG_SYNC (FLAG_NO_SYNC | FLAG_SYNC_DONE) +#define FLAG_NO_NEG_WIDE (FLAG_NO_WIDE | FLAG_WIDE_DONE) +#define FLAG_NO_NEGOTIATE (FLAG_NO_NEG_SYNC | FLAG_NO_NEG_WIDE) + u_int8_t TCS_JS_Period; +#define PERIOD_WIDE_SCSI 0x80 /* Enable Wide SCSI */ +#define PERIOD_SYXPD 0x70 /* Synch. SCSI Xfer rate */ +#define PERIOD_SYOFS 0x0f /* Synch. SCSI Offset */ + u_int8_t TCS_SConfig0; + u_int8_t TCS_TagCnt; + + struct iha_scsi_req_q *TCS_NonTagScb; +}; + +struct iha_softc { + struct device 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; + + struct scsi_link sc_link; + struct scsi_adapter sc_adapter; + + void *sc_ih; + + /* + * Initio specific fields + */ + u_int8_t HCS_Flags; +#define FLAG_EXPECT_DISC 0x01 +#define FLAG_EXPECT_SELECT 0x02 +#define FLAG_EXPECT_RESET 0x10 +#define FLAG_EXPECT_DONE_DISC 0x20 + u_int8_t HCS_Semaph; +#define SEMAPH_IN_MAIN 0x00 /* Already in tulip_main */ + u_int8_t HCS_Phase; /* MSG C/D I/O */ +#define PHASE_DATA_OUT 0x00 /* 0 0 0 */ +#define PHASE_DATA_IN 0x01 /* 0 0 1 */ +#define PHASE_CMD_OUT 0x02 /* 0 1 0 */ +#define PHASE_STATUS_IN 0x03 /* 0 1 1 */ +#define PHASE_MSG_OUT 0x06 /* 1 1 0 */ +#define PHASE_MSG_IN 0x07 /* 1 1 1 */ + u_int8_t HCS_JSInt; + u_int8_t HCS_JSStatus0; + u_int8_t HCS_JSStatus1; + u_int8_t HCS_SConf1; + u_int8_t HCS_Msg[IHA_MAX_EXTENDED_MSG]; /* [0] len, [1] Msg Code */ +#define MSG_LEN_SYNC_XFER 0x03 +#define MSG_CODE_SYNC_XFER 0x01 +#define MSG_LEN_WIDE_XFER 0x02 +#define MSG_CODE_WIDE_XFER 0x03 + + struct iha_scsi_req_q *HCS_Scb; /* SCB array */ + struct iha_scsi_req_q *HCS_ActScb; /* SCB using SCSI bus */ + + TAILQ_HEAD(, iha_scsi_req_q) HCS_FreeScb, HCS_PendScb, HCS_DoneScb; + + struct tcs HCS_Tcs[IHA_MAX_TARGETS]; +}; + +/* + * EEPROM for one SCSI Channel + * + */ +struct nvram_scsi { + u_int8_t NVM_SCSI_Id; /* 0x00 Channel Adapter SCSI Id */ + u_int8_t NVM_SCSI_Cfg; /* 0x01 Channel configuration */ +#define CFG_SCSI_RESET 0x0001 /* Reset bus at power up */ +#define CFG_EN_PAR 0x0002 /* SCSI parity enable */ +#define CFG_ACT_TERM1 0x0004 /* Enable active term 1 */ +#define CFG_ACT_TERM2 0x0008 /* Enable active term 2 */ +#define CFG_AUTO_TERM 0x0010 /* Enable auto terminator */ +#define CFG_EN_PWR 0x0080 /* Enable power mgmt */ +#define CFG_DEFAULT (CFG_SCSI_RESET | CFG_AUTO_TERM | CFG_EN_PAR) + u_int8_t NVM_SCSI_CfgByte2; /* 0x02 Unused Channel Cfg byte 2*/ + u_int8_t NVM_SCSI_Targets; /* 0x03 Number of SCSI targets */ + /* 0x04 Lower bytes of targ flags*/ + u_int8_t NVM_SCSI_TargetFlags[IHA_MAX_TARGETS]; +#define FLAG_DEFAULT (FLAG_NO_WIDE | FLAG_1GIGA | FLAG_EN_DISC) +}; + +/* + * Tulip (aka ini-950) Serial EEPROM Layout + * + */ +struct nvram { + /* ---------- Header ------------------------------------------------*/ + u_int16_t NVM_Signature; /* 0x00 NVRAM Signature */ +#define SIGNATURE 0xC925 + u_int8_t NVM_Size; /* 0x02 Size of data structure*/ + u_int8_t NVM_Revision; /* 0x03 Rev. of data structure*/ + + /* ---------- Host Adapter Structure --------------------------------*/ + u_int8_t NVM_ModelByte0; /* 0x04 Model number (byte 0) */ + u_int8_t NVM_ModelByte1; /* 0x05 Model number (byte 1) */ + u_int8_t NVM_ModelInfo; /* 0x06 Model information */ + u_int8_t NVM_NumOfCh; /* 0x07 Number of SCSI channel*/ + u_int8_t NVM_BIOSConfig1; /* 0x08 BIOS configuration 1 */ +#define BIOSCFG_ENABLE 0x01 /* BIOS enable */ +#define BIOSCFG_8DRIVE 0x02 /* Support > 2 drives */ +#define BIOSCFG_REMOVABLE 0x04 /* Support removable drv */ +#define BIOSCFG_INT19 0x08 /* Intercept int 19h */ +#define BIOSCFG_BIOSSCAN 0x10 /* Dynamic BIOS scan */ +#define BIOSCFG_LUNSUPPORT 0x40 /* Support LUN */ +#define BIOSCFG_DEFAULT (BIOSCFG_ENABLE) + u_int8_t NVM_BIOSConfig2; /* 0x09 BIOS configuration 2 */ + u_int8_t NVM_HAConfig1; /* 0x0a Host adapter config 1 */ +#define HACFG_BOOTIDMASK 0x0F /* Boot ID number */ +#define HACFG_LUNMASK 0x70 /* Boot LUN number */ +#define HACFG_CHANMASK 0x80 /* Boot Channel number */ + u_int8_t NVM_HAConfig2; /* 0x0b Host adapter config 2 */ + struct nvram_scsi NVM_Scsi[2]; /* 0x0c */ + u_int8_t NVM_Reserved[10]; /* 0x34 */ + + /* --------- CheckSum -----------------------------------------------*/ + u_int16_t NVM_CheckSum; /* 0x3E Checksum of NVRam */ +}; + +/* + * Tulip (aka inic-950) PCI Configuration Space Initio Specific Registers + * + * Offsets 0x00 through 0x3f are the standard PCI Configuration Header + * registers. + * + * Offsets 0x40 through 0x4f, 0x51, 0x53, 0x57, 0x5b, 0x5e and 0x5f are + * reserved registers. + * + * Registers 0x50 and 0x52 always read as 0. + * + * The register offset names and associated bit field names are taken + * from the Init-950 Data Sheet, Version 2.1, March 1997 + */ +#define TUL_GCTRL0 0x54 /* R/W Global Control 0 */ +#define EEPRG 0x04 /* Enable EEPROM Programming */ +#define TUL_GCTRL1 0x55 /* R/W Global Control 1 */ +#define ATDEN 0x01 /* Auto Termination Detect Enable */ +#define TUL_GSTAT 0x56 /* R/W Global Status - connector type */ +#define TUL_EPAD0 0x58 /* R/W External EEPROM Addr (lo byte) */ +#define TUL_EPAD1 0x59 /* R/W External EEPROM Addr (hi byte) */ +#define TUL_PNVPG 0x5A /* R/W Data port to external BIOS */ +#define TUL_EPDATA 0x5C /* R/W EEPROM Data port */ +#define TUL_NVRAM 0x5D /* R/W Non-volatile RAM port */ +#define READ 0x80 /* Read from given NVRAM addr */ +#define WRITE 0x40 /* Write to given NVRAM addr */ +#define ENABLE_ERASE 0x30 /* Enable NVRAM Erase/Write */ +#define NVRCS 0x08 /* Select external NVRAM */ +#define NVRCK 0x04 /* NVRAM Clock */ +#define NVRDO 0x02 /* NVRAM Write Data */ +#define NVRDI 0x01 /* NVRAM Read Data */ + +/* + * Tulip (aka inic-950) SCSI Registers + */ +#define TUL_STCNT0 0x80 /* R/W 24 bit SCSI Xfer Count */ +#define TCNT 0x00ffffff /* SCSI Xfer Transfer Count */ +#define TUL_SFIFOCNT 0x83 /* R/W 5 bit FIFO counter */ +#define FIFOC 0x1f /* SCSI Offset Fifo Count */ +#define TUL_SISTAT 0x84 /* R Interrupt Register */ +#define RSELED 0x80 /* Reselected */ +#define STIMEO 0x40 /* Selected/Reselected Timeout */ +#define SBSRV 0x20 /* SCSI Bus Service */ +#define SRSTD 0x10 /* SCSI Reset Detected */ +#define DISCD 0x08 /* Disconnected Status */ +#define SELED 0x04 /* Select Interrupt */ +#define SCAMSCT 0x02 /* SCAM selected */ +#define SCMDN 0x01 /* Command Complete */ +#define TUL_SIEN 0x84 /* W Interrupt enable */ +#define ALL_INTERRUPTS 0xff +#define TUL_STAT0 0x85 /* R Status 0 */ +#define INTPD 0x80 /* Interrupt pending */ +#define SQACT 0x40 /* Sequencer active */ +#define XFCZ 0x20 /* Xfer counter zero */ +#define SFEMP 0x10 /* FIFO empty */ +#define SPERR 0x08 /* SCSI parity error */ +#define PH_MASK 0x07 /* SCSI phase mask */ +#define TUL_SCTRL0 0x85 /* W Control 0 */ +#define RSSQC 0x20 /* Reset sequence counter */ +#define RSFIFO 0x10 /* Flush FIFO */ +#define CMDAB 0x04 /* Abort command (sequence) */ +#define RSMOD 0x02 /* Reset SCSI Chip */ +#define RSCSI 0x01 /* Reset SCSI Bus */ +#define TUL_STAT1 0x86 /* R Status 1 */ +#define STRCV 0x80 /* Status received */ +#define MSGST 0x40 /* Message sent */ +#define CPDNE 0x20 /* Data phase done */ +#define DPHDN 0x10 /* Data phase done */ +#define STSNT 0x08 /* Status sent */ +#define SXCMP 0x04 /* Xfer completed */ +#define SLCMP 0x02 /* Selection completed */ +#define ARBCMP 0x01 /* Arbitration completed */ +#define TUL_SCTRL1 0x86 /* W Control 1 */ +#define ENSCAM 0x80 /* Enable SCAM */ +#define NIDARB 0x40 /* No ID for Arbitration */ +#define ENLRS 0x20 /* Low Level Reselect */ +#define PWDN 0x10 /* Power down mode */ +#define WCPU 0x08 /* Wide CPU */ +#define EHRSL 0x04 /* Enable HW reselect */ +#define ESBUSOUT 0x02 /* Enable SCSI data bus out latch */ +#define ESBUSIN 0x01 /* Enable SCSI data bus in latch */ +#define TUL_SSTATUS2 0x87 /* R Status 2 */ +#define SABRT 0x80 /* Command aborted */ +#define OSCZ 0x40 /* Offset counter zero */ +#define SFFUL 0x20 /* FIFO full */ +#define TMCZ 0x10 /* Timeout counter zero */ +#define BSYGN 0x08 /* Busy release */ +#define PHMIS 0x04 /* Phase mismatch */ +#define SBEN 0x02 /* SCSI data bus enable */ +#define SRST 0x01 /* SCSI bus reset in progress */ +#define TUL_SCONFIG0 0x87 /* W Configuration */ +#define PHLAT 0x80 /* Enable phase latch */ +#define ITMOD 0x40 /* Initiator mode */ +#define SPCHK 0x20 /* Enable SCSI parity */ +#define ADMA8 0x10 /* Alternate dma 8-bits mode */ +#define ADMAW 0x08 /* Alternate dma 16-bits mode */ +#define EDACK 0x04 /* Enable DACK in wide SCSI xfer */ +#define ALTPD 0x02 /* Alternate sync period mode */ +#define DSRST 0x01 /* Disable SCSI Reset signal */ +#define SCONFIG0DEFAULT (PHLAT | ITMOD | ALTPD | DSRST) +#define TUL_SOFSC 0x88 /* R Offset */ +#define TUL_SYNCM 0x88 /* W Sync. Xfer Period & Offset */ +#define TUL_SBID 0x89 /* R SCSI BUS ID */ +#define TUL_SID 0x89 /* W SCSI ID */ +#define TUL_SALVC 0x8A /* R FIFO Avail Cnt/Identify Msg */ +#define TUL_STIMO 0x8A /* W Sel/Resel Time Out Register */ +#define TUL_SDATI 0x8B /* R SCSI Bus contents */ +#define TUL_SDAT0 0x8B /* W SCSI Data Out */ +#define TUL_SFIFO 0x8C /* R/W FIFO */ +#define MSG_COMP 0x00 /* Command Complete */ +#define MSG_EXTEND 0x01 /* Extended Message */ +#define MSG_SDP 0x02 /* Save Data Pointer */ +#define MSG_RESTORE 0x03 /* Restore Pointers */ +#define MSG_DISC 0x04 /* Disconnect */ +#define MSG_IDE 0x05 /* Initiator Detected Error */ +#define MSG_ABORT 0x06 /* Abort */ +#define MSG_REJ 0x07 /* Message Reject */ +#define MSG_NOP 0x08 /* No Operation */ +#define MSG_PARITY 0x09 /* Message Parity Error */ +#define MSG_LINK_COMP 0x0A /* Linked Command Complete */ +#define MSG_LINK_FLAG 0x0B /* Linked Command Complete w Flag */ +#define MSG_DEVRST 0x0C /* Bus Device Reset */ +#define MSG_ABORT_TAG 0x0D /* Abort Tag */ +#define MSG_CLEAR_QUEUE 0x0E /* Clear Queue */ +#define MSG_INIT_RCVRY 0x0F /* Initiate Recovery */ +#define MSG_RLSE_RCVRY 0x10 /* Release Recovery */ +#define MSG_TERM_IO 0x11 /* Terminate I/O Process */ +#define MSG_CONT_IO 0x12 /* Continue I/O */ +#define MSG_TARG_XF_DIS 0x13 /* Target Transfer Disable */ + /* 0x14 -> 0x15 are reserved */ +#define MSG_CLEAR_ACA 0x16 /* Clear Auto Contingent Active */ + /* 0x17 -> 0x1f are reserved */ +#define MSG_STAG 0x20 /* Simple Queue Tag */ +#define MSG_HTAG 0x21 /* Head of Queue Tag */ +#define MSG_OTAG 0x22 /* Ordered Queue Tag */ +#define MSG_IGNOREWIDE 0x23 /* Ignore Wide Residue */ + /* 0x24 -> 0x7f are reserved */ + /* 0x80 -> 0xff are identify msgs */ +#define TUL_SSIGI 0x90 /* R SCSI signal in */ +#define REQ 0x80 /* REQ signal */ +#define ACK 0x40 /* ACK signal */ +#define BSY 0x20 /* BSY signal */ +#define SEL 0x10 /* SEL signal */ +#define ATN 0x08 /* ATN signal */ +#define MSG 0x04 /* MSG signal */ +#define CD 0x02 /* C/D signal */ +#define IO 0x01 /* I/O signal */ +#define TUL_SSIGO 0x90 /* W SCSI signal out */ +#define TUL_SCMD 0x91 /* R/W SCSI Command */ +#define NO_OP 0x00 /* Place Holder for tulip_wait() */ +#define SEL_NOATN 0x01 /* Select w/o ATN Sequence */ +#define XF_FIFO_OUT 0x03 /* FIFO Xfer Infomation out */ +#define MSG_ACCEPT 0x0F /* Message Accept */ +#define SEL_ATN 0x11 /* Select w ATN Sequence */ +#define SEL_ATNSTOP 0x12 /* Select w ATN & Stop Sequence */ +#define SELATNSTOP 0x1E /* Select w ATN & Stop Sequence */ +#define SEL_ATN3 0x31 /* Select w ATN3 Sequence */ +#define XF_DMA_OUT 0x43 /* DMA Xfer Infomation out */ +#define EN_RESEL 0x80 /* Enable Reselection */ +#define XF_FIFO_IN 0x83 /* FIFO Xfer Infomation in */ +#define CMD_COMP 0x84 /* Command Complete Sequence */ +#define XF_DMA_IN 0xC3 /* DMA Xfer Infomation in */ +#define TUL_STEST0 0x92 /* R/W Test0 */ +#define TUL_STEST1 0x93 /* R/W Test1 */ + +/* + * Tulip (aka inic-950) DMA Registers + */ +#define TUL_DXPA 0xC0 /* R/W DMA Xfer Physcl Addr 0-31*/ +#define TUL_DXPAE 0xC4 /* R/W DMA Xfer Physcl Addr 32-63*/ +#define TUL_DCXA 0xC8 /* R DMA Curr Xfer Physcl Addr 0-31*/ +#define TUL_DCXAE 0xCC /* R DMA Curr Xfer Physcl Addr 32-63*/ +#define TUL_DXC 0xD0 /* R/W DMA Xfer Counter */ +#define TUL_DCXC 0xD4 /* R DMA Current Xfer Counter */ +#define TUL_DCMD 0xD8 /* R/W DMA Command Register */ +#define SGXFR 0x80 /* Scatter/Gather Xfer */ +#define RSVD 0x40 /* Reserved - always reads as 0 */ +#define XDIR 0x20 /* Xfer Direction 0/1 = out/in */ +#define BMTST 0x10 /* Bus Master Test */ +#define CLFIFO 0x08 /* Clear FIFO */ +#define ABTXFR 0x04 /* Abort Xfer */ +#define FRXFR 0x02 /* Force Xfer */ +#define STRXFR 0x01 /* Start Xfer */ +#define ST_X_IN (XDIR | STRXFR) +#define ST_X_OUT ( STRXFR) +#define ST_SG_IN (SGXFR | ST_X_IN) +#define ST_SG_OUT (SGXFR | ST_X_OUT) +#define TUL_ISTUS0 0xDC /* R/W Interrupt Status Register */ +#define DGINT 0x80 /* DMA Global Interrupt */ +#define RSVRD0 0x40 /* Reserved */ +#define RSVRD1 0x20 /* Reserved */ +#define SCMP 0x10 /* SCSI Complete */ +#define PXERR 0x08 /* PCI Xfer Error */ +#define DABT 0x04 /* DMA Xfer Aborted */ +#define FXCMP 0x02 /* Forced Xfer Complete */ +#define XCMP 0x01 /* Bus Master Xfer Complete */ +#define TUL_ISTUS1 0xDD /* R DMA status Register */ +#define SCBSY 0x08 /* SCSI Busy */ +#define FFULL 0x04 /* FIFO Full */ +#define FEMPT 0x02 /* FIFO Empty */ +#define XPEND 0x01 /* Xfer pending */ +#define TUL_IMSK 0xE0 /* R/W Interrupt Mask Register */ +#define MSCMP 0x10 /* Mask SCSI Complete */ +#define MPXFER 0x08 /* Mask PCI Xfer Error */ +#define MDABT 0x04 /* Mask Bus Master Abort */ +#define MFCMP 0x02 /* Mask Force Xfer Complete */ +#define MXCMP 0x01 /* Mask Bus Master Xfer Complete */ +#define MASK_ALL (MXCMP | MFCMP | MDABT | MPXFER | MSCMP) +#define TUL_DCTRL0 0xE4 /* R/W DMA Control Register */ +#define SXSTP 0x80 /* SCSI Xfer Stop */ +#define RPMOD 0x40 /* Reset PCI Module */ +#define RSVRD2 0x20 /* SCSI Xfer Stop */ +#define PWDWN 0x10 /* Power Down */ +#define ENTM 0x08 /* Enable SCSI Terminator Low */ +#define ENTMW 0x04 /* Enable SCSI Terminator High */ +#define DISAFC 0x02 /* Disable Auto Clear */ +#define LEDCTL 0x01 /* LED Control */ +#define TUL_DCTRL1 0xE5 /* R/W DMA Control Register 1 */ +#define SDWS 0x01 /* SCSI DMA Wait State */ +#define TUL_DFIFO 0xE8 /* R/W DMA FIFO */ + +#define TUL_WCTRL 0xF7 /* ?/? Bus master wait state control */ +#define TUL_DCTRL 0xFB /* ?/? DMA delay control */ + +/* Functions used by higher SCSI layers, the kernel, or iha.c and iha_pci.c */ + +int iha_scsi_cmd __P((struct scsi_xfer *)); +int iha_intr __P((void *)); +void iha_minphys __P((struct buf *)); +int iha_init_tulip __P((struct iha_softc *)); + + + + + + + diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index ea40d9b9230..7b630a71b71 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.90 2001/01/13 19:53:50 aaron Exp $ +# $OpenBSD: files.pci,v 1.91 2001/01/23 04:19:42 krw Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -321,3 +321,7 @@ file dev/pci/if_an_pci.c an_pci device cmpci: audio, auconv, mulaw attach cmpci at pci file dev/pci/cmpci.c cmpci + +# Initio ULTRA WIDE/ULTRA2 WIDE SCSI Controllers +attach iha at pci with iha_pci +file dev/pci/iha_pci.c iha_pci diff --git a/sys/dev/pci/iha_pci.c b/sys/dev/pci/iha_pci.c new file mode 100644 index 00000000000..98365e6d306 --- /dev/null +++ b/sys/dev/pci/iha_pci.c @@ -0,0 +1,138 @@ +/* $OpenBSD: iha_pci.c,v 1.1 2001/01/23 04:19:42 krw Exp $ */ +/* + * Initio INI-9xxxU/UW SCSI Device Driver + * + * Copyright (c) 2000 Ken Westerback + * 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, + * without modification, immediately at the beginning of the file. + * 2. 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 OR HIS RELATIVES 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 MIND, 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 i91u.c, provided by Initio Corporation, which credits: + * + * Device driver for the INI-9XXXU/UW or INIC-940/950 PCI SCSI Controller. + * + * FreeBSD + * + * Written for 386bsd and FreeBSD by + * Winston Hung <winstonh@initio.com> + * + * Copyright (c) 1997-99 Initio Corp. All rights reserved. + * + *------------------------------------------------------------------------- + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcivar.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/iha.h> + +static int iha_pci_probe __P((struct device *, void *, void *)); +static void iha_pci_attach __P((struct device *, struct device *, void *)); + +struct cfattach iha_pci_ca = { + sizeof(struct iha_softc), iha_pci_probe, iha_pci_attach +}; + +static int +iha_pci_probe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INITIO) + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_INITIO_INIC950: + return (1); + } + + return (0); +} + +static void +iha_pci_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct pci_attach_args *pa = aux; + bus_space_handle_t ioh; + pci_intr_handle_t ih; + struct iha_softc *sc = (void *)self; + bus_space_tag_t iot; + const char *intrstr; + pcireg_t command; + int ioh_valid; + + command = pci_conf_read(pa->pa_pc,pa->pa_tag,PCI_COMMAND_STATUS_REG); + command |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_PARITY_ENABLE; + + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command); + + /* + * XXX - Tried memory mapping (using code from adw and ahc) + * rather that IO mapping, but it didn't work at all.. + */ + ioh_valid = pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_IO, 0, + &iot, &ioh, NULL, NULL); + + if (ioh_valid != 0) { + printf("%s: unable to map registers\n", sc->sc_dev.dv_xname); + return; + } + + sc->sc_iot = iot; + sc->sc_ioh = ioh; + sc->sc_dmat = pa->pa_dmat; + + if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + + sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO, iha_intr, sc, + sc->sc_dev.dv_xname); + + if (sc->sc_ih == NULL) { + printf(": couldn't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + } else { + if (intrstr != NULL) + printf(": %s\n", intrstr); + + if (iha_init_tulip(sc) == 0) + config_found(&sc->sc_dev, &sc->sc_link, scsiprint); + } +} |